import 'package:flutter/material.dart';
import 'dart:async';
import 'package:lanebow/services/public_event_service.dart';
import 'package:dio/dio.dart';
import 'package:lanebow/screens/public_event/public_event_entry_screen.dart';
import 'package:lanebow/models/public_event.dart';
import 'package:lanebow/models/public_participant.dart';
import 'package:lanebow/models/public_team.dart';
import 'package:lanebow/utils/ranking_utils_public.dart';
import 'package:lanebow/screens/public_event/join_dialog.dart';

enum _ResultSort { total, avg }

class PublicEventScreen extends StatefulWidget {
  final String publicHash;
  final PublicEventService? service;
  const PublicEventScreen({super.key, required this.publicHash, this.service});

  @override
  State<PublicEventScreen> createState() => _PublicEventScreenState();
}

class _PublicEventScreenState extends State<PublicEventScreen> {
  late final PublicEventService _service = widget.service ?? PublicEventService();
  bool _loading = true;
  String? _error;
  PublicEvent? _eventModel;
  List<PublicParticipant> _participants = const [];
  List<PublicTeam> _teams = const [];
  final Map<String, String> _scoreStates = {}; // key: pid-g -> idle|saving|saved|error
  final Map<String, Timer> _debouncers = {};
  final Map<String, Timer> _postTimers = {};
  _ResultSort _resultSort = _ResultSort.total;

  String _statusLabel(EventStatus s) {
    switch (s) {
      case EventStatus.ready:
        return 'ready';
      case EventStatus.active:
        return 'active';
      case EventStatus.completed:
        return 'completed';
      case EventStatus.canceled:
        return 'canceled';
      default:
        return 'unknown';
    }
  }

  Color _statusColor(EventStatus s) {
    switch (s) {
      case EventStatus.ready:
        return Colors.blue.shade50;
      case EventStatus.active:
        return Colors.green.shade50;
      case EventStatus.completed:
        return Colors.grey.shade200;
      case EventStatus.canceled:
        return Colors.red.shade50;
      default:
        return Colors.grey.shade100;
    }
  }

  String _typeLabel(EventType t) {
    switch (t) {
      case EventType.regular:
        return 'regular';
      case EventType.tournament:
        return 'tournament';
      case EventType.practice:
        return 'practice';
      case EventType.league:
        return 'league';
      default:
        return 'unknown';
    }
  }

  String _participantStatusLabel(ParticipantStatus s) {
    switch (s) {
      case ParticipantStatus.pending:
        return 'pending';
      case ParticipantStatus.confirmed:
        return 'confirmed';
      case ParticipantStatus.canceled:
        return 'canceled';
      default:
        return 'unknown';
    }
  }

  String _paymentLabel(PaymentStatus s) {
    switch (s) {
      case PaymentStatus.paid:
        return 'paid';
      case PaymentStatus.unpaid:
        return 'unpaid';
      case PaymentStatus.refunded:
        return 'refunded';
      case PaymentStatus.partial:
        return 'partial';
      default:
        return 'unknown';
    }
  }

  @override
  void initState() {
    super.initState();
    _load();
  }

  Color _scoreColor(PublicParticipant p, int g) {
    final idx = g - 1;
    int base = 0;
    if (idx >= 0 && idx < p.rawScores.length && p.rawScores[idx] != null) {
      base = p.rawScores[idx]!;
    }
    int hc = 0;
    if (idx >= 0 && idx < p.handicaps.length) {
      hc = p.handicaps[idx];
    }
    final total = base + hc;
    if (base == 0) return Colors.grey;
    return total >= 200 ? Colors.red : Colors.black87;
  }

  Widget _stateIcon(String key) {
    final st = _scoreStates[key];
    if (st == 'saving') return const Icon(Icons.autorenew, size: 14, color: Colors.grey);
    if (st == 'saved') return const Icon(Icons.check_circle, size: 14, color: Colors.green);
    if (st == 'error') return const Icon(Icons.error_outline, size: 14, color: Colors.orange);
    return const Icon(Icons.circle, size: 10, color: Colors.grey);
  }

  @override
  void dispose() {
    for (final t in _debouncers.values) {
      t.cancel();
    }
    _debouncers.clear();
    for (final t in _postTimers.values) {
      t.cancel();
    }
    _postTimers.clear();
    super.dispose();
  }

  String _keyFor(String pid, int g) => pid + '-' + g.toString();

