import 'dart:math';

import 'package:flutter_test/flutter_test.dart';

/// 글로벌 쿨타임(GCD) 시뮬레이션 테스트
///
/// 다양한 GCD 값에 대해 전투 효율성을 측정
void main() {
  test('GCD 시뮬레이션 - 다양한 값 비교', () {
    print('\n' + '=' * 80);
    print('글로벌 쿨타임(GCD) 시뮬레이션 결과');
    print('=' * 80);

    // 테스트할 GCD 값들 (ms)
    final gcdValues = [0, 500, 1000, 1500, 2000, 2500, 3000];

    // 시뮬레이션 파라미터
    const playerAttackDelay = 1000; // 플레이어 기본 공격 딜레이
    const monsterAttackDelay = 1200; // 몬스터 공격 딜레이
    const skillUseProbability = 0.30; // 스킬 사용 확률 (30%)
    const normalAttackDamage = 50; // 일반 공격 데미지
    const skillDamage = 120; // 스킬 데미지 (평균)
    const monsterHp = 1000; // 몬스터 HP
    const simulationRuns = 1000; // 시뮬레이션 반복 횟수

    final results = <int, SimulationResult>{};

    for (final gcd in gcdValues) {
      final result = _runSimulation(
        gcd: gcd,
        playerAttackDelay: playerAttackDelay,
        monsterAttackDelay: monsterAttackDelay,
        skillUseProbability: skillUseProbability,
        normalAttackDamage: normalAttackDamage,
        skillDamage: skillDamage,
        monsterHp: monsterHp,
        runs: simulationRuns,
      );
      results[gcd] = result;
    }

    // 결과 출력
    print('\n### 시뮬레이션 조건');
    print('- 플레이어 공격 딜레이: ${playerAttackDelay}ms');
    print('- 몬스터 HP: $monsterHp');
    print('- 일반 공격 데미지: $normalAttackDamage');
    print('- 스킬 데미지 (평균): $skillDamage');
    print('- 스킬 사용 확률: ${(skillUseProbability * 100).toInt()}%');
    print('- 시뮬레이션 횟수: $simulationRuns회');

    print('\n### 결과 비교');
    print('| GCD (ms) | 전투시간 (ms) | 총 공격 | 스킬 사용 | 스킬비율 | DPS | 효율 |');
    print(
      '|----------|---------------|---------|-----------|----------|-----|------|',
    );

    final baselineDps = results[0]!.avgDps;
    for (final gcd in gcdValues) {
      final r = results[gcd]!;
      final efficiency = (r.avgDps / baselineDps * 100).toStringAsFixed(0);
      print(
        '| ${gcd.toString().padLeft(8)} '
        '| ${r.avgCombatTime.toStringAsFixed(0).padLeft(13)} '
        '| ${r.avgTotalAttacks.toStringAsFixed(1).padLeft(7)} '
        '| ${r.avgSkillUses.toStringAsFixed(1).padLeft(9)} '
        '| ${(r.skillRatio * 100).toStringAsFixed(1).padLeft(7)}% '
        '| ${r.avgDps.toStringAsFixed(1).padLeft(3)} '
        '| ${efficiency.padLeft(4)}% |',
      );
    }

    // 권장 GCD 분석
    print('\n### 분석 및 권장');

    // GCD 1500ms 기준 분석
    final gcd1500 = results[1500]!;
    final gcd2000 = results[2000]!;
    // gcd1000은 비교용으로 추후 활용 가능

    print('\n#### GCD별 특징');
    print('- **0ms (없음)**: 기준선, 스킬 남용 가능');
    print('- **500ms**: 거의 제한 없음, 빠른 연속 스킬 가능');
    print('- **1000ms**: 공격 딜레이와 동일, 매 공격마다 스킬 선택 가능');
    print('- **1500ms**: 스킬 후 1회 일반공격 강제, 적절한 제한');
    print('- **2000ms**: 스킬 후 2회 일반공격 강제, 스킬이 특별해짐');
    print('- **2500ms+**: 스킬 사용이 매우 제한적, 전략적 선택 필요');

    print('\n#### 권장 GCD');
    print('**1500ms ~ 2000ms 권장**');
    print('- 스킬 사용 빈도가 자연스럽게 제한됨');
    print('- 일반 공격의 중요성이 유지됨');
    print(
      '- DPS 손실이 크지 않음 (${((1 - gcd1500.avgDps / baselineDps) * 100).toStringAsFixed(0)}% ~ ${((1 - gcd2000.avgDps / baselineDps) * 100).toStringAsFixed(0)}%)',
    );

    print('\n' + '=' * 80);

    // 테스트는 항상 통과 (정보 출력용)
    expect(true, isTrue);
  });

  test('GCD 상세 시뮬레이션 - 레벨별 영향', () {
    print('\n' + '=' * 80);
    print('레벨별 GCD 영향 분석');
    print('=' * 80);

    // 레벨별 파라미터
    final levelConfigs = [
      {'level': 1, 'playerAtk': 30, 'skillDmg': 80, 'monsterHp': 200},
      {'level': 10, 'playerAtk': 50, 'skillDmg': 130, 'monsterHp': 500},
      {'level': 30, 'playerAtk': 90, 'skillDmg': 220, 'monsterHp': 1500},
      {'level': 50, 'playerAtk': 130, 'skillDmg': 320, 'monsterHp': 3000},
    ];

    const gcdToTest = [0, 1500, 2000];

    print('\n| 레벨 | GCD | 전투시간 | 스킬횟수 | DPS | 효율 |');
    print('|------|-----|----------|----------|-----|------|');

    for (final config in levelConfigs) {
      final level = config['level'] as int;
      final playerAtk = config['playerAtk'] as int;
      final skillDmg = config['skillDmg'] as int;
      final monsterHp = config['monsterHp'] as int;

      double? baselineDps;

      for (final gcd in gcdToTest) {
        final result = _runSimulation(
          gcd: gcd,
          playerAttackDelay: 1000,
          monsterAttackDelay: 1200,
          skillUseProbability: 0.30,
          normalAttackDamage: playerAtk,
          skillDamage: skillDmg,
          monsterHp: monsterHp,
          runs: 500,
        );

        baselineDps ??= result.avgDps;
        final efficiency = (result.avgDps / baselineDps * 100).toStringAsFixed(
          0,
        );

        print(
          '| ${level.toString().padLeft(4)} '
          '| ${gcd.toString().padLeft(3)} '
          '| ${result.avgCombatTime.toStringAsFixed(0).padLeft(8)} '
          '| ${result.avgSkillUses.toStringAsFixed(1).padLeft(8)} '
          '| ${result.avgDps.toStringAsFixed(1).padLeft(3)} '
          '| ${efficiency.padLeft(4)}% |',
        );
      }
    }

    print('\n' + '=' * 80);
    expect(true, isTrue);
  });
}

