import 'package:asciineverdie/data/class_data.dart';
import 'package:asciineverdie/data/skill_data.dart';
import 'package:asciineverdie/src/core/engine/combat_calculator.dart';
import 'package:asciineverdie/src/core/model/class_traits.dart';
import 'package:asciineverdie/src/core/engine/player_attack_processor.dart';
import 'package:asciineverdie/src/core/engine/potion_service.dart';
import 'package:asciineverdie/src/core/engine/skill_service.dart';
import 'package:asciineverdie/src/core/model/combat_event.dart';
import 'package:asciineverdie/src/core/model/combat_state.dart';
import 'package:asciineverdie/src/core/model/combat_stats.dart';
import 'package:asciineverdie/src/core/model/game_state.dart';
import 'package:asciineverdie/src/core/model/monster_combat_stats.dart';
import 'package:asciineverdie/src/core/model/potion.dart';
import 'package:asciineverdie/src/core/model/skill.dart';
import 'package:asciineverdie/src/core/util/deterministic_random.dart';

/// 전투 틱 처리 결과
class CombatTickResult {
  const CombatTickResult({
    required this.combat,
    required this.skillSystem,
    this.potionInventory,
  });

  final CombatState combat;
  final SkillSystemState skillSystem;
  final PotionInventory? potionInventory;
}

/// 전투 틱 처리 서비스
///
/// ProgressService에서 분리된 전투 로직 담당:
/// - 스킬 자동 사용
/// - DOT 처리
/// - 물약 자동 사용
/// - 플레이어/몬스터 공격 처리
class CombatTickService {
  CombatTickService({required this.rng});

  final DeterministicRandom rng;

