import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; import '../modelos/inicio_partida_multijugador.dart'; import '../modelos/partida.dart'; import '../modelos/snapshot_partida_online.dart'; import '../servicios/servicio_nearby.dart'; import '../tema/tema_app.dart'; import 'pantalla_debate_cliente.dart'; import 'pantalla_fin_partida_online.dart'; import 'pantalla_notas_online.dart'; import 'pantalla_revision_palabra.dart'; import 'pantalla_votacion_cliente.dart'; import 'package:provider/provider.dart'; class PantallaResultadoOnline extends StatefulWidget { final SnapshotPartidaOnline snapshot; final List jugadoresControlados; final String? pistaCategoria; const PantallaResultadoOnline({ super.key, required this.snapshot, required this.jugadoresControlados, this.pistaCategoria, }); @override State createState() => _PantallaResultadoOnlineState(); } class _PantallaResultadoOnlineState extends State { OnMensajeCallback? _listener; ServicioNearby? _nearby; late SnapshotPartidaOnline _snapshot; @override void initState() { super.initState(); _snapshot = widget.snapshot; _listener = (endpointId, mensaje) { if (!mounted) return; if (mensaje.tipo == TipoMensaje.partidaFin) { _abrirFin(mensaje.datos); return; } if (mensaje.tipo == TipoMensaje.votacionResultado) { setState(() => _snapshot = SnapshotPartidaOnline.fromJson(mensaje.datos)); return; } if (mensaje.tipo != TipoMensaje.fase) return; final fase = mensaje.datos['fase'] as String?; if (fase == 'debate') { _abrirDebate(mensaje.datos); } else if (fase == 'votacion') { _abrirVotacion(mensaje.datos); } else if (fase == 'adivinanza' || fase == 'resultado') { setState(() => _snapshot = SnapshotPartidaOnline.fromJson(mensaje.datos)); } else if (fase == 'finPartida') { _abrirFin(mensaje.datos); } }; WidgetsBinding.instance.addPostFrameCallback((_) { final listener = _listener; if (listener != null && mounted) { _nearby = context.read(); _nearby!.onMensaje(listener); } }); } @override void dispose() { final listener = _listener; if (listener != null) { _nearby?.removeMensajeListener(listener); } super.dispose(); } void _abrirDebate(Map datos) { final snapshot = SnapshotPartidaOnline.fromJson(datos); Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => PantallaDebateCliente( tiempoDebateSegundos: datos['tiempoDebateSegundos'] as int?, primerTurnoNombre: datos['primerTurnoNombre'] as String?, partidaId: snapshot.roomId, pistaCategoria: widget.pistaCategoria, jugadores: snapshot.jugadores, jugadoresControlados: widget.jugadoresControlados, onSolicitarVotacion: _solicitarVotacion, ), ), ); } void _abrirVotacion(Map datos) { final snapshot = SnapshotPartidaOnline.fromJson(datos); Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => PantallaVotacionCliente( jugadores: snapshot.jugadores, jugadoresControlados: widget.jugadoresControlados, partidaId: snapshot.roomId, pistaCategoria: widget.pistaCategoria, onVotos: _enviarVotos, ), ), ); } void _abrirFin(Map datos) { final snapshot = SnapshotPartidaOnline.fromJson(datos); Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => PantallaFinPartidaOnline( snapshot: snapshot, jugadoresControlados: widget.jugadoresControlados, pistaCategoria: widget.pistaCategoria, ), ), ); } void _solicitarVotacion() { final nearby = context.read(); if (nearby.hostEndpointId == null) return; nearby.enviarMensaje( nearby.hostEndpointId!, MensajeP2P( tipo: TipoMensaje.ping, datos: {'solicitoVotacion': true}, ), ); } void _enviarVotos(Map votos) { final nearby = context.read(); if (nearby.hostEndpointId == null) return; for (final entry in votos.entries) { nearby.enviarMensaje( nearby.hostEndpointId!, MensajeP2P( tipo: TipoMensaje.voto, datos: { 'votanteId': entry.key, 'votadoId': entry.value, 'votoporId': entry.value, }, ), ); } } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final resultado = _snapshot.resultadoActual; return Scaffold( backgroundColor: TemaApp.colorFondo, appBar: AppBar( title: Text(_snapshot.fase == 'adivinanza' ? l10n.impostorGuessTitle : l10n.result), automaticallyImplyLeading: false, backgroundColor: Colors.transparent, elevation: 0, actions: _acciones(context, l10n), ), body: Padding( padding: const EdgeInsets.all(24), child: resultado == null ? _buildEsperaAdivinanza(context, l10n) : _buildResultado(context, l10n, resultado), ), ); } List _acciones(BuildContext context, AppLocalizations l10n) => [ IconButton( tooltip: l10n.seeYourWord, icon: const Icon(Icons.visibility), onPressed: widget.jugadoresControlados.isEmpty ? null : () => mostrarRevisionPalabraOnline( context: context, jugadoresControlados: widget.jugadoresControlados, pistaCategoria: widget.pistaCategoria, ), ), IconButton( tooltip: l10n.notesTitle, icon: const Icon(Icons.edit_note), onPressed: _snapshot.roomId == null || widget.jugadoresControlados.isEmpty ? null : () => Navigator.push( context, MaterialPageRoute( builder: (_) => PantallaNotasOnline( partidaId: _snapshot.roomId!, jugadores: _snapshot.jugadores, autoresControlados: widget.jugadoresControlados, ), ), ), ), ]; Widget _buildEsperaAdivinanza( BuildContext context, AppLocalizations l10n, ) { return Center( child: Card( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.hourglass_top, size: 48), const SizedBox(height: 16), Text( _snapshot.mensaje ?? l10n.impostorCanGuess, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 12), Text( l10n.waitingForHost, textAlign: TextAlign.center, style: TextStyle(color: TemaApp.colorTextoSecundario), ), ], ), ), ), ); } Widget _buildResultado( BuildContext context, AppLocalizations l10n, ResultadoVotacion resultado, ) { final conteo = {}; for (final votadoId in resultado.votos.values) { conteo[votadoId] = (conteo[votadoId] ?? 0) + 1; } final maxVotos = conteo.values.isEmpty ? 1 : conteo.values.reduce((a, b) => a > b ? a : b); final ranking = conteo.entries.toList() ..sort((a, b) => b.value.compareTo(a.value)); final jugadores = {for (final jugador in _snapshot.jugadores) jugador.id: jugador}; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: TemaApp.decoracionPanel( color: resultado.eraImpostor ? TemaApp.colorVerde.withValues(alpha: 0.18) : TemaApp.colorAcento.withValues(alpha: 0.18), borderColor: resultado.eraImpostor ? TemaApp.colorVerde : TemaApp.colorAcento, ), child: Column( children: [ Text( resultado.eliminadoNombre, style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( resultado.eraImpostor ? l10n.wasImpostor : l10n.wasInnocent, style: TextStyle( color: resultado.eraImpostor ? TemaApp.colorVerde : TemaApp.colorAcento, fontWeight: FontWeight.bold, ), ), if (_snapshot.mensaje != null) ...[ const SizedBox(height: 12), Text(_snapshot.mensaje!, textAlign: TextAlign.center), ], ], ), ), const SizedBox(height: 20), Text(l10n.votesThisRound, style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 12), Expanded( child: ListView( children: [ ...ranking.map((entry) { final jugador = jugadores[entry.key]; final destacado = entry.key == resultado.eliminadoId; final color = destacado ? TemaApp.colorAcento : TemaApp.colorNaranja; return Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded(child: Text(jugador?.nombre ?? '?')), Text( '${entry.value}', style: TextStyle( color: color, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(999), child: LinearProgressIndicator( value: (entry.value / maxVotos).clamp(0.0, 1.0).toDouble(), minHeight: 10, backgroundColor: TemaApp.colorSuperficie, valueColor: AlwaysStoppedAnimation(color), ), ), ], ), ); }), const Divider(height: 24), ...resultado.votos.entries.map((entry) { final votante = jugadores[entry.key]?.nombre ?? '?'; final votado = jugadores[entry.value]?.nombre ?? '?'; return ListTile( dense: true, leading: const Icon(Icons.how_to_vote), title: Text('$votante → $votado'), ); }), ], ), ), ], ); } }