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/data/auth_repository_http.dart';
import 'package:bolao_mobile/features/auth/presentation/login_page.dart';
import 'package:bolao_mobile/features/auth/presentation/sessao_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

/// Execução dos casos de tela do plano `docs/testes/AC-2-plano.md`. A tela de
/// login não tinha teste nenhum — é a porta de entrada de quem já tem conta.

class _RepoFalso extends AuthRepositoryHttp {
  _RepoFalso({this.erro, this.demora = Duration.zero})
    : super(ApiClient(SessaoStore()), SessaoStore());

  final ApiError? erro;

  /// Atraso do servidor, para observar o estado de submissão.
  final Duration demora;

  int tentativas = 0;
  String? emailRecebido;

  @override
  Future<void> entrar({required String email, required String senha}) async {
    tentativas++;
    emailRecebido = email;
    if (demora > Duration.zero) await Future<void>.delayed(demora);
    if (erro != null) throw erro!;
  }

  @override
  Future<Conta> minhaConta() async =>
      const Conta(id: 'u1', email: 'ana@teste.com', displayName: 'Ana');
}

/// Sessão que **não toca o secure storage**: a implementação real grava a conta
/// no armazenamento seguro, que não existe no ambiente de teste e deixa o
/// `pumpAndSettle` pendurado para sempre.
class _SessaoFalsa extends SessaoNotifier {
  @override
  EstadoSessao build() => const SemSessao();

  @override
  Future<void> entrar({required String email, required String senha}) async {
    await ref.read(authRepositoryProvider).entrar(email: email, senha: senha);
    state = const Autenticado(
      Conta(id: 'u1', email: 'ana@teste.com', displayName: 'Ana'),
    );
  }
}

Widget _app(_RepoFalso repo) => ProviderScope(
  overrides: [
    authRepositoryProvider.overrideWithValue(repo),
    sessaoProvider.overrideWith(_SessaoFalsa.new),
  ],
  child: const MaterialApp(home: LoginPage()),
);

Future<void> _preencher(
  WidgetTester t, {
  String email = 'ana@teste.com',
}) async {
  await t.enterText(find.byType(TextFormField).at(0), email);
  await t.enterText(find.byType(TextFormField).at(1), 'senhaSegura123');
  await t.pumpAndSettle();
}

void main() {
  group('AC-2 tela de login', () {
    testWidgets('AC-2-09 não oferece "esqueci minha senha"', (t) async {
      await t.pumpWidget(_app(_RepoFalso()));
      await t.pumpAndSettle();

      // `FE-3` foi cancelada em 03/08: não há recuperação de senha. Oferecer o
      // que não existe é pior que não oferecer — a pessoa tenta, não recebe nada
      // e conclui que o app está quebrado.
      expect(find.textContaining('Esqueci'), findsNothing);
      expect(find.textContaining('esqueci'), findsNothing);
      expect(find.textContaining('Recuperar'), findsNothing);
    });

    testWidgets('AC-2-07 erro do servidor aparece com o texto dele', (t) async {
      final repo = _RepoFalso(erro: ApiError('e-mail ou senha inválidos'));
      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();
      await _preencher(t);

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

      // Mensagem genérica de propósito no servidor (AC-2-02) — mas ela precisa
      // chegar à tela, senão a pessoa não sabe se errou a senha ou caiu a rede.
      expect(find.text('e-mail ou senha inválidos'), findsOneWidget);
    });

    testWidgets('AC-2-08 durante o envio o botão não dispara de novo', (
      t,
    ) async {
      final repo = _RepoFalso(demora: const Duration(milliseconds: 400));
      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();
      await _preencher(t);

      await t.tap(find.widgetWithText(FilledButton, 'Entrar'));
      await t.pump(const Duration(milliseconds: 100));
      // Segundo toque enquanto o primeiro está no ar.
      await t.tap(find.byType(FilledButton));
      await t.pump(const Duration(milliseconds: 600));

      // Dois logins em sequência criariam duas sessões e consumiriam o rate
      // limit da conta por acidente.
      expect(repo.tentativas, 1);
    });

    testWidgets('campos vazios são recusados antes de chamar o servidor', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();

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

      expect(repo.tentativas, 0);
      expect(find.text('informe seu e-mail'), findsOneWidget);
    });

    testWidgets(
      'AC-2-06 e-mail com maiúscula e espaço chega limpo ao servidor',
      (t) async {
        final repo = _RepoFalso();
        await t.pumpWidget(_app(repo));
        await t.pumpAndSettle();
        await _preencher(t, email: '  Ana@Teste.com ');

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

        // Teclado de celular capitaliza a primeira letra sozinho; o servidor
        // normaliza, mas mandar espaço em volta é erro do app.
        expect(repo.emailRecebido, 'Ana@Teste.com');
      },
    );
  });
}
