/**
 * Key-as-content i18n for the extension shell. Matches the readest-app's
 * `stubTranslation as _` convention — the English source string IS the
 * key. Import as `_` at every call site so the surface reads the same
 * as the main repo:
 *
 *     import { translate as _ } from '../lib/i18n';
 *     _('Send to Readest')
 *     _('Sent. {count} images could not be fetched.', { count: 3 })
 *
 * Translation tables live at `src/locales/<locale>.json`. The English bundle is
 * literally `{}` — fall-through to the key. Non-English bundles are
 * `{ "<english source>": "<translation>" }`.
 *
 * Why not Chrome's native `_locales/messages.json` for these strings?
 * Chrome's spec restricts message names to `[a-zA-Z0-9_@]+`, no spaces
 * or punctuation — which makes English-as-key impossible. So we keep
 * `_locales/` only for the manifest-side translations the store needs
 * (`name`, `description`, `default_title`) and use this helper for the
 * runtime UI.
 */

import { bundles as rawBundles } from '../locales/index';

type Messages = Record<string, string>;

const SENTINEL = '__STRING_NOT_TRANSLATED__';

function loadBundle(raw: unknown): Messages {
  // Drop sentinel entries so a partially-translated bundle falls back
  // to the English source via `active[source] ?? source` instead of
  // surfacing the placeholder string to users.
  const out: Messages = {};
  for (const [k, v] of Object.entries((raw ?? {}) as Record<string, unknown>)) {
    if (typeof v !== 'string' || v === SENTINEL || v === '') continue;
    out[k] = v;
  }
  return out;
}

// `src/i18n/index.ts` is generated by `pnpm i18n:extract` and lists every
// locale in `apps/readest-app/i18n-langs.json`. Run the extractor after
// adding or removing a locale to keep this map current.
const bundles: Record<string, Messages> = Object.fromEntries(
  Object.entries(rawBundles).map(([code, raw]) => [code, loadBundle(raw)]),
);
const bundleCodeByLower = new Map(Object.keys(bundles).map((code) => [code.toLowerCase(), code]));

function resolveBundleCode(uiLocale: string): string {
  const normalized = uiLocale.replace(/_/g, '-');
  const exact = bundleCodeByLower.get(normalized.toLowerCase());
  if (exact) return exact;

  const language = normalized.split('-')[0]?.toLowerCase();
  if (language) {
    const base = bundleCodeByLower.get(language);
    if (base) return base;
  }
  return 'en';
}

function pickLocale(): string {
  // `chrome` is undeclared (not just undefined) outside extension
  // contexts — use `globalThis` to read it safely from anywhere
  // (vitest, ad-hoc imports, etc).
  const c = (globalThis as { chrome?: typeof chrome }).chrome;
  const ui = c?.i18n?.getUILanguage?.() ?? 'en';
  return resolveBundleCode(ui);
}

const active: Messages = bundles[pickLocale()] ?? bundles['en'] ?? {};

/**
 * Resolve a key against the active locale bundle, falling back to the
 * key itself when no translation is registered. `vars` are interpolated
 * into `{placeholder}` slots in the source string AND in the translated
 * string, mirroring i18next's `{{var}}` semantics but with single braces
 * to match the existing readest-app conventions.
 */
export function translate(source: string, vars?: Record<string, string | number>): string {
  const translated = active[source] ?? source;
  if (!vars) return translated;
  return translated.replace(/\{(\w+)\}/g, (match, name: string) =>
    name in vars ? String(vars[name]) : match,
  );
}

/**
 * Walk a subtree and replace text on any element carrying a
 * `data-i18n` attribute. The attribute value is the English source
 * string (key-as-content), so the HTML reads naturally without a
 * separate string-key dictionary.
 */
export function localizeDom(root: ParentNode = document): void {
  for (const el of Array.from(root.querySelectorAll<HTMLElement>('[data-i18n]'))) {
    const source = el.dataset['i18n'];
    if (source) el.textContent = translate(source);
  }
  for (const el of Array.from(root.querySelectorAll<HTMLElement>('[data-i18n-title]'))) {
    const source = el.dataset['i18nTitle'];
    if (source) el.title = translate(source);
  }
}
