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/boloes_repository_http.dart';
import 'package:bolao_mobile/features/boloes/data/rascunho_store.dart';
import 'package:bolao_mobile/features/boloes/presentation/prazo.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';
import 'package:shared_preferences/shared_preferences.dart';

class _StoreFalso extends RascunhoStore {
  final Map<String, List<PalpiteRascunho>> _dados = {};

  @override
  Future<List<PalpiteRascunho>> ler(String poolId, int matchday) async =>
      _dados['$poolId.$matchday'] ?? const [];

  @override
  Future<void> salvar(
    String poolId,
    int matchday,
    List<PalpiteRascunho> palpites,
  ) async {
    _dados['$poolId.$matchday'] = palpites;
  }
}

class _RepoFalso extends BoloesRepositoryHttp {
  _RepoFalso({required this.kickoff, this.recusarPorPrazo = false})
    : super(ApiClient(SessaoStore()));

  final DateTime kickoff;
  final bool recusarPorPrazo;

  /// Palpite que o servidor tem guardado — o que realmente conta.
  int? salvoCasa;
  int? salvoFora;

  @override
  Future<Rodada> rodada(String poolId, int matchday) async => Rodada(
    serverTime: DateTime.utc(2026, 8, 6, 12),
    jogos: [
      JogoDaRodada(
        matchId: 'm1',
        homeTeam: 'Flamengo',
        awayTeam: 'Vasco',
        kickoffAt: kickoff,
        deadlineOpen: kickoff.isAfter(DateTime.utc(2026, 8, 6, 12)),
        palpiteCasa: salvoCasa,
        palpiteFora: salvoFora,
      ),
    ],
  );

  @override
  Future<ResultadoEnvio> palpitar(
    String poolId,
    List<({int casa, int fora, String matchId})> palpites,
  ) async {
    if (recusarPorPrazo) {
      return const ResultadoEnvio(
        salvos: 0,
        recusados: 1,
        motivos: {'m1': 'PRAZO_FECHADO'},
      );
    }
    salvoCasa = palpites.first.casa;
    salvoFora = palpites.first.fora;
    return ResultadoEnvio(
      salvos: palpites.length,
      recusados: 0,
      motivos: const {},
    );
  }
}

Widget _app(_RepoFalso repo) => ProviderScope(
  overrides: [
    boloesRepositoryProvider.overrideWithValue(repo),
    rascunhoStoreProvider.overrideWithValue(_StoreFalso()),
  ],
  child: const MaterialApp(home: RodadaPage(poolId: 'p1', matchday: 20)),
);

void main() {
  setUp(() => SharedPreferences.setMockInitialValues({}));

  group('formatarHorarioLocal', () {
    test('PP-3-10 horário local com o fuso indicado', () {
      final texto = formatarHorarioLocal(DateTime.utc(2026, 8, 6, 21, 30));
      final local = DateTime.utc(2026, 8, 6, 21, 30).toLocal();

      // O fuso escrito é o que resolve a dúvida em jogo que fecha de madrugada.
      expect(texto, contains(nomeDoFuso(local)));
      expect(texto, contains('${local.day.toString().padLeft(2, '0')}/'));
    });

    test('nomeia o fuso a partir do offset, sem base de fusos', () {
      // UTC puro tem nome próprio; offsets quebrados aparecem com minutos.
      expect(nomeDoFuso(DateTime.utc(2026, 1, 1)), 'UTC');
      expect(nomeDoFuso(DateTime.utc(2026, 1, 1)).startsWith('UTC'), isTrue);
    });
  });

  group('Prazo na tela de palpite', () {
    testWidgets('mostra o horário local do jogo, não só a contagem', (t) async {
      final kickoff = DateTime.utc(2026, 8, 7, 21, 0);
      await t.pumpWidget(_app(_RepoFalso(kickoff: kickoff)));
      await t.pumpAndSettle();

      expect(find.text(formatarHorarioLocal(kickoff)), findsOneWidget);
      expect(find.textContaining('fecha em'), findsOneWidget);
    });

    testWidgets('PP-3-02 aberto/fechado é decisão do servidor, e a tela obedece', (
      t,
    ) async {
      // Kickoff antes do serverTime: o prazo é do servidor, não do device.
      await t.pumpWidget(
        _app(_RepoFalso(kickoff: DateTime.utc(2026, 8, 6, 10))),
      );
      await t.pumpAndSettle();

      expect(find.text('prazo fechado'), findsOneWidget);
      final campos = t.widgetList<TextFormField>(find.byType(TextFormField));
      for (final c in campos) {
        expect(c.enabled, isFalse);
      }
    });

    testWidgets('recusa por prazo mostra o palpite que valeu, não o digitado', (
      t,
    ) async {
      final repo = _RepoFalso(
        kickoff: DateTime.utc(2026, 8, 7, 21),
        recusarPorPrazo: true,
      );
      // O servidor já tinha 1x0 guardado desta pessoa.
      repo.salvoCasa = 1;
      repo.salvoFora = 0;

      await t.pumpWidget(_app(repo));
      await t.pumpAndSettle();

      await t.enterText(find.byType(TextFormField).at(0), '5');
      await t.enterText(find.byType(TextFormField).at(1), '5');
      await t.pumpAndSettle();
      await t.tap(find.text('Enviar palpites'));
      await t.pumpAndSettle();

      // Deixar 5x5 na tela faria o participante acreditar num palpite que não
      // conta — e brigar depois quando o extrato mostrasse 1x0.
      expect(
        find.textContaining('vale o palpite que estava salvo'),
        findsOneWidget,
      );
      final campo = t.widget<TextFormField>(find.byType(TextFormField).at(0));
      expect(campo.initialValue, '1');
    });
  });
}
