import 'package:bolao_mobile/core/api/api_client.dart';
import 'package:bolao_mobile/core/sessao/sessao_store.dart';
import 'package:bolao_mobile/features/auth/data/auth_repository.dart';
import 'package:bolao_mobile/features/auth/presentation/sessao_controller.dart';
import 'package:bolao_mobile/features/boloes/data/boloes_repository.dart';
import 'package:bolao_mobile/features/boloes/presentation/palpites_do_jogo_page.dart';
import 'package:bolao_mobile/features/boloes/presentation/rodada_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

class _RepoFalso extends BoloesRepository {
  _RepoFalso(this.resposta, {this.erro}) : super(ApiClient(SessaoStore()));

  final PalpitesDoJogo resposta;
  final String? erro;
  int chamadas = 0;

  @override
  Future<PalpitesDoJogo> palpitesDoJogo(String poolId, String matchId) async {
    chamadas++;
    if (erro != null) throw ApiError(erro!, semConexao: true);
    return resposta;
  }
}

class _SessaoFalsa extends SessaoNotifier {
  @override
  EstadoSessao build() => const Autenticado(
    Conta(id: 'ana', email: 'ana@teste.com', displayName: 'Ana'),
  );
}

Widget _app(_RepoFalso repo) => ProviderScope(
  overrides: [
    boloesRepositoryProvider.overrideWithValue(repo),
    sessaoProvider.overrideWith(_SessaoFalsa.new),
  ],
  child: MaterialApp(
    home: PalpitesDoJogoPage(
      poolId: 'p1',
      matchId: 'm1',
      timeCasa: 'Flamengo',
      timeFora: 'Vasco',
      kickoffAt: DateTime.utc(2026, 8, 7, 21),
    ),
  ),
);

void main() {
  group('Sigilo do palpite', () {
    testWidgets('antes do fim do jogo, diz que está em segredo', (t) async {
      await t.pumpWidget(
        _app(_RepoFalso(const PalpitesDoJogo(revelado: false, palpites: []))),
      );
      await t.pumpAndSettle();

      // Estado explícito, não lista vazia: vazio pareceria bolão sem palpite.
      expect(find.text('Ainda em segredo'), findsOneWidget);
      // O sigilo valer para o organizador também é o que torna a competição
      // justa — e precisa estar escrito, não só implementado.
      expect(find.textContaining('nem o organizador vê antes'), findsOneWidget);
    });

    testWidgets('depois do fim, mostra placar, autor e ponto de cada um', (
      t,
    ) async {
      await t.pumpWidget(
        _app(
          _RepoFalso(
            const PalpitesDoJogo(
              revelado: true,
              palpites: [
                PalpiteDeTerceiro(
                  userId: 'ana',
                  displayName: 'Ana',
                  casa: 2,
                  fora: 1,
                  pontos: 10,
                ),
                PalpiteDeTerceiro(
                  userId: 'bruno',
                  displayName: 'Bruno',
                  casa: 0,
                  fora: 3,
                  pontos: 0,
                ),
              ],
            ),
          ),
        ),
      );
      await t.pumpAndSettle();

      expect(find.text('Ana (você)'), findsOneWidget);
      expect(find.text('Bruno'), findsOneWidget);
      expect(find.text('2x1'), findsOneWidget);
      expect(find.text('0x3'), findsOneWidget);
      expect(find.text('10 pts'), findsOneWidget);
      expect(find.text('0 pts'), findsOneWidget);
    });

    testWidgets('jogo terminado sem palpite de ninguém tem texto próprio', (
      t,
    ) async {
      await t.pumpWidget(
        _app(_RepoFalso(const PalpitesDoJogo(revelado: true, palpites: []))),
      );
      await t.pumpAndSettle();

      // Revelado e vazio é diferente de em segredo: confundir os dois faria
      // parecer que o app está escondendo algo.
      expect(find.text('Ninguém palpitou neste jogo'), findsOneWidget);
      expect(find.text('Ainda em segredo'), findsNothing);
    });

    testWidgets('placar apurado ainda pendente aparece como traço', (t) async {
      await t.pumpWidget(
        _app(
          _RepoFalso(
            const PalpitesDoJogo(
              revelado: true,
              palpites: [
                PalpiteDeTerceiro(
                  userId: 'bruno',
                  displayName: 'Bruno',
                  casa: 1,
                  fora: 1,
                  pontos: null,
                ),
              ],
            ),
          ),
        ),
      );
      await t.pumpAndSettle();

      // Mostrar 0 acusaria o participante de errar um jogo não apurado.
      expect(find.text('—'), findsOneWidget);
      expect(find.text('0 pts'), findsNothing);
    });

    testWidgets('falha de rede mostra o motivo, sem inventar sigilo', (
      t,
    ) async {
      await t.pumpWidget(
        _app(
          _RepoFalso(
            const PalpitesDoJogo(revelado: true, palpites: []),
            erro: 'sem conexão com o servidor',
          ),
        ),
      );
      await t.pumpAndSettle();

      expect(find.text('sem conexão com o servidor'), findsOneWidget);
      expect(find.text('Ainda em segredo'), findsNothing);
      expect(find.text('Tentar de novo'), findsOneWidget);
    });

    testWidgets('a tela sempre busca do servidor, sem cache local', (t) async {
      final repo = _RepoFalso(
        const PalpitesDoJogo(revelado: false, palpites: []),
      );
      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();
      expect(repo.chamadas, 1);

      // Sai da tela e volta: o State é descartado, como numa navegação real.
      await t.pumpWidget(const MaterialApp(home: SizedBox.shrink()));
      await t.pumpAndSettle();

      // Cache de palpite de terceiro em jogo não terminado é o mesmo
      // vazamento por outro caminho: cada abertura tem que perguntar.
      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();
      expect(repo.chamadas, 2);
    });
  });
}
