import { parse } from 'yaml';
import type { Locale } from './localeCodes';
import type { LocaleYamlDoc, QuestLocaleBundle, Translations } from './types';

/** Parsed `en.yml` / `ru.yml` without bundled `quests.*.yml`. */
type MainLocaleYamlDoc = Omit<LocaleYamlDoc, 'quests'>;

import enRaw from './en.yml?raw';
import ruRaw from './ru.yml?raw';
import questsEnRaw from './quests.en.yml?raw';
import questsRuRaw from './quests.ru.yml?raw';

/** Matches backend townVisitLineSlugs (legacy town_visit.type.N → Nth slug). */
export const TOWN_VISIT_SLUG_ORDER: Record<string, readonly string[]> = {
  merchant: [
    'crates_in_shade',
    'practiced_tired_smile',
    'chalk_prices_twice',
    'rumors_bandits_carts',
    'bell_traveler_pack',
    'step_back_tally_gold',
  ],
  healer: [
    'linens_herbs_tent',
    'professional_frown_onceover',
    'slept_badly_nod',
    'tonic_steams_table',
    'blessings_salves_bandages',
    'lighter_under_canvas',
  ],
  quest_giver: [
    'scrolls_wax_desk',
    'ink_stained_map_tap',
    'busy_roads_noncommittal',
    'draft_parchment_smell',
    'squint_spine_legend',
    'promise_listen_worth_it',
  ],
  bounty_hunter: [
    'scrolls_wax_desk',
    'ink_stained_map_tap',
    'busy_roads_noncommittal',
    'draft_parchment_smell',
    'squint_spine_legend',
    'promise_listen_worth_it',
  ],
  elder: [
    'scrolls_wax_desk',
    'ink_stained_map_tap',
    'busy_roads_noncommittal',
    'draft_parchment_smell',
    'squint_spine_legend',
    'promise_listen_worth_it',
  ],
  armorer: [
    'crates_in_shade',
    'practiced_tired_smile',
    'chalk_prices_twice',
    'rumors_bandits_carts',
    'bell_traveler_pack',
    'step_back_tally_gold',
  ],
  weapon: [
    'crates_in_shade',
    'practiced_tired_smile',
    'chalk_prices_twice',
    'rumors_bandits_carts',
    'bell_traveler_pack',
    'step_back_tally_gold',
  ],
  jeweler: [
    'crates_in_shade',
    'practiced_tired_smile',
    'chalk_prices_twice',
    'rumors_bandits_carts',
    'bell_traveler_pack',
    'step_back_tally_gold',
  ],
  generic: [
    'town_noise_blanket',
    'grain_prices_argument',
    'dust_sunbeam_time',
    'strap_tighten_pretend',
    'dog_boring_sleeps',
    'breathe_ready_move_on',
  ],
};

function parseQuestBundle(raw: string, label: string): QuestLocaleBundle {
  const doc = parse(raw) as unknown;
  if (!doc || typeof doc !== 'object') {
    throw new Error(`Invalid ${label} quest YAML root`);
  }
  const out: QuestLocaleBundle = {};
  for (const [k, v] of Object.entries(doc as Record<string, unknown>)) {
    if (!v || typeof v !== 'object') {
      throw new Error(`${label} quests.${k}: expected title/description map`);
    }
    const o = v as Record<string, unknown>;
    const title = typeof o.title === 'string' ? o.title.trim() : '';
    const description = typeof o.description === 'string' ? o.description.trim() : '';
    if (!title || !description) {
      throw new Error(`${label} quests.${k}: missing title or description`);
    }
    out[k] = { title, description };
  }
  return out;
}

function assertQuestBundlesMatch(enQ: QuestLocaleBundle, ruQ: QuestLocaleBundle): void {
  const ek = Object.keys(enQ).sort();
  const rk = Object.keys(ruQ).sort();
  if (ek.length !== rk.length) {
    throw new Error(`quests: key count mismatch en=${ek.length} ru=${rk.length}`);
  }
  for (let i = 0; i < ek.length; i++) {
    if (ek[i] !== rk[i]) {
      throw new Error(`quests: sorted keys differ at ${i}: en=${ek[i]} ru=${rk[i]}`);
    }
  }
}

function assertRecordKeysMatch(name: string, a: Record<string, string>, b: Record<string, string>): void {
  const ak = Object.keys(a).sort();
  const bk = Object.keys(b).sort();
  if (ak.length !== bk.length) {
    throw new Error(`${name}: key count mismatch en=${ak.length} ru=${bk.length}`);
  }
  for (let i = 0; i < ak.length; i++) {
    if (ak[i] !== bk[i]) {
      throw new Error(`${name}: sorted keys differ at ${i}: en=${ak[i]} ru=${bk[i]}`);
    }
  }
}

