// ─── Combat Tick Callback Builder ─────────────────────────────────────────────
// Extracts the large combat callback lambdas from gameStore.ts tick()
// to keep the coordinator under the 400-line file limit.

import { HOURS_PER_TICK } from '../../constants';
import { getGuardianForFloor } from '../../data/guardian-encounters';
import { hasSpecial, SPECIAL_EFFECTS } from '../../effects/special-effects';
import type { ComputedEffects } from '../../effects/upgrade-effects.types';
import type { EnemyState } from '../../types';
import type { CombatStore } from '../combat-state.types';
import { countdownGolemRoomDuration } from '../golem-combat-actions';
import { useAttunementStore } from '../attunementStore';

// ─── Enemy Defense Context ────────────────────────────────────────────────────
// Snapshot of the current tick's enemy defense state, captured once per tick
// when makeOnDamageDealt is invoked. This avoids changing the onDamageDealt
// callback signature across the entire call chain.

export interface EnemyDefenseCtx {
  roomType: string;
  enemy: EnemyState | null;
}

// ─── Params ───────────────────────────────────────────────────────────────────

interface BuildCombatCallbacksParams {
  ctx: {
    combat: {
      floorHP: number;
      floorMaxHP: number;
      currentFloor: number;
      guardianShield: number;
      guardianShieldMax: number;
      guardianBarrier: number;
      guardianBarrierMax: number;
      currentRoom: { roomType: string; enemies: EnemyState[] };
    };
  };
  effects: ComputedEffects;
  maxMana: number;
  addLog: (msg: string) => void;
  useCombatStore: { setState: (s: Partial<CombatStore>) => void; getState: () => CombatStore };
  usePrestigeStore: { getState: () => { addDefeatedGuardian: (floor: number) => void; defeatedGuardians: number[] } };
}

/** Speed-room bonus added to agile dodge chance (spec §4.5) */
const SPEED_ROOM_DODGE_BONUS = 0.20;

// ─── Standalone Enemy Defenses (for DoT/debuff pipeline) ─────────────────────

/**
 * Apply regular enemy defenses: dodge → barrier → armor (spec §5.2).
 * Returns modified damage, or 0 on dodge.
 * Exported for use by the DoT/debuff tick processing system (spec §6.3).
 *
 * @param bypassArmor — if true, skip armor reduction entirely (spec §6.4, AC-13)
 * @param bypassBarrier — if true, skip barrier absorption (spec §6.4)
 */
export function applyEnemyDefenses(
  dmg: number,
  enemy: EnemyState | null,
  roomType: string,
  addLog: (msg: string) => void,
  bypassArmor?: boolean,
  bypassBarrier?: boolean,
): number {
  if (!enemy) return dmg;

  // 0. Curse amplification (spec §6.3) — amplifies all incoming damage
  let curseMult = 1;
  for (const effect of enemy.activeEffects) {
    if (effect.type === 'curse') {
      curseMult *= (1 + effect.magnitude);
    }
  }
  if (curseMult > 1) {
    dmg *= curseMult;
  }

  // 1. Dodge check (spec §5.2, §4.5)
  let effectiveDodge = enemy.dodgeChance;
  if (roomType === 'speed') {
    const hasAgile = enemy.name.toLowerCase().includes('agile');
    if (hasAgile) {
      effectiveDodge = Math.min(0.75, enemy.dodgeChance + SPEED_ROOM_DODGE_BONUS);
    }
  }
  if (effectiveDodge > 0 && Math.random() < effectiveDodge) {
    addLog('Attack dodged!');
    return 0;
  }

  // 2a. Shield pool absorption (flat HP one-time pool, spec §5.1) — skipped if bypassBarrier
  if (!bypassBarrier && enemy.shieldPool && enemy.shieldPool > 0) {
    const absorb = Math.min(enemy.shieldPool, dmg);
    enemy.shieldPool -= absorb;
    dmg -= absorb;
  }

  // 2b. Barrier absorption (percentage, spec §5.2) — skipped if bypassBarrier
  if (!bypassBarrier && enemy.barrier && enemy.barrier > 0) {
    dmg *= (1 - enemy.barrier);
  }

  // 3. Armor reduction — skipped if bypassArmor (spec §6.4, AC-13)
  if (!bypassArmor) {
    const armorValue = (enemy as EnemyState & { effectiveArmor?: number }).effectiveArmor ?? enemy.armor;
    if (armorValue && armorValue > 0) {
      dmg *= (1 - armorValue);
    }
  }

  return dmg;
}

