/**
 * Spell casting workflow — handles standard, sacred (Magia Sakralna),
 * witch (Wiedźmia Magia), and blood (Magia Krwi) modes.
 *
 * Pipeline per cast:
 *   1. validateCast()              — gate on resources, race, talent, discipline,
 *                                    deity, group-cast, in-combat, witch symbols
 *   2. Resource deduction          — mana / zeal / blood
 *   3. TS test roll                — pool depends on mode
 *   4. (success) damage roll       — parses spell.damageBase
 *   5. Status effect auto-apply    — from spell.statusEffects[]
 *   6. Trigger registration        — from spell.triggers[]
 *
 * Bypasses for GM workflows: opts.bypassSuperspellWarning, opts.bypassNonCombatBlock.
 */

import { HbmTSRoll } from '../dice/ts-roll';
import { TS_DEFAULT_REQUIRED, TS_DEFAULT_THRESHOLD } from '../constants';
import { validateCast } from './spell-validation';
import type { ValidationResult } from './spell-validation';
import { rollSpellDamage } from './spell-damage';
import { registerSpellTriggers } from './spell-triggers';
import { CONDITIONS } from './conditions';

export interface CastableActor {
  id: string;
  name: string;
  type: 'character' | 'npc';
  system: {
    attributes: {
      magic: { value: number };
      soul: { value: number };
      mana: { value: number; max: number; maxPerSpell: number };
      zeal: { value: number; max: number };
      blood?: { value: number; max: number };
    };
    skills?: Record<string, { value: number }>;
  };
  update: (changes: Record<string, unknown>) => Promise<unknown>;
}

export interface SpellLike {
  id?: string;
  name: string;
  system: {
    castingMode: 'standard' | 'sacred' | 'witch' | 'blood';
    school?: string;
    deity?: string;
    manaCost: number;
    bloodCost?: number;
    complexityLevel: number;
    isSuperspell?: boolean;
    requiresGroupCast?: boolean;
    minCasters?: number;
    nonCombatOnly?: boolean;
    damageBase?: string;
    damageType?: string;
    ignoresArmor?: boolean;
    statusEffects?: string[];
    saveAttribute?: string;
    saveSkill?: string;
    triggers?: Array<{ event: string; effect: string }>;
    components?: { symbols?: string[] };
    requirements?: { race?: string[]; talent?: string[]; discipline?: string[] };
    difficulty: { threshold: number; successes: number };
  };
}

export interface CastOptions {
  manaSpent?: number;
  zealSpent?: number;
  bloodSpent?: number;
  hekateMode?: 'sacred' | 'witch';
  bypassSuperspellWarning?: boolean;
  bypassNonCombatBlock?: boolean;
  groupCasters?: string[];
  /** Override required successes (used by variableSuccesses summons). */
  requiredOverride?: number;
  speaker?: ChatMessage.SpeakerData;
}

export interface CastResult {
  validation: ValidationResult;
  baseRoll: HbmTSRoll | null;
  damageRoll: Roll | null;
  triggersRegistered: number;
}

function notify(text: string, severity: 'warn' | 'error' | 'info' = 'info'): void {
  const u = ui as unknown as { notifications?: { warn: (s: string) => void; error: (s: string) => void; info: (s: string) => void } };
  u.notifications?.[severity](text);
}

export async function castSpell(actor: CastableActor, spell: SpellLike, opts: CastOptions = {}): Promise<CastResult> {
  // 1. Validate
  const validation = validateCast(actor, spell, opts);
  if (!validation.ok) {
    for (const err of validation.errors) {
      notify(err.i18nKey ? game.i18n.format(err.i18nKey, (err.i18nArgs ?? {}) as Record<string, string>) : err.message, 'warn');
    }
    return { validation, baseRoll: null, damageRoll: null, triggersRegistered: 0 };
  }
  for (const w of validation.warnings) {
    notify(w.i18nKey ? game.i18n.format(w.i18nKey, (w.i18nArgs ?? {}) as Record<string, string>) : w.message, 'info');
  }

  // 2-3. Mode dispatch
  const mode = spell.system.castingMode ?? 'standard';
  let baseRoll: HbmTSRoll | null = null;
  if (mode === 'sacred') {
    baseRoll = opts.hekateMode === 'witch'
      ? await castWitch(actor, spell, opts)
      : await castSacred(actor, spell, opts);
  } else if (mode === 'witch') {
    baseRoll = await castWitch(actor, spell, opts);
  } else if (mode === 'blood') {
    baseRoll = await castBlood(actor, spell, opts);
  } else {
    baseRoll = await castStandard(actor, spell, opts);
  }

  let damageRoll: Roll | null = null;
  let triggersRegistered = 0;

  if (baseRoll?.ts?.isSuccess) {
    // 4. Damage
    if (spell.system.damageBase) {
      damageRoll = await rollSpellDamage(spell.system.damageBase, { actor, spellName: spell.name });
    }

    // 6. Triggers
    triggersRegistered = await registerSpellTriggers(actor as any, spell);
  }

  // Render unified rich chat card (Phase 1.6)
  await renderCastCard(actor, spell, opts, baseRoll, damageRoll, triggersRegistered);

  return { validation, baseRoll, damageRoll, triggersRegistered };
}