/// 시뮬레이션 실행
SimulationResult _runSimulation({
  required int gcd,
  required int playerAttackDelay,
  required int monsterAttackDelay,
  required double skillUseProbability,
  required int normalAttackDamage,
  required int skillDamage,
  required int monsterHp,
  required int runs,
}) {
  final random = Random(42); // 재현 가능한 시드

  var totalCombatTime = 0;
  var totalAttacks = 0;
  var totalSkillUses = 0;
  var totalDamage = 0;

  for (var i = 0; i < runs; i++) {
    var currentHp = monsterHp;
    var elapsedTime = 0;
    var attackCount = 0;
    var skillCount = 0;
    var gcdRemaining = 0; // 남은 GCD 시간

    var playerAccum = 0;

    while (currentHp > 0) {
      // 시간 진행 (100ms 단위)
      const tickMs = 100;
      elapsedTime += tickMs;
      playerAccum += tickMs;

      // GCD 감소
      if (gcdRemaining > 0) {
        gcdRemaining -= tickMs;
        if (gcdRemaining < 0) gcdRemaining = 0;
      }

      // 플레이어 공격 체크
      if (playerAccum >= playerAttackDelay) {
        playerAccum -= playerAttackDelay;
        attackCount++;

        // 스킬 사용 여부 결정
        final canUseSkill = gcdRemaining <= 0;
        final wantsToUseSkill = random.nextDouble() < skillUseProbability;

        if (canUseSkill && wantsToUseSkill) {
          // 스킬 사용
          currentHp -= skillDamage;
          totalDamage += skillDamage;
          skillCount++;
          gcdRemaining = gcd; // GCD 시작
        } else {
          // 일반 공격
          currentHp -= normalAttackDamage;
          totalDamage += normalAttackDamage;
        }
      }

      // 무한 루프 방지
      if (elapsedTime > 60000) break;
    }

    totalCombatTime += elapsedTime;
    totalAttacks += attackCount;
    totalSkillUses += skillCount;
  }

  final avgCombatTime = totalCombatTime / runs;
  final avgTotalAttacks = totalAttacks / runs;
  final avgSkillUses = totalSkillUses / runs;
  final skillRatio = avgSkillUses / avgTotalAttacks;
  final avgDps = totalDamage / runs / (avgCombatTime / 1000);

  return SimulationResult(
    avgCombatTime: avgCombatTime,
    avgTotalAttacks: avgTotalAttacks,
    avgSkillUses: avgSkillUses,
    skillRatio: skillRatio,
    avgDps: avgDps,
  );
}

class SimulationResult {
  const SimulationResult({
    required this.avgCombatTime,
    required this.avgTotalAttacks,
    required this.avgSkillUses,
    required this.skillRatio,
    required this.avgDps,
  });

  final double avgCombatTime;
  final double avgTotalAttacks;
  final double avgSkillUses;
  final double skillRatio;
  final double avgDps;
}
