/**
 * i18n facade.
 *
 * Bundles are NOT statically imported here — that would bundle all translations
 * into every client JS file. Instead:
 *   - Server: i18nMiddleware (src/server/middleware/i18n.ts) calls setBundles({ he, en })
 *             at Worker module load time, before any request handling.
 *   - Client: BaseLayout.astro injects window.__I18N__ as an inline script (runs
 *             synchronously before module JS). This module auto-reads it at import time.
 */

/*
 * Dynamic key conventions:
 * - ALLCAPS-suffixed keys (deal_type_COUPON, state_ACTIVE, etc.) are dynamic interpolation
 *   targets — do not flag them as unused in lint audits.
 * - group_deal.* and admin_dashboard.* are reserved for pending features
 *   (group-deal admin page, dashboard KPIs). Re-audit after Wave A+B merge.
 */

import { captureCaught } from '@/lib/observability';
import type { StringBundle } from './types';

export type Locale = 'he' | 'en';
export const DEFAULT_LOCALE: Locale = 'he';
export const LOCALES: readonly Locale[] = ['he', 'en'] as const;

// ─── Server locale resolution ─────────────────────────────────────────────────

const LOCALE_COOKIE_NAME = 'multideal_locale';

function parseCookieHeader(header: string | null, name: string): string | undefined {
  if (!header) return undefined;
  for (const part of header.split(';')) {
    const trimmed = part.trim();
    const eqIdx = trimmed.indexOf('=');
    if (eqIdx === -1) continue;
    if (trimmed.slice(0, eqIdx).trim() === name) return trimmed.slice(eqIdx + 1).trim();
  }
  return undefined;
}

function normalizeLocale(raw: string | undefined | null): Locale | undefined {
  if (!raw) return undefined;
  const lower = raw.toLowerCase().split('-')[0] ?? '';
  return LOCALES.find((l) => l === lower);
}

/**
 * Resolve the effective locale for an incoming request.
 * Precedence: `?lang=` query → `multideal_locale` cookie → default (`he`).
 *
 * Mirrors the precedence implemented by `server/middleware/locale.ts` so callers
 * outside the middleware (e.g. layouts that need a deterministic locale before
 * `Astro.locals.locale` is read) get identical results without coupling to
 * middleware internals. Accept-Language is intentionally NOT consulted — Hebrew
 * is the product default for new visitors regardless of browser language.
 */
export function readServerLocale(request: Request): Locale {
  try {
    const url = new URL(request.url);
    const queryLocale = normalizeLocale(url.searchParams.get('lang'));
    if (queryLocale) return queryLocale;
  } catch (err) {
    // Malformed Request.url — log and fall through to cookie resolution.
    captureCaught(err, { scope: 'lib.i18n.readServerLocale', severity: 'info' });
  }
  const cookieLocale = normalizeLocale(
    parseCookieHeader(request.headers.get('cookie'), LOCALE_COOKIE_NAME),
  );
  if (cookieLocale) return cookieLocale;
  return DEFAULT_LOCALE;
}

// ─── Registry ─────────────────────────────────────────────────────────────────

let _bundles: Record<Locale, StringBundle> | null = null;

/**
 * Called server-side by i18nMiddleware before any page renders.
 * Also used to unit-test with a mock bundle.
 */
export function setBundles(data: Record<Locale, StringBundle>): void {
  _bundles = data;
}

/**
 * Auto-initializes from window.__I18N__ injected by BaseLayout.astro.
 * Called at module load time — by the time any island imports this module,
 * the inline script tag has already run and populated window.__I18N__.
 */
function initFromWindow(): void {
  if (typeof window !== 'undefined') {
    const win = window as unknown as { __I18N__?: Record<Locale, StringBundle> };
    if (win.__I18N__) _bundles = win.__I18N__;
  }
}
initFromWindow();

function requireBundles(): Record<Locale, StringBundle> {
  if (!_bundles) {
    throw new Error(
      '[i18n] Bundles not initialized. i18nMiddleware must call setBundles() ' +
        'on the server, and BaseLayout.astro must inject window.__I18N__ for the client.',
    );
  }
  return _bundles;
}