function loadDoc(raw: string): MainLocaleYamlDoc {
  const doc = parse(raw) as unknown;
  if (!doc || typeof doc !== 'object') {
    throw new Error('Invalid locale YAML root');
  }
  const d = doc as Record<string, unknown>;
  const ui = d.ui as Translations | undefined;
  const adventure_log = d.adventure_log as Record<string, string> | undefined;
  const achievements = d.achievements as Record<string, string> | undefined;
  const roadside = d.roadside as Record<string, string> | undefined;
  const town_npc_visit = d.town_npc_visit as Record<string, Record<string, string>> | undefined;
  const enemy_types = d.enemy_types as Record<string, string> | undefined;
  if (!ui || !adventure_log || !achievements || !roadside || !town_npc_visit || !enemy_types) {
    throw new Error(
      'Locale YAML missing required sections (ui, adventure_log, achievements, roadside, town_npc_visit, enemy_types)',
    );
  }
  for (const [k, v] of Object.entries(enemy_types)) {
    if (typeof v !== 'string' || !v.trim()) {
      throw new Error(`Locale YAML empty enemy_types.${k}`);
    }
  }
  for (const [k, v] of Object.entries(roadside)) {
    if (typeof v !== 'string' || !v.trim()) {
      throw new Error(`Locale YAML empty roadside.${k}`);
    }
  }
  for (const npcType of Object.keys(TOWN_VISIT_SLUG_ORDER)) {
    const pack = town_npc_visit[npcType];
    if (!pack || typeof pack !== 'object') {
      throw new Error(`Locale YAML missing town_npc_visit.${npcType}`);
    }
    for (const slug of TOWN_VISIT_SLUG_ORDER[npcType]!) {
      if (typeof pack[slug] !== 'string' || !pack[slug]) {
        throw new Error(`Locale YAML missing town_npc_visit.${npcType}.${slug}`);
      }
    }
  }
  return { ui, adventure_log, achievements, roadside, town_npc_visit, enemy_types } satisfies MainLocaleYamlDoc;
}

const questsEn = parseQuestBundle(questsEnRaw, 'en');
const questsRu = parseQuestBundle(questsRuRaw, 'ru');
assertQuestBundlesMatch(questsEn, questsRu);

const mainEn = loadDoc(enRaw);
const mainRu = loadDoc(ruRaw);
export const enDoc: LocaleYamlDoc = { ...mainEn, quests: questsEn };
export const ruDoc: LocaleYamlDoc = { ...mainRu, quests: questsRu };
assertRecordKeysMatch('enemy_types', enDoc.enemy_types, ruDoc.enemy_types);
assertRecordKeysMatch('roadside', enDoc.roadside, ruDoc.roadside);

export const en: Translations = enDoc.ui;
export const ru: Translations = ruDoc.ui;

export function adventureLogTemplate(locale: Locale, code: string): string | undefined {
  const map = locale === 'ru' ? ruDoc.adventure_log : enDoc.adventure_log;
  return map[code];
}

export function achievementLogTitle(locale: Locale, achievementId: string): string {
  if (!achievementId) return '';
  const m = locale === 'ru' ? ruDoc.achievements : enDoc.achievements;
  return m[achievementId] ?? '';
}

/** quest_key from DB → title or description; falls back to server string. */
export function localizedQuestText(
  locale: Locale,
  questKey: string | undefined,
  part: 'title' | 'description',
  fallback: string,
): string {
  if (!questKey) return fallback;
  const bundle = locale === 'ru' ? ruDoc.quests : enDoc.quests;
  const row = bundle[questKey];
  if (!row) return fallback;
  const s = part === 'title' ? row.title : row.description;
  return s?.trim() ? s : fallback;
}

/** Localized display name for DB `enemies.type` slug; empty if unknown. */
export function enemyTypeLabel(locale: Locale, enemyTypeSlug: string): string {
  const slug = enemyTypeSlug?.trim() ?? '';
  if (!slug) return '';
  const map = locale === 'ru' ? ruDoc.enemy_types : enDoc.enemy_types;
  return map[slug] ?? '';
}

/** Random roadside rest line for the hero thought bubble (same pool as server adventure_log). */
export function randomRoadsideThoughtLine(locale: Locale): string {
  const map = locale === 'ru' ? ruDoc.roadside : enDoc.roadside;
  const keys = Object.keys(map);
  if (!keys.length) return '';
  const slug = keys[Math.floor(Math.random() * keys.length)]!;
  return map[slug] ?? '';
}

/** Phrase code `roadside.<slug>` → localized line. */
export function roadsidePhraseText(locale: Locale, phraseCode: string): string {
  if (!phraseCode.startsWith('roadside.')) return '';
  const slug = phraseCode.slice('roadside.'.length);
  if (!slug || /^\d+$/.test(slug)) return '';
  const map = locale === 'ru' ? ruDoc.roadside : enDoc.roadside;
  return map[slug] ?? '';
}

/** Full phrase code `town_visit.<type>.<slug>` or legacy `town_visit.<type>.<lineIdx>`. */
export function townNpcVisitPhraseText(locale: Locale, phraseCode: string): string {
  if (!phraseCode.startsWith('town_visit.')) return '';
  const rest = phraseCode.slice('town_visit.'.length);
  const lastDot = rest.lastIndexOf('.');
  if (lastDot < 0) return '';
  const codeNpcType = rest.slice(0, lastDot);
  const tail = rest.slice(lastDot + 1);
  const packAll = locale === 'ru' ? ruDoc.town_npc_visit : enDoc.town_npc_visit;
  const pack = packAll[codeNpcType] ?? packAll.generic;
  if (!pack) return '';
  const line = parseInt(tail, 10);
  if (Number.isFinite(line) && String(line) === tail) {
    const slugOrderKey = packAll[codeNpcType] != null ? codeNpcType : 'generic';
    const byType = TOWN_VISIT_SLUG_ORDER as Record<string, readonly string[] | undefined>;
    const picked = byType[slugOrderKey];
    const order: readonly string[] =
      picked !== undefined ? picked : (TOWN_VISIT_SLUG_ORDER as { generic: readonly string[] }).generic;
    const i = Math.max(0, Math.min(order.length - 1, line));
    const slug = order[i] as string;
    return pack[slug] ?? '';
  }
  return pack[tail] ?? '';
}
