/**
 * Loyalty Platform Service API client — real backend only (Phase 7.0–7.6).
 * Browser → /api/loyalty BFF; SSR → direct upstream.
 */

import { getValidAccessToken, refreshSession } from "@/lib/auth";
import type {
  AuditEntry,
  Campaign,
  CampaignApplication,
  CampaignApplicationPage,
  HealthResponse,
  ListParams,
  Member,
  MembershipLifecycleEvent,
  MembershipTier,
  LoyaltyProgram,
  PointAccount,
  PointBalance,
  PointLedgerEntry,
  PointLedgerPage,
  ReferralAttribution,
  ReferralAttributionPage,
  ReferralCode,
  ReferralCodePage,
  ReferralProgram,
  Reward,
  RewardRedemption,
  RewardRedemptionPage,
  WalletAccount,
  WalletBalance,
  WalletLedgerEntry,
  WalletLedgerPage,
  WalletTransferResult,
} from "@/modules/loyalty/types";

const LOYALTY_BASE =
  typeof window !== "undefined"
    ? "/api/loyalty"
    : process.env.LOYALTY_SERVICE_URL ||
      process.env.NEXT_PUBLIC_LOYALTY_API_URL ||
      "http://localhost:8004";

export class LoyaltyApiError extends Error {
  constructor(
    public status: number,
    public code: string,
    message: string
  ) {
    super(message);
    this.name = "LoyaltyApiError";
  }
}

type RequestOptions = RequestInit & { tenantId: string; auth?: boolean };

