import '../models/public_participant.dart';
import '../models/public_team.dart';

class PublicRankingUtils {
  static int capScore(int scoreWithHandicap) {
    if (scoreWithHandicap > 300) return 300;
    if (scoreWithHandicap < 0) return 0;
    return scoreWithHandicap;
  }

  // 평균(핸디 포함), 소수 1자리. 입력된 게임 수 기준으로 계산
  static double participantAverageWithHc(PublicParticipant p) {
    int total = 0;
    int cnt = 0;
    for (int i = 0; i < p.rawScores.length; i++) {
      final raw = p.rawScores[i];
      int hc = (i < p.handicaps.length) ? p.handicaps[i] : 0;
      if (hc == 0) {
        hc = p.handicap;
      }
      if (raw == null) continue;
      total += capScore(raw + hc);
      cnt += 1;
    }
    if (cnt == 0) return 0.0;
    return total / cnt;
  }

  // 개인별 총점(핸디캡 포함) 계산
  static int participantTotalWithHc(PublicParticipant p) {
    int total = 0;
    for (int i = 0; i < p.rawScores.length; i++) {
      final raw = p.rawScores[i];
      int hc = (i < p.handicaps.length) ? p.handicaps[i] : 0;
      if (hc == 0) {
        // per-game 핸디캡이 없으면 참가자 기본 핸디캡 사용
        hc = p.handicap;
      }
      if (raw == null) continue;
      total += capScore(raw + hc);
    }
    return total;
  }

  // 동점자 순위 처리(1,1,3 방식)
  static List<int> ranksFor(List<int> valuesDesc) {
    if (valuesDesc.isEmpty) return const [];
    final ranks = List<int>.filled(valuesDesc.length, 0);
    int rank = 1;
    for (int i = 0; i < valuesDesc.length; i++) {
      if (i > 0 && valuesDesc[i] == valuesDesc[i - 1]) {
        ranks[i] = ranks[i - 1];
      } else {
        ranks[i] = rank;
      }
      rank = i + 2; // 다음 베이스 랭크
    }
    return ranks;
  }

  static List<Map<String, dynamic>> individualRanking(List<PublicParticipant> list) {
    // 내림차순 정렬
    final items = list
        .map((p) => {
              'p': p,
              'total': participantTotalWithHc(p),
            })
        .toList();
    items.sort((a, b) => (b['total'] as int).compareTo(a['total'] as int));
    final totals = items.map<int>((e) => e['total'] as int).toList();
    final rs = ranksFor(totals);
    for (int i = 0; i < items.length; i++) {
      items[i]['rank'] = rs[i];
    }
    return items;
  }

  // 동점자 순위 처리 for double 리스트 (1,1,3 방식)
  static List<int> ranksForDoubles(List<double> valuesDesc) {
    if (valuesDesc.isEmpty) return const [];
    final ranks = List<int>.filled(valuesDesc.length, 0);
    int rank = 1;
    const eps = 1e-9;
    for (int i = 0; i < valuesDesc.length; i++) {
      if (i > 0 && (valuesDesc[i] - valuesDesc[i - 1]).abs() < eps) {
        ranks[i] = ranks[i - 1];
      } else {
        ranks[i] = rank;
      }
      rank = i + 2;
    }
    return ranks;
  }

  // 평균(핸디 포함) 기준 개인 랭킹 (내림차순). item: {p, total, avg, rank}
  static List<Map<String, dynamic>> individualRankingByAvg(List<PublicParticipant> list) {
    final items = list
        .map((p) => {
              'p': p,
              'total': participantTotalWithHc(p),
              'avg': participantAverageWithHc(p),
            })
        .toList();
    items.sort((a, b) => (b['avg'] as double).compareTo(a['avg'] as double));
    final avgs = items.map<double>((e) => e['avg'] as double).toList();
    final rs = ranksForDoubles(avgs);
    for (int i = 0; i < items.length; i++) {
      items[i]['rank'] = rs[i];
    }
    return items;
  }

  static List<Map<String, dynamic>> teamRanking(List<PublicTeam> teams) {
    // 팀 총점 = 구성원 총점 합 + 팀 핸디캡 합(여기서는 팀 handicap만 단일 값으로 가정)
    final items = teams
        .map((t) {
          int membersTotal = 0;
          for (final m in t.members) {
            int mt = 0;
            for (int i = 0; i < m.rawScores.length; i++) {
              final raw = m.rawScores[i];
              final hc = (i < m.handicaps.length) ? m.handicaps[i] : 0;
              if (raw == null) continue;
              mt += capScore(raw + hc);
            }
            membersTotal += mt;
          }
          final total = membersTotal + t.handicap;
          return {'team': t, 'total': total};
        })
        .toList();
    items.sort((a, b) => (b['total'] as int).compareTo(a['total'] as int));
    final totals = items.map<int>((e) => e['total'] as int).toList();
    final rs = ranksFor(totals);
    for (int i = 0; i < items.length; i++) {
      items[i]['rank'] = rs[i];
    }
    return items;
  }

  // 참가자별 총점과 랭크 맵 생성 (participantId -> {total, rank})
  static Map<String, Map<String, int>> buildParticipantRankMap(List<PublicParticipant> list) {
    final ranked = individualRanking(list);
    final map = <String, Map<String, int>>{};
    for (final item in ranked) {
      final p = item['p'] as PublicParticipant;
      map[p.participantId] = {
        'total': item['total'] as int,
        'rank': item['rank'] as int,
      };
    }
    return map;
  }

  // 팀별 총점 맵 (teamNumber -> {total, rank})
  static Map<int, Map<String, int>> buildTeamRankMap(List<PublicTeam> teams) {
    final ranked = teamRanking(teams);
    final map = <int, Map<String, int>>{};
    for (final item in ranked) {
      final t = item['team'] as PublicTeam;
      map[t.teamNumber] = {
        'total': item['total'] as int,
        'rank': item['rank'] as int,
      };
    }
    return map;
  }

  // 미배정 참가자 목록 계산
  static List<PublicParticipant> unassignedParticipants(List<PublicParticipant> participants, List<PublicTeam> teams) {
    final assigned = <String>{};
    for (final t in teams) {
      for (final m in t.members) {
        final pid = m.participantId;
        if (pid != null && pid.isNotEmpty) assigned.add(pid);
      }
    }
    return participants.where((p) => !assigned.contains(p.participantId)).toList();
  }
}
