import 'package:flutter_test/flutter_test.dart';
import 'package:farolero/modelos/sala_multijugador.dart';
import 'package:farolero/modelos/usuario.dart';

void main() {
  group('EstadoSalaMultijugador', () {
    late EstadoSalaMultijugador sala;

    setUp(() {
      sala = EstadoSalaMultijugador.crear(
        roomId: 'room-1',
        nombreSala: 'Ana - Farolero',
        hostClientId: 'host',
        hostNombre: 'Ana',
      );
    });

    test('registra al host como cliente conectado de la sala', () {
      expect(sala.hostClientId, 'host');
      expect(sala.clientes['host']?.esHost, isTrue);
      expect(sala.clientes['host']?.conectado, isTrue);
    });

    test('permite seleccionar varios usuarios para un mismo cliente', () {
      sala.crearUsuario(Usuario(id: 'ana', nombre: 'Ana'));
      sala.crearUsuario(Usuario(id: 'juan', nombre: 'Juan'));

      final seleccionAna = sala.seleccionarUsuario(
        usuarioId: 'ana',
        clienteId: 'host',
      );
      final seleccionJuan = sala.seleccionarUsuario(
        usuarioId: 'juan',
        clienteId: 'host',
      );

      expect(seleccionAna.exitoso, isTrue);
      expect(seleccionJuan.exitoso, isTrue);
      expect(sala.usuariosPorCliente('host').map((u) => u.nombre), [
        'Ana',
        'Juan',
      ]);
    });

    test('impide que dos clientes seleccionen el mismo usuario', () {
      sala.registrarCliente(
        const ClienteSala(
          clientId: 'cliente-jorge',
          endpointId: 'endpoint-1',
          nombre: 'Jorge',
        ),
      );
      sala.crearUsuario(Usuario(id: 'sofia', nombre: 'Sofía'));

      final primeraSeleccion = sala.seleccionarUsuario(
        usuarioId: 'sofia',
        clienteId: 'host',
      );
      final segundaSeleccion = sala.seleccionarUsuario(
        usuarioId: 'sofia',
        clienteId: 'cliente-jorge',
      );

      expect(primeraSeleccion.exitoso, isTrue);
      expect(segundaSeleccion.exitoso, isFalse);
      expect(segundaSeleccion.codigo, 'usuario_ya_seleccionado');
      expect(sala.usuarios['sofia']?.clienteIdSeleccionado, 'host');
    });

    test('solo permite eliminar usuarios no seleccionados y por el host', () {
      sala.registrarCliente(
        const ClienteSala(
          clientId: 'cliente-jorge',
          endpointId: 'endpoint-1',
          nombre: 'Jorge',
        ),
      );
      sala.crearUsuario(Usuario(id: 'ana', nombre: 'Ana'));
      sala.crearUsuario(Usuario(id: 'javier', nombre: 'Javier'));
      sala.seleccionarUsuario(usuarioId: 'ana', clienteId: 'host');

      final clienteElimina = sala.eliminarUsuario(
        usuarioId: 'javier',
        solicitanteClientId: 'cliente-jorge',
      );
      final hostEliminaSeleccionado = sala.eliminarUsuario(
        usuarioId: 'ana',
        solicitanteClientId: 'host',
      );
      final hostEliminaLibre = sala.eliminarUsuario(
        usuarioId: 'javier',
        solicitanteClientId: 'host',
      );

      expect(clienteElimina.exitoso, isFalse);
      expect(clienteElimina.codigo, 'solo_host');
      expect(hostEliminaSeleccionado.exitoso, isFalse);
      expect(hostEliminaSeleccionado.codigo, 'usuario_seleccionado');
      expect(hostEliminaLibre.exitoso, isTrue);
      expect(sala.usuarios.containsKey('javier'), isFalse);
    });

    test('valida inicio con minimo tres usuarios y host seleccionado', () {
      sala
        ..crearUsuario(Usuario(id: 'ana', nombre: 'Ana'))
        ..crearUsuario(Usuario(id: 'juan', nombre: 'Juan'))
        ..crearUsuario(Usuario(id: 'sofia', nombre: 'Sofía'));

      expect(sala.validarInicio().exitoso, isFalse);
      expect(sala.validarInicio().codigo, 'faltan_jugadores');

      sala
        ..seleccionarUsuario(usuarioId: 'ana', clienteId: 'host')
        ..seleccionarUsuario(usuarioId: 'juan', clienteId: 'host')
        ..seleccionarUsuario(usuarioId: 'sofia', clienteId: 'host');

      expect(sala.validarInicio().exitoso, isTrue);
      expect(sala.iniciarPartida().exitoso, isTrue);
      expect(sala.fase, FaseSalaMultijugador.enPartida);
    });

    test('libera usuarios de un cliente desconectado durante lobby', () {
      sala.registrarCliente(
        const ClienteSala(
          clientId: 'cliente-sofia',
          endpointId: 'endpoint-2',
          nombre: 'Sofía',
        ),
      );
      sala
        ..crearUsuario(Usuario(id: 'sofia', nombre: 'Sofía'))
        ..crearUsuario(Usuario(id: 'helena', nombre: 'Helena'))
        ..seleccionarUsuario(usuarioId: 'sofia', clienteId: 'cliente-sofia')
        ..seleccionarUsuario(usuarioId: 'helena', clienteId: 'cliente-sofia');

      sala.desconectarCliente('cliente-sofia');

      expect(sala.clientes['cliente-sofia']?.conectado, isFalse);
      expect(sala.usuarios['sofia']?.estaDisponible, isTrue);
      expect(sala.usuarios['helena']?.estaDisponible, isTrue);
    });


    test('registra actividad y conserva usuarios seleccionados en partida al desconectar', () {
      sala.registrarCliente(
        const ClienteSala(
          clientId: 'cliente-sofia',
          endpointId: 'endpoint-2',
          nombre: 'Sofía',
        ),
      );
      sala
        ..crearUsuario(Usuario(id: 'ana', nombre: 'Ana'))
        ..crearUsuario(Usuario(id: 'sofia', nombre: 'Sofía'))
        ..crearUsuario(Usuario(id: 'helena', nombre: 'Helena'))
        ..seleccionarUsuario(usuarioId: 'ana', clienteId: 'host')
        ..seleccionarUsuario(usuarioId: 'sofia', clienteId: 'cliente-sofia')
        ..seleccionarUsuario(usuarioId: 'helena', clienteId: 'cliente-sofia');

      sala.registrarActividadCliente('cliente-sofia', ahoraMs: 1234);
      expect(sala.clientes['cliente-sofia']?.conectado, isTrue);
      expect(sala.clientes['cliente-sofia']?.ultimaActividadMs, 1234);

      sala.iniciarPartida();
      sala.desconectarCliente('cliente-sofia');

      expect(sala.clientes['cliente-sofia']?.conectado, isFalse);
      expect(sala.usuarios['sofia']?.clienteIdSeleccionado, 'cliente-sofia');
      expect(sala.usuarios['helena']?.clienteIdSeleccionado, 'cliente-sofia');
      expect(sala.usuariosDeClientesDesconectados.length, 2);

      final reasignados = sala.reasignarUsuariosDeCliente(
        clientIdOrigen: 'cliente-sofia',
        clientIdDestino: 'host',
      );

      expect(reasignados, 2);
      expect(sala.usuarios['sofia']?.clienteIdSeleccionado, 'host');
      expect(sala.usuarios['helena']?.clienteIdSeleccionado, 'host');
    });
    test('serializa y restaura clientes y usuarios seleccionados', () {
      sala
        ..crearUsuario(Usuario(id: 'ana', nombre: 'Ana'))
        ..seleccionarUsuario(usuarioId: 'ana', clienteId: 'host');

      final restaurada = EstadoSalaMultijugador.fromJson(sala.toJson());

      expect(restaurada.roomId, 'room-1');
      expect(restaurada.clientes['host']?.esHost, isTrue);
      expect(restaurada.usuarios['ana']?.clienteIdSeleccionado, 'host');
      expect(restaurada.usuariosSeleccionados.single.nombre, 'Ana');
    });
  });
}
