import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart';
import 'package:lanebow/models/member_model.dart';
import 'package:lanebow/models/score_model.dart';
import 'package:lanebow/models/tie_breaker_option.dart';
import 'package:lanebow/utils/tie_breaker_util.dart';

void main() {
  group('TieBreakerUtil 테스트', () {
    // 테스트용 점수 데이터 생성
    List<Score> createTestScores() {
      return [
        // 1위: 100점
        Score(
          id: '1',
          memberId: '1',
          eventId: '1',
          clubId: '1',
          frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 1',
        ),
        // 공동 2위: 90점, 핸디캡 10
        Score(
          id: '2',
          memberId: '2',
          eventId: '1',
          clubId: '1',
          frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
          totalScore: 90,
          handicap: 10, // 핸디캡 포함 시 100점
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 2',
        ),
        // 공동 2위: 90점, 핸디캡 0
        Score(
          id: '3',
          memberId: '3',
          eventId: '1',
          clubId: '1',
          frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
          totalScore: 90,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 3',
        ),
        // 4위: 80점, 핸디캡 20
        Score(
          id: '4',
          memberId: '4',
          eventId: '1',
          clubId: '1',
          frames: [8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
          totalScore: 80,
          handicap: 20, // 핸디캡 포함 시 100점
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 4',
        ),
      ];
    }

    // 테스트용 회원 데이터 생성
    List<Member> createTestMembers() {
      return [
        Member(
          id: '1',
          userId: 'user1',
          clubId: '1',
          name: '참가자 1',
          email: 'user1@example.com',
          isActive: true,
          birthDate: DateTime(1990, 1, 1), // 1990년생
        ),
        Member(
          id: '2',
          userId: 'user2',
          clubId: '1',
          name: '참가자 2',
          email: 'user2@example.com',
          isActive: true,
          birthDate: DateTime(1985, 1, 1), // 1985년생 (더 연장자)
        ),
        Member(
          id: '3',
          userId: 'user3',
          clubId: '1',
          name: '참가자 3',
          email: 'user3@example.com',
          isActive: true,
          birthDate: DateTime(1995, 1, 1), // 1995년생
        ),
        Member(
          id: '4',
          userId: 'user4',
          clubId: '1',
          name: '참가자 4',
          email: 'user4@example.com',
          isActive: true,
          birthDate: DateTime(1980, 1, 1), // 1980년생 (가장 연장자)
        ),
      ];
    }

    // 점수 격차가 다른 테스트 점수 데이터 생성
    List<Score> createScoreGapTestScores() {
      return [
        // 모두 동일한 총점 100점
        Score(
          id: '1',
          memberId: '1',
          eventId: '1',
          clubId: '1',
          frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], // 격차 0
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 1',
        ),
        Score(
          id: '2',
          memberId: '2',
          eventId: '1',
          clubId: '1',
          frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 2',
        ),
        Score(
          id: '3',
          memberId: '3',
          eventId: '1',
          clubId: '1',
          frames: [0, 20, 0, 20, 0, 20, 0, 20, 0, 20], // 격차 20
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 3',
        ),
      ];
    }

    test('기본 점수 정렬 테스트 (핸디캡 미포함)', () {
      // given
      final scores = createTestScores();
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        null, // 옵션 없음
        null, 
        false // 핸디캡 미포함
      );
      
      // then
      expect(sortedScores[0].id, '1'); // 100점
      expect(sortedScores[1].id, '2'); // 90점
      expect(sortedScores[2].id, '3'); // 90점
      expect(sortedScores[3].id, '4'); // 80점
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: null);
      expect(ranks['1'], 1); // 1위
      expect(ranks['2'], 2); // 2위
      expect(ranks['3'], 2); // 2위
      expect(ranks['4'], 4); // 4위
    });
    
    test('기본 점수 정렬 테스트 (핸디캡 포함)', () {
      // given
      final scores = createTestScores();
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        null, // 옵션 없음
        null, 
        true // 핸디캡 포함
      );
      
      // then
      // 핸디캡 포함 시: 참가자1(100), 참가자2(100), 참가자4(100), 참가자3(90)
      expect(sortedScores[0].id, equals('1')); // 100점
      expect(sortedScores[1].id, equals('2')); // 100점
      expect(sortedScores[2].id, equals('4')); // 100점
      expect(sortedScores[3].id, equals('3')); // 90점
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, true);
      expect(ranks['1'], 1); // 1위
      expect(ranks['2'], 1); // 1위
      expect(ranks['4'], 1); // 1위
      expect(ranks['3'], 4); // 4위
    });
    
    test('동점자 처리 옵션: 핸디캡이 적은 순 (lowerHandicap)', () {
      // given
      final scores = createTestScores();
      final options = [TieBreakerOption.lowerHandicap];
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        null, 
        true // 핸디캡 포함
      );
      
      // then
      // 핸디캡 포함 시 동점인 경우, 핸디캡이 적은 순으로 정렬
      // 참가자1(100점, 핸디캡0), 참가자2(100점, 핸디캡10), 참가자4(100점, 핸디캡20), 참가자3(90점)
      expect(sortedScores[0].id, equals('1')); // 핸디캡 0
      expect(sortedScores[1].id, equals('2')); // 핸디캡 10
      expect(sortedScores[2].id, equals('4')); // 핸디캡 20
      expect(sortedScores[3].id, equals('3')); // 90점
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, true);
      expect(ranks['1'], 1); // 1위
      expect(ranks['2'], 1); // 1위
      expect(ranks['4'], 1); // 1위
      expect(ranks['3'], 4); // 4위
    });
    
    test('동점자 처리 옵션: 점수 격차가 적은 순 (lowerScoreGap)', () {
      // given
      final scores = createScoreGapTestScores();
      final options = [TieBreakerOption.lowerScoreGap];
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        null, 
        false // 핸디캡 미포함
      );
      
      // then
      // 모두 100점으로 동점이지만, 점수 격차가 적은 순으로 정렬
      // 참가자1(격차0), 참가자2(격차10), 참가자3(격차20)
      expect(sortedScores[0].id, equals('1')); // 격차 0
      expect(sortedScores[1].id, equals('2')); // 격차 10
      expect(sortedScores[2].id, equals('3')); // 격차 20
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
      expect(ranks['1'], 1); // 1위
      expect(ranks['2'], 1); // 1위
      expect(ranks['3'], 1); // 1위
    });
    
    test('동점자 처리 옵션: 연장자 우선 (olderAge)', () {
      // given
      final scores = createTestScores();
      final members = createTestMembers();
      final options = [TieBreakerOption.olderAge];
      
      // 테스트 데이터 확인용 로그
      debugPrint('\n===== 테스트 데이터 확인 =====');
      for (var score in scores) {
        debugPrint('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
      }
      for (var member in members) {
        debugPrint('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
      }
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        members, 
        true // 핸디캡 포함
      );
      
      // 정렬 결과 확인용 로그
      debugPrint('\n===== 정렬 결과 확인 =====');
      for (var score in sortedScores) {
        debugPrint('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
      }
      
      // then
      // 핸디캡 포함 시 동점인 경우, 연장자 우선으로 정렬
      // 참가자4(1980년생), 참가자2(1985년생), 참가자1(1990년생), 참가자3(90점)
      expect(sortedScores[0].id, equals('4')); // 1980년생
      expect(sortedScores[1].id, equals('2')); // 1985년생
      expect(sortedScores[2].id, equals('1')); // 1990년생
      expect(sortedScores[3].id, equals('3')); // 90점
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, true, options: options);
      
      // 순위 계산 결과 확인용 로그
      debugPrint('\n===== 순위 계산 결과 확인 =====');
      for (var id in ranks.keys) {
        debugPrint('ID: $id, 순위: ${ranks[id]}');
      }
      
      expect(ranks['4'], 1); // 1위
      expect(ranks['2'], 2); // 2위
      expect(ranks['1'], 3); // 3위
      expect(ranks['3'], 4); // 4위
    });
    
    test('동점자 처리 옵션 우선순위 적용 (lowerHandicap -> olderAge)', () {
      // given
      // 핸디캐프이 같은 경우 연장자 우선
      final options = [TieBreakerOption.lowerHandicap, TieBreakerOption.olderAge];
      final members = createTestMembers();
      
      debugPrint('\n===== lowerHandicap -> olderAge 테스트 데이터 확인 =====');
      for (var member in members) {
        debugPrint('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
      }
      
      // 핸디캐프이 서로 다른 새 점수 데이터 생성
      final scores = [
        // 참가자1: 100점, 핸디캐프 0, 1990년생
        Score(
          id: '1',
          memberId: '1',
          eventId: '1',
          clubId: '1',
          frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 1',
        ),
        // 참가자2: 90점, 핸디캐프 10, 1985년생
        Score(
          id: '2',
          memberId: '2',
          eventId: '1',
          clubId: '1',
          frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
          totalScore: 90,
          handicap: 10,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 2',
        ),
        // 참가자3: 90점, 핸디캐프 0, 1995년생
        Score(
          id: '3',
          memberId: '3',
          eventId: '1',
          clubId: '1',
          frames: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
          totalScore: 90,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 3',
        ),
      ];
      
      // 점수 데이터 확인
      debugPrint('\n===== 점수 데이터 확인 =====');
      for (var score in scores) {
        debugPrint('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
      }
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        members, 
        false // 핸디캡 미포함
      );
      
      // 정렬 결과 확인
      debugPrint('\n===== 정렬 결과 확인 =====');
      for (var score in sortedScores) {
        debugPrint('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}');
      }
      
      // then
      // 기본 점수 정렬 후 핸디캡이 낮은 순, 연장자 순으로 정렬
      expect(sortedScores[0].id, equals('1')); // 100점
      expect(sortedScores[1].id, equals('3')); // 90점, 핸디캡 0
      expect(sortedScores[2].id, equals('2')); // 90점, 핸디캡 10
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
      
      // 순위 계산 결과 확인
      debugPrint('\n===== 순위 계산 결과 확인 =====');
      for (var id in ranks.keys) {
        debugPrint('ID: $id, 순위: ${ranks[id]}');
      }
      
      expect(ranks['1'], 1); // 1위
      expect(ranks['3'], 2); // 2위
      expect(ranks['2'], 3); // 3위
    });
    
    test('동점자 처리 옵션 우선순위 적용 (lowerScoreGap -> lowerHandicap -> olderAge)', () {
      // given
      final scores = [
        Score(
          id: '1',
          memberId: '1',
          eventId: '1',
          clubId: '1',
          frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
          totalScore: 100,
          handicap: 10,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 1',
        ),
        Score(
          id: '2',
          memberId: '2',
          eventId: '1',
          clubId: '1',
          frames: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], // 격차 0
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 2',
        ),
        Score(
          id: '3',
          memberId: '3',
          eventId: '1',
          clubId: '1',
          frames: [5, 15, 5, 15, 5, 15, 5, 15, 5, 15], // 격차 10
          totalScore: 100,
          handicap: 0,
          date: DateTime.now(),
          notes: '',
          participantName: '참가자 3',
        ),
      ];
      
      final members = createTestMembers();
      // 점수 격차 -> 핸디캡 -> 연장자 순으로 우선순위 적용
      final options = [
        TieBreakerOption.lowerScoreGap, 
        TieBreakerOption.lowerHandicap, 
        TieBreakerOption.olderAge
      ];
      
      // 테스트 데이터 확인용 로그
      debugPrint('\n===== lowerScoreGap -> lowerHandicap -> olderAge 테스트 데이터 확인 =====');
      for (var score in scores) {
        debugPrint('Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}, 프레임: ${score.frames}');
      }
      for (var member in members) {
        debugPrint('Member ID: ${member.id}, 생년월일: ${member.birthDate}');
      }
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        members, 
        false // 핸디캡 미포함
      );
      
      // 정렬 결과 확인용 로그
      debugPrint('\n===== 정렬 결과 확인 =====');
      for (var score in sortedScores) {
        debugPrint('Sorted Score ID: ${score.id}, 총점: ${score.totalScore}, 핸디캡: ${score.handicap}, 프레임: ${score.frames}');
      }
      
      // then
      // 1. 점수 격차가 적은 순: 참가자2(격차0) > 참가자1,3(격차10)
      // 2. 핸디캡이 적은 순: 참가자3(핸디캡0) > 참가자1(핸디캡10)
      expect(sortedScores[0].id, equals('2')); // 격차0, 핸디캡0
      expect(sortedScores[1].id, equals('3')); // 격차10, 핸디캡0
      expect(sortedScores[2].id, equals('1')); // 격차10, 핸디캡10
      
      // 순위 계산 확인
      final ranks = TieBreakerUtil.calculateRanks(sortedScores, false, options: options);
      
      // 순위 계산 결과 확인용 로그
      debugPrint('\n===== 순위 계산 결과 확인 =====');
      for (var id in ranks.keys) {
        debugPrint('ID: $id, 순위: ${ranks[id]}');
      }
      
      expect(ranks['2'], 1); // 1위
      expect(ranks['3'], 1); // 1위 (lowerScoreGap 옵션에 의해 동일 순위)
      expect(ranks['1'], 3); // 3위
    });
    
    test('옵션이 없는 경우 기본 정렬만 적용', () {
      // given
      final scores = createTestScores();
      final options = [TieBreakerOption.none];
      
      // when
      final sortedScores = TieBreakerUtil.sortScoresByOptions(
        scores, 
        options,
        null, 
        false // 핸디캡 미포함
      );
      
      // then
      // 기본 점수 정렬만 적용
      expect(sortedScores[0].id, equals('1')); // 100점
      expect(sortedScores[1].id, equals('2')); // 90점
      expect(sortedScores[2].id, equals('3')); // 90점
      expect(sortedScores[3].id, equals('4')); // 80점
    });
  });
}