async function request<T>(path: string, options: RequestOptions): Promise<T> {
  const { tenantId, auth = true, headers: customHeaders, ...init } = options;
  const headers = new Headers(customHeaders);
  if (!headers.has("Content-Type") && init.body) {
    headers.set("Content-Type", "application/json");
  }
  headers.set("X-Tenant-ID", tenantId);
  if (auth) {
    const token = await getValidAccessToken();
    if (token) headers.set("Authorization", `Bearer ${token}`);
  }

  const url = path.startsWith("http")
    ? path
    : `${LOYALTY_BASE.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;

  let res: Response;
  try {
    res = await fetch(url, { ...init, headers });
  } catch (err) {
    const detail = err instanceof Error ? err.message : "network_error";
    throw new Error(`اتصال به سرویس وفاداری برقرار نشد (${url}) — ${detail}`);
  }

  if (res.status === 401 && auth) {
    const refreshed = await refreshSession({ force: true });
    if (refreshed) {
      headers.set("Authorization", `Bearer ${refreshed}`);
      res = await fetch(url, { ...init, headers });
    }
  }

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new LoyaltyApiError(
      res.status,
      body?.error?.code || body?.detail || "unknown_error",
      body?.error?.message || body?.detail || res.statusText
    );
  }
  if (res.status === 204) return undefined as T;
  return res.json() as Promise<T>;
}

function qs(params?: Record<string, string | number | boolean | undefined | null>) {
  if (!params) return "";
  const sp = new URLSearchParams();
  Object.entries(params).forEach(([k, v]) => {
    if (v !== undefined && v !== null && v !== "") sp.set(k, String(v));
  });
  const s = sp.toString();
  return s ? `?${s}` : "";
}

function apiPath(segment: string) {
  return `/api/v1${segment.startsWith("/") ? segment : `/${segment}`}`;
}

const ZERO_UUID = "00000000-0000-0000-0000-000000000000";

export type LedgerListParams = {
  offset?: number;
  limit?: number;
  entry_type?: string;
  q?: string;
};

export type WalletLedgerListParams = {
  offset?: number;
  limit?: number;
  entry_type?: string;
};

export const loyaltyApi = {
  health: () =>
    request<HealthResponse>("/health", { tenantId: ZERO_UUID, auth: false }),

  programs: {
    list: (tenantId: string, params?: ListParams) =>
      request<LoyaltyProgram[]>(`${apiPath("/programs")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<LoyaltyProgram>(apiPath(`/programs/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<LoyaltyProgram>(apiPath("/programs"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<LoyaltyProgram>(apiPath(`/programs/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<LoyaltyProgram>(apiPath(`/programs/${id}/delete`), {
        tenantId,
        method: "POST",
      }),
  },

  tiers: {
    list: (tenantId: string, params?: ListParams) =>
      request<MembershipTier[]>(`${apiPath("/tiers")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<MembershipTier>(apiPath(`/tiers/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<MembershipTier>(apiPath("/tiers"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<MembershipTier>(apiPath(`/tiers/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<MembershipTier>(apiPath(`/tiers/${id}/delete`), {
        tenantId,
        method: "POST",
      }),
  },

  members: {
    list: (tenantId: string, params?: ListParams) =>
      request<Member[]>(`${apiPath("/members")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<Member>(apiPath(`/members/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Member>(apiPath("/members"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    enroll: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/enroll`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    activate: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/activate`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    renew: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/renew`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    freeze: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/freeze`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    resume: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/resume`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    cancel: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/cancel`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    expire: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/expire`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    transfer: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Member>(apiPath(`/members/${id}/transfer`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    lifecycle: (tenantId: string, id: string, limit?: number) =>
      request<MembershipLifecycleEvent[]>(
        `${apiPath(`/members/${id}/lifecycle`)}${qs({ limit })}`,
        { tenantId }
      ),
    delete: (tenantId: string, id: string) =>
      request<Member>(apiPath(`/members/${id}/delete`), { tenantId, method: "POST" }),
  },

  pointAccounts: {
    list: (tenantId: string, params?: ListParams) =>
      request<PointAccount[]>(`${apiPath("/point-accounts")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<PointAccount>(apiPath(`/point-accounts/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<PointAccount>(apiPath("/point-accounts"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<PointAccount>(apiPath(`/point-accounts/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<PointAccount>(apiPath(`/point-accounts/${id}/delete`), {
        tenantId,
        method: "POST",
      }),
    balance: (tenantId: string, id: string) =>
      request<PointBalance>(apiPath(`/point-accounts/${id}/balance`), { tenantId }),
    ledger: (tenantId: string, id: string, params?: LedgerListParams) =>
      request<PointLedgerPage>(`${apiPath(`/point-accounts/${id}/ledger`)}${qs(params)}`, {
        tenantId,
      }),
    earn: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<PointLedgerEntry>(apiPath(`/point-accounts/${id}/earn`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    redeem: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<PointLedgerEntry>(apiPath(`/point-accounts/${id}/redeem`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    adjust: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<PointLedgerEntry>(apiPath(`/point-accounts/${id}/adjust`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    expirePoints: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<PointLedgerEntry>(apiPath(`/point-accounts/${id}/expire-points`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },

  rewards: {
    list: (tenantId: string, params?: ListParams) =>
      request<Reward[]>(`${apiPath("/rewards")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<Reward>(apiPath(`/rewards/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Reward>(apiPath("/rewards"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Reward>(apiPath(`/rewards/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<Reward>(apiPath(`/rewards/${id}/delete`), { tenantId, method: "POST" }),
    redeem: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<RewardRedemption>(apiPath(`/rewards/${id}/redeem`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    redemptions: (tenantId: string, id: string, params?: ListParams) =>
      request<RewardRedemptionPage>(
        `${apiPath(`/rewards/${id}/redemptions`)}${qs(params)}`,
        { tenantId }
      ),
  },

  redemptions: {
    list: (tenantId: string, params?: ListParams) =>
      request<RewardRedemptionPage>(`${apiPath("/redemptions")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<RewardRedemption>(apiPath(`/redemptions/${id}`), { tenantId }),
    fulfill: (tenantId: string, id: string) =>
      request<RewardRedemption>(apiPath(`/redemptions/${id}/fulfill`), {
        tenantId,
        method: "POST",
      }),
    cancel: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<RewardRedemption>(apiPath(`/redemptions/${id}/cancel`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },

  campaigns: {
    list: (tenantId: string, params?: ListParams) =>
      request<Campaign[]>(`${apiPath("/campaigns")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Campaign>(apiPath("/campaigns"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Campaign>(apiPath(`/campaigns/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/delete`), { tenantId, method: "POST" }),
    schedule: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/schedule`), { tenantId, method: "POST" }),
    activate: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/activate`), { tenantId, method: "POST" }),
    pause: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/pause`), { tenantId, method: "POST" }),
    resume: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/resume`), { tenantId, method: "POST" }),
    expire: (tenantId: string, id: string) =>
      request<Campaign>(apiPath(`/campaigns/${id}/expire`), { tenantId, method: "POST" }),
    rules: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Campaign>(apiPath(`/campaigns/${id}/rules`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    apply: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<CampaignApplication>(apiPath(`/campaigns/${id}/apply`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    applications: (tenantId: string, id: string, params?: ListParams) =>
      request<CampaignApplicationPage>(
        `${apiPath(`/campaigns/${id}/applications`)}${qs(params)}`,
        { tenantId }
      ),
  },

  referralPrograms: {
    list: (tenantId: string, params?: ListParams) =>
      request<ReferralProgram[]>(`${apiPath("/referral-programs")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<ReferralProgram>(apiPath(`/referral-programs/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<ReferralProgram>(apiPath("/referral-programs"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<ReferralProgram>(apiPath(`/referral-programs/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<ReferralProgram>(apiPath(`/referral-programs/${id}/delete`), {
        tenantId,
        method: "POST",
      }),
  },

  referralCodes: {
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<ReferralCode>(apiPath("/referral-codes"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    list: (tenantId: string, params?: ListParams & { referral_program_id?: string }) =>
      request<ReferralCodePage>(`${apiPath("/referral-codes")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<ReferralCode>(apiPath(`/referral-codes/${id}`), { tenantId }),
    revoke: (tenantId: string, id: string) =>
      request<ReferralCode>(apiPath(`/referral-codes/${id}/revoke`), {
        tenantId,
        method: "POST",
      }),
  },

  referrals: {
    attribute: (tenantId: string, body: Record<string, unknown>) =>
      request<ReferralAttribution>(apiPath("/referrals/attribute"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    list: (tenantId: string, params?: ListParams & { referral_program_id?: string }) =>
      request<ReferralAttributionPage>(`${apiPath("/referrals")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<ReferralAttribution>(apiPath(`/referrals/${id}`), { tenantId }),
    convert: (tenantId: string, id: string) =>
      request<ReferralAttribution>(apiPath(`/referrals/${id}/convert`), {
        tenantId,
        method: "POST",
      }),
    reward: (tenantId: string, id: string) =>
      request<ReferralAttribution>(apiPath(`/referrals/${id}/reward`), {
        tenantId,
        method: "POST",
      }),
    cancel: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<ReferralAttribution>(apiPath(`/referrals/${id}/cancel`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },

  wallets: {
    list: (tenantId: string, params?: ListParams) =>
      request<WalletAccount[]>(`${apiPath("/wallets")}${qs(params)}`, { tenantId }),
    get: (tenantId: string, id: string) =>
      request<WalletAccount>(apiPath(`/wallets/${id}`), { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<WalletAccount>(apiPath("/wallets"), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<WalletAccount>(apiPath(`/wallets/${id}`), {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    delete: (tenantId: string, id: string) =>
      request<WalletAccount>(apiPath(`/wallets/${id}/delete`), { tenantId, method: "POST" }),
    balance: (tenantId: string, id: string) =>
      request<WalletBalance>(apiPath(`/wallets/${id}/balance`), { tenantId }),
    ledger: (tenantId: string, id: string, params?: WalletLedgerListParams) =>
      request<WalletLedgerPage>(`${apiPath(`/wallets/${id}/ledger`)}${qs(params)}`, { tenantId }),
    credit: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<WalletLedgerEntry>(apiPath(`/wallets/${id}/credit`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    debit: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<WalletLedgerEntry>(apiPath(`/wallets/${id}/debit`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    adjust: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<WalletLedgerEntry>(apiPath(`/wallets/${id}/adjust`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    transfer: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<WalletTransferResult>(apiPath(`/wallets/${id}/transfer`), {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },

  audit: {
    list: (
      tenantId: string,
      entityType: string,
      entityId: string,
      params?: { limit?: number }
    ) =>
      request<AuditEntry[]>(
        `${apiPath("/audit")}${qs({ entity_type: entityType, entity_id: entityId, ...params })}`,
        { tenantId }
      ),
  },
};

/** Fetch all pages client-side (backend has no total count on plain list endpoints). */
export async function loyaltyFetchAll<T>(
  fetchPage: (params: ListParams) => Promise<T[]>,
  pageSize = 500
): Promise<T[]> {
  const all: T[] = [];
  let page = 1;
  for (;;) {
    const batch = await fetchPage({ page, page_size: pageSize });
    all.push(...batch);
    if (batch.length < pageSize) break;
    page += 1;
    if (page > 50) break;
  }
  return all;
}
