// ─── Golem Combat Actions (Component-Based) ──────────────────────────────────
// Runtime golem combat logic for the component-based construction system.
// All external data is passed in as parameters (no cross-store getState() calls).
// Implements spec §§10-14: summoning, maintenance, combat, mana, duration.

import { HOURS_PER_TICK, SPELLS_DEF } from '../constants';
import { CORES, FRAMES, MIND_CIRCUITS } from '../data/golems';
import { computeGolemStats, getGolemSlots } from '../data/golems/utils';
import {
  resolveEnchantmentEffects,
  computeBasicAttackDamage,
  processBasicAttack,
} from './golem-combat-helpers';
import type { BasicAttackResult } from './golem-combat-helpers';
import type { GolemEnchantmentEffect } from './golem-combat-helpers';
import type { SerializedDesign } from '../data/golems/types';
import type {
  RuntimeActiveGolem,
  GolemLoadoutEntry,
  EnemyState,
} from '../types';

// ─── Types ───────────────────────────────────────────────────────────────────

export interface GolemCombatResult {
  rawMana: number;
  elements: Record<string, { current: number; max: number; unlocked: boolean }>;
  activeGolems: RuntimeActiveGolem[];
  logMessages: string[];
  totalDamageDealt: number;
}

// ─── Summoning (spec §10) ───────────────────────────────────────────────────

/**
 * Attempt to summon golems from the loadout on room entry.
 * For each enabled design: if player has enough mana, deduct cost and activate.
 * Designs that can't be afforded are NOT re-attempted mid-room.
 */
export function summonGolemsOnRoomEntry(
  loadout: GolemLoadoutEntry[],
  rawMana: number,
  elements: Record<string, { current: number; max: number; unlocked: boolean }>,
  currentFloor: number,
  existingActiveGolems: RuntimeActiveGolem[],
  disciplineSlotsBonus: number,
  fabricatorLevel: number,
): {
  rawMana: number;
  elements: Record<string, { current: number; max: number; unlocked: boolean }>;
  activeGolems: RuntimeActiveGolem[];
  logMessages: string[];
} {
  let newRawMana = rawMana;
  const newElements = { ...elements };
  const newActiveGolems = [...existingActiveGolems];
  const logMessages: string[] = [];

  const baseSlots = getGolemSlots(fabricatorLevel);
  const totalSlots = Math.min(7, baseSlots + disciplineSlotsBonus);

  for (const entry of loadout) {
    if (!entry.enabled) continue;
    if (newActiveGolems.length >= totalSlots) {
      logMessages.push('No golem slots available');
      break;
    }

    const design = entry.design as SerializedDesign;
    const core = CORES[design.coreId];
    const frame = FRAMES[design.frameId];
    const circuit = MIND_CIRCUITS[design.mindCircuitId];
    if (!core || !frame || !circuit) {
      logMessages.push(`${entry.design.name} has invalid components — skipped`);
      continue;
    }

    const alreadyActive = newActiveGolems.some((ag) => ag.designId === entry.designId);
    if (alreadyActive) continue;

    const stats = computeGolemStats({
      id: design.id,
      name: design.name,
      core: { ...core, manaTypes: design.selectedManaTypes.length > 0 ? design.selectedManaTypes : core.manaTypes },
      frame,
      mindCircuit: circuit,
      enchantments: [],
      selectedManaTypes: design.selectedManaTypes,
      selectedSpells: design.selectedSpells,
      guardianSummonCost: design.guardianSummonCost,
      guardianPrimaryManaType: design.guardianPrimaryManaType,
    });

    let canAfford = true;
    for (const cost of stats.totalSummonCost) {
      if (cost.type === 'raw') {
        if (newRawMana < cost.amount) { canAfford = false; break; }
      } else if (cost.element) {
        const elem = newElements[cost.element];
        if (!elem?.unlocked || elem.current < cost.amount) { canAfford = false; break; }
      }
    }

    if (!canAfford) {
      logMessages.push(`Not enough mana to summon ${entry.design.name} — skipped`);
      continue;
    }

    for (const cost of stats.totalSummonCost) {
      if (cost.type === 'raw') {
        newRawMana -= cost.amount;
      } else if (cost.element && newElements[cost.element]) {
        newElements[cost.element] = {
          ...newElements[cost.element],
          current: newElements[cost.element].current - cost.amount,
        };
      }
    }

    newActiveGolems.push({
      designId: entry.designId,
      summonedFloor: currentFloor,
      attackProgress: 0,
      roomsRemaining: stats.maxRoomDuration,
      currentMana: stats.manaCapacity,
      spellCastIndex: 0,
    });

    logMessages.push(`${entry.design.name} summoned`);
  }

  return { rawMana: newRawMana, elements: newElements, activeGolems: newActiveGolems, logMessages };
}

