import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../core/api/api_client.dart';
import '../../../core/cache/cache_local.dart';
import '../data/participantes_repository.dart';
import 'editor_de_regras.dart';
import 'participantes_page.dart';
import 'prazo.dart';
import 'rodada_page.dart';
import '../../../core/tema/tema.dart';

/// Regras do bolão, consultáveis a qualquer momento (CC-6).
///
/// É a resposta a "por que ele levou 5 e eu levei 3": sem esta tela, a única
/// fonte da regra é a memória do organizador.
class RegrasPage extends ConsumerStatefulWidget {
  const RegrasPage({required this.poolId, this.souAdmin = false, super.key});

  final String poolId;

  /// Habilita a edição (CC-2 FE-3), que só o admin faz — e só enquanto as
  /// regras não travaram.
  final bool souAdmin;

  @override
  ConsumerState<RegrasPage> createState() => _RegrasPageState();
}

class _RegrasPageState extends ConsumerState<RegrasPage> {
  Regras? _regras;
  String? _erro;
  bool _carregando = true;
  bool _editando = false;
  bool _salvando = false;

  /// Explicação do travamento que aconteceu **durante** a edição. Fica no modo
  /// leitura: mostrar só no formulário fazia a mensagem desaparecer no instante
  /// em que a tela volta — exatamente quando ela precisa ser lida.
  String? _avisoTravamento;
  ValoresDeRegras? _rascunho;

  /// Idade do dado quando veio do cache. Nulo = veio da rede agora.
  Duration? _idadeDoCache;

  @override
  void initState() {
    super.initState();
    _carregar();
  }

  Future<void> _carregar() async {
    setState(() => _erro = null);
    final cache = ref.read(cacheLocalProvider);
    final chave = 'regras.${widget.poolId}';
    try {
      final r = await ref
          .read(participantesRepositoryProvider)
          .regras(widget.poolId);
      if (!mounted) return;
      setState(() {
        _regras = r;
        _idadeDoCache = null;
      });
      await cache.gravar(chave, r.paraJson());
    } on ApiError catch (e) {
      if (!mounted) return;
      // A regra é o que o participante consulta no meio de uma discussão, e
      // discussão não espera rede. Guardada, ela continua consultável offline —
      // rotulada como antiga, porque regra destravada pode ter mudado.
      final guardado = await cache.ler(chave);
      if (!mounted) return;
      if (guardado != null && guardado.valor is Map) {
        setState(() {
          _regras = Regras.doJson(
            (guardado.valor as Map).cast<String, dynamic>(),
          );
          _idadeDoCache = guardado.idade;
          _erro = null;
        });
      } else {
        setState(() => _erro = e.mensagem);
      }
    } finally {
      if (mounted) setState(() => _carregando = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    final r = _regras;
    // Editar só faz sentido para admin e só antes do travamento. Oferecer para
    // os outros levaria a 403; oferecer com regras travadas, a 409.
    final podeEditar = widget.souAdmin && r != null && !r.travadas;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Regras do bolão'),
        actions: [
          if (podeEditar && !_editando)
            IconButton(
              tooltip: 'Editar regras',
              icon: const Icon(Icons.edit_outlined),
              onPressed: () => setState(() {
                _editando = true;
                _rascunho = ValoresDeRegras(
                  exato: r.pontosExato,
                  vencedorSaldo: r.pontosVencedorSaldo,
                  vencedor: r.pontosVencedor,
                  ordemDesempate: r.ordemDesempate,
                );
              }),
            ),
        ],
      ),
      body: _editando ? _formulario() : _corpo(),
    );
  }

