import 'package:asciineverdie/src/core/engine/arena_service.dart';
import 'package:asciineverdie/src/core/model/arena_match.dart';
import 'package:asciineverdie/src/core/model/combat_stats.dart';
import 'package:asciineverdie/src/core/model/equipment_item.dart';
import 'package:asciineverdie/src/core/model/equipment_slot.dart';
import 'package:asciineverdie/src/core/model/hall_of_fame.dart';
import 'package:asciineverdie/src/core/model/item_stats.dart';
import 'package:asciineverdie/src/core/util/deterministic_random.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  late ArenaService service;

  setUp(() {
    service = ArenaService(rng: DeterministicRandom(42));
  });

  // ============================================================================
  // 상대 결정 (matchmaking)
  // ============================================================================

  group('findOpponent', () {
    test('엔트리가 2개 미만이면 null 반환', () {
      final hof = HallOfFame(entries: [_createEntry(id: 'a', level: 10)]);
      final result = service.findOpponent(hof, 'a');
      expect(result, isNull);
    });

    test('1위 도전자는 2위와 대결', () {
      // 점수(score) 높은 순: a(Lv50) > b(Lv30) > c(Lv10)
      final hof = HallOfFame(
        entries: [
          _createEntry(id: 'a', level: 50),
          _createEntry(id: 'b', level: 30),
          _createEntry(id: 'c', level: 10),
        ],
      );

      final opponent = service.findOpponent(hof, 'a');
      expect(opponent, isNotNull);
      expect(opponent!.id, 'b');
    });

    test('2위 이하는 바로 위 순위와 대결', () {
      final hof = HallOfFame(
        entries: [
          _createEntry(id: 'a', level: 50),
          _createEntry(id: 'b', level: 30),
          _createEntry(id: 'c', level: 10),
        ],
      );

      // c는 3위 → 2위(b)와 대결
      final opponent = service.findOpponent(hof, 'c');
      expect(opponent, isNotNull);
      expect(opponent!.id, 'b');
    });

    test('존재하지 않는 ID는 null 반환', () {
      final hof = HallOfFame(
        entries: [
          _createEntry(id: 'a', level: 50),
          _createEntry(id: 'b', level: 30),
        ],
      );

      final opponent = service.findOpponent(hof, 'nonexistent');
      expect(opponent, isNull);
    });
  });

  // ============================================================================
  // 장비 교환 (equipment exchange)
  // ============================================================================

  group('createResultFromSimulation', () {
    test('도전자 승리 시 상대 베팅 슬롯 장비를 획득', () {
      final challengerEquip = _createEquipmentList(helmName: 'Basic Helm');
      final opponentEquip = _createEquipmentList(helmName: 'Epic Helm');

      final match = ArenaMatch(
        challenger: _createEntry(
          id: 'challenger',
          level: 10,
          equipment: challengerEquip,
        ),
        opponent: _createEntry(
          id: 'opponent',
          level: 10,
          equipment: opponentEquip,
        ),
        // 도전자가 상대의 helm 슬롯을 노림
        challengerBettingSlot: EquipmentSlot.helm,
        // 상대가 도전자의 shield 슬롯을 노림
        opponentBettingSlot: EquipmentSlot.shield,
      );

      final result = service.createResultFromSimulation(
        match: match,
        challengerHp: 50,
        opponentHp: 0,
        turns: 10,
      );

      // 도전자 승리
      expect(result.isVictory, isTrue);

      // 도전자가 상대의 helm(Epic Helm)을 획득
      final challengerHelm = result.updatedChallenger.finalEquipment!
          .firstWhere((e) => e.slot == EquipmentSlot.helm);
      expect(challengerHelm.name, 'Epic Helm');

      // 상대의 helm은 기본 장비로 대체
      final opponentHelm = result.updatedOpponent.finalEquipment!.firstWhere(
        (e) => e.slot == EquipmentSlot.helm,
      );
      expect(opponentHelm.name, isNot('Epic Helm'));
    });

    test('도전자 패배 시 도전자 베팅 슬롯 장비를 상대가 획득', () {
      final challengerEquip = _createEquipmentList(shieldName: 'Rare Shield');
      final opponentEquip = _createEquipmentList(shieldName: 'Old Shield');

      final match = ArenaMatch(
        challenger: _createEntry(
          id: 'challenger',
          level: 10,
          equipment: challengerEquip,
        ),
        opponent: _createEntry(
          id: 'opponent',
          level: 10,
          equipment: opponentEquip,
        ),
        challengerBettingSlot: EquipmentSlot.helm,
        // 상대가 도전자의 shield를 노림
        opponentBettingSlot: EquipmentSlot.shield,
      );

      final result = service.createResultFromSimulation(
        match: match,
        challengerHp: 0,
        opponentHp: 50,
        turns: 10,
      );

      // 도전자 패배
      expect(result.isVictory, isFalse);

      // 상대가 도전자의 shield(Rare Shield)를 획득
      final opponentShield = result.updatedOpponent.finalEquipment!.firstWhere(
        (e) => e.slot == EquipmentSlot.shield,
      );
      expect(opponentShield.name, 'Rare Shield');

      // 도전자의 shield는 기본 장비로 대체
      final challengerShield = result.updatedChallenger.finalEquipment!
          .firstWhere((e) => e.slot == EquipmentSlot.shield);
      expect(challengerShield.name, isNot('Rare Shield'));
    });

    test('양쪽 모두 HP > 0이면 패배 처리', () {
      final match = _createSimpleMatch();
      final result = service.createResultFromSimulation(
        match: match,
        challengerHp: 50,
        opponentHp: 50,
        turns: 100,
      );

      // 도전자 HP > 0이지만 상대도 > 0이므로 패배
      expect(result.isVictory, isFalse);
    });
  });

  // ============================================================================
  // 베팅 슬롯 선택 (betting slot selection)
  // ============================================================================

  group('selectOpponentBettingSlot', () {
    test('장비가 없으면 기본 슬롯(helm) 반환', () {
      final entry = _createEntry(id: 'test', level: 5, equipment: []);
      final slot = service.selectOpponentBettingSlot(entry);
      expect(slot, EquipmentSlot.helm);
    });

    test('무기(weapon)는 약탈 대상에서 제외', () {
      // 무기만 높은 점수(score)를 가진 장비
      final equipment = [
        const EquipmentItem(
          name: 'God Keyboard',
          slot: EquipmentSlot.weapon,
          level: 99,
          weight: 5,
          stats: ItemStats(atk: 999),
          rarity: ItemRarity.legendary,
        ),
        const EquipmentItem(
          name: 'Basic Shield',
          slot: EquipmentSlot.shield,
          level: 1,
          weight: 8,
          stats: ItemStats(def: 1),
          rarity: ItemRarity.common,
        ),
      ];

      final entry = _createEntry(id: 'test', level: 50, equipment: equipment);
      final slot = service.selectOpponentBettingSlot(entry);

      // 무기가 아닌 shield가 선택되어야 함
      expect(slot, isNot(EquipmentSlot.weapon));
    });
  });

  group('getBettableSlots', () {
    test('무기(weapon)를 제외한 슬롯 목록 반환', () {
      final slots = service.getBettableSlots();
      expect(slots, isNot(contains(EquipmentSlot.weapon)));
      expect(slots.length, EquipmentSlot.values.length - 1);
    });
  });

  // ============================================================================
  // 아레나 점수 계산 (arena score)
  // ============================================================================

  group('HallOfFameArenaX.calculateArenaScore', () {
    test('레벨이 높을수록 점수가 높다', () {
      final low = _createEntry(id: 'low', level: 5);
      final high = _createEntry(id: 'high', level: 50);

      final lowScore = HallOfFameArenaX.calculateArenaScore(low);
      final highScore = HallOfFameArenaX.calculateArenaScore(high);

      expect(highScore, greaterThan(lowScore));
    });

    test('장비가 좋을수록 점수가 높다', () {
      final weak = _createEntry(id: 'weak', level: 10);
      final strong = _createEntry(
        id: 'strong',
        level: 10,
        equipment: [
          const EquipmentItem(
            name: 'Epic Sword',
            slot: EquipmentSlot.weapon,
            level: 50,
            weight: 5,
            stats: ItemStats(atk: 100),
            rarity: ItemRarity.epic,
          ),
        ],
      );

      final weakScore = HallOfFameArenaX.calculateArenaScore(weak);
      final strongScore = HallOfFameArenaX.calculateArenaScore(strong);

      expect(strongScore, greaterThan(weakScore));
    });
  });
}