export function buildCombatCallbacks(params: BuildCombatCallbacksParams) {
  const { ctx, effects, maxMana, useCombatStore, usePrestigeStore } = params;

  const onFloorCleared = (floor: number, wasGuardian: boolean) => {
    if (wasGuardian) {
      const defeatedGuardian = getGuardianForFloor(floor);
      params.addLog((defeatedGuardian?.name || 'Unknown') + ' defeated! Visit the Grimoire to sign a pact.');
      usePrestigeStore.getState().addDefeatedGuardian(floor);

      // Auto-unlock Invoker when the first guardian (floor 10) is defeated
      if (floor === 10) {
        const prestigeState = usePrestigeStore.getState();
        const unlocked = useAttunementStore.getState().unlockAttunement('invoker', prestigeState.defeatedGuardians);
        if (unlocked) {
          params.addLog('💜 The path of the Invoker is now available!');
        }
      }

      // Auto-unlock Fabricator when the second guardian (floor 20) is defeated
      if (floor === 20) {
        const prestigeState = usePrestigeStore.getState();
        const unlocked = useAttunementStore.getState().unlockAttunement('fabricator', prestigeState.defeatedGuardians);
        if (unlocked) {
          params.addLog('⚒️ The path of the Fabricator is now available!');
        }
      }
    } else if (floor % 5 === 0) {
      params.addLog('Floor ' + floor + ' cleared!');
    }
    useCombatStore.setState({ guardianShield: 0, guardianShieldMax: 0, guardianBarrier: 0, guardianBarrierMax: 0 });

    // ── Golem room-duration countdown (spec §14) ──────────────────────
    const cs = useCombatStore.getState();
    const activeGolems = cs.golemancy.activeGolems;
    const golemDesigns = cs.golemancy.golemDesigns;
    if (activeGolems.length > 0) {
      const result = countdownGolemRoomDuration(activeGolems, golemDesigns);
      if (result.logMessages.length > 0) {
        result.logMessages.forEach((msg) => params.addLog(msg));
      }
      useCombatStore.setState({
        golemancy: { ...cs.golemancy, activeGolems: result.remainingGolems },
      });
    }
  };

  /** Mage barrier recharge rate (spec §5.2): 5% per tick */
  const MAGE_BARRIER_RECHARGE_RATE = 0.05;
  const MAGE_BARRIER_MAX = 0.4; // maxBarrier from MODIFIER_CONFIG.mage

  /**
   * Apply mage barrier recharge (spec §5.2).
   * Returns recharged enemy copy, or null if not a mage enemy.
   */
  const applyMageBarrierRecharge = (enemy: EnemyState | null): EnemyState | null => {
    if (!enemy || !enemy.barrier || enemy.barrier <= 0) return null;
    if (!enemy.name.startsWith('Mage')) return null;
    const recharged = enemy.barrier + MAGE_BARRIER_RECHARGE_RATE * HOURS_PER_TICK;
    return { ...enemy, barrier: recharged };
  };

  // Local reference to the module-level applyEnemyDefenses
  const defApply = applyEnemyDefenses;

  /**
   * Create the onDamageDealt callback for this tick.
   * Closes over the enemy defense context (captured once per tick from currentRoom).
   */
  const makeOnDamageDealt = (
    rawManaRef: () => number,
    elementsRef: () => Record<string, { current: number; max: number; unlocked: boolean }>,
    defCtx: EnemyDefenseCtx,
    addLog: (msg: string) => void,
  ) => {
    return (damage: number, skipSpecials?: boolean) => {
      const rawMana = rawManaRef();
      const elements = elementsRef();
      let dmg = damage;

      // Discipline specials (Executioner, Berserker) — before enemy defenses
      // Skipped for golem attacks (spec §9.4, D-04 fix)
      if (!skipSpecials) {
        // Executioner: per-enemy HP check (spec §4.4, D-15 fix)
        const executionerTarget = defCtx.enemy;
        if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && executionerTarget && executionerTarget.hp < executionerTarget.maxHP * 0.25) {
          dmg *= 2;
        }
        if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) {
          dmg *= 1.5;
        }
      }

      // Apply regular enemy defenses for ALL enemies (spec §5.2)
      dmg = defApply(dmg, defCtx.enemy, defCtx.roomType, addLog);

      // Mage barrier recharge (spec §5.2) — after damage, recharge mage barrier on the enemy
      if (defCtx.enemy && defCtx.enemy.barrier && defCtx.enemy.barrier > 0 && defCtx.enemy.name.startsWith('Mage')) {
        const rechargedBarrier = Math.min(
          MAGE_BARRIER_MAX,
          defCtx.enemy.barrier + MAGE_BARRIER_RECHARGE_RATE * HOURS_PER_TICK,
        );
        // Update enemy barrier on the store (mutates via reference)
        defCtx.enemy.barrier = rechargedBarrier;
      }

      // Guardian-specific defensive pipeline (spec §5.3, §11)
      const guardian = getGuardianForFloor(ctx.combat.currentFloor);
      if (guardian) {
        let shield = ctx.combat.guardianShield;
        const shieldMax = ctx.combat.guardianShieldMax;
        let barrier = ctx.combat.guardianBarrier;
        const barrierMax = ctx.combat.guardianBarrierMax;

        // Shield absorption (flat pool, per-hit)
        if (shield > 0 && dmg > 0) {
          const absorb = Math.min(shield, dmg);
          shield -= absorb;
          dmg -= absorb;
        }

        // Barrier reduction (percentage, per-hit)
        if (barrier > 0 && dmg > 0) {
          dmg *= (1 - barrier);
        }

        // Guardian armor reduction (spec §11, D-26 fix)
        if (guardian.armor && guardian.armor > 0 && dmg > 0) {
          dmg *= (1 - guardian.armor);
        }

        // Health regen reduces net damage (per-hit, already scaled by HOURS_PER_TICK)
        if (guardian.healthRegen && guardian.healthRegen > 0) {
          const healAmount = guardian.healthRegenIsPercent
            ? Math.floor(ctx.combat.floorMaxHP * guardian.healthRegen / 100 * HOURS_PER_TICK)
            : Math.floor(guardian.healthRegen * HOURS_PER_TICK);
          dmg -= healAmount;
        }

        useCombatStore.setState({
          guardianShield: shield,
          guardianShieldMax: shieldMax,
          guardianBarrier: barrier,
          guardianBarrierMax: barrierMax,
        });
      }

      return { rawMana, elements, modifiedDamage: dmg };
    };
  };

  // ─── Guardian Regen (once per tick, spec §5.3, D-25 fix) ─────────────────

  /**
   * Apply guardian shield/barrier regen once per tick.
   * Must be called exactly once per combat tick, not per-damage-event.
   */
  const applyGuardianRegen = () => {
    const guardian = getGuardianForFloor(ctx.combat.currentFloor);
    if (!guardian) return;

    let shield = ctx.combat.guardianShield;
    const shieldMax = ctx.combat.guardianShieldMax;
    let barrier = ctx.combat.guardianBarrier;
    const barrierMax = ctx.combat.guardianBarrierMax;
    let changed = false;

    if (guardian.shieldRegen && shield < shieldMax) {
      shield = Math.min(shieldMax, shield + guardian.shieldRegen * HOURS_PER_TICK);
      changed = true;
    }
    if (guardian.barrierRegen && barrier < barrierMax) {
      barrier = Math.min(barrierMax, barrier + guardian.barrierRegen * HOURS_PER_TICK);
      changed = true;
    }

    if (changed) {
      useCombatStore.setState({
        guardianShield: shield,
        guardianShieldMax: shieldMax,
        guardianBarrier: barrier,
        guardianBarrierMax: barrierMax,
      });
    }
  };

  return { onFloorCleared, makeOnDamageDealt, applyMageBarrierRecharge, applyGuardianRegen };
}