  Widget _formulario() {
    final rascunho = _rascunho!;
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        // `Text` não interpreta markdown: `**assim**` aparecia com os
        // asteriscos na tela.
        const Text.rich(
          TextSpan(
            children: [
              TextSpan(
                text: 'A pontuação e o desempate valem para o bolão inteiro e ',
              ),
              TextSpan(
                text: 'travam no primeiro palpite',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              TextSpan(text: ' registrado. Depois disso, nem você muda.'),
            ],
          ),
          style: TextStyle(fontSize: 13),
        ),
        const Divider(height: 24),
        EditorDeRegras(
          valores: rascunho,
          onMudou: (v) => setState(() => _rascunho = v),
        ),
        if (_erro != null) ...[
          const SizedBox(height: 12),
          Text(
            _erro!,
            style: TextStyle(
              color: Theme.of(context).colorScheme.error,
              fontSize: 12,
            ),
          ),
        ],
        const SizedBox(height: 20),
        Row(
          children: [
            Expanded(
              child: OutlinedButton(
                onPressed: _salvando
                    ? null
                    : () => setState(() {
                        _editando = false;
                        _erro = null;
                      }),
                child: const Text('Cancelar'),
              ),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: FilledButton(
                onPressed: _salvando ? null : _salvar,
                child: const Text('Salvar regras'),
              ),
            ),
          ],
        ),
      ],
    );
  }

  Future<void> _salvar() async {
    final rascunho = _rascunho!;
    if (rascunho.inconsistencia != null) {
      setState(() => _erro = rascunho.inconsistencia);
      return;
    }

    setState(() {
      _salvando = true;
      _erro = null;
    });
    try {
      await ref
          .read(boloesRepositoryProvider)
          .alterarRegras(
            widget.poolId,
            pontosExato: rascunho.exato,
            pontosVencedorSaldo: rascunho.vencedorSaldo,
            pontosVencedor: rascunho.vencedor,
            ordemDesempate: rascunho.ordemDesempate,
          );
      if (!mounted) return;
      setState(() => _editando = false);
      await _carregar();
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: const Text('Regras salvas'),
            backgroundColor: CoresDoApp.sucesso,
          ),
        );
      }
    } on ApiError catch (e) {
      // 409 = alguém palpitou enquanto esta tela estava aberta. É fluxo
      // esperado, não erro: recarrega para mostrar as regras já travadas.
      if (!mounted) return;
      final travou = e.status == 409;
      setState(() {
        _erro = travou ? null : e.mensagem;
        _avisoTravamento = travou
            ? 'Alguém palpitou enquanto você editava, então as regras travaram: '
                  '${e.mensagem}. Valem as que estão nesta tela.'
            : null;
      });
      if (travou) {
        await _carregar();
        if (mounted) setState(() => _editando = false);
      }
    } finally {
      if (mounted) setState(() => _salvando = false);
    }
  }

  Widget _corpo() {
    if (_carregando) return const Center(child: CircularProgressIndicator());
    if (_regras == null) {
      return Center(
        child: Padding(
          padding: const EdgeInsets.all(32),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.cloud_off, size: 48),
              const SizedBox(height: 12),
              Text(_erro ?? 'falha ao carregar', textAlign: TextAlign.center),
              const SizedBox(height: 16),
              FilledButton(
                onPressed: _carregar,
                child: const Text('Tentar de novo'),
              ),
            ],
          ),
        ),
      );
    }

    final r = _regras!;
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        if (_idadeDoCache != null) ...[
          Container(
            width: double.infinity,
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.secondaryContainer,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Text(
              'Sem conexão — regras guardadas no aparelho, '
              '${descreverIdade(_idadeDoCache!)}.',
              style: const TextStyle(fontSize: 12),
            ),
          ),
          const SizedBox(height: 16),
        ],
        if (_avisoTravamento != null) ...[
          Container(
            width: double.infinity,
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.errorContainer,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Text(
              _avisoTravamento!,
              style: const TextStyle(fontSize: 12),
            ),
          ),
          const SizedBox(height: 16),
        ],
        Text('Pontuação', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        _Regra(
          pontos: r.pontosExato,
          titulo: 'Placar exato',
          detalhe: 'Acertou o resultado exato do jogo.',
        ),
        _Regra(
          pontos: r.pontosVencedorSaldo,
          titulo: 'Vencedor e saldo de gols',
          detalhe:
              'Acertou quem ganhou e por quantos gols de diferença. '
              'Só vale em jogo com vencedor.',
        ),
        _Regra(
          pontos: r.pontosVencedor,
          titulo: 'Só o vencedor',
          detalhe:
              'Acertou quem ganhou, com outra diferença de gols — '
              'ou acertou que foi empate, com outro placar.',
        ),
        const _Regra(
          pontos: 0,
          titulo: 'Errou o vencedor',
          detalhe: 'Nenhum ponto. Empate palpitado só vale se der empate.',
        ),
        const SizedBox(height: 8),
        // Sem esta linha o participante que palpitou 1x1 num 4x4 espera a faixa
        // do saldo (todo empate tem saldo 0) e recebe a de "só vencedor".
        Text(
          'Em jogo que terminou empatado não existe faixa de saldo: ou você '
          'acertou o placar exato, ou acertou apenas que foi empate.',
          style: TextStyle(
            fontSize: 12,
            color: Theme.of(context).colorScheme.outline,
          ),
        ),
        const Divider(height: 32),
        Text('Desempate', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        Text(
          'Empate em pontos é decidido por: '
          '${descreverOrdemDesempate(r.ordemDesempate)}.',
        ),
        const SizedBox(height: 8),
        const Text(
          'Se o empate resistir a todos os critérios, os participantes '
          'dividem a posição — não há desempate arbitrário.',
        ),
        const Divider(height: 32),
        Text('Prazo', style: Theme.of(context).textTheme.titleMedium),
        const SizedBox(height: 8),
        const Text(
          'O palpite de cada jogo fecha no horário oficial do apito inicial. '
          'O prazo é o do servidor, não o do seu aparelho.',
        ),
        if (r.proximoPrazo != null) ...[
          const SizedBox(height: 8),
          Text(
            'Próximo prazo: ${_formatarLocal(r.proximoPrazo!)}.',
            style: const TextStyle(fontWeight: FontWeight.bold),
          ),
        ],
        const Divider(height: 32),
        Row(
          children: [
            Icon(
              r.travadas ? Icons.lock_outline : Icons.lock_open_outlined,
              size: 18,
            ),
            const SizedBox(width: 8),
            Expanded(
              // Saber se ainda dá para mudar é o que impede a briga: regra
              // alterada no meio do campeonato é o pior cenário do épico.
              child: Text(
                r.travadas
                    ? 'As regras estão travadas: o primeiro palpite já foi '
                          'registrado e ninguém mais pode alterá-las.'
                    : 'As regras ainda podem ser alteradas pelo administrador. '
                          'Elas travam no primeiro palpite registrado.',
                style: const TextStyle(fontSize: 13),
              ),
            ),
          ],
        ),
        // Admin com regras travadas fica sem saída dentro desta tela. Dizer
        // qual é o caminho (encerrar e abrir outro) evita que ele tente burlar
        // — ou peça para alguém mexer no banco.
        if (r.travadas && widget.souAdmin) ...[
          const SizedBox(height: 12),
          Text(
            'Se a pontuação combinada estava errada, o caminho é encerrar este '
            'bolão e abrir outro com as regras certas. Mudar a regra no meio, '
            'com palpite já registrado, é o que gera briga.',
            style: TextStyle(
              fontSize: 12,
              color: Theme.of(context).colorScheme.outline,
            ),
          ),
        ],
      ],
    );
  }

  /// Fuso local: horário do jogo em UTC na tela é motivo declarado de perder
  /// prazo achando que tinha mais tempo.
  String _formatarLocal(DateTime utc) {
    final l = utc.toLocal();
    final d =
        '${l.day.toString().padLeft(2, '0')}/'
        '${l.month.toString().padLeft(2, '0')}';
    final h =
        '${l.hour.toString().padLeft(2, '0')}:'
        '${l.minute.toString().padLeft(2, '0')}';
    return '$d às $h (${formatarRestante(l.difference(DateTime.now()))})';
  }
}

class _Regra extends StatelessWidget {
  const _Regra({
    required this.pontos,
    required this.titulo,
    required this.detalhe,
  });

  final int pontos;
  final String titulo;
  final String detalhe;

  @override
  Widget build(BuildContext context) => Padding(
    padding: const EdgeInsets.symmetric(vertical: 6),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SizedBox(
          width: 52,
          child: Text(
            '$pontos pt${pontos == 1 ? '' : 's'}',
            style: Theme.of(context).textTheme.titleMedium,
          ),
        ),
        const SizedBox(width: 8),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(titulo),
              Text(
                detalhe,
                style: TextStyle(
                  fontSize: 12,
                  color: Theme.of(context).colorScheme.outline,
                ),
              ),
            ],
          ),
        ),
      ],
    ),
  );
}
