import type { Translations } from '../i18n/types';
import type { Locale } from '../i18n/localeCodes';
import { t } from '../i18n/index';
import {
  dialogueText,
  enemyFamilyLabel,
  npcLabel,
  townLabel,
  WANDERING_MERCHANT_DIALOGUE_KEY,
} from '../i18n/contentLabels';
import {
  adventureLogTemplate,
  achievementLogTitle,
  roadsidePhraseText,
  townNpcVisitPhraseText,
} from '../i18n/loadLocales';
import { localizedQuestText } from '../i18n/questCopy';

export interface AdventureLogEventWire {
  code: string;
  args?: Record<string, unknown>;
}

function strArg(args: Record<string, unknown> | undefined, key: string): string {
  if (!args) return '';
  const v = args[key];
  if (v == null) return '';
  return String(v);
}

function numArg(args: Record<string, unknown> | undefined, key: string): number {
  if (!args) return 0;
  const v = args[key];
  if (typeof v === 'number') return v;
  if (typeof v === 'string') {
    const n = Number(v);
    return Number.isFinite(n) ? n : 0;
  }
  return 0;
}

function intArg(args: Record<string, unknown> | undefined, key: string): number {
  return Math.floor(numArg(args, key));
}

function boolArg(args: Record<string, unknown> | undefined, key: string): boolean {
  if (!args) return false;
  const v = args[key];
  if (typeof v === 'boolean') return v;
  if (v === 'true') return true;
  return false;
}

function debuffName(tr: Translations, raw: string): string {
  const m: Record<string, keyof Translations> = {
    poison: 'debuffPoison',
    freeze: 'debuffFreeze',
    burn: 'debuffBurn',
    stun: 'debuffStun',
    slow: 'debuffSlow',
    weaken: 'debuffWeaken',
    ice_slow: 'debuffIceSlow',
  };
  const k = m[raw.toLowerCase()];
  return k ? tr[k] : raw;
}

function buffName(tr: Translations, raw: string): string {
  const m: Record<string, keyof Translations> = {
    rush: 'buffRush',
    rage: 'buffRage',
    shield: 'buffShield',
    luck: 'buffLuck',
    resurrection: 'buffResurrection',
    heal: 'buffHeal',
    power_potion: 'buffPowerPotion',
    war_cry: 'buffWarCry',
  };
  const k = m[raw.toLowerCase()];
  return k ? tr[k] : raw;
}

function slotName(tr: Translations, raw: string): string {
  const m: Record<string, keyof Translations> = {
    main_hand: 'slotWeapon',
    off_hand: 'slotOffHand',
    head: 'slotHead',
    chest: 'slotChest',
    legs: 'slotLegs',
    feet: 'slotFeet',
    cloak: 'slotCloak',
    neck: 'slotNeck',
    finger: 'slotRing',
    wrist: 'slotWrist',
    hands: 'slotHands',
    quiver: 'slotQuiver',
  };
  const k = m[raw.toLowerCase()];
  return k ? tr[k] : raw;
}

function rarityName(tr: Translations, raw: string): string {
  const m: Record<string, keyof Translations> = {
    common: 'common',
    uncommon: 'uncommon',
    rare: 'rare',
    epic: 'epic',
    legendary: 'legendary',
  };
  const k = m[raw.toLowerCase()];
  return k ? tr[k] : raw;
}

function subscriptionDuration(locale: Locale, key: string): string {
  if (key === 'subscription.week') {
    return locale === 'ru' ? 'неделю подписки' : 'one week of subscription';
  }
  return key;
}

