import 'package:bolao_mobile/core/api/api_client.dart';
import 'package:bolao_mobile/core/sessao/sessao_store.dart';
import 'package:bolao_mobile/features/boloes/data/boloes_repository.dart';
import 'package:bolao_mobile/features/boloes/data/ranking_repository.dart';
import 'package:bolao_mobile/features/boloes/presentation/corrigir_placar_dialog.dart';
import 'package:bolao_mobile/features/boloes/presentation/ranking_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.erro, this.afetados = 3, this.avisos = 2})
    : super(ApiClient(SessaoStore()));

  final String? erro;
  final int afetados;
  final int avisos;
  final List<({int casa, int fora})> aplicadas = [];

  @override
  Future<({int afetados, int avisos})> corrigirPlacar(
    String poolId,
    String matchId,
    int casa,
    int fora,
  ) async {
    if (erro != null) throw ApiError(erro!, status: 409);
    aplicadas.add((casa: casa, fora: fora));
    return (afetados: afetados, avisos: avisos);
  }
}

class _ExtratoFalso extends RankingRepository {
  _ExtratoFalso(this.resposta) : super(ApiClient(SessaoStore()));

  final Extrato resposta;

  @override
  Future<Extrato> extrato(String poolId, int matchday) async => resposta;
}

Widget _appCorrecao(_RepoFalso repo) => ProviderScope(
  overrides: [boloesRepositoryProvider.overrideWithValue(repo)],
  child: const MaterialApp(
    home: Scaffold(
      body: CorrigirPlacarDialog(
        poolId: 'p1',
        matchId: 'm1',
        timeCasa: 'Flamengo',
        timeFora: 'Vasco',
        oficialCasa: 2,
        oficialFora: 0,
      ),
    ),
  ),
);

Widget _appExtrato(Extrato e) => ProviderScope(
  overrides: [rankingRepositoryProvider.overrideWithValue(_ExtratoFalso(e))],
  child: const MaterialApp(
    home: Scaffold(body: ExtratoPage(poolId: 'p1', matchday: 20)),
  ),
);