  /// 전투 틱 처리 (스킬 자동 사용, DOT, 물약 포함)
  ///
  /// [state] 현재 게임 상태
  /// [combat] 현재 전투 상태
  /// [skillSystem] 스킬 시스템 상태
  /// [elapsedMs] 경과 시간 (밀리초)
  CombatTickResult processTick({
    required GameState state,
    required CombatState combat,
    required SkillSystemState skillSystem,
    required int elapsedMs,
  }) {
    if (!combat.isActive || combat.isCombatOver) {
      return CombatTickResult(
        combat: combat,
        skillSystem: skillSystem,
        potionInventory: null,
      );
    }

    final calculator = CombatCalculator(rng: rng);
    final skillService = SkillService(rng: rng);
    final potionService = const PotionService();
    var playerStats = combat.playerStats;
    var monsterStats = combat.monsterStats;
    var playerAccumulator = combat.playerAttackAccumulatorMs + elapsedMs;
    var monsterAccumulator = combat.monsterAttackAccumulatorMs + elapsedMs;
    var totalDamageDealt = combat.totalDamageDealt;
    var totalDamageTaken = combat.totalDamageTaken;
    var turnsElapsed = combat.turnsElapsed;
    var updatedSkillSystem = skillSystem;
    var activeDoTs = [...combat.activeDoTs];
    var lastPotionUsedMs = combat.lastPotionUsedMs;
    var activeDebuffs = [...combat.activeDebuffs];
    PotionInventory? updatedPotionInventory;

    // 새 전투 이벤트 수집
    final newEvents = <CombatEvent>[];
    final timestamp = updatedSkillSystem.elapsedMs;

    // 만료된 디버프 정리
    activeDebuffs = activeDebuffs
        .where((debuff) => !debuff.isExpired(timestamp))
        .toList();

    // DOT 틱 처리
    final dotResult = _processDotTicks(
      activeDoTs: activeDoTs,
      monsterStats: monsterStats,
      elapsedMs: elapsedMs,
      timestamp: timestamp,
      totalDamageDealt: totalDamageDealt,
    );
    activeDoTs = dotResult.activeDoTs;
    monsterStats = dotResult.monsterStats;
    totalDamageDealt = dotResult.totalDamageDealt;
    newEvents.addAll(dotResult.events);

    // 클래스 패시브 조회 (healingBonus, firstStrikeBonus, multiAttack)
    final klass = ClassData.findById(state.traits.classId);
    final healingBonus =
        klass?.getPassiveValue(ClassPassiveType.healingBonus) ?? 0.0;
    final healingMultiplier = 1.0 + healingBonus;
    final firstStrikeBonus =
        klass?.getPassiveValue(ClassPassiveType.firstStrikeBonus) ?? 0.0;
    final hasMultiAttack =
        klass?.hasPassive(ClassPassiveType.multiAttack) ?? false;
    var isFirstPlayerAttack = combat.isFirstPlayerAttack;

    // 긴급 물약 자동 사용 (HP < 30% 또는 MP < 50%)
    final potionResult = _tryEmergencyPotion(
      playerStats: playerStats,
      potionInventory: state.potionInventory,
      lastPotionUsedMs: lastPotionUsedMs,
      playerLevel: state.traits.level,
      timestamp: timestamp,
      potionService: potionService,
      healingMultiplier: healingMultiplier,
    );
    if (potionResult != null) {
      playerStats = potionResult.playerStats;
      lastPotionUsedMs = potionResult.lastPotionUsedMs;
      updatedPotionInventory = potionResult.potionInventory;
      newEvents.addAll(potionResult.events);
    }

    // 플레이어 공격 체크
    if (playerAccumulator >= playerStats.attackDelayMs) {
      final attackProcessor = PlayerAttackProcessor(rng: rng);
      final attackResult = attackProcessor.processAttack(
        state: state,
        playerStats: playerStats,
        monsterStats: monsterStats,
        updatedSkillSystem: updatedSkillSystem,
        activeDoTs: activeDoTs,
        activeDebuffs: activeDebuffs,
        totalDamageDealt: totalDamageDealt,
        timestamp: timestamp,
        calculator: calculator,
        skillService: skillService,
        isFirstPlayerAttack: isFirstPlayerAttack,
        firstStrikeBonus: firstStrikeBonus > 0 ? firstStrikeBonus : 1.0,
        hasMultiAttack: hasMultiAttack,
        healingMultiplier: healingMultiplier,
      );

      playerStats = attackResult.playerStats;
      monsterStats = attackResult.monsterStats;
      updatedSkillSystem = attackResult.skillSystem;
      activeDoTs = attackResult.activeDoTs;
      activeDebuffs = attackResult.activeDebuffs;
      totalDamageDealt = attackResult.totalDamageDealt;
      newEvents.addAll(attackResult.events);
      isFirstPlayerAttack = attackResult.isFirstPlayerAttack;

      playerAccumulator -= playerStats.attackDelayMs;
      turnsElapsed++;
    }

    // 몬스터가 살아있으면 반격
    if (monsterStats.isAlive &&
        monsterAccumulator >= monsterStats.attackDelayMs) {
      final monsterAttackResult = _processMonsterAttack(
        playerStats: playerStats,
        monsterStats: monsterStats,
        activeDebuffs: activeDebuffs,
        totalDamageTaken: totalDamageTaken,
        timestamp: timestamp,
        calculator: calculator,
      );

      playerStats = monsterAttackResult.playerStats;
      totalDamageTaken = monsterAttackResult.totalDamageTaken;
      newEvents.addAll(monsterAttackResult.events);
      monsterAccumulator -= monsterStats.attackDelayMs;
    }

    // 전투 종료 체크
    final isActive = playerStats.isAlive && monsterStats.isAlive;

    // 기존 이벤트와 합쳐서 최대 10개 유지
    final combinedEvents = [...combat.recentEvents, ...newEvents];
    final recentEvents = combinedEvents.length > 10
        ? combinedEvents.sublist(combinedEvents.length - 10)
        : combinedEvents;

    return CombatTickResult(
      combat: combat.copyWith(
        playerStats: playerStats,
        monsterStats: monsterStats,
        playerAttackAccumulatorMs: playerAccumulator,
        monsterAttackAccumulatorMs: monsterAccumulator,
        totalDamageDealt: totalDamageDealt,
        totalDamageTaken: totalDamageTaken,
        turnsElapsed: turnsElapsed,
        isActive: isActive,
        recentEvents: recentEvents,
        activeDoTs: activeDoTs,
        lastPotionUsedMs: lastPotionUsedMs,
        activeDebuffs: activeDebuffs,
        isFirstPlayerAttack: isFirstPlayerAttack,
      ),
      skillSystem: updatedSkillSystem,
      potionInventory: updatedPotionInventory,
    );
  }

