/**
 * The mechanics kernel: structured, enforceable game rules derived on-demand
 * from the already-cached compendium JSON. No new assets are loaded — these
 * wrap the existing lazy loaders in src/lib/compendium, so the PWA cache config
 * is unchanged. Derived mechanics are memoized per process.
 */
import { loadSpells, loadArmor5e, loadMonsters } from '@/lib/compendium';
import { normalizeSpell5e } from './normalize/spell';
import { normalizeArmor5e } from './normalize/armor';
import { normalizeMonsterDefenses } from './normalize/monsterDefenses';
import type { ArmorMechanics, DamageDefenses, SpellMechanics } from './types';

export * from './types';
export type { EnforceResult } from './result';
export { normalizeSpell5e, normalizeSpellPf2e } from './normalize/spell';
export { normalizeArmor5e } from './normalize/armor';
export { normalizeMonsterDefenses } from './normalize/monsterDefenses';
export { castSpell, dropConcentration, concentrationDC } from './cast';
export { spendResource, regainResource } from './resources';
export { rollDeathSave, type DeathSaveOutcome } from './deathSaves';
export { conditionEffects, CONDITION_EFFECTS_5E, CONDITION_EFFECTS_PF2E, type ConditionEffect } from './conditionEffects';
export { deriveState, type MechanicalState } from './creatureState';

let spellMechCache: Map<string, SpellMechanics> | null = null;
let armorMechCache: Map<string, ArmorMechanics> | null = null;

/** Mechanics for a single 5e spell by slug (undefined if not in the SRD set). */
export async function spellMechanics5e(slug: string): Promise<SpellMechanics | undefined> {
  if (!spellMechCache) {
    const raw = await loadSpells();
    spellMechCache = new Map();
    for (const s of raw) {
      const m = normalizeSpell5e(s);
      if (m) spellMechCache.set(m.slug, m);
    }
  }
  return spellMechCache.get(slug);
}

/** Mechanics for a single 5e armor by name (case-insensitive). */
export async function armorMechanics5e(name: string): Promise<ArmorMechanics | undefined> {
  if (!armorMechCache) {
    const raw = await loadArmor5e();
    armorMechCache = new Map();
    for (const a of raw) {
      const m = normalizeArmor5e(a);
      if (m) armorMechCache.set(m.name.toLowerCase(), m);
    }
  }
  return armorMechCache.get(name.toLowerCase());
}

/** All 5e armor mechanics (for the "equip" picker). */
export async function allArmorMechanics5e(): Promise<ArmorMechanics[]> {
  await armorMechanics5e('');
  return [...(armorMechCache?.values() ?? [])];
}

/** Damage/condition defenses for a 5e monster by slug. */
export async function monsterDefenses5e(slug: string): Promise<DamageDefenses | undefined> {
  const raw = await loadMonsters();
  const m = raw.find((x) => x.slug === slug);
  return m ? normalizeMonsterDefenses(m) : undefined;
}