// ─── Maintenance Upkeep (spec §13) ───────────────────────────────────────────

/**
 * Deduct player upkeep cost for each active golem per tick.
 * Upkeep = Core.manaRegen × 2 per hour, converted to per-tick.
 * Golems that can't be maintained are dismissed immediately.
 */
export function processGolemMaintenance(
  activeGolems: RuntimeActiveGolem[],
  golemDesigns: Record<string, SerializedDesign>,
  rawMana: number,
  elements: Record<string, { current: number; max: number; unlocked: boolean }>,
): {
  rawMana: number;
  elements: Record<string, { current: number; max: number; unlocked: boolean }>;
  maintainedGolems: RuntimeActiveGolem[];
  logMessages: string[];
} {
  let newRawMana = rawMana;
  const newElements = { ...elements };
  const maintainedGolems: RuntimeActiveGolem[] = [];
  const logMessages: string[] = [];

  for (const golem of activeGolems) {
    const design = golemDesigns[golem.designId];
    if (!design) continue;
    const core = CORES[design.coreId];
    if (!core) continue;

    const upkeepPerTick = core.manaRegen * 2 * HOURS_PER_TICK;
    // For multi-type cores, split upkeep evenly across all mana types (spec §13)
    const upkeepManaTypes =
      design.selectedManaTypes.length > 0
        ? design.selectedManaTypes
        : core.manaTypes;
    const splitCount = Math.max(1, upkeepManaTypes.length);
    const upkeepPerType = upkeepPerTick / splitCount;

    // Check if player can afford upkeep across all required mana types
    let canMaintain = true;
    for (const manaType of upkeepManaTypes) {
      const elem = newElements[manaType];
      if (!elem || !elem.unlocked || elem.current < upkeepPerType) {
        canMaintain = false;
        break;
      }
    }

    if (canMaintain) {
      for (const manaType of upkeepManaTypes) {
        const elem = newElements[manaType]!;
        newElements[manaType] = { ...elem, current: elem.current - upkeepPerType };
      }
      maintainedGolems.push(golem);
    } else {
      const missingTypes = upkeepManaTypes
        .filter((t) => {
          const elem = newElements[t];
          return !elem || !elem.unlocked || elem.current < upkeepPerType;
        })
        .join(', ');
      logMessages.push(
        `${design.name} dismissed — insufficient mana for upkeep (${missingTypes})`,
      );
    }
  }

  return { rawMana: newRawMana, elements: newElements, maintainedGolems, logMessages };
}

// ─── Golem Mana Regen (spec §12) ────────────────────────────────────────────

/**
 * Regenerate golem mana pools per tick.
 */
export function processGolemManaRegen(
  activeGolems: RuntimeActiveGolem[],
  golemDesigns: Record<string, SerializedDesign>,
): RuntimeActiveGolem[] {
  return activeGolems.map((golem) => {
    const design = golemDesigns[golem.designId];
    if (!design) return golem;
    const core = CORES[design.coreId];
    if (!core) return golem;
    const manaGain = core.manaRegen * HOURS_PER_TICK;
    return { ...golem, currentMana: Math.min(core.manaCapacity, golem.currentMana + manaGain) };
  });
}

// ─── Golem Combat Tick (spec §11) ───────────────────────────────────────────

/**
 * Process golem attacks for one combat tick.
 * Each golem accumulates attackProgress and fires when >= 1.
 * Supports spell casting via Mind Circuit behavior.
 *
 * Fixes applied (issue #313):
 * 1. Spell damage: uses actual SPELLS_DEF[spellId].dmg * frame.magicAffinity
 * 2. Spell mana cost: uses actual SPELLS_DEF[spellId].cost.amount
 * 3. Elemental matchup: applies getElementalBonus to basic attacks
 * 4. Enchantment effects: applies golem enchantment effects on basic attacks
 * 5. Armor pierce: per spire-combat spec §9.4, dmg = baseDamage × (1 + armorPierce)
 */