/** Map legacy DB/WS semantic codes to phrase keys (or dynamic roadside/town). */
const LEGACY_SEMANTIC_TO_PHRASE: Record<string, string> = {
  defeated_enemy: 'log.defeated_enemy',
  leveled_up: 'log.leveled_up',
  equipped_new: 'log.equipped_new',
  inventory_full_dropped: 'log.inventory_full_dropped',
  buff_activated: 'log.buff_activated',
  hero_revived: 'log.hero_revived',
  wandering_merchant_encounter: 'log.wandering_merchant_encounter',
  encountered_enemy: 'log.encountered_enemy',
  died_fighting: 'log.died_fighting',
  auto_revive_hours: 'log.auto_revive_hours',
  auto_revive_after_sec: 'log.auto_revive_after_sec',
  purchased_buff_refill: 'log.purchased_buff_refill',
  purchased_buff_refill_rub: 'log.purchased_buff_refill_rub',
  subscribed: 'log.subscribed',
  used_healing_potion: 'log.used_healing_potion',
  achievement_unlocked: 'log.achievement_unlocked',
  met_npc: 'log.met_npc',
  wandering_alms_equipped: 'log.wandering_alms_equipped',
  wandering_alms_dropped: 'log.wandering_alms_dropped',
  wandering_alms_stashed: 'log.wandering_alms_stashed',
  healed_full_town: 'log.healed_full_town',
  bought_potion_town: 'log.bought_potion_town',
  sold_items_merchant: 'log.sold_items_merchant',
  npc_skipped_visit: 'log.npc_skipped_visit',
  purchased_potion_from_npc: 'log.purchased_potion_from_npc',
  paid_healer_full: 'log.paid_healer_full',
  quest_giver_checked: 'log.quest_giver_checked',
  quest_accepted: 'log.quest_accepted',
  town_npc_visit_line: '__dynamic_town_visit__',
  combat_swing: 'combat_swing',
};

/**
 * Normalize event.code to a phrase key (log.*, roadside.<slug>, town_visit.type.<slug|line>).
 * Legacy rows without dots are mapped; phrase keys pass through.
 */
export function normalizeAdventureLogCode(code: string, args?: Record<string, unknown>): string {
  if (!code) return '';
  if (code.includes('.')) {
    return code;
  }
  const mapped = LEGACY_SEMANTIC_TO_PHRASE[code];
  const a = args ?? {};
  if (mapped === '__dynamic_town_visit__') {
    const npcType = strArg(a, 'npcType') || 'generic';
    const line = intArg(a, 'line');
    return `town_visit.${npcType}.${line}`;
  }
  if (mapped) return mapped;
  return code;
}

function dynamicRoadsideLine(locale: Locale, code: string): string | undefined {
  if (!code.startsWith('roadside.')) return undefined;
  const text = roadsidePhraseText(locale, code);
  return text || undefined;
}

function dynamicTownVisitLine(locale: Locale, code: string): string | undefined {
  if (!code.startsWith('town_visit.')) return undefined;
  const text = townNpcVisitPhraseText(locale, code);
  return text || undefined;
}

function formatLegacyCombatSwing(
  locale: Locale,
  tr: Translations,
  args: Record<string, unknown>,
  legacyMessage?: string,
): string {
  const source = strArg(args, 'source');
  const outcome = strArg(args, 'outcome');
  const damage = intArg(args, 'damage');
  const isCrit = boolArg(args, 'isCrit');
  const enemySlug = strArg(args, 'enemyType');
  const enemyDbName = strArg(args, 'enemyName');
  const enemy = enemyFamilyLabel(locale, enemySlug, enemyDbName || enemySlug);
  const debuff = strArg(args, 'debuffType');
  const dn = debuff ? debuffName(tr, debuff) : '';
  const debuffPart =
    debuff && dn ? (locale === 'ru' ? ` ${dn} применён.` : ` ${dn} applied.`) : '';
  const crit = isCrit ? (locale === 'ru' ? ' (крит)' : ' (crit)') : '';

  let phraseKey = 'log.combat.hero_hit';
  if (source === 'hero') {
    if (outcome === 'stun') phraseKey = 'log.combat.hero_stun';
    else if (outcome === 'dodge') phraseKey = 'log.combat.hero_dodge';
  } else if (source === 'enemy') {
    phraseKey = outcome === 'block' ? 'log.combat.enemy_block' : 'log.combat.enemy_hit';
  }

  const tmpl = adventureLogTemplate(locale, phraseKey);
  if (!tmpl) return legacyMessage ?? '';

  const vars: Record<string, string | number> = {
    enemy,
    damage,
    crit: phraseKey === 'log.combat.hero_stun' ? '' : crit,
    debuffPart,
  };
  return t(tmpl, vars);
}