async function renderCastCard(
  actor: CastableActor,
  spell: SpellLike,
  opts: CastOptions,
  baseRoll: HbmTSRoll | null,
  damageRoll: Roll | null,
  triggersRegistered: number,
): Promise<void> {
  const success = baseRoll?.ts?.isSuccess ?? false;
  const mode = spell.system.castingMode ?? 'standard';

  // Costs ledger
  const costs: Array<{ label: string; amount: number }> = [];
  if ((opts.manaSpent ?? 0) > 0) costs.push({ label: game.i18n.localize('HBM.spellCast.manaSpent'), amount: opts.manaSpent! });
  if ((opts.zealSpent ?? 0) > 0) costs.push({ label: game.i18n.localize('HBM.spellCast.zealSpent'), amount: opts.zealSpent! });
  if ((opts.bloodSpent ?? 0) > 0) costs.push({ label: game.i18n.localize('HBM.spellCast.bloodSpent'), amount: opts.bloodSpent! });

  // Damage block
  let damage: Record<string, unknown> | null = null;
  if (success && damageRoll) {
    const targets: Array<{ uuid: string; name: string }> = [];
    const userTargets = (game.user as any)?.targets;
    if (userTargets && typeof userTargets[Symbol.iterator] === 'function') {
      for (const t of userTargets) {
        if (t?.actor?.uuid) targets.push({ uuid: t.actor.uuid, name: t.actor.name ?? t.name });
      }
    }
    damage = {
      total: damageRoll.total,
      formula: damageRoll.formula,
      type: spell.system.damageType ?? 'magical',
      ignoresArmor: !!spell.system.ignoresArmor,
      targets: targets.length > 0 ? targets : null,
    };
  }

  // Status effects (with apply buttons)
  const statusEffects: Array<{ id: string; label: string }> = [];
  if (success && Array.isArray(spell.system.statusEffects)) {
    for (const id of spell.system.statusEffects) {
      const def = CONDITIONS.find((c) => c.id === id);
      const label = def ? game.i18n.localize(def.i18nKey) : id;
      statusEffects.push({ id, label });
    }
  }

  // Save line
  const save = success && spell.system.saveAttribute
    ? { attribute: spell.system.saveAttribute, skill: spell.system.saveSkill ?? '' }
    : null;

  const triggers = success && triggersRegistered > 0 && Array.isArray(spell.system.triggers)
    ? spell.system.triggers
    : [];

  const data = {
    spell: { name: spell.name },
    mode,
    school: spell.system.school ?? '',
    circle: (spell.system as any).circle ?? 0,
    outcome: { success },
    costs,
    damage,
    save,
    statusEffects,
    triggers,
    description: (spell.system as any).description ?? '',
  };

  const html = await renderTemplate('systems/hbm-rpg-v3/templates/chat/spell-cast.hbs', data);
  await ChatMessage.create({
    content: html,
    speaker: opts.speaker ?? ChatMessage.getSpeaker({ actor: actor as any }),
    flags: { 'hbm-rpg-v3': { spellCast: { spellName: spell.name, success } } },
  });
}

