import 'dart:math' as math;

import 'package:asciineverdie/src/core/model/combat_result.dart';
import 'package:asciineverdie/src/core/model/combat_stats.dart';
import 'package:asciineverdie/src/core/model/monster_combat_stats.dart';
import 'package:asciineverdie/src/core/util/deterministic_random.dart';

/// 전투 계산 서비스
///
/// 전투 데미지 계산, 명중/회피 판정 등 전투 관련 로직 전담.
/// SRP 원칙에 따라 계산 로직만 담당하며, 상태 관리는 외부에서 수행.
class CombatCalculator {
  CombatCalculator({required this.rng});

  /// 결정론적 난수 생성기 (재현 가능한 전투를 위해)
  final DeterministicRandom rng;

  // ============================================================================
  // 공격 계산
  // ============================================================================

  /// 플레이어가 몬스터를 공격
  ///
  /// [attacker] 공격자 (플레이어) 스탯
  /// [defender] 방어자 (몬스터) 스탯
  /// Returns: 공격 결과 및 업데이트된 몬스터 스탯
  ({AttackResult result, MonsterCombatStats updatedDefender})
  playerAttackMonster({
    required CombatStats attacker,
    required MonsterCombatStats defender,
  }) {
    final result = _calculateAttack(
      attackerAtk: attacker.atk,
      attackerAccuracy: attacker.accuracy,
      attackerCriRate: attacker.criRate,
      attackerCriDamage: attacker.criDamage,
      defenderDef: defender.def,
      defenderEvasion: defender.evasion,
      defenderBlockRate: 0.0, // 몬스터는 방패 없음
      defenderParryRate: 0.0, // 몬스터는 쳐내기 없음
    );

    final updatedDefender = defender.applyDamage(result.damage);

    return (result: result, updatedDefender: updatedDefender);
  }

  /// 몬스터가 플레이어를 공격
  ///
  /// [attacker] 공격자 (몬스터) 스탯
  /// [defender] 방어자 (플레이어) 스탯
  /// Returns: 공격 결과 및 업데이트된 플레이어 스탯
  ({AttackResult result, CombatStats updatedDefender}) monsterAttackPlayer({
    required MonsterCombatStats attacker,
    required CombatStats defender,
  }) {
    final result = _calculateAttack(
      attackerAtk: attacker.atk,
      attackerAccuracy: attacker.accuracy,
      attackerCriRate: attacker.criRate,
      attackerCriDamage: attacker.criDamage,
      defenderDef: defender.def,
      defenderEvasion: defender.evasion,
      defenderBlockRate: defender.blockRate,
      defenderParryRate: defender.parryRate,
    );

    final updatedDefender = defender.applyDamage(result.damage);

    return (result: result, updatedDefender: updatedDefender);
  }

  /// 범용 공격 계산
  AttackResult _calculateAttack({
    required int attackerAtk,
    required double attackerAccuracy,
    required double attackerCriRate,
    required double attackerCriDamage,
    required int defenderDef,
    required double defenderEvasion,
    required double defenderBlockRate,
    required double defenderParryRate,
  }) {
    // 1. 명중 판정
    final hitChance = attackerAccuracy - defenderEvasion;
    final hitRoll = rng.nextDouble();
    final isHit = hitRoll < hitChance;

    if (!isHit) {
      return AttackResult.miss();
    }

    // 2. 방어 판정
    final blockRoll = rng.nextDouble();
    final isBlocked = blockRoll < defenderBlockRate;

    final parryRoll = rng.nextDouble();
    final isParried = parryRoll < defenderParryRate;

    // 3. 기본 데미지 계산 (0.8 ~ 1.2 변동)
    // DEF 감산 비율: 0.4 (전체 데미지 상승 조정)
    final damageVariation = 0.8 + rng.nextDouble() * 0.4;
    var baseDamage = (attackerAtk * damageVariation - defenderDef * 0.4);

    // 4. 크리티컬 판정
    final criRoll = rng.nextDouble();
    final isCritical = criRoll < attackerCriRate;

    if (isCritical) {
      baseDamage *= attackerCriDamage;
    }

    // 5. 방어 적용
    if (isBlocked) {
      baseDamage *= 0.3; // 방패로 70% 감소
    } else if (isParried) {
      baseDamage *= 0.5; // 쳐내기로 50% 감소
    }

    // 6. 최종 데미지 (최소 1)
    final finalDamage = math.max(1, baseDamage.round());

    return AttackResult(
      damage: finalDamage,
      isCritical: isCritical,
      isHit: true,
      isBlocked: isBlocked,
      isParried: isParried,
      isEvaded: false,
    );
  }

  // ============================================================================
  // 전투 턴 처리
  // ============================================================================