void main() {
  group('CorrigirPlacarDialog', () {
    testWidgets('vem pré-preenchido com o placar oficial atual', (t) async {
      await t.pumpWidget(_appCorrecao(_RepoFalso()));
      await t.pumpAndSettle();

      // Corrigir quase sempre é ajustar um gol: começar do zero é trabalho a
      // mais e chance de errar o outro time.
      final campos = t.widgetList<TextField>(find.byType(TextField)).toList();
      expect(campos[0].controller?.text, '2');
      expect(campos[1].controller?.text, '0');
    });

    testWidgets('avisa que fica registrado e que a fonte oficial prevalece', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_appCorrecao(repo));
      await t.pumpAndSettle();

      await t.enterText(find.byType(TextField).at(0), '3');
      await t.tap(find.text('Corrigir placar'));
      await t.pumpAndSettle();

      // O poder de mexer no ponto de todos só é aceitável com rastro visível.
      expect(find.textContaining('registrada com seu nome'), findsOneWidget);
      // O aviso aparece nas duas camadas: no corpo do diálogo e na confirmação.
      expect(find.textContaining('fonte oficial'), findsWidgets);
      expect(repo.aplicadas, isEmpty);

      await t.tap(find.widgetWithText(FilledButton, 'Aplicar'));
      await t.pumpAndSettle();

      expect(repo.aplicadas, [(casa: 3, fora: 0)]);
      expect(
        find.textContaining('3 palpite(s) reapurado(s), 2 participante(s)'),
        findsOneWidget,
      );
    });

    testWidgets('correção que não afeta ninguém diz isso', (t) async {
      final repo = _RepoFalso(afetados: 0, avisos: 0);
      await t.pumpWidget(_appCorrecao(repo));
      await t.pumpAndSettle();

      await t.enterText(find.byType(TextField).at(1), '1');
      await t.tap(find.text('Corrigir placar'));
      await t.pumpAndSettle();
      await t.tap(find.widgetWithText(FilledButton, 'Aplicar'));
      await t.pumpAndSettle();

      expect(find.textContaining('Nenhum palpite foi afetado'), findsOneWidget);
    });

    testWidgets('recusa placar fora do limite antes de chamar o servidor', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_appCorrecao(repo));
      await t.pumpAndSettle();

      await t.enterText(find.byType(TextField).at(0), '99');
      await t.tap(find.text('Corrigir placar'));
      await t.pumpAndSettle();

      expect(find.textContaining('placar máximo é 20'), findsOneWidget);
      expect(repo.aplicadas, isEmpty);
    });

    testWidgets('recusa do servidor aparece com o motivo', (t) async {
      final repo = _RepoFalso(erro: 'o jogo ainda não terminou');
      await t.pumpWidget(_appCorrecao(repo));
      await t.pumpAndSettle();

      await t.tap(find.text('Corrigir placar'));
      await t.pumpAndSettle();
      await t.tap(find.widgetWithText(FilledButton, 'Aplicar'));
      await t.pumpAndSettle();

      expect(find.text('o jogo ainda não terminou'), findsOneWidget);
    });
  });

  group('Extrato com placar corrigido', () {
    LinhaExtrato linha({Correcao? correcao}) => LinhaExtrato(
      homeTeam: 'Flamengo',
      awayTeam: 'Vasco',
      estado: 'APURADO',
      points: 3,
      band: 'SO_VENCEDOR',
      palpiteCasa: 2,
      palpiteFora: 1,
      oficialCasa: 2,
      oficialFora: 0,
      matchId: 'm1',
      correcao: correcao,
    );

    testWidgets('mostra o placar anterior e quem alterou', (t) async {
      await t.pumpWidget(
        _appExtrato(
          Extrato(
            total: 3,
            linhas: [
              linha(
                correcao: const Correcao(
                  anteriorCasa: 1,
                  anteriorFora: 1,
                  manual: true,
                ),
              ),
            ],
          ),
        ),
      );
      await t.pumpAndSettle();

      // Ponto que cai sem explicação é indistinguível de fraude.
      expect(
        find.textContaining('placar corrigido — antes era 1x1'),
        findsOneWidget,
      );
      expect(
        find.textContaining('alterado pelo administrador'),
        findsOneWidget,
      );
    });

    testWidgets('correção da fonte oficial não é atribuída ao admin', (
      t,
    ) async {
      await t.pumpWidget(
        _appExtrato(
          Extrato(
            total: 3,
            linhas: [
              linha(
                correcao: const Correcao(
                  anteriorCasa: 0,
                  anteriorFora: 0,
                  manual: false,
                ),
              ),
            ],
          ),
        ),
      );
      await t.pumpAndSettle();

      expect(find.textContaining('antes era 0x0'), findsOneWidget);
      // Acusar o admin de uma correção da fonte seria inventar intervenção.
      expect(find.textContaining('alterado pelo administrador'), findsNothing);
    });

    testWidgets('jogo sem correção não ganha marca nenhuma', (t) async {
      await t.pumpWidget(_appExtrato(Extrato(total: 3, linhas: [linha()])));
      await t.pumpAndSettle();

      expect(find.textContaining('placar corrigido'), findsNothing);
    });

    testWidgets('jogo que não tinha placar antes é descrito como sem placar', (
      t,
    ) async {
      await t.pumpWidget(
        _appExtrato(
          Extrato(
            total: 3,
            linhas: [
              linha(
                correcao: const Correcao(
                  anteriorCasa: null,
                  anteriorFora: null,
                  manual: false,
                ),
              ),
            ],
          ),
        ),
      );
      await t.pumpAndSettle();

      expect(find.textContaining('antes era sem placar'), findsOneWidget);
    });
  });
}
