/**
 * Translation helpers (Session 12).
 *
 * Two surfaces:
 *   - `getTranslations(locale)`   — synchronous loader used by server
 *                                    components. Returns `{ t, locale, dir }`.
 *   - `useT()` + `<LocaleProvider>` — client-side hook backed by a
 *                                      React context populated by the
 *                                      root layout.
 *
 * Translation keys use dot notation: `t('nav.home')` → 'Home'. Missing
 * keys fall back to English, then to the key itself (visible during
 * dev so the gap is obvious, harmless in prod).
 *
 * No async fetch — JSON files are bundled at build time. The bundle
 * cost is ~3 KB per locale gzipped (we ship all 10 even on en pages
 * for now; a future optimization is dynamic import per locale).
 */

import en from '@/locales/en.json';
import es from '@/locales/es.json';
import fr from '@/locales/fr.json';
import pt from '@/locales/pt.json';
import ar from '@/locales/ar.json';
import sw from '@/locales/sw.json';
import hi from '@/locales/hi.json';
import ja from '@/locales/ja.json';
import ko from '@/locales/ko.json';
import zh from '@/locales/zh.json';

import { Locale, DEFAULT_LOCALE, isLocale, LOCALE_META } from './locales';

type Dict = Record<string, unknown>;

const DICTS: Record<Locale, Dict> = {
  en: en as Dict,
  es: es as Dict,
  fr: fr as Dict,
  pt: pt as Dict,
  ar: ar as Dict,
  sw: sw as Dict,
  hi: hi as Dict,
  ja: ja as Dict,
  ko: ko as Dict,
  zh: zh as Dict,
};

function getByPath(dict: Dict, path: string): string | null {
  const parts = path.split('.');
  let cursor: unknown = dict;
  for (const part of parts) {
    if (!cursor || typeof cursor !== 'object') return null;
    cursor = (cursor as Dict)[part];
  }
  return typeof cursor === 'string' ? cursor : null;
}

export type TFunction = (key: string, vars?: Record<string, string | number>) => string;

function interpolate(template: string, vars?: Record<string, string | number>): string {
  if (!vars) return template;
  return template.replace(/\{(\w+)\}/g, (_, name) => {
    const v = vars[name];
    return v == null ? `{${name}}` : String(v);
  });
}

function makeT(locale: Locale): TFunction {
  const primary = DICTS[locale] || DICTS[DEFAULT_LOCALE];
  const fallback = DICTS[DEFAULT_LOCALE];
  return function t(key, vars) {
    const hit = getByPath(primary, key);
    if (hit !== null) return interpolate(hit, vars);
    // Fallback to English so we never render a raw key in prod just
    // because a translation is missing.
    const fallbackHit = getByPath(fallback, key);
    if (fallbackHit !== null) return interpolate(fallbackHit, vars);
    // Last resort — the key itself. Makes missing strings visible
    // during dev without crashing.
    return key;
  };
}

export interface TranslationBundle {
  locale: Locale;
  dir: 'ltr' | 'rtl';
  t: TFunction;
}

export function getTranslations(locale: string | null | undefined): TranslationBundle {
  const resolved: Locale = isLocale(locale) ? locale : DEFAULT_LOCALE;
  return {
    locale: resolved,
    dir: LOCALE_META[resolved].dir,
    t: makeT(resolved),
  };
}

/**
 * Server-component convenience — reads the locale header that the
 * middleware stamped on the request and returns a translation bundle.
 * Only safe to call in a server component (uses next/headers).
 *
 * The dynamic import keeps `next/headers` out of client bundles even
 * though this file is imported from both contexts.
 */
export async function getServerTranslations(): Promise<TranslationBundle> {
  // Inline require so the client bundle never sees `next/headers`.
  // eslint-disable-next-line @typescript-eslint/no-require-imports
  const { headers } = require('next/headers');
  const hdr = await headers();
  // The header name lives in lib/locales to keep middleware + here in sync.
  const { LOCALE_HEADER } = await import('./locales');
  return getTranslations(hdr.get(LOCALE_HEADER));
}