  /// 전투 턴 실행
  ///
  /// [playerStats] 플레이어 전투 스탯
  /// [monsterStats] 몬스터 전투 스탯
  /// [elapsedMs] 경과 시간 (밀리초)
  /// [playerAttackAccumulatorMs] 플레이어 공격 누적 시간
  /// [monsterAttackAccumulatorMs] 몬스터 공격 누적 시간
  CombatTurnResult processTurn({
    required CombatStats playerStats,
    required MonsterCombatStats monsterStats,
    required int elapsedMs,
    required int playerAttackAccumulatorMs,
    required int monsterAttackAccumulatorMs,
  }) {
    var currentPlayerStats = playerStats;
    var currentMonsterStats = monsterStats;
    AttackResult? playerAttackResult;
    AttackResult? monsterAttackResult;

    // 플레이어 공격 시간 체크
    final newPlayerAccumulator = playerAttackAccumulatorMs + elapsedMs;
    if (newPlayerAccumulator >= currentPlayerStats.attackDelayMs) {
      // 플레이어 공격
      final attackResult = playerAttackMonster(
        attacker: currentPlayerStats,
        defender: currentMonsterStats,
      );
      playerAttackResult = attackResult.result;
      currentMonsterStats = attackResult.updatedDefender;
    }

    // 몬스터가 살아있으면 반격
    if (currentMonsterStats.isAlive) {
      final newMonsterAccumulator = monsterAttackAccumulatorMs + elapsedMs;
      if (newMonsterAccumulator >= currentMonsterStats.attackDelayMs) {
        // 몬스터 공격
        final attackResult = monsterAttackPlayer(
          attacker: currentMonsterStats,
          defender: currentPlayerStats,
        );
        monsterAttackResult = attackResult.result;
        currentPlayerStats = attackResult.updatedDefender;
      }
    }

    // 전투 종료 체크
    final isCombatOver =
        currentPlayerStats.isDead || currentMonsterStats.isDead;
    final isPlayerVictory = isCombatOver && currentMonsterStats.isDead;

    return CombatTurnResult(
      playerAttack: playerAttackResult,
      monsterAttack: monsterAttackResult,
      updatedPlayerStats: currentPlayerStats,
      updatedMonsterStats: currentMonsterStats,
      isCombatOver: isCombatOver,
      isPlayerVictory: isPlayerVictory,
    );
  }

  // ============================================================================
  // 유틸리티
  // ============================================================================

  /// 예상 전투 시간 계산 (밀리초)
  ///
  /// 플레이어와 몬스터의 예상 DPS를 기반으로 대략적인 전투 시간 추정.
  /// Idle 게임 특성상 태스크 바 진행 속도 결정에 사용.
  int estimateCombatDurationMs({
    required CombatStats player,
    required MonsterCombatStats monster,
  }) {
    // 플레이어 DPS (초당 데미지)
    final playerDamagePerHit = math.max(1, player.atk - monster.def * 0.4);
    final playerHitsPerSecond = 1000 / player.attackDelayMs;
    final playerDps =
        playerDamagePerHit * playerHitsPerSecond * player.accuracy;

    // 몬스터를 처치하는 데 필요한 시간 (밀리초)
    final timeToKillMonster = (monster.hpMax / playerDps * 1000).round();

    // 최소 2초, 최대 30초
    return timeToKillMonster.clamp(2000, 30000);
  }

  /// 전투 난이도 평가 (0.0 ~ 1.0)
  ///
  /// 0.0 = 매우 쉬움, 0.5 = 적당, 1.0 = 매우 어려움
  double evaluateDifficulty({
    required CombatStats player,
    required MonsterCombatStats monster,
  }) {
    // 플레이어 예상 생존 시간
    final monsterDamagePerHit = math.max(1, monster.atk - player.def * 0.4);
    final monsterHitsPerSecond = 1000 / monster.attackDelayMs;
    final monsterDps =
        monsterDamagePerHit * monsterHitsPerSecond * monster.accuracy;
    final playerSurvivalTime = player.hpCurrent / monsterDps;

    // 몬스터 예상 생존 시간
    final playerDamagePerHit = math.max(1, player.atk - monster.def * 0.4);
    final playerHitsPerSecond = 1000 / player.attackDelayMs;
    final playerDps =
        playerDamagePerHit * playerHitsPerSecond * player.accuracy;
    final monsterSurvivalTime = monster.hpCurrent / playerDps;

    // 난이도 = 몬스터 생존시간 / (플레이어 생존시간 + 몬스터 생존시간)
    final difficulty =
        monsterSurvivalTime / (playerSurvivalTime + monsterSurvivalTime);

    return difficulty.clamp(0.0, 1.0);
  }
}
