"use client";

export type CdcIssueMatrixTranslationDictionary =
  Record<string, string>;

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") ||
  "http://localhost:3001/api/v1";

type DictionaryApiResponse = {
  data?: {
    data?: {
      translations?: Record<string, unknown>;
    };
  };
};

type TranslationRow = {
  translationKey?: unknown;
  englishText?: unknown;
  khmerText?: unknown;
  module?: unknown;
  page?: unknown;
  isActive?: unknown;
};

type TranslationListApiResponse = {
  data?: {
    data?: {
      items?: TranslationRow[];
      meta?: {
        page?: number;
        totalPages?: number;
      };
    };
  };
};

const requestCache = new Map<
  string,
  Promise<CdcIssueMatrixTranslationDictionary>
>();

function getAccessToken(): string | null {
  if (typeof window === "undefined") {
    return null;
  }

  return (
    window.localStorage.getItem("accessToken") ||
    window.localStorage.getItem("access_token") ||
    window.localStorage.getItem("token")
  );
}

function buildHeaders(): HeadersInit {
  const token = getAccessToken();

  return {
    Accept: "application/json",
    ...(token
      ? {
          Authorization: `Bearer ${token}`,
        }
      : {}),
  };
}

function normalizeDictionary(
  translations:
    | Record<string, unknown>
    | undefined,
): CdcIssueMatrixTranslationDictionary {
  const result:
    CdcIssueMatrixTranslationDictionary = {};

  if (!translations) {
    return result;
  }

  for (
    const [key, value] of
    Object.entries(translations)
  ) {
    if (
      typeof value === "string" &&
      value.trim()
    ) {
      result[key] = value.trim();
    }
  }

  return result;
}

function getRowText(
  row: TranslationRow,
  language: "en" | "kh",
): string | null {
  const value =
    language === "kh"
      ? row.khmerText
      : row.englishText;

  if (
    typeof value !== "string" ||
    !value.trim()
  ) {
    return null;
  }

  return value.trim();
}

async function fetchJson(
  url: string,
): Promise<unknown> {
  const response = await fetch(url, {
    method: "GET",
    cache: "no-store",
    credentials: "include",
    headers: buildHeaders(),
  });

  if (!response.ok) {
    throw new Error(
      `Translation request failed (${response.status}).`,
    );
  }

  return response.json();
}

async function getDictionaryEndpointLabels(
  language: "en" | "kh",
  moduleName: "cdc" | "cefp",
  page: string,
): Promise<CdcIssueMatrixTranslationDictionary> {
  const apiLanguage =
    language === "kh" ? "km" : "en";

  const params = new URLSearchParams({
    language: apiLanguage,
    module: moduleName,
    page,
  });

  const payload =
    (await fetchJson(
      `${API_BASE_URL}/translations/dictionary?${params.toString()}`,
    )) as DictionaryApiResponse;

  return normalizeDictionary(
    payload?.data?.data?.translations,
  );
}

async function getAllPageRows(
  moduleName: "cdc" | "cefp",
  page: string,
): Promise<TranslationRow[]> {
  const PAGE_SIZE = 100;

  async function getPage(
    pageNumber: number,
  ): Promise<{
    items: TranslationRow[];
    totalPages: number;
  }> {
    const params = new URLSearchParams({
      pageNumber: String(pageNumber),
      limit: String(PAGE_SIZE),
      module: moduleName,
      pageKey: page,
      isActive: "true",
    });

    const payload =
      (await fetchJson(
        `${API_BASE_URL}/translations?${params.toString()}`,
      )) as TranslationListApiResponse;

    const data = payload?.data?.data;

    return {
      items:
        Array.isArray(data?.items)
          ? data.items
          : [],
      totalPages:
        typeof data?.meta?.totalPages ===
          "number" &&
        data.meta.totalPages > 0
          ? data.meta.totalPages
          : 1,
    };
  }

  const first = await getPage(1);
  const rows = [...first.items];

  for (
    let pageNumber = 2;
    pageNumber <= first.totalPages;
    pageNumber += 1
  ) {
    const next =
      await getPage(pageNumber);

    rows.push(...next.items);
  }

  return rows;
}

async function getListEndpointLabels(
  language: "en" | "kh",
  moduleName: "cdc" | "cefp",
  page: string,
): Promise<CdcIssueMatrixTranslationDictionary> {
  const rows =
    await getAllPageRows(
      moduleName,
      page,
    );

  const result:
    CdcIssueMatrixTranslationDictionary = {};

  /*
   * The list endpoint is the final source-of-truth supplement.
   * It explicitly loads page 1..N, so newly created form labels cannot
   * disappear just because the page now contains more than 100 labels.
   */
  for (const row of rows) {
    if (row.isActive === false) {
      continue;
    }

    if (
      typeof row.translationKey !==
        "string" ||
      !row.translationKey.trim()
    ) {
      continue;
    }

    const text =
      getRowText(row, language);

    if (!text) {
      continue;
    }

    result[
      row.translationKey.trim()
    ] = text;
  }

  return result;
}

export async function getCdcIssueMatrixTranslationDictionary(
  language: "en" | "kh",
  moduleName: "cdc" | "cefp" = "cdc",
): Promise<CdcIssueMatrixTranslationDictionary> {
  const page =
    moduleName === "cefp"
      ? "cefp_issue_matrix"
      : "cdc_issue_matrix";

  const cacheKey =
    `${moduleName}:${page}:${language}`;

  const cached =
    requestCache.get(cacheKey);

  if (cached) {
    return cached;
  }

  const request = (async () => {
    let dictionary:
      CdcIssueMatrixTranslationDictionary = {};

    /*
     * 1. Use the optimized dictionary endpoint when available.
     */
    try {
      dictionary =
        await getDictionaryEndpointLabels(
          language,
          moduleName,
          page,
        );
    } catch {
      dictionary = {};
    }

    /*
     * 2. Always supplement/override from the paginated list API.
     *
     * The user's current cdc_issue_matrix page has more than 100 labels.
     * Loading every page guarantees form.* labels such as
     * form.working_group.label are included.
     */
    try {
      const allPageLabels =
        await getListEndpointLabels(
          language,
          moduleName,
          page,
        );

      dictionary = {
        ...dictionary,
        ...allPageLabels,
      };
    } catch {
      /*
       * If the list API is unavailable but dictionary API succeeded,
       * keep dictionary values. If both fail, the UI's static fallback
       * remains active.
       */
    }

    return dictionary;
  })();

  requestCache.set(
    cacheKey,
    request,
  );

  try {
    return await request;
  } finally {
    // In-flight cache only. Admin label edits must be visible on next fetch.
    requestCache.delete(
      cacheKey,
    );
  }
}