function resolveAdventureLogVars(
  locale: Locale,
  tr: Translations,
  phraseKey: string,
  args: Record<string, unknown>,
  legacyMessage?: string,
): Record<string, string | number> | null {
  const a = args;

  switch (phraseKey) {
    case 'log.defeated_enemy':
    case 'log.encountered_enemy':
    case 'log.died_fighting': {
      const slug = strArg(a, 'enemyType');
      const dbName = strArg(a, 'enemyName');
      return {
        enemy: enemyFamilyLabel(locale, slug, dbName || slug),
        xp: numArg(a, 'xp'),
        gold: numArg(a, 'gold'),
      };
    }
    case 'log.leveled_up':
      return { level: intArg(a, 'level') };
    case 'log.equipped_new':
    case 'log.wandering_alms_equipped':
    case 'log.bought_gear_town_merchant': {
      const slot = slotName(tr, strArg(a, 'slot'));
      const rarity = rarityName(tr, strArg(a, 'rarity'));
      const legacyName = strArg(a, 'itemName');
      const item =
        legacyName ||
        (locale === 'ru' ? `${rarity} (${slot})` : `${rarity} — ${slot}`);
      return { slot, item };
    }
    case 'log.inventory_full_dropped':
    case 'log.wandering_alms_dropped':
    case 'log.wandering_alms_stashed': {
      const slot = slotName(tr, strArg(a, 'slot'));
      const rarity = rarityName(tr, strArg(a, 'rarity'));
      const legacyName = strArg(a, 'itemName');
      const item =
        legacyName ||
        (locale === 'ru' ? `${rarity} (${slot})` : `${rarity} (${slot})`);
      return { item };
    }
    case 'log.buff_activated':
      return { buff: buffName(tr, strArg(a, 'buffType')) };
    case 'log.hero_revived':
    case 'log.auto_revive_hours':
    case 'log.healed_full_town':
    case 'log.bought_potion_town':
      return {};
    case 'log.auto_revive_after_sec':
      return { seconds: intArg(a, 'seconds') };
    case 'log.purchased_buff_refill':
      return { buff: buffName(tr, strArg(a, 'buffType')) };
    case 'log.purchased_buff_refill_rub':
      return {
        buff: buffName(tr, strArg(a, 'buffType')),
        price: intArg(a, 'priceRub'),
      };
    case 'log.subscribed':
      return {
        duration: subscriptionDuration(locale, strArg(a, 'durationKey')),
        price: intArg(a, 'priceRub'),
      };
    case 'log.used_healing_potion':
      return { amount: intArg(a, 'amount') };
    case 'log.achievement_unlocked': {
      const rt = strArg(a, 'rewardType');
      const ra = intArg(a, 'rewardAmount');
      let rewardSuffix = '';
      if (rt === 'gold') {
        rewardSuffix = locale === 'ru' ? ` (+${ra} золота)` : ` (+${ra} gold)`;
      } else if (rt === 'potion') {
        rewardSuffix = locale === 'ru' ? ` (+${ra} зелий)` : ` (+${ra} potions)`;
      }
      const id = strArg(a, 'achievementId');
      const legacyTitle = strArg(a, 'title');
      const title = achievementLogTitle(locale, id) || legacyTitle || id || '';
      return {
        title,
        rewardSuffix,
      };
    }
    case 'log.met_npc': {
      const nk = strArg(a, 'npcKey');
      const tk = strArg(a, 'townKey');
      return {
        npc: npcLabel(locale, nk, nk),
        town: townLabel(locale, tk, tk),
      };
    }
    case 'log.sold_items_merchant': {
      const nk = strArg(a, 'npcKey');
      return {
        count: intArg(a, 'count'),
        gold: numArg(a, 'gold'),
        npc: npcLabel(locale, nk, legacyMessage ?? nk),
      };
    }
    case 'log.npc_skipped_visit':
    case 'log.purchased_potion_from_npc':
    case 'log.paid_healer_full':
    case 'log.quest_giver_checked': {
      const nk = strArg(a, 'npcKey');
      return { npc: npcLabel(locale, nk, nk) };
    }
    case 'log.quest_accepted': {
      const qk = strArg(a, 'questKey');
      const legacyTitle = strArg(a, 'title');
      const fromKey = localizedQuestText(locale, qk, 'title', qk || '');
      return { title: fromKey || legacyTitle || qk };
    }
    case 'log.combat.hero_hit':
    case 'log.combat.hero_dodge':
    case 'log.combat.hero_stun':
    case 'log.combat.enemy_hit':
    case 'log.combat.enemy_block': {
      const slug = strArg(a, 'enemyType');
      const enemy = enemyFamilyLabel(locale, slug, slug);
      const debuff = strArg(a, 'debuffType');
      const dn = debuff ? debuffName(tr, debuff) : '';
      const debuffPart =
        debuff && dn ? (locale === 'ru' ? ` ${dn} применён.` : ` ${dn} applied.`) : '';
      const isCrit = boolArg(a, 'isCrit');
      const crit =
        phraseKey === 'log.combat.hero_stun'
          ? ''
          : isCrit
            ? locale === 'ru'
              ? ' (крит)'
              : ' (crit)'
            : '';
      return {
        enemy,
        damage: intArg(a, 'damage'),
        crit,
        debuffPart,
      };
    }
    default:
      return null;
  }
}