  void _onScoreChanged(PublicParticipant p, int gameNumber, String value) {
    final key = _keyFor(p.participantId, gameNumber);
    _scoreStates[key] = 'idle';
    // 3자리 입력 시 즉시 저장, 아니면 디바운스
    _debouncers[key]?.cancel();
    if (value.length == 3) {
      final v = int.tryParse(value);
      if (v != null && v >= 0 && v <= 300) {
        _saveInlineScore(p, gameNumber, v);
        return;
      }
    }
    _debouncers[key] = Timer(const Duration(milliseconds: 200), () {
      final v = int.tryParse(value);
      if (v != null && v >= 0 && v <= 300) {
        _saveInlineScore(p, gameNumber, v);
      }
    });
  }

  Future<void> _saveInlineScore(PublicParticipant p, int gameNumber, int score) async {
    final key = _keyFor(p.participantId, gameNumber);
    _scoreStates[key] = 'saving';
    setState(() {});
    try {
      await _service.updateScore(widget.publicHash, participantId: p.participantId, gameNumber: gameNumber, score: score);
      // 로컬 모델 즉시 갱신
      final idx = _participants.indexWhere((pp) => pp.participantId == p.participantId);
      if (idx >= 0) {
        final cur = _participants[idx];
        final newScores = List<int?>.from(cur.rawScores);
        if (gameNumber >= 1 && gameNumber <= newScores.length) newScores[gameNumber - 1] = score;
        _participants[idx] = PublicParticipant(
          participantId: cur.participantId,
          memberId: cur.memberId,
          name: cur.name,
          gender: cur.gender,
          memberType: cur.memberType,
          average: cur.average,
          handicap: cur.handicap,
          status: cur.status,
          paymentStatus: cur.paymentStatus,
          comment: cur.comment,
          isGuest: cur.isGuest,
          rawScores: newScores,
          handicaps: cur.handicaps,
        );
      }
      _scoreStates[key] = 'saved';
      setState(() {});
      _postTimers[key]?.cancel();
      _postTimers[key] = Timer(const Duration(milliseconds: 1200), () {
        if (!mounted) return;
        _scoreStates[key] = 'idle';
        setState(() {});
      });
    } on DioException catch (e) {
      final status = e.response?.statusCode;
      if (status == 401 || status == 403) {
        if (!mounted) return;
        Navigator.of(context).pushReplacement(
          MaterialPageRoute(builder: (_) => PublicEventEntryScreen(publicHash: widget.publicHash)),
        );
        return;
      }
      _scoreStates[key] = 'error';
      setState(() {});
    } catch (_) {
      _scoreStates[key] = 'error';
      setState(() {});
    }
  }

