import { logError, logInfo } from "@/lib/logger";
import { UpdotAuthManager } from "@/lib/updot-auth";

/**
 * Read-only analytics service that aggregates member data from Updot Core
 * to power campaign / promo-code charts and user lists.
 *
 * IMPORTANT: This service is GET-only against Updot Core (production).
 * Never add POST/PATCH/DELETE calls here.
 *
 * Updot member field names assumed: signupPromoCode, country, servicingOffice.
 * If Updot does not yet expose these filters, the proxied list will be empty
 * and the aggregations will return zeros — the UI still renders gracefully.
 */
export class PromoCodeAnalyticsService {
  private static instance: PromoCodeAnalyticsService | null = null;
  private externalApiUrl: string;

  private constructor(externalApiUrl?: string) {
    this.externalApiUrl =
      externalApiUrl ??
      process.env.UPDOT_CORE_BASE_URL ??
      "https://uat.core.karmagroup.com";
  }

  static getInstance(externalApiUrl?: string) {
    if (!PromoCodeAnalyticsService.instance) {
      PromoCodeAnalyticsService.instance = new PromoCodeAnalyticsService(
        externalApiUrl,
      );
    }
    return PromoCodeAnalyticsService.instance;
  }

  private async getSession() {
    const session = await UpdotAuthManager.getInstance().getOrLogin();
    if (!session) throw new Error("Unable to obtain Updot core session.");
    return session;
  }

  private headers(session: any) {
    const cookies = [
      `admin_session=${session.admin_session}`,
      `admin_session_id=${session.admin_session_id}`,
      `__sstkn=${session.admin_session}`,
      `__ssid=${session.admin_session_id}`,
      `kc_session=${session.admin_session}`,
      `kc_session_id=${session.admin_session_id}`,
    ].join("; ");
    return { "Content-Type": "application/json", Cookie: cookies };
  }

  private debugEnabled() {
    return (
      process.env.NODE_ENV !== "production" ||
      process.env.DEBUG_UPDOT_PROMO_ANALYTICS === "1"
    );
  }


  private async safeGet(url: string, label: string): Promise<any> {
    try {
      const session = await this.getSession();
      const res = await fetch(url, {
        method: "GET",
        headers: this.headers(session),
      });
      const text = await res.text();
      if (!res.ok) {
        logError(`${label}: upstream ${res.status} ${text.slice(0, 200)}`);
        return null;
      }
      try {
        return JSON.parse(text);
      } catch {
        return text;
      }
    } catch (err) {
      logError(`${label}: error`, err);
      return null;
    }
  }

  /**
   * GET-only fetch of members attributed to a specific promo code.
   * Returns paginated raw member rows from Updot Core.
   */
  async listUsersByPromoCode(
    promoCodeId: string,
    opts: { page: number; pageSize: number; search?: string; from?: string; to?: string },
  ): Promise<{
    items: any[];
    pagination: { page: number; pageSize: number; total: number; totalPages: number };
  }> {
    const offset = (opts.page - 1) * opts.pageSize;
    const url = new URL(`${this.externalApiUrl}/v1/admin/members`);
    url.searchParams.set("signupPromoCode", promoCodeId);
    url.searchParams.set("limit", String(opts.pageSize));
    url.searchParams.set("offset", String(offset));
    if (opts.search) url.searchParams.set("searchTerm", opts.search);
    if (opts.from) url.searchParams.set("from", opts.from);
    if (opts.to) url.searchParams.set("to", opts.to);

    logInfo(`PromoCodeAnalyticsService.listUsersByPromoCode -> ${url}`);
    const parsed = await this.safeGet(url.toString(), "listUsersByPromoCode");

    const items: any[] = Array.isArray(parsed?.data)
      ? parsed.data
      : Array.isArray(parsed?.items)
        ? parsed.items
        : Array.isArray(parsed)
          ? parsed
          : [];
    const total = Number(parsed?.pagination?.total ?? parsed?.total ?? items.length);
    return {
      items,
      pagination: {
        page: opts.page,
        pageSize: opts.pageSize,
        total,
        totalPages: Math.max(1, Math.ceil(total / opts.pageSize)),
      },
    };
  }

  /**
   * Fetches ALL members attributed to the given promo codes within a date range
   * (capped for safety) and groups them client-side for chart rendering.
   */
  async aggregate(
    promoCodeIds: string[],
    opts: { from?: string; to?: string },
  ): Promise<{
    byPromoCode: Array<{ promo_code_id: string; signups: number }>;
    byPromoCodeArea: Array<{
      promo_code_id: string;
      area: string;
      signups: number;
    }>;
    byCountryPromoCode: Array<{
      country: string;
      promo_code_id: string;
      signups: number;
    }>;
    total: number;
  }> {
    if (promoCodeIds.length === 0) {
      return { byPromoCode: [], byPromoCodeArea: [], byCountryPromoCode: [], total: 0 };
    }

    const byPromoCode = new Map<string, number>();
    const byPromoCodeArea = new Map<string, number>();
    const byCountryPromoCode = new Map<string, number>();
    let total = 0;

    for (const codeId of promoCodeIds) {
      const url = new URL(`${this.externalApiUrl}/v1/admin/members`);
      url.searchParams.set("signupPromoCode", codeId);
      url.searchParams.set("limit", "1000");
      if (opts.from) url.searchParams.set("from", opts.from);
      if (opts.to) url.searchParams.set("to", opts.to);

      if (this.debugEnabled()) {
        logInfo(
          `[PromoCodeAnalyticsService.aggregate] codeId=${codeId} url=${url.toString()}`,
        );
      }

      const parsed = await this.safeGet(url.toString(), "aggregate");
      const members: any[] = Array.isArray(parsed?.data)
        ? parsed.data
        : Array.isArray(parsed?.items)
          ? parsed.items
          : Array.isArray(parsed)
            ? parsed
            : [];

      if (this.debugEnabled()) {
        const sample = members.slice(0, 3);
        logInfo(
          `[PromoCodeAnalyticsService.aggregate] upstreamKeys=${Object.keys(parsed ?? {})} membersCount=${members.length} sample=${JSON.stringify(sample).slice(0, 2000)}`,
        );
      }

      byPromoCode.set(codeId, (byPromoCode.get(codeId) ?? 0) + members.length);
      total += members.length;

      for (const m of members) {

        const area =
          (m.servicingOffice as string | undefined) ||
          (m.servicing_office as string | undefined) ||
          (m.signupArea as string | undefined) ||
          "Unknown";
        const country =
          (m.country as string | undefined) ||
          (m.nationality as string | undefined) ||
          "Unknown";
        const areaKey = `${codeId}::${area}`;
        const countryKey = `${country}::${codeId}`;
        byPromoCodeArea.set(areaKey, (byPromoCodeArea.get(areaKey) ?? 0) + 1);
        byCountryPromoCode.set(
          countryKey,
          (byCountryPromoCode.get(countryKey) ?? 0) + 1,
        );
      }
    }

    return {
      byPromoCode: Array.from(byPromoCode.entries()).map(([promo_code_id, signups]) => ({
        promo_code_id,
        signups,
      })),
      byPromoCodeArea: Array.from(byPromoCodeArea.entries()).map(([key, signups]) => {
        const [promo_code_id, area] = key.split("::");
        return { promo_code_id, area, signups };
      }),
      byCountryPromoCode: Array.from(byCountryPromoCode.entries()).map(
        ([key, signups]) => {
          const [country, promo_code_id] = key.split("::");
          return { country, promo_code_id, signups };
        },
      ),
      total,
    };
  }
}