  /// DOT 틱 처리
  ({
    List<DotEffect> activeDoTs,
    MonsterCombatStats monsterStats,
    int totalDamageDealt,
    List<CombatEvent> events,
  })
  _processDotTicks({
    required List<DotEffect> activeDoTs,
    required MonsterCombatStats monsterStats,
    required int elapsedMs,
    required int timestamp,
    required int totalDamageDealt,
  }) {
    var dotDamageThisTick = 0;
    final updatedDoTs = <DotEffect>[];
    final events = <CombatEvent>[];
    var updatedMonster = monsterStats;

    for (final dot in activeDoTs) {
      final (updatedDot, ticksTriggered) = dot.tick(elapsedMs);

      if (ticksTriggered > 0) {
        final damage = dot.damagePerTick * ticksTriggered;
        dotDamageThisTick += damage;

        // DOT 데미지 이벤트 생성
        final dotSkillName =
            SkillData.getSkillById(dot.skillId)?.name ?? dot.skillId;
        events.add(
          CombatEvent.dotTick(
            timestamp: timestamp,
            skillName: dotSkillName,
            damage: damage,
            targetName: updatedMonster.name,
          ),
        );
      }

      // 만료되지 않은 DOT만 유지
      if (updatedDot.isActive) {
        updatedDoTs.add(updatedDot);
      }
    }

    // DOT 데미지 적용
    if (dotDamageThisTick > 0 && updatedMonster.isAlive) {
      final newMonsterHp = (updatedMonster.hpCurrent - dotDamageThisTick).clamp(
        0,
        updatedMonster.hpMax,
      );
      updatedMonster = updatedMonster.copyWith(hpCurrent: newMonsterHp);
      totalDamageDealt += dotDamageThisTick;
    }

    return (
      activeDoTs: updatedDoTs,
      monsterStats: updatedMonster,
      totalDamageDealt: totalDamageDealt,
      events: events,
    );
  }