  Future<void> _openScoreDialog(String participantId, {int? initial}) async {
    final controller = TextEditingController(text: initial?.toString() ?? '');
    int selectedGame = 1;
    String? errorText;
    final gameCount = _eventModel?.gameCount ?? 3;
    final result = await showDialog<Map<String, int>?>(
      context: context,
      builder: (ctx) {
        return StatefulBuilder(builder: (ctx2, setState) {
          void validate() {
            final v = int.tryParse(controller.text);
            if (v == null || v < 0 || v > 300) {
              setState(() => errorText = '0~300 사이의 정수를 입력하세요');
            } else {
              setState(() => errorText = null);
            }
          }
          return AlertDialog(
            title: const Text('점수 입력'),
            content: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                DropdownButton<int>(
                  key: const Key('game_select'),
                  value: selectedGame,
                  items: [for (int i = 1; i <= gameCount; i++) DropdownMenuItem(value: i, child: Text('게임 ' + i.toString()))],
                  onChanged: (v) => setState(() => selectedGame = v ?? 1),
                ),
                TextField(
                  key: const Key('score_input'),
                  controller: controller,
                  keyboardType: TextInputType.number,
                  decoration: InputDecoration(labelText: '점수(0-300)', errorText: errorText),
                  onChanged: (_) => validate(),
                ),
              ],
            ),
            actions: [
              TextButton(
                onPressed: () => Navigator.of(ctx).pop(null),
                child: const Text('취소'),
              ),
              ElevatedButton(
                key: const Key('score_save_button'),
                onPressed: () {
                  final v = int.tryParse(controller.text);
                  if (v == null || v < 0 || v > 300) {
                    validate();
                    return;
                  }
                  Navigator.of(ctx).pop({'game': selectedGame, 'score': v});
                },
                child: const Text('저장'),
              ),
            ],
          );
        });
      },
    );
    if (result == null) return;
    try {
      final gameNumber = result['game']!;
      final score = result['score']!;
      await _service.updateScore(widget.publicHash, participantId: participantId, gameNumber: gameNumber, score: score);
      if (!mounted) return;
      // 로컬 모델 즉시 갱신
      final idx = _participants.indexWhere((pp) => pp.participantId == participantId);
      if (idx >= 0) {
        final p = _participants[idx];
        final newScores = List<int?>.from(p.rawScores);
        if (gameNumber >= 1 && gameNumber <= newScores.length) {
          newScores[gameNumber - 1] = score;
          _participants[idx] = PublicParticipant(
            participantId: p.participantId,
            memberId: p.memberId,
            name: p.name,
            gender: p.gender,
            memberType: p.memberType,
            average: p.average,
            handicap: p.handicap,
            status: p.status,
            paymentStatus: p.paymentStatus,
            comment: p.comment,
            isGuest: p.isGuest,
            rawScores: newScores,
            handicaps: p.handicaps,
          );
          setState(() {});
        }
      }
      ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('점수가 저장되었습니다')));
    } on DioException catch (e) {
      if (!mounted) return;
      final status = e.response?.statusCode;
      if (status == 401 || status == 403) {
        Navigator.of(context).pushReplacement(
          MaterialPageRoute(builder: (_) => PublicEventEntryScreen(publicHash: widget.publicHash)),
        );
        return;
      }
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('저장 실패: ' + e.toString())));
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('저장 실패: ' + e.toString())));
    }
  }

  Future<void> _load() async {
    setState(() {
      _loading = true;
      _error = null;
    });
    try {
      final d = await _service.fetchEvent(widget.publicHash);
      if (!mounted) return;
      final eventJson = (d['event'] as Map?)?.cast<String, dynamic>() ?? <String, dynamic>{};
      final eventModel = PublicEvent.fromJson(eventJson);
      final gc = eventModel.gameCount;
      final parts = (d['participants'] is List)
          ? (d['participants'] as List)
              .whereType<Map>()
              .map((e) => PublicParticipant.fromJson(e.cast<String, dynamic>(), gameCount: gc))
              .toList()
          : <PublicParticipant>[];
      final teams = (d['teams'] is List)
          ? (d['teams'] as List)
              .whereType<Map>()
              .map((e) => PublicTeam.fromJson(e.cast<String, dynamic>(), gameCount: gc))
              .toList()
          : <PublicTeam>[];

      setState(() {
        _eventModel = eventModel;
        _participants = parts;
        _teams = teams;
      });
    } on DioException catch (e) {
      if (!mounted) return;
      final status = e.response?.statusCode;
      if (status == 401 || status == 403) {
        // 인증 만료: 엔트리 화면으로 복귀
        Navigator.of(context).pushReplacement(
          MaterialPageRoute(builder: (_) => PublicEventEntryScreen(publicHash: widget.publicHash)),
        );
        return;
      } else if (status == 404) {
        setState(() {
          _error = '이벤트를 찾을 수 없습니다';
        });
        return;
      }
      setState(() {
        _error = e.toString();
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _error = e.toString();
      });
    } finally {
      if (mounted) {
        setState(() {
          _loading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final event = _eventModel;
    final participants = _participants;
    final teams = _teams;
    final partRankMap = PublicRankingUtils.buildParticipantRankMap(participants);
    final teamRankMap = PublicRankingUtils.buildTeamRankMap(teams);
    final unassigned = PublicRankingUtils.unassignedParticipants(participants, teams);
    final now = DateTime.now();
    final registrationClosed = (event?.registrationDeadline != null) && now.isAfter(event!.registrationDeadline!);
    final canJoin = (event?.isReady == true || event?.isActive == true) && !registrationClosed;
    final canInputScore = (event?.isActive == true);

    return Scaffold(
      appBar: AppBar(title: const Text('공개 이벤트')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: _loading
            ? const Center(child: CircularProgressIndicator())
            : _error != null
                ? Center(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text(_error!, style: const TextStyle(color: Colors.red)),
                        const SizedBox(height: 12),
                        ElevatedButton(
                          key: const Key('go_entry_button'),
                          onPressed: () {
                            Navigator.of(context).pushReplacement(
                              MaterialPageRoute(builder: (_) => PublicEventEntryScreen(publicHash: widget.publicHash)),
                            );
                          },
                          child: const Text('입장 화면으로 돌아가기'),
                        ),
                      ],
                    ),
                  )
                : ListView(
                    children: [
                      Text('hash: ' + widget.publicHash, key: const Key('public_hash_label')),
                      const SizedBox(height: 8),
                      if (event != null) ...[
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Expanded(child: Text(event.title, key: const Key('event_title'))),
                            if (canJoin)
                              ElevatedButton(
                                key: const Key('join_button'),
                                onPressed: () async {
                                  final ok = await showDialog<bool>(
                                    context: context,
                                    builder: (_) => JoinDialog(publicHash: widget.publicHash, service: _service),
                                  );
                                  if (ok == true) {
                                    await _load();
                                  }
                                },
                                child: const Text('참가 신청/수정'),
                              ),
                          ],
                        ),
                        const SizedBox(height: 8),
                        Wrap(
                          spacing: 8,
                          runSpacing: 8,
                          children: [
                            Chip(
                              key: const Key('status_chip'),
                              label: Text(_statusLabel(event.statusEnum)),
                              backgroundColor: _statusColor(event.statusEnum),
                            ),
                            if (event.eventType != null && event.eventType!.isNotEmpty)
                              Chip(key: const Key('type_chip'), label: Text(_typeLabel(event.typeEnum))),
                            if (event.startDate != null || event.endDate != null)
                              Chip(
                                key: const Key('period_chip'),
                                label: Text(
                                  ((event.startDate != null) ? event.startDate!.toLocal().toString().split('.').first : '') +
                                      (event.endDate != null ? ' ~ ' + event.endDate!.toLocal().toString().split('.').first : ''),
                                ),
                              ),
                            Chip(key: const Key('game_chip'), label: Text('게임 ' + event.gameCount.toString())),
                            if (event.registrationDeadline != null)
                              Chip(key: const Key('deadline_chip'), label: Text('신청마감 ' + event.registrationDeadline!.toLocal().toString().split('.').first)),
                            if (event.participantFee != null)
                              Chip(key: const Key('fee_chip'), label: Text('참가비 ₩' + event.participantFee!.toString())),
                            if (event.location != null && event.location!.isNotEmpty)
                              Chip(key: const Key('location_chip'), label: Text(event.location!)),
                          ],
                        ),
                        const SizedBox(height: 8),
                      ],
                      if (teams.isEmpty) ...[
                        const SizedBox(height: 8),
                        const Text('참가자', style: TextStyle(fontWeight: FontWeight.bold), key: Key('section_participants')),
                        Text('총 ' + participants.length.toString() + '명', key: const Key('participants_count')),
                        const SizedBox(height: 8),
                        Card(
                          child: participants.isEmpty
                              ? const ListTile(
                                  key: Key('empty_participants'),
                                  title: Text('참가자가 없습니다'),
                                )
                              : Column(
                                  children: [
                                    for (final p in participants)
                                      ListTile(
                                      key: Key('participant_tile_' + p.participantId),
                                      title: Text(p.name),
                                      subtitle: Column(
                                        crossAxisAlignment: CrossAxisAlignment.start,
                                        children: [
                                          Wrap(
                                            spacing: 6,
                                            runSpacing: 6,
                                            children: [
                                              Chip(
                                                key: Key('participant_status_' + p.participantId),
                                                label: Text(_participantStatusLabel(p.statusEnum)),
                                              ),
                                              if (p.paymentStatus != null && p.paymentStatus!.isNotEmpty)
                                                Chip(
                                                  key: Key('participant_payment_' + p.participantId),
                                                  label: Text(_paymentLabel(p.paymentEnum)),
                                                ),
                                              if (p.memberType != null && p.memberType!.isNotEmpty)
                                                Chip(
                                                  key: Key('participant_type_' + p.participantId),
                                                  label: Text(p.memberType!),
                                                ),
                                              Text('avg:' + (p.average?.toString() ?? '-') + ' · hc:' + p.handicap.toString(), key: Key('participant_info_' + p.participantId)),
                                            ],
                                          ),
                                          if (canInputScore && event != null)
                                            Padding(
                                              padding: const EdgeInsets.only(top: 8),
                                              child: Row(
                                                children: [
                                                  for (int g = 1; g <= event.gameCount; g++)
                                                    Padding(
                                                      padding: const EdgeInsets.only(right: 6),
                                                      child: SizedBox(
                                                        width: 60,
                                                        child: Column(
                                                          children: [
                                                            Row(
                                                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                                              children: [
                                                                Container(
                                                                  key: Key('color_' + p.participantId + '_' + g.toString()),
                                                                  width: 8,
                                                                  height: 8,
                                                                  decoration: BoxDecoration(
                                                                    color: _scoreColor(p, g),
                                                                    shape: BoxShape.circle,
                                                                  ),
                                                                ),
                                                                _stateIcon(_keyFor(p.participantId, g)),
                                                              ],
                                                            ),
                                                            TextFormField(
                                                              key: Key('cell_' + p.participantId + '_' + g.toString()),
                                                              initialValue: (g - 1 < p.rawScores.length && p.rawScores[g - 1] != null)
                                                                  ? p.rawScores[g - 1].toString()
                                                                  : '',
                                                              keyboardType: TextInputType.number,
                                                              decoration: const InputDecoration(
                                                                isDense: true,
                                                                hintText: '---',
                                                                counterText: '',
                                                                contentPadding: EdgeInsets.symmetric(vertical: 6, horizontal: 8),
                                                              ),
                                                              maxLength: 3,
                                                              onChanged: (v) => _onScoreChanged(p, g, v),
                                                            ),
                                                          ],
                                                        ),
                                                      ),
                                                    ),
                                                ],
                                              ),
                                            ),
                                        ],
                                      ),
                                      trailing: Column(
                                        mainAxisSize: MainAxisSize.min,
                                        crossAxisAlignment: CrossAxisAlignment.end,
                                        children: [
                                          Container(
                                            key: Key('participant_rank_' + p.participantId),
                                            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
                                            decoration: BoxDecoration(
                                              color: Colors.blue.shade50,
                                              borderRadius: BorderRadius.circular(10),
                                            ),
                                            child: Text('R ' + (partRankMap[p.participantId]?['rank']?.toString() ?? '-'), style: const TextStyle(fontSize: 12)),
                                          ),
                                          const SizedBox(height: 6),
                                          Text(
                                            '총 ' + PublicRankingUtils.participantTotalWithHc(p).toString(),
                                            key: Key('participant_total_' + p.participantId),
                                          ),
                                        ],
                                      ),
                                      onTap: () {
                                        if (!canInputScore) {
                                          ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('완료 상태에서는 점수 입력이 불가합니다')));
                                          return;
                                        }
                                        _openScoreDialog(p.participantId, initial: p.rawScores.isNotEmpty ? p.rawScores[0] : null);
                                      },
                                      ),
                                  ],
                                ),
                        ),
                      ],
                      const SizedBox(height: 24),
                      const Text('팀', style: TextStyle(fontWeight: FontWeight.bold), key: Key('section_teams')),
                      Text('총 ' + teams.length.toString() + '팀', key: const Key('teams_count')),
                      const SizedBox(height: 8),
                      Card(
                        child: teams.isEmpty
                            ? const ListTile(
                              key: Key('empty_teams'),
                              title: Text('팀이 없습니다'),
                            )
                            : Column(
                              children: [
                                for (final t in teams)
                                  Padding(
                                    padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
                                    child: Column(
                                      crossAxisAlignment: CrossAxisAlignment.start,
                                      children: [
                                        Row(
                                          children: [
                                            Text('팀 ' + t.teamNumber.toString()),
                                            const SizedBox(width: 6),
                                            Container(
                                              key: Key('team_rank_' + t.teamNumber.toString()),
                                              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
                                              decoration: BoxDecoration(
                                                color: Colors.blue.shade50,
                                                borderRadius: BorderRadius.circular(10),
                                              ),
                                              child: Text('R ' + (teamRankMap[t.teamNumber]?['rank']?.toString() ?? '-'), style: const TextStyle(fontSize: 12)),
                                            ),
                                            const Spacer(),
                                            SizedBox(
                                              width: 100,
                                              child: TextFormField(
                                                key: Key('team_hc_input_' + t.teamNumber.toString()),
                                                initialValue: t.handicap.toString(),
                                                keyboardType: TextInputType.number,
                                                decoration: const InputDecoration(
                                                  isDense: true,
                                                  labelText: '팀 HC',
                                                  counterText: '',
                                                ),
                                                maxLength: 3,
                                                onChanged: (v) async {
                                                  final val = int.tryParse(v);
                                                  if (val == null || val < 0 || val > 300) return;
                                                  try {
                                                    await _service.updateTeamHandicap(widget.publicHash, teamNumber: t.teamNumber, handicap: val);
                                                    // 로컬 반영
                                                    final idx = _teams.indexWhere((tt) => tt.teamNumber == t.teamNumber);
                                                    if (idx >= 0) {
                                                      _teams[idx] = PublicTeam(teamId: _teams[idx].teamId, teamNumber: _teams[idx].teamNumber, handicap: val, members: _teams[idx].members);
                                                      setState(() {});
                                                    }
                                                  } catch (_) {}
                                                },
                                              ),
                                            ),
                                          ],
                                        ),
                                        const SizedBox(height: 6),
                                        Builder(builder: (context) {
                                          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 += PublicRankingUtils.capScore(raw + hc);
                                            }
                                            membersTotal += mt;
                                          }
                                          final total = membersTotal + t.handicap;
                                          final avg = t.members.isNotEmpty ? (total / t.members.length).toStringAsFixed(1) : '-';
                                          return Text(
                                            '총 ' + total.toString() + ' · 평균 ' + avg + ' (팀 HC ' + t.handicap.toString() + ')',
                                            key: Key('team_summary_' + t.teamNumber.toString()),
                                          );
                                        }),
                                        const SizedBox(height: 6),
                                        // 팀 구성원 리스트
                                        Column(
                                          crossAxisAlignment: CrossAxisAlignment.start,
                                          children: [
                                            for (final m in t.members)
                                              Padding(
                                                padding: const EdgeInsets.symmetric(vertical: 2),
                                                child: Text('• ' + m.name),
                                              ),
                                          ],
                                        ),
                                      ],
                                    ),
                                  ),
                              ],
                            ),
                      ),
                      const SizedBox(height: 24),
                      const Text('미배정', style: TextStyle(fontWeight: FontWeight.bold), key: Key('section_unassigned')),
                      Text('총 ' + unassigned.length.toString() + '명', key: const Key('unassigned_count')),
                      const SizedBox(height: 8),
                      Card(
                        child: unassigned.isEmpty
                            ? const ListTile(
                                key: Key('empty_unassigned'),
                                title: Text('미배정 참가자가 없습니다'),
                              )
                            : Column(
                                children: [
                                  for (final p in unassigned)
                                    ListTile(
                                      key: Key('unassigned_' + p.participantId),
                                      title: Text(p.name),
                                    ),
                                ],
                              ),
                      ),
                      if (event?.isFinished == true) ...[
                        const SizedBox(height: 24),
                        const Text('최종 결과', style: TextStyle(fontWeight: FontWeight.bold), key: Key('section_final_results')),
                        const SizedBox(height: 8),
                        Row(
                          children: [
                            ChoiceChip(
                              key: const Key('sort_total_chip'),
                              label: const Row(children: [Icon(Icons.summarize, size: 16), SizedBox(width: 4), Text('총점')]),
                              selected: _resultSort == _ResultSort.total,
                              onSelected: (_) => setState(() => _resultSort = _ResultSort.total),
                            ),
                            const SizedBox(width: 8),
                            ChoiceChip(
                              key: const Key('sort_avg_chip'),
                              label: const Row(children: [Icon(Icons.show_chart, size: 16), SizedBox(width: 4), Text('평균')]),
                              selected: _resultSort == _ResultSort.avg,
                              onSelected: (_) => setState(() => _resultSort = _ResultSort.avg),
                            ),
                          ],
                        ),
                        const SizedBox(height: 8),
                        Card(
                          child: Column(
                            children: [
                              for (final item in (_resultSort == _ResultSort.total
                                  ? PublicRankingUtils.individualRanking(participants)
                                  : PublicRankingUtils.individualRankingByAvg(participants)))
                                Builder(builder: (_) {
                                  final p = item['p'] as PublicParticipant;
                                  final rank = item['rank'] as int;
                                  final total = item['total'] as int;
                                  final avg = PublicRankingUtils.participantAverageWithHc(p).toStringAsFixed(1);
                                  return ListTile(
                                    key: Key('result_row_' + p.participantId),
                                    leading: Text(rank.toString(), key: Key('result_rank_' + p.participantId)),
                                    title: Text(p.name),
                                    trailing: Text('총 ' + total.toString() + ' · 평균 ' + avg),
                                  );
                                }),
                            ],
                          ),
                        ),
                      ],
                    ],
                  ),
      ),
    );
  }
}
