import 'chamadas.dart';

/// Hora do servidor, com o desvio do aparelho medido uma vez por sessão.
///
/// **O relógio do aparelho não decide prazo** (`PP-3`, `PP-5`): adiantar o
/// celular não pode abrir palpite fechado. No backend HTTP a resposta da rodada
/// trazia `serverTime` em toda chamada; no Firestore a leitura é direta e não
/// passa por servidor nenhum, então o horário vem de uma callable e o app guarda
/// a **diferença**, não o instante.
///
/// Quem recusa de verdade continua sendo a rule, que compara com `request.time`.
/// Isto aqui é para a tela não mentir enquanto o participante decide.
class RelogioDoServidor {
  RelogioDoServidor([Callables? callables])
    : _callables = callables ?? Callables();

  final Callables _callables;

  Duration? _desvio;
  Future<void>? _emAndamento;

  /// Diferença conhecida entre o relógio do servidor e o do aparelho.
  Duration get desvio => _desvio ?? Duration.zero;

  /// Verdadeiro enquanto a diferença nunca pôde ser medida — offline no cold
  /// start, por exemplo. A tela pode usar isso para não afirmar um prazo que ela
  /// não tem como garantir.
  bool get sincronizado => _desvio != null;

  DateTime get agora => DateTime.now().add(desvio);

  /// Mede a diferença. Chamada uma vez por sessão; falha não derruba nada — sem
  /// medida, a contagem cai no relógio do aparelho e a recusa fica com a rule.
  Future<void> sincronizar() {
    return _emAndamento ??= _medir();
  }

  Future<void> _medir() async {
    try {
      final antes = DateTime.now();
      final r = await _callables.chamar<Map<String, dynamic>>('horaDoServidor');
      final depois = DateTime.now();
      final servidor = DateTime.parse(r['agora'] as String);

      // Desconta metade da ida e volta: a resposta descreve um instante que já
      // passou, e sem isso o desvio embute a latência da rede.
      final meiaViagem = depois.difference(antes) ~/ 2;
      _desvio = servidor.add(meiaViagem).difference(depois);
    } on Object {
      _emAndamento = null;
    }
  }
}
