import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

/// Um palpite digitado e ainda não confirmado pelo servidor.
class PalpiteRascunho {
  const PalpiteRascunho({
    required this.matchId,
    required this.casa,
    required this.fora,
    required this.enviado,
  });

  factory PalpiteRascunho.doJson(Map<String, dynamic> j) => PalpiteRascunho(
    matchId: j['matchId'] as String,
    casa: j['casa'] as int,
    fora: j['fora'] as int,
    enviado: j['enviado'] as bool? ?? false,
  );

  Map<String, dynamic> paraJson() => {
    'matchId': matchId,
    'casa': casa,
    'fora': fora,
    'enviado': enviado,
  };

  final String matchId;
  final int casa;
  final int fora;

  /// Já confirmado pelo servidor. Rascunho não enviado é o que a fila retenta e
  /// o que a tela marca como pendente.
  final bool enviado;

  PalpiteRascunho comEnvio(bool valor) =>
      PalpiteRascunho(matchId: matchId, casa: casa, fora: fora, enviado: valor);
}

/// Rascunho de palpite persistido no device, por bolão e rodada.
///
/// Existe porque perder palpite digitado é motivo declarado de abandonar o app:
/// o casual digita a rodada inteira no ônibus, sem rede, e não pode descobrir
/// depois que nada foi salvo. Fica em `SharedPreferences` e não no armazenamento
/// seguro — é dado do próprio participante, sem valor para quem roubar o
/// aparelho, e o volume não caberia bem em keychain.
class RascunhoStore {
  RascunhoStore([SharedPreferences? prefs]) : _injetado = prefs;

  final SharedPreferences? _injetado;
  SharedPreferences? _cache;

  Future<SharedPreferences> get _prefs async =>
      _injetado ?? (_cache ??= await SharedPreferences.getInstance());

  String _chave(String poolId, int matchday) =>
      'bolao.rascunho.$poolId.$matchday';

  Future<List<PalpiteRascunho>> ler(String poolId, int matchday) async {
    final bruto = (await _prefs).getString(_chave(poolId, matchday));
    if (bruto == null) return const [];
    try {
      return (jsonDecode(bruto) as List<dynamic>)
          .cast<Map<String, dynamic>>()
          .map(PalpiteRascunho.doJson)
          .toList();
    } on FormatException {
      // Rascunho corrompido não pode impedir o participante de palpitar de
      // novo: descarta e segue.
      await limpar(poolId, matchday);
      return const [];
    }
  }

  Future<void> salvar(
    String poolId,
    int matchday,
    List<PalpiteRascunho> palpites,
  ) async {
    final p = await _prefs;
    if (palpites.isEmpty) {
      await p.remove(_chave(poolId, matchday));
      return;
    }
    await p.setString(
      _chave(poolId, matchday),
      jsonEncode(palpites.map((e) => e.paraJson()).toList()),
    );
  }

  Future<void> limpar(String poolId, int matchday) async =>
      (await _prefs).remove(_chave(poolId, matchday));
}