export function processGolemAttacks(
  activeGolems: RuntimeActiveGolem[],
  golemDesigns: Record<string, SerializedDesign>,
  onDamageDealt: (damage: number, skipSpecials?: boolean) => {
    rawMana: number;
    elements: Record<string, { current: number; max: number; unlocked: boolean }>;
    modifiedDamage?: number;
  },
  applyDamageToRoom: (dmg: number) => { floorHP: number; floorMaxHP: number; roomCleared: boolean },
  enemyElement: string,
  getTargetEnemy: () => EnemyState | null,
  getTargetEnemies: () => EnemyState[],
  onApplyEnchantmentEffects: (enemyId: string, effects: GolemEnchantmentEffect[]) => void,
  currentRawMana: number,
  currentElements: Record<string, { current: number; max: number; unlocked: boolean }>,
): GolemCombatResult {
  let rawMana = currentRawMana;
  let elements: Record<string, { current: number; max: number; unlocked: boolean }> = { ...currentElements };
  let floorHP = 0;
  let floorMaxHP = 0;
  const logMessages: string[] = [];
  let totalDamageDealt = 0;
  const updatedGolems: RuntimeActiveGolem[] = [];

  for (const golem of activeGolems) {
    const design = golemDesigns[golem.designId];
    if (!design) continue;

    const core = CORES[design.coreId];
    const frame = FRAMES[design.frameId];
    const circuit = MIND_CIRCUITS[design.mindCircuitId];
    if (!core || !frame || !circuit) continue;

    const enchantmentEffects = resolveEnchantmentEffects(design.enchantmentIds);
    const bonusArmorPierce = design.enchantmentIds.includes('sword_metal') ? 0.15 : 0;
    const isAoe = frame.aoeTargets > 1;

    let attackProgress = golem.attackProgress + HOURS_PER_TICK * frame.attackSpeed;
    const updatedGolem = { ...golem };
    let safetyCounter = 0;
    const MAX_GOLEM_ATTACKS_PER_TICK = 100;

    while (attackProgress >= 1 && safetyCounter < MAX_GOLEM_ATTACKS_PER_TICK) {
      // Try spell cast first if circuit supports it
      if (circuit.spellSlots > 0 && design.selectedSpells.length > 0) {
        const spellIdx = updatedGolem.spellCastIndex % design.selectedSpells.length;
        const spellId = design.selectedSpells[spellIdx];
        const spellDef = spellId ? SPELLS_DEF[spellId] : null;

        if (spellDef) {
          const spellManaCost = spellDef.cost.amount;
          if (updatedGolem.currentMana >= spellManaCost) {
            // FIX #1: Use actual spell damage * magic affinity
            const spellDmg = spellDef.dmg * frame.magicAffinity;
            // FIX #2: Deduct actual spell mana cost
            updatedGolem.currentMana -= spellManaCost;
            updatedGolem.spellCastIndex = (updatedGolem.spellCastIndex + 1) % design.selectedSpells.length;

            const dmgResult = onDamageDealt(spellDmg, true);
            const finalDamage = dmgResult.modifiedDamage || spellDmg;

            if (Number.isFinite(finalDamage)) {
              const roomResult = applyDamageToRoom(finalDamage);
              floorHP = roomResult.floorHP;
              floorMaxHP = roomResult.floorMaxHP;
              totalDamageDealt += Math.max(0, finalDamage);
              rawMana = dmgResult.rawMana;
              elements = dmgResult.elements;
            }

            attackProgress -= 1;
            safetyCounter++;
            continue;
          }
        }
      }

      // Basic attack: AoE or single-target (delegated to helper)
      const basicResult: BasicAttackResult = processBasicAttack({
        frame: { ...frame, aoeTargets: frame.aoeTargets },
        bonusArmorPierce,
        enchantmentEffects,
        enemyElement,
        getTargetEnemy,
        getTargetEnemies,
        onDamageDealt,
        applyDamageToRoom,
        onApplyEnchantmentEffects,
      }, rawMana, elements);
      rawMana = basicResult.rawMana;
      elements = basicResult.elements;
      floorHP = basicResult.floorHP;
      floorMaxHP = basicResult.floorMaxHP;
      totalDamageDealt += basicResult.totalDamageDealt;

      attackProgress -= 1;
      safetyCounter++;
    }

    updatedGolem.attackProgress = attackProgress;
    updatedGolems.push(updatedGolem);
  }

  return { rawMana, elements, activeGolems: updatedGolems, logMessages, totalDamageDealt };
}

// ─── Room Duration Countdown (spec §14) ─────────────────────────────────────

/**
 * Decrement roomsRemaining for each active golem on room clear.
 * Golems at 0 remaining are dismissed.
 */
export function countdownGolemRoomDuration(
  activeGolems: RuntimeActiveGolem[],
  golemDesigns: Record<string, SerializedDesign>,
): {
  remainingGolems: RuntimeActiveGolem[];
  dismissedNames: string[];
  logMessages: string[];
} {
  const remainingGolems: RuntimeActiveGolem[] = [];
  const dismissedNames: string[] = [];
  const logMessages: string[] = [];

  for (const golem of activeGolems) {
    const design = golemDesigns[golem.designId];
    if (!design) continue;
    const core = CORES[design.coreId];
    if (!core) continue;

    const newRoomsRemaining = golem.roomsRemaining - 1;
    if (newRoomsRemaining <= 0) {
      dismissedNames.push(design.name);
      logMessages.push(`${design.name} has faded after ${core.maxRoomDuration} rooms`);
    } else {
      remainingGolems.push({ ...golem, roomsRemaining: newRoomsRemaining });
    }
  }

  return { remainingGolems, dismissedNames, logMessages };
}