  /// 긴급 물약 자동 사용 (HP/MP 통합 글로벌 쿨타임)
  ({
    CombatStats playerStats,
    int lastPotionUsedMs,
    PotionInventory potionInventory,
    List<CombatEvent> events,
  })?
  _tryEmergencyPotion({
    required CombatStats playerStats,
    required PotionInventory potionInventory,
    required int lastPotionUsedMs,
    required int playerLevel,
    required int timestamp,
    required PotionService potionService,
    double healingMultiplier = 1.0,
  }) {
    // 글로벌 쿨타임 체크
    if (timestamp - lastPotionUsedMs < PotionService.globalPotionCooldownMs) {
      return null;
    }

    // 우선순위 1: HP 물약 (소모된 HP >= 물약 회복량)
    final hpPotion = potionService.selectEmergencyHpPotion(
      currentHp: playerStats.hpCurrent,
      maxHp: playerStats.hpMax,
      inventory: potionInventory,
      playerLevel: playerLevel,
    );

    if (hpPotion != null) {
      final result = potionService.usePotion(
        potionId: hpPotion.id,
        inventory: potionInventory,
        currentHp: playerStats.hpCurrent,
        maxHp: playerStats.hpMax,
        currentMp: playerStats.mpCurrent,
        maxMp: playerStats.mpMax,
        healingMultiplier: healingMultiplier,
      );

      if (result.success) {
        return (
          playerStats: playerStats.copyWith(hpCurrent: result.newHp),
          lastPotionUsedMs: timestamp,
          potionInventory: result.newInventory!,
          events: [
            CombatEvent.playerPotion(
              timestamp: timestamp,
              potionName: hpPotion.name,
              healAmount: result.healedAmount,
              isHp: true,
            ),
          ],
        );
      }
    }

    // 우선순위 2: MP 물약 (소모된 MP >= 물약 회복량)
    final mpPotion = potionService.selectEmergencyMpPotion(
      currentMp: playerStats.mpCurrent,
      maxMp: playerStats.mpMax,
      inventory: potionInventory,
      playerLevel: playerLevel,
    );

    if (mpPotion != null) {
      final result = potionService.usePotion(
        potionId: mpPotion.id,
        inventory: potionInventory,
        currentHp: playerStats.hpCurrent,
        maxHp: playerStats.hpMax,
        currentMp: playerStats.mpCurrent,
        maxMp: playerStats.mpMax,
        healingMultiplier: healingMultiplier,
      );

      if (result.success) {
        return (
          playerStats: playerStats.copyWith(mpCurrent: result.newMp),
          lastPotionUsedMs: timestamp,
          potionInventory: result.newInventory!,
          events: [
            CombatEvent.playerPotion(
              timestamp: timestamp,
              potionName: mpPotion.name,
              healAmount: result.healedAmount,
              isHp: false,
            ),
          ],
        );
      }
    }

    return null;
  }

  /// 몬스터 공격 처리
  ({CombatStats playerStats, int totalDamageTaken, List<CombatEvent> events})
  _processMonsterAttack({
    required CombatStats playerStats,
    required MonsterCombatStats monsterStats,
    required List<ActiveBuff> activeDebuffs,
    required int totalDamageTaken,
    required int timestamp,
    required CombatCalculator calculator,
  }) {
    final events = <CombatEvent>[];

    // 디버프 효과 적용된 몬스터 스탯 계산
    var debuffedMonster = monsterStats;
    if (activeDebuffs.isNotEmpty) {
      double atkMod = 0;
      for (final debuff in activeDebuffs) {
        if (!debuff.isExpired(timestamp)) {
          atkMod += debuff.effect.atkModifier;
        }
      }
      // ATK 감소 적용 (최소 10% ATK 유지)
      final newAtk = (monsterStats.atk * (1 + atkMod)).round().clamp(
        monsterStats.atk ~/ 10,
        monsterStats.atk,
      );
      debuffedMonster = monsterStats.copyWith(atk: newAtk);
    }

    final attackResult = calculator.monsterAttackPlayer(
      attacker: debuffedMonster,
      defender: playerStats,
    );

    final result = attackResult.result;
    if (result.isEvaded) {
      events.add(
        CombatEvent.playerEvade(
          timestamp: timestamp,
          attackerName: monsterStats.name,
        ),
      );
    } else if (result.isBlocked) {
      events.add(
        CombatEvent.playerBlock(
          timestamp: timestamp,
          reducedDamage: result.damage,
          attackerName: monsterStats.name,
        ),
      );
    } else if (result.isParried) {
      events.add(
        CombatEvent.playerParry(
          timestamp: timestamp,
          reducedDamage: result.damage,
          attackerName: monsterStats.name,
        ),
      );
    } else {
      events.add(
        CombatEvent.monsterAttack(
          timestamp: timestamp,
          damage: result.damage,
          attackerName: monsterStats.name,
          attackDelayMs: monsterStats.attackDelayMs,
        ),
      );
    }

    return (
      playerStats: attackResult.updatedDefender,
      totalDamageTaken: totalDamageTaken + attackResult.result.damage,
      events: events,
    );
  }
}