// ─── Core API ─────────────────────────────────────────────────────────────────

type Namespace = keyof StringBundle;
type NamespaceKeys<N extends Namespace> = keyof StringBundle[N];

function resolveBundleString(ns: Record<string, unknown>, key: string): string | undefined {
  const direct = ns[key];
  if (typeof direct === 'string') return direct;

  if (!key.includes('.')) return undefined;

  let cur: unknown = ns;
  for (const part of key.split('.')) {
    if (cur == null || typeof cur !== 'object') return undefined;
    cur = (cur as Record<string, unknown>)[part];
  }
  return typeof cur === 'string' ? cur : undefined;
}

/**
 * Synchronous lookup. Works after setBundles() (server) or window.__I18N__ (client).
 * Supports dotted paths (e.g. `admin.form.code_label`) for nested namespace objects.
 */
export function getT(locale: Locale): StringBundle;
export function getT<N extends Namespace>(
  locale: Locale,
  namespace: N,
): (key: NamespaceKeys<N>) => string;
export function getT<N extends Namespace>(
  locale: Locale,
  namespace?: N,
): StringBundle | ((key: NamespaceKeys<N>) => string) {
  const bundle = requireBundles()[locale];
  if (!namespace) return bundle;
  const ns = bundle[namespace] as Record<string, unknown>;
  return function t(key: NamespaceKeys<N>): string {
    return resolveBundleString(ns, key as string) ?? (ns[key as string] as string);
  };
}

export function getBundle(locale: Locale): StringBundle {
  return requireBundles()[locale];
}

/**
 * Locale direction — drives `<html dir>`.
 */
const LOCALE_DIRECTION: Record<Locale, 'rtl' | 'ltr'> = {
  he: 'rtl',
  en: 'ltr',
};

export function dirForLocale(locale: Locale): 'rtl' | 'ltr' {
  return LOCALE_DIRECTION[locale];
}

export const LOCALE_CHANGE_EVENT = 'multideal:locale-change';

export function applyClientLocale(locale: Locale): void {
  if (typeof document === 'undefined' || typeof window === 'undefined') return;
  document.documentElement.lang = locale;
  document.documentElement.dir = dirForLocale(locale);
  document.cookie = `${LOCALE_COOKIE_NAME}=${locale};path=/;max-age=31536000;SameSite=Lax`;
  const url = new URL(window.location.href);
  if (url.searchParams.has('lang')) {
    url.searchParams.set('lang', locale);
    window.history.replaceState(window.history.state, '', url);
  }
  window.dispatchEvent(new CustomEvent(LOCALE_CHANGE_EVENT, { detail: { locale } }));
}

export type { StringBundle } from './types';

type BilingualItem = {
  nameHe?: string | null;
  nameEn?: string | null;
  titleHe?: string | null;
  titleEn?: string | null;
  labelHe?: string | null;
  labelEn?: string | null;
  bodyHe?: string | null;
  bodyEn?: string | null;
};

/**
 * Pick the locale-appropriate string from a bilingual object.
 * Replaces 16+ inline `locale === 'he' ? x.nameHe : x.nameEn` patterns.
 *
 * pickLocalized(tag, locale)           → uses nameHe/nameEn (default)
 * pickLocalized(item, locale, 'title') → uses titleHe/titleEn
 * pickLocalized(item, locale, 'label') → uses labelHe/labelEn
 * pickLocalized(item, locale, 'body')  → uses bodyHe/bodyEn
 */
export function pickLocalized<T extends BilingualItem>(
  item: T,
  locale: Locale,
  field: 'name' | 'title' | 'label' | 'body' = 'name',
): string {
  const he = item[`${field}He` as keyof BilingualItem] as string | null | undefined;
  const en = item[`${field}En` as keyof BilingualItem] as string | null | undefined;
  return (locale === 'he' ? he : en) ?? he ?? en ?? '';
}
