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_http.dart';
import 'package:bolao_mobile/features/boloes/data/convite_store.dart';
import 'package:bolao_mobile/features/boloes/presentation/convite_dialog.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';

/// Store em memória: o secure storage do device não existe em teste, e o que
/// importa aqui é o comportamento (não gerar sem pedir), não a persistência.
class _StoreFalso extends ConviteStore {
  _StoreFalso([String? inicial]) {
    if (inicial != null) _valores['p1'] = inicial;
  }

  final Map<String, String> _valores = {};

  @override
  Future<String?> ler(String poolId) async => _valores[poolId];

  @override
  Future<void> salvar(String poolId, String token) async {
    _valores[poolId] = token;
  }

  @override
  Future<void> limpar(String poolId) async {
    _valores.remove(poolId);
  }
}

class _RepoFalso extends BoloesRepositoryHttp {
  _RepoFalso({this.erroAoGerar}) : super(ApiClient(SessaoStore()));

  final String? erroAoGerar;
  int geracoes = 0;
  int invalidacoes = 0;

  @override
  Future<({DateTime expiraEm, String token})> gerarConvite(
    String poolId,
  ) async {
    if (erroAoGerar != null) throw ApiError(erroAoGerar!, status: 409);
    geracoes++;
    return (token: 'codigo-novo-$geracoes', expiraEm: DateTime(2026, 8, 7));
  }

  @override
  Future<void> invalidarConvite(String poolId) async {
    invalidacoes++;
  }
}

Widget _app(_RepoFalso repo, _StoreFalso store) => ProviderScope(
  overrides: [
    boloesRepositoryProvider.overrideWithValue(repo),
    conviteStoreProvider.overrideWithValue(store),
  ],
  child: const MaterialApp(
    home: Scaffold(body: ConviteDialog(poolId: 'p1')),
  ),
);

void main() {
  group('ConviteDialog', () {
    testWidgets('abrir a tela não gera convite — gerar revogaria o anterior', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo, _StoreFalso('codigo-ja-compartilhado')));
      await t.pumpAndSettle();

      // Este era o bug: o admin abria para conferir e matava o link que tinha
      // acabado de mandar no grupo.
      expect(repo.geracoes, 0);
      expect(find.text('codigo-ja-compartilhado'), findsOneWidget);
    });

    testWidgets('sem código no aparelho, oferece gerar', (t) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo, _StoreFalso()));
      await t.pumpAndSettle();

      expect(find.textContaining('Nenhum código ativo'), findsOneWidget);

      await t.tap(find.text('Gerar código'));
      await t.pumpAndSettle();

      expect(repo.geracoes, 1);
      expect(find.text('codigo-novo-1'), findsOneWidget);
    });

    testWidgets('gerar outro exige confirmação avisando que o atual morre', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo, _StoreFalso('codigo-antigo')));
      await t.pumpAndSettle();

      await t.tap(find.text('Gerar novo'));
      await t.pumpAndSettle();

      expect(
        find.textContaining('O código atual para de funcionar na hora'),
        findsOneWidget,
      );
      // Desistir não pode gerar nada.
      await t.tap(find.text('Cancelar'));
      await t.pumpAndSettle();
      expect(repo.geracoes, 0);
      expect(find.text('codigo-antigo'), findsOneWidget);
    });

    testWidgets('confirmando, o código novo substitui o antigo na tela', (
      t,
    ) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo, _StoreFalso('codigo-antigo')));
      await t.pumpAndSettle();

      await t.tap(find.text('Gerar novo'));
      await t.pumpAndSettle();
      await t.tap(find.widgetWithText(FilledButton, 'Gerar novo'));
      await t.pumpAndSettle();

      expect(repo.geracoes, 1);
      expect(find.text('codigo-novo-1'), findsOneWidget);
      expect(find.text('codigo-antigo'), findsNothing);
    });

    testWidgets('invalidar pede confirmação e limpa o código', (t) async {
      final repo = _RepoFalso();
      await t.pumpWidget(_app(repo, _StoreFalso('codigo-vazado')));
      await t.pumpAndSettle();

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

      expect(repo.invalidacoes, 1);
      expect(find.text('codigo-vazado'), findsNothing);
      expect(find.textContaining('Nenhum código ativo'), findsOneWidget);
    });

    testWidgets('oferece compartilhar pelos apps do device, não só copiar', (
      t,
    ) async {
      await t.pumpWidget(_app(_RepoFalso(), _StoreFalso('codigo-x')));
      await t.pumpAndSettle();

      // É por onde o convite circula de verdade: o grupo do WhatsApp.
      expect(find.text('Compartilhar'), findsOneWidget);
      expect(find.text('Copiar'), findsOneWidget);
    });

    testWidgets(
      'recusa do servidor aparece com o motivo, não como erro genérico',
      (t) async {
        final repo = _RepoFalso(
          erroAoGerar: 'este bolão já começou e não aceita mais participantes',
        );
        await t.pumpWidget(_app(repo, _StoreFalso()));
        await t.pumpAndSettle();

        await t.tap(find.text('Gerar código'));
        await t.pumpAndSettle();

        expect(
          find.text('este bolão já começou e não aceita mais participantes'),
          findsOneWidget,
        );
      },
    );
  });
}
