import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import '../../../core/erros/erro_do_app.dart';
import '../../../core/firebase/chamadas.dart';
import '../../../core/sessao/sessao_store.dart';
import 'auth_repository.dart';

/// Autenticação pelo Firebase Auth (`MG-4`).
///
/// A diferença invisível para a tela: **não há mais token para guardar**. O
/// Firebase persiste a sessão sozinho e renova o próprio token, então o
/// `SessaoStore` deixa de guardar credencial e passa a guardar só o retrato da
/// conta — que é o que faz o cold start **sem rede** não jogar o participante no
/// login (`AC-3`). Sem recuperação de senha, deslogar por rede ruim é caro.
class AuthRepositoryFirebase implements AuthRepository {
  AuthRepositoryFirebase(
    this._sessao, {
    FirebaseAuth? auth,
    FirebaseFirestore? db,
  }) : _auth = auth ?? FirebaseAuth.instance,
       _db = db ?? FirebaseFirestore.instance;

  final SessaoStore _sessao;
  final FirebaseAuth _auth;
  final FirebaseFirestore _db;

  @override
  Future<Conta> cadastrar({
    required String email,
    required String senha,
    required String nomeExibicao,
  }) async {
    // O mínimo de 8 caracteres é do produto (`AC-1`) e o Auth só exige 6 no
    // plano atual. Validar aqui não substitui o servidor — substitui **nada**,
    // porque é o mesmo que o app já fazia; a checagem de verdade fica pendente
    // até o Identity Platform, e isso está registrado no PRD.
    try {
      final credencial = await _auth.createUserWithEmailAndPassword(
        email: email.trim(),
        password: senha,
      );
      await credencial.user!.updateDisplayName(nomeExibicao.trim());

      // **O nome escolhido tem de chegar ao perfil.** O gatilho de cadastro
      // dispara na criação da conta, quando o `displayName` do Auth ainda está
      // vazio (ele só é preenchido na linha acima), e cai no trecho antes do
      // arroba. Sem esta escrita, quem se cadastrou como "Bruno" aparecia no
      // bolão como "bruno1786151672" — defeito que só apareceu em device, porque
      // o gatilho não roda em teste de widget.
      //
      // `merge` resolve a corrida nos dois sentidos: se o gatilho chegou antes,
      // isto corrige o nome; se chegou depois, ele vê o documento e não toca.
      await _db.doc('usuarios/${credencial.user!.uid}').set({
        'displayName': nomeExibicao.trim(),
        'email': email.trim(),
      }, SetOptions(merge: true));

      final conta = Conta(
        id: credencial.user!.uid,
        email: email.trim(),
        displayName: nomeExibicao.trim(),
      );
      await _sessao.salvarConta({
        'id': conta.id,
        'email': conta.email,
        'displayName': conta.displayName,
      });
      return conta;
    } on FirebaseAuthException catch (e) {
      throw traduzirAuth(e);
    }
  }

  @override
  Future<void> entrar({required String email, required String senha}) async {
    try {
      await _auth.signInWithEmailAndPassword(
        email: email.trim(),
        password: senha,
      );
      await _guardarRetrato();
    } on FirebaseAuthException catch (e) {
      throw traduzirAuth(e);
    }
  }

  @override
  Future<Conta> minhaConta() async {
    final usuario = _auth.currentUser;
    if (usuario == null) throw SessaoExpirada();
    return _guardarRetrato();
  }

  @override
  Future<void> sair() async {
    await _auth.signOut();
    await _sessao.limpar();
  }

  /// Lê o perfil e guarda o retrato para o cold start offline.
  ///
  /// O nome de exibição mora no documento de perfil, criado pelo gatilho de
  /// cadastro: o `displayName` do Auth existe, mas é o perfil que a lista de
  /// participantes copia, e ter **uma** fonte evita os dois divergirem.
  Future<Conta> _guardarRetrato() async {
    final usuario = _auth.currentUser!;
    String nome = usuario.displayName ?? '';
    try {
      final perfil = await _db.doc('usuarios/${usuario.uid}').get();
      nome = (perfil.data()?['displayName'] as String?) ?? nome;
    } on FirebaseException {
      // Sem rede o perfil não vem, e o retrato guardado antes é melhor que
      // derrubar a sessão.
      final guardado = await _sessao.conta;
      if (guardado != null) nome = guardado['displayName'] as String? ?? nome;
    }
    if (nome.isEmpty) nome = usuario.email?.split('@').first ?? 'participante';

    final conta = Conta(
      id: usuario.uid,
      email: usuario.email ?? '',
      displayName: nome,
    );
    await _sessao.salvarConta({
      'id': conta.id,
      'email': conta.email,
      'displayName': conta.displayName,
    });
    return conta;
  }
}
