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/data/boloes_repository_http.dart';
import 'package:bolao_mobile/features/boloes/presentation/rodada_page.dart';
import 'package:bolao_mobile/features/home/presentation/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';

/// Execução de `CC-5-11` do plano `docs/testes/CC-5-plano.md`: cada motivo de
/// recusa do convite tem texto próprio na tela. "Erro" genérico aqui é o que faz
/// o convidado desistir — e ele desiste calado, sem reclamar com o organizador.
class _RepoFalso extends BoloesRepositoryHttp {
  _RepoFalso({this.erro, this.jaParticipava = false})
    : super(ApiClient(SessaoStore()));

  final ApiError? erro;
  final bool jaParticipava;

  @override
  Future<List<BolaoResumo>> meus() async => const [];

  @override
  Future<({bool jaParticipava, String poolId, String poolName})>
  entrarPorConvite(String token) async {
    if (erro != null) throw erro!;
    return (
      poolId: 'p1',
      poolName: 'Bolão do setor',
      jaParticipava: jaParticipava,
    );
  }
}

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: const MaterialApp(home: HomePage()),
);

Future<void> _colarCodigo(WidgetTester t) async {
  await t.tap(find.text('Entrar por convite'));
  await t.pumpAndSettle();
  await t.enterText(find.byType(TextField), 'codigo-qualquer');
  await t.tap(find.widgetWithText(FilledButton, 'Entrar'));
  await t.pumpAndSettle();
}

void main() {
  // A home grava e lê o cache da lista de bolões.
  setUp(() => SharedPreferences.setMockInitialValues({}));

  group('CC-5-11 — cada recusa com texto próprio', () {
    // Os textos vêm do servidor; o que se verifica aqui é que a tela **mostra o
    // motivo** em vez de trocá-lo por um genérico.
    final motivos = {
      'convite inválido ou já substituído': 404,
      'este convite expirou. Peça um novo ao administrador': 410,
      'este bolão já começou e não aceita mais participantes': 409,
      'este bolão já foi encerrado': 410,
    };

    for (final entrada in motivos.entries) {
      testWidgets('mostra "${entrada.key}"', (t) async {
        await t.pumpWidget(
          _app(_RepoFalso(erro: ApiError(entrada.key, status: entrada.value))),
        );
        await t.pumpAndSettle();

        await _colarCodigo(t);

        expect(find.text(entrada.key), findsOneWidget);
      });
    }

    testWidgets('sucesso confirma em qual bolão entrou', (t) async {
      await t.pumpWidget(_app(_RepoFalso()));
      await t.pumpAndSettle();

      await _colarCodigo(t);

      expect(find.text('Você entrou em Bolão do setor'), findsOneWidget);
    });

    testWidgets('CC-5-08 quem já participa não vê erro', (t) async {
      await t.pumpWidget(_app(_RepoFalso(jaParticipava: true)));
      await t.pumpAndSettle();

      await _colarCodigo(t);

      // Tratar reuso como erro faria o participante achar que perdeu o acesso.
      expect(find.text('Você já participa de Bolão do setor'), findsOneWidget);
    });
  });
}
