import 'package:asciineverdie/src/core/model/game_state.dart';
import 'package:asciineverdie/src/core/model/pq_config.dart';
import 'package:asciineverdie/src/core/util/deterministic_random.dart';
import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
import 'package:flutter_test/flutter_test.dart';

void main() {
  const config = PqConfig();

  test('levelUpTime grows with level and matches expected seconds', () {
    // Act 진행과 동기화된 레벨업 시간
    // Act I (레벨 1-20): 300 + level * 6
    expect(pq_logic.levelUpTime(1), 306); // 300 + 6 = 306초 (~5분)
    expect(pq_logic.levelUpTime(10), 360); // 300 + 60 = 360초 (6분)
    expect(pq_logic.levelUpTime(20), 420); // 300 + 120 = 420초 (7분)
    // Act II/III (레벨 21-60): 400 + (level-X) * 10
    expect(pq_logic.levelUpTime(30), 500); // 400 + 100 = 500초
    expect(pq_logic.levelUpTime(50), 500); // 400 + 100 = 500초
    // Act V (레벨 81-100): 60 + (level-80) * 3 (후반 가속)
    expect(pq_logic.levelUpTime(100), 120); // 60 + 60 = 120초 (2분)
  });

  test('roughTime formats seconds into human-readable strings', () {
    // < 120초: seconds
    expect(pq_logic.roughTime(60), '60 seconds');
    expect(pq_logic.roughTime(119), '119 seconds');

    // 120초 ~ 2시간: minutes
    expect(pq_logic.roughTime(120), '2 minutes');
    expect(pq_logic.roughTime(3600), '60 minutes');
    expect(pq_logic.roughTime(7199), '119 minutes');

    // 2시간 ~ 48시간: hours
    expect(pq_logic.roughTime(7200), '2 hours');
    expect(pq_logic.roughTime(86400), '24 hours');
    expect(pq_logic.roughTime(172799), '47 hours');

    // 48시간 이상: days
    expect(pq_logic.roughTime(172800), '2 days');
    expect(pq_logic.roughTime(604800), '7 days');
  });

  test('generateName produces deterministic names per seed', () {
    expect(pq_logic.generateName(DeterministicRandom(123)), 'Grunax');
    expect(pq_logic.generateName(DeterministicRandom(42)), 'Bregpran');
  });

  test('indefinite/definite/pluralize honor English rules', () {
    expect(pq_logic.indefinite('apple', 1), 'an apple');
    expect(pq_logic.indefinite('orc', 3), '3 orcs');
    expect(pq_logic.definite('goblin', 2), 'the goblins');
    expect(pq_logic.pluralize('baby'), 'babies');
  });

  test('item and reward helpers are deterministic with seed', () {
    // 아스키나라(ASCII-Nara) 세계관 데이터로 업데이트
    // 결정론적 결과가 일관되게 생성되는지 확인 (비어있지 않음)
    expect(pq_logic.boringItem(config, DeterministicRandom(12)), isNotEmpty);
    expect(
      pq_logic.interestingItem(config, DeterministicRandom(12)),
      isNotEmpty,
    );
    expect(pq_logic.specialItem(config, DeterministicRandom(12)), isNotEmpty);
    // 원본 Main.pas:770-774 RandomLow 방식으로 수정됨
    final spell = pq_logic.winSpell(config, DeterministicRandom(22), 7, 4);
    expect(spell, isNotEmpty);
    expect(spell, contains('|'));
    final weapon = pq_logic.winEquip(
      config,
      DeterministicRandom(12),
      5,
      0, // weapon slot
    );
    expect(weapon, isNotEmpty);
    final armor = pq_logic.winEquip(
      config,
      DeterministicRandom(15),
      2,
      2, // helm slot (armor category)
    );
    expect(armor, isNotEmpty);
    final item = pq_logic.winItem(config, DeterministicRandom(10), 3);
    expect(item, isNotEmpty);
    expect(pq_logic.winItem(config, DeterministicRandom(10), 1000), isEmpty);
  });

  test('monsterTask picks level-appropriate monsters with modifiers', () {
    // 아스키나라(ASCII-Nara) 세계관 데이터로 업데이트
    // 결정론적 결과가 일관되게 생성되는지 확인
    final result1 = pq_logic.monsterTask(
      config,
      DeterministicRandom(99),
      5,
      null,
      null,
    );
    expect(result1.displayName, isNotEmpty);
    expect(result1.baseName, isNotEmpty);
    expect(result1.part, isNotEmpty);

    final result2 = pq_logic.monsterTask(
      config,
      DeterministicRandom(7),
      10,
      null,
      null,
    );
    expect(result2.displayName, isNotEmpty);
    expect(result2.baseName, isNotEmpty);

    final result3 = pq_logic.monsterTask(
      config,
      DeterministicRandom(5),
      6,
      'Memory Leak|6|leaked byte',
      6,
    );
    expect(result3.displayName, isNotEmpty);
  });

  test('completeQuest and completeAct return deterministic results', () {
    // 아스키나라(ASCII-Nara) 세계관 데이터로 업데이트 (Phase 7 확장 반영)
    final quest = pq_logic.completeQuest(config, DeterministicRandom(33), 4);
    expect(quest.caption, 'Transfer this session token');
    expect(quest.reward, pq_logic.RewardKind.item);
    expect(quest.monsterName, isNull);

    final act2 = pq_logic.completeAct(2);
    expect(act2.actTitle, 'Act II');
    // 새 배열 기반: Act II = 10800초 (3시간) - 10시간 완주 목표
    expect(act2.plotBarMaxSeconds, 10800);
    expect(act2.rewards, contains(pq_logic.RewardKind.item));
    // 장비 보상: existingActCount >= 1 부터 지급 (프롤로그 완료 시점부터)
    expect(act2.rewards, contains(pq_logic.RewardKind.equip));

    final act3 = pq_logic.completeAct(3);
    expect(
      act3.rewards,
      containsAll(<pq_logic.RewardKind>{
        pq_logic.RewardKind.item,
        pq_logic.RewardKind.equip,
      }),
    );
  });

  test('dequeue starts next task and drains queue', () {
    const progress = ProgressState(
      task: ProgressBarState(position: 100, max: 100),
      quest: ProgressBarState(position: 0, max: 10),
      plot: ProgressBarState(position: 0, max: 10),
      exp: ProgressBarState(position: 0, max: 10),
      encumbrance: ProgressBarState(position: 0, max: 1),
      currentTask: TaskInfo(caption: 'Idle', type: TaskType.neutral),
      plotStageCount: 1,
      questCount: 0,
    );
    final queue = QueueState(
      entries: const [
        QueueEntry(
          kind: QueueKind.task,
          durationMillis: 120,
          caption: 'Fight',
          taskType: TaskType.kill,
        ),
        QueueEntry(
          kind: QueueKind.plot,
          durationMillis: 80,
          caption: 'Plot',
          taskType: TaskType.plot,
        ),
      ],
    );

    final result = pq_logic.dequeue(progress, queue);
    expect(result, isNotNull);
    expect(result!.caption, 'Fight...');
    expect(result.taskType, TaskType.kill);
    expect(result.kind, QueueKind.task);
    expect(result.progress.task.position, 0);
    expect(result.progress.task.max, 120);
    expect(result.queue.entries.length, 1);
    expect(result.queue.entries.first.kind, QueueKind.plot);
  });

  test('impressiveGuy generates deterministic NPC titles', () {
    // case 0: "the King of the Elves" 형태
    final guy1 = pq_logic.impressiveGuy(config, DeterministicRandom(42));
    expect(guy1, contains('of the'));

    // case 1: "King Vrognak of Zoxzik" 형태
    final guy2 = pq_logic.impressiveGuy(config, DeterministicRandom(7));
    expect(guy2, contains(' of '));

    // 결정적(deterministic) 결과 확인
    expect(
      pq_logic.impressiveGuy(config, DeterministicRandom(100)),
      pq_logic.impressiveGuy(config, DeterministicRandom(100)),
    );
  });

  test('namedMonster generates named monster with level matching', () {
    final monster = pq_logic.namedMonster(config, DeterministicRandom(42), 10);
    // "GeneratedName the MonsterType" 형태
    expect(monster, contains(' the '));

    // 결정적(deterministic) 결과 확인
    expect(
      pq_logic.namedMonster(config, DeterministicRandom(50), 5),
      pq_logic.namedMonster(config, DeterministicRandom(50), 5),
    );
  });

  test('interplotCinematic generates queue entries with plot ending', () {
    final entries = pq_logic.interplotCinematic(
      config,
      DeterministicRandom(42),
      10,
      3,
    );

    // 최소 1개 이상의 엔트리 생성
    expect(entries, isNotEmpty);

    // 마지막은 항상 plot 타입의 'Compiling' (아스키나라 세계관)
    expect(entries.last.kind, QueueKind.plot);
    expect(entries.last.caption, 'Compiling');
    expect(entries.last.durationMillis, 2000);

    // 나머지는 task 타입
    for (var i = 0; i < entries.length - 1; i++) {
      expect(entries[i].kind, QueueKind.task);
    }
  });

  test('interplotCinematic has three distinct scenarios', () {
    // 여러 시드를 테스트해서 3가지 시나리오가 모두 나오는지 확인
    // 아스키나라(ASCII-Nara) 세계관 텍스트로 업데이트
    final scenariosFound = <String>{};

    for (var seed = 0; seed < 100; seed++) {
      final entries = pq_logic.interplotCinematic(
        config,
        DeterministicRandom(seed),
        5,
        1,
      );

      final firstCaption = entries.first.caption;
      if (firstCaption.contains('Cache Zone')) {
        scenariosFound.add('cache');
        // 캐시 존 시나리오: 4개 task + 1개 plot = 5개
        expect(entries.length, 5);
      } else if (firstCaption.contains('target')) {
        scenariosFound.add('combat');
        // 전투 시나리오: 가변 길이 (combatRounds에 따라)
        expect(entries.length, greaterThanOrEqualTo(5));
      } else if (firstCaption.contains('relief')) {
        scenariosFound.add('betrayal');
        // 배신 시나리오: 6개 task + 1개 plot = 7개
        expect(entries.length, 7);
      }
    }

    // 3가지 시나리오가 모두 발견되어야 함
    expect(scenariosFound, containsAll(['cache', 'combat', 'betrayal']));
  });
}