/** Localized single log line from structured event (+ optional legacy message). */
export function formatAdventureLogEvent(
  locale: Locale,
  tr: Translations,
  code: string,
  args?: Record<string, unknown>,
  legacyMessage?: string,
): string {
  const rawArgs = args ?? {};
  const phraseKey = normalizeAdventureLogCode(code, rawArgs);

  if (phraseKey === 'combat_swing') {
    return formatLegacyCombatSwing(locale, tr, rawArgs, legacyMessage);
  }

  if (phraseKey === 'log.wandering_merchant_encounter') {
    return dialogueText(locale, WANDERING_MERCHANT_DIALOGUE_KEY, legacyMessage ?? '');
  }

  if (phraseKey === 'log.hero_meet.scripted_line') {
    const speaker = strArg(rawArgs, 'speaker');
    const lineKey = strArg(rawArgs, 'lineKey');
    const lineBody = adventureLogTemplate(locale, lineKey) || lineKey;
    return `${speaker}: ${lineBody}`;
  }
  if (phraseKey === 'log.hero_meet.player_said') {
    const speaker = strArg(rawArgs, 'speaker');
    const text = strArg(rawArgs, 'text');
    const tmpl = adventureLogTemplate(locale, 'log.hero_meet.player_said');
    if (tmpl) return t(tmpl, { speaker, text });
    return `${speaker}: ${text}`;
  }

  const roadside = dynamicRoadsideLine(locale, phraseKey);
  if (roadside !== undefined) return roadside;

  const town = dynamicTownVisitLine(locale, phraseKey);
  if (town !== undefined) return town;

  const template = adventureLogTemplate(locale, phraseKey);
  if (!template) {
    if (import.meta.env.DEV) {
      console.warn('[adventure log] missing template for', phraseKey, 'raw code', code);
    }
    return legacyMessage?.trim() ? legacyMessage : '';
  }

  const vars = resolveAdventureLogVars(locale, tr, phraseKey, rawArgs, legacyMessage);
  if (vars === null) {
    return legacyMessage?.trim() ? legacyMessage : '';
  }
  return t(template, vars);
}

export function formatAdventureLogPayload(
  locale: Locale,
  tr: Translations,
  payload: { message?: string; event?: AdventureLogEventWire },
): string {
  const legacy = payload.message?.trim() ? payload.message : '';
  if (payload.event?.code) {
    const formatted = formatAdventureLogEvent(
      locale,
      tr,
      payload.event.code,
      payload.event.args,
      legacy,
    );
    if (formatted) return formatted;
  }
  return legacy;
}

/** Client-only log lines (no server event). */
export function formatClientLogLine(
  tr: Translations,
  templateKey: 'enteredTown' | 'declinedWanderingMerchant' | 'merchantMovedOn',
  vars?: { town?: string },
): string {
  switch (templateKey) {
    case 'enteredTown':
      return t(tr.logEnteredTown, { town: vars?.town ?? '' });
    case 'declinedWanderingMerchant':
      return tr.logDeclinedWanderingMerchant;
    case 'merchantMovedOn':
      return tr.logMerchantMovedOn;
    default:
      return '';
  }
}
