/**
 * Locale configuration for the admin UI runtime.
 *
 * Locale definitions are in `./locales.ts` -- the single source of truth
 * shared by this file, lingui.config.ts and lunaria.config.ts.
 */

import { ENABLED_LOCALES, LOCALES, SOURCE_LOCALE } from "./locales.js";

export type { LocaleDefinition as SupportedLocale } from "./locales.js";

function isValidLocale(code: string): boolean {
	try {
		const locale = new Intl.Locale(code);
		return locale.baseName !== "";
	} catch {
		if (import.meta.env.DEV) {
			throw new Error(`Invalid locale code: "${code}"`);
		}
		return false;
	}
}

// Injected by the EmDash Vite integration from process.env.EMDASH_PSEUDO_LOCALE.
// Only true in dev when EMDASH_PSEUDO_LOCALE=1 is set.
declare const __EMDASH_PSEUDO_LOCALE__: boolean;

/**
 * The pseudo locale, injected into the supported list only when
 * EMDASH_PSEUDO_LOCALE=1 is set. Never available in production.
 */
const PSEUDO_LOCALE =
	typeof __EMDASH_PSEUDO_LOCALE__ !== "undefined" && __EMDASH_PSEUDO_LOCALE__
		? LOCALES.find((l) => l.code === "pseudo")
		: undefined;

/** Available locales at runtime, validated against BCP 47. */
export const SUPPORTED_LOCALES = [
	...ENABLED_LOCALES.filter((l) => isValidLocale(l.code)),
	...(PSEUDO_LOCALE ? [PSEUDO_LOCALE] : []),
];

export const SUPPORTED_LOCALE_CODES = new Set(SUPPORTED_LOCALES.map((l) => l.code));

export const DEFAULT_LOCALE = SOURCE_LOCALE.code;

/** Maps base language codes to supported locales (e.g. "pt" -> "pt-BR"). */
const BASE_LANGUAGE_MAP = new Map<string, string>();
/** Maps script codes to supported locales (e.g. "zh-Hant" -> "zh-TW", "zh-Hans" -> "zh-CN"). */
const SCRIPT_LANGUAGE_MAP = new Map<string, string>();

for (const l of SUPPORTED_LOCALES) {
	const base = l.code.split("-")[0]!.toLowerCase();
	// First match wins -- if we have both "pt" and "pt-BR", exact wins via direct lookup.
	if (!BASE_LANGUAGE_MAP.has(base)) {
		BASE_LANGUAGE_MAP.set(base, l.code);
	}

	const maximized = new Intl.Locale(l.code).maximize();
	if (maximized.script) {
		const scriptKey = `${maximized.language}-${maximized.script}`.toLowerCase();
		if (!SCRIPT_LANGUAGE_MAP.has(scriptKey)) {
			SCRIPT_LANGUAGE_MAP.set(scriptKey, l.code);
		}
	}
}

/**
 * Find the best matching supported locale for a BCP 47 tag.
 * Canonicalizes via Intl.Locale so case differences (e.g. "pt-br" vs "pt-BR")
 * don't prevent matching. Supports script codes (zh-Hant -> zh-TW, zh-Hans -> zh-CN)
 * and falls back to base language (pt-PT -> pt-BR).
 */
function matchLocale(tag: string): string | undefined {
	const trimmed = tag.trim();
	if (!trimmed) return undefined;
	let canonical: string;
	try {
		canonical = new Intl.Locale(trimmed).baseName;
	} catch {
		return undefined;
	}

	// Exact match (case-insensitive via Intl.Locale)
	if (SUPPORTED_LOCALE_CODES.has(canonical)) return canonical;

	// Try script-based matching (zh-Hant -> zh-TW, zh-Hans -> zh-CN)
	const locale = new Intl.Locale(trimmed);
	if (locale.script) {
		const scriptKey = `${locale.language}-${locale.script}`.toLowerCase();
		const scriptMatch = SCRIPT_LANGUAGE_MAP.get(scriptKey);
		if (scriptMatch) return scriptMatch;
	}

	// Fallback to base language (pt-PT -> pt-BR)
	const base = canonical.split("-")[0]!.toLowerCase();
	return BASE_LANGUAGE_MAP.get(base);
}

const LOCALE_LABELS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.label]));

/** Get a display label for a locale code, falling back to uppercase code. */
export function getLocaleLabel(code: string): string {
	return LOCALE_LABELS.get(code) ?? code.toUpperCase();
}

const LOCALE_DIRS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.dir]));

/** Get the text direction for a locale code. Defaults to "ltr" if not specified. */
export function getLocaleDir(code: string): "ltr" | "rtl" {
	return LOCALE_DIRS.get(code) ?? "ltr";
}

const LOCALE_COOKIE_RE = /(?:^|;\s*)emdash-locale=([^;]+)/;

/**
 * Resolve the admin locale from a Request.
 * Priority: emdash-locale cookie -> Accept-Language -> DEFAULT_LOCALE.
 */
export function resolveLocale(request: Request): string {
	const cookieHeader = request.headers.get("cookie") ?? "";
	const cookieMatch = cookieHeader.match(LOCALE_COOKIE_RE);
	const cookieLocale = cookieMatch?.[1]?.trim() ?? "";

	if (SUPPORTED_LOCALE_CODES.has(cookieLocale)) return cookieLocale;

	const acceptLang = request.headers.get("accept-language") ?? "";
	for (const entry of acceptLang.split(",")) {
		const tag = entry.split(";")[0]!.trim();
		const matched = matchLocale(tag);
		if (matched) return matched;
	}

	return DEFAULT_LOCALE;
}