// ============================================================================
// 테스트 헬퍼 (test helpers)
// ============================================================================

/// 테스트용 HallOfFameEntry 생성
HallOfFameEntry _createEntry({
  required String id,
  required int level,
  List<EquipmentItem>? equipment,
  CombatStats? stats,
}) {
  return HallOfFameEntry(
    id: id,
    characterName: 'Test Character $id',
    race: 'Human',
    klass: 'Fighter',
    level: level,
    totalPlayTimeMs: 3600000,
    totalDeaths: 0,
    monstersKilled: 100,
    questsCompleted: 10,
    clearedAt: DateTime(2025, 1, 1),
    finalStats: stats,
    finalEquipment: equipment,
  );
}

/// 테스트용 장비 목록 생성 (helm, shield 이름 커스텀 가능)
List<EquipmentItem> _createEquipmentList({
  String helmName = 'Test Helm',
  String shieldName = 'Test Shield',
}) {
  return [
    EquipmentItem.defaultWeapon(),
    EquipmentItem(
      name: shieldName,
      slot: EquipmentSlot.shield,
      level: 5,
      weight: 8,
      stats: const ItemStats(def: 5),
      rarity: ItemRarity.common,
    ),
    EquipmentItem(
      name: helmName,
      slot: EquipmentSlot.helm,
      level: 5,
      weight: 4,
      stats: const ItemStats(def: 3),
      rarity: ItemRarity.common,
    ),
  ];
}

/// 간단한 ArenaMatch 생성
ArenaMatch _createSimpleMatch() {
  final equip = _createEquipmentList();
  return ArenaMatch(
    challenger: _createEntry(id: 'c', level: 10, equipment: equip),
    opponent: _createEntry(id: 'o', level: 10, equipment: equip),
    challengerBettingSlot: EquipmentSlot.helm,
    opponentBettingSlot: EquipmentSlot.shield,
  );
}
