// ─── Melee Combat Processing ───────────────────────────────────────────────────
// Extracted from combat-actions.ts to stay under the 400-line file limit.

import { HOURS_PER_TICK, EQUIPMENT_TYPES } from '../constants';
import type { CombatStore, CombatState } from './combat-state.types';
import type { EquipmentInstance } from '../types';
import { getFloorElement, getMultiElementBonus, calcMeleeDamage } from '../utils';
import { getGuardianForFloor } from '../data/guardian-encounters';
import { applyDamageToRoom, deductWeaponEnchantCosts } from './combat-damage';
import { getChannelMultiplier } from './combat-channel';

export interface MeleeTickParams {
  get: () => CombatStore;
  set: (s: Partial<CombatState>) => void;
  rawMana: number;
  elements: Record<string, { current: number; max: number; unlocked: boolean }>;
  attackSpeedMult: number;
  equippedSwords: Record<string, EquipmentInstance>;
  floorHP: number;
  currentRoom: import('../types').FloorState;
}

export interface MeleeTickResult {
  rawMana: number;
  elements: Record<string, { current: number; max: number; unlocked: boolean }>;
  floorHP: number;
  floorMaxHP: number;
  currentFloor: number;
  currentRoom: import('../types').FloorState;
  meleeSwordProgress: Record<string, number>;
  logMessages: string[];
}

export function processMeleeTick(
  params: MeleeTickParams,
  applyEnemyDefenses: (
    dmg: number,
    enemy: import('../types').EnemyState | null,
    roomType: string,
    addLog: (msg: string) => void,
    bypassArmor?: boolean,
    bypassBarrier?: boolean,
  ) => number,
  onFloorCleared: (floor: number, wasGuardian: boolean) => void,
): MeleeTickResult {
  const { get, set, attackSpeedMult, equippedSwords } = params;
  let rawMana = params.rawMana;
  let elements = params.elements;
  let floorHP = params.floorHP;
  let floorMaxHP = params.get().floorMaxHP;
  let currentFloor = params.get().currentFloor;
  let currentRoom = params.currentRoom;
  const logMessages: string[] = [];

  const updatedMeleeSwordProgress = { ...params.get().meleeSwordProgress };
  const floorElement = getFloorElement(currentFloor);
  const guardian = getGuardianForFloor(currentFloor);
  const floorElems = guardian && guardian.element.length > 0 ? guardian.element : [floorElement];

  if (equippedSwords && Object.keys(equippedSwords).length > 0 && floorHP > 0) {
    for (const [instanceId, swordInstance] of Object.entries(equippedSwords)) {
      const swordType = EQUIPMENT_TYPES[swordInstance.typeId];
      if (!swordType || !swordType.stats?.attackSpeed) continue;
      const swordAttackSpeed = swordType.stats.attackSpeed;
      const channelMult = getChannelMultiplier(get);
      const meleeProgressPerTick = HOURS_PER_TICK * swordAttackSpeed * attackSpeedMult * channelMult;
      let meleeProgress = (updatedMeleeSwordProgress[instanceId] || 0) + meleeProgressPerTick;
      let meleeSafetyCounter = 0;
      while (meleeProgress >= 1 && meleeSafetyCounter < 100) {
        const meleeDamage = calcMeleeDamage(swordInstance as EquipmentInstance, swordType, floorElems[0]);

        const enchantCost = deductWeaponEnchantCosts(swordInstance as EquipmentInstance, rawMana, elements);
        rawMana = enchantCost.rawMana;
        elements = enchantCost.elements;

        const currentRoomState = get().currentRoom;
        const livingEnemies = currentRoomState.enemies.filter(e => e.hp > 0);
        const targetEnemy = livingEnemies.length > 0
          ? livingEnemies.reduce((lowest, e) => e.hp < lowest.hp ? e : lowest)
          : null;
        const finalMeleeDamage = applyEnemyDefenses(meleeDamage, targetEnemy, currentRoomState.roomType, (msg) => logMessages.push(msg));
        if (!Number.isFinite(finalMeleeDamage)) break;

        const meleeRoomResult = applyDamageToRoom(get, set, finalMeleeDamage, false);
        floorHP = meleeRoomResult.floorHP;
        floorMaxHP = meleeRoomResult.floorMaxHP;
        currentRoom = get().currentRoom;
        meleeProgress -= 1;
        meleeSafetyCounter++;

        if (meleeRoomResult.roomCleared) {
          const g = getGuardianForFloor(currentFloor);
          onFloorCleared(currentFloor, !!g);
          get().advanceRoomOrFloor();
          const ns = get();
          currentFloor = ns.currentFloor;
          floorMaxHP = ns.floorMaxHP;
          floorHP = ns.floorHP;
          currentRoom = ns.currentRoom;
          meleeProgress = 0;
          break;
        }
      }
      updatedMeleeSwordProgress[instanceId] = meleeProgress % 1;
    }
  }

  return { rawMana, elements, floorHP, floorMaxHP, currentFloor, currentRoom, meleeSwordProgress: updatedMeleeSwordProgress, logMessages };
}