async function castStandard(actor: CastableActor, spell: SpellLike, opts: CastOptions): Promise<HbmTSRoll | null> {
  const baseCost = Math.max(0, spell.system.manaCost ?? 0);
  const manaSpent = Math.max(baseCost, Math.floor(opts.manaSpent ?? baseCost));
  const a = actor.system.attributes;

  await actor.update({ 'system.attributes.mana.value': a.mana.value - manaSpent });

  const pool = a.magic.actual + (actor.system.skills?.magicalAbilities?.value ?? 0);
  const flavor = `${spell.name}`;
  const required = opts.requiredOverride ?? spell.system.difficulty.successes ?? TS_DEFAULT_REQUIRED;
  const baseRoll = HbmTSRoll.fromParams({
    pool,
    threshold: spell.system.difficulty.threshold ?? TS_DEFAULT_THRESHOLD,
    required,
    flavor,
  });
  await baseRoll.evaluate();
  await baseRoll.toMessage({ flavor, speaker: opts.speaker });

  // Overcast — extra TS test if manaSpent exceeds maxPerSpell.
  if (manaSpent > a.mana.maxPerSpell && a.mana.maxPerSpell > 0) {
    const excess = manaSpent - a.mana.maxPerSpell;
    const tThreshold = Math.min(6, Math.max(2, baseCost));
    const ySuccesses = Math.min(10, Math.max(1, excess));
    const overcastFlavor = `${game.i18n.localize('HBM.spellCast.overcastTest')}: ${spell.name}`;
    const overcastRoll = HbmTSRoll.fromParams({
      pool,
      threshold: tThreshold,
      required: ySuccesses,
      flavor: overcastFlavor,
    });
    await overcastRoll.evaluate();
    await overcastRoll.toMessage({ flavor: overcastFlavor, speaker: opts.speaker });
  }

  return baseRoll;
}

async function castSacred(actor: CastableActor, spell: SpellLike, opts: CastOptions): Promise<HbmTSRoll | null> {
  const a = actor.system.attributes;
  const zealCost = Math.max(1, opts.zealSpent ?? 1);
  await actor.update({ 'system.attributes.zeal.value': a.zeal.value - zealCost });

  const pool = a.soul.value + (actor.system.skills?.devotion?.value ?? 0);
  const flavor = `${spell.name} — ${game.i18n.localize('HBM.spell.castingMode.sacred')}`;
  const required = opts.requiredOverride ?? spell.system.difficulty.successes ?? TS_DEFAULT_REQUIRED;
  const roll = HbmTSRoll.fromParams({
    pool,
    threshold: spell.system.difficulty.threshold ?? TS_DEFAULT_THRESHOLD,
    required,
    flavor,
  });
  await roll.evaluate();
  await roll.toMessage({ flavor, speaker: opts.speaker });
  return roll;
}

async function castWitch(actor: CastableActor, spell: SpellLike, opts: CastOptions): Promise<HbmTSRoll | null> {
  const a = actor.system.attributes;
  const required = Math.max(0, spell.system.complexityLevel ?? spell.system.components?.symbols?.length ?? 0);
  const maxSymbols = Math.ceil(a.magic.actual / 2);

  // Witch magic still has a mana cost.
  const baseCost = Math.max(0, spell.system.manaCost ?? 0);
  await actor.update({ 'system.attributes.mana.value': a.mana.value - baseCost });

  const pool = a.magic.actual + (actor.system.skills?.magicalAbilities?.value ?? 0);
  const flavor = `${spell.name} — ${game.i18n.localize('HBM.spell.castingMode.witch')} (${required}/${maxSymbols})`;
  const reqSuccesses = opts.requiredOverride ?? spell.system.difficulty.successes ?? TS_DEFAULT_REQUIRED;
  const roll = HbmTSRoll.fromParams({
    pool,
    threshold: spell.system.difficulty.threshold ?? TS_DEFAULT_THRESHOLD,
    required: reqSuccesses,
    flavor,
  });
  await roll.evaluate();
  await roll.toMessage({ flavor, speaker: opts.speaker });
  return roll;
}

async function castBlood(actor: CastableActor, spell: SpellLike, opts: CastOptions): Promise<HbmTSRoll | null> {
  const a = actor.system.attributes;
  const baseCost = Math.max(0, spell.system.bloodCost ?? 0);
  const bloodSpent = Math.max(baseCost, Math.floor(opts.bloodSpent ?? baseCost));
  const blood = a.blood;
  if (blood) {
    await actor.update({ 'system.attributes.blood.value': Math.max(0, blood.value - bloodSpent) });
  }

  // Mana cost (some blood spells may also have one)
  const manaCost = Math.max(0, spell.system.manaCost ?? 0);
  if (manaCost > 0) {
    await actor.update({ 'system.attributes.mana.value': a.mana.value - manaCost });
  }

  const pool = a.magic.actual + (actor.system.skills?.magicalAbilities?.value ?? 0);
  const flavor = `${spell.name} — ${game.i18n.localize('HBM.spell.castingMode.blood')}`;
  const required = opts.requiredOverride ?? spell.system.difficulty.successes ?? TS_DEFAULT_REQUIRED;
  const roll = HbmTSRoll.fromParams({
    pool,
    threshold: spell.system.difficulty.threshold ?? TS_DEFAULT_THRESHOLD,
    required,
    flavor,
  });
  await roll.evaluate();
  await roll.toMessage({ flavor, speaker: opts.speaker });
  return roll;
}
