import type { Dictionary } from "@/lib/i18n";
import type { Locale } from "@/lib/utils";

/* Academic Terms Renaming — merges the institution's Control Panel label
   overrides into the i18n dictionary at runtime so every label, table
   header, dropdown option and print document picks the renamed terms
   without touching each page.

   Storage: one override per locale — `CLASS` (English) and `CLASS_BN`
   (Bangla). The Bangla UI shows the Bangla override; when it is absent the
   default Bangla term stays (an English rename never leaks into Bangla).

   Rules:
   - English: whole-word, case-preserving replacement of the default term
     ("Class"/"class"/"CLASS"), plural first ("All Classes" → "All Courses").
   - Bangla: substring replacement of the default term.
   - Keys in EXCLUDED_PATHS stay untouched ("Subject" of a support ticket or
     an email is not the academic subject). */

const EXCLUDED_PATHS = new Set([
  "portal.communicationPage.subjectLabel", // message/email subject line
  "portal.supportPage.subjectLabel", // support-ticket subject
  "portal.complaintsPage.subjectLabel", // complaint subject
]);

function pluralizeEn(word: string): string {
  const lower = word.toLowerCase();
  if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;
  if (/[bcdfgjklmnpqrstvwxyz]y$/.test(lower)) return `${word.slice(0, -1)}ies`;
  return `${word}s`;
}

/** keep the matched token's casing profile on the replacement */
function matchCase(matched: string, custom: string): string {
  if (matched.length > 1 && matched === matched.toUpperCase()) return custom.toUpperCase();
  if (matched[0] === matched[0].toUpperCase()) {
    return custom.charAt(0).toUpperCase() + custom.slice(1);
  }
  return custom.toLowerCase();
}

function escapeRegExp(v: string): string {
  return v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

export function applyAcademicLabels(
  dict: Dictionary,
  labels: Record<string, string>,
  locale: Locale
): Dictionary {
  const categories = (dict as unknown as { categories?: Record<string, string> }).categories ?? {};
  const replacements: { def: string; custom: string }[] = [];
  for (const cat of Object.keys(categories)) {
    const def = categories[cat];
    const custom = String(locale === "bn" ? labels[`${cat}_BN`] : labels[cat] ?? "").trim();
    if (custom && custom !== def) replacements.push({ def, custom });
  }
  if (replacements.length === 0) return dict;

  const walk = (node: unknown, path: string): unknown => {
    if (typeof node === "string") {
      if (EXCLUDED_PATHS.has(path)) return node;
      let out = node;
      for (const { def, custom } of replacements) {
        if (!out.includes(def)) continue;
        if (locale === "bn") {
          out = out.split(def).join(custom);
          continue;
        }
        const esc = escapeRegExp(def);
        out = out.replace(new RegExp(`\\b${esc}es\\b`, "gi"), (m) => matchCase(m, pluralizeEn(custom)));
        out = out.replace(new RegExp(`\\b${esc}\\b`, "gi"), (m) => matchCase(m, custom));
      }
      return out;
    }
    if (Array.isArray(node)) return node.map((x) => walk(x, path));
    if (node && typeof node === "object") {
      const out: Record<string, unknown> = {};
      for (const [k, v] of Object.entries(node as Record<string, unknown>)) {
        out[k] = walk(v, path ? `${path}.${k}` : k);
      }
      return out;
    }
    return node;
  };

  return walk(dict, "") as Dictionary;
}
