import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:local_auth/local_auth.dart';
import 'package:local_auth/error_codes.dart' as auth_error;
import 'package:flutter/services.dart';

/// Esito dello sblocco all'avvio.
enum UnlockResult {
  /// Nessun login salvato: mostra la schermata di login normale.
  noSavedLogin,

  /// Token disponibile e pronto all'uso: entra diretto.
  success,

  /// C'è un login salvato ma la biometria è fallita/annullata:
  /// resta sul login, l'utente può accedere con password.
  biometricFailed,

  /// Biometria richiesta ma non disponibile (niente impronte / hardware):
  /// fallback a login con password (scelta B).
  biometricUnavailable,
}

/// Custode unico di token + preferenze di accesso (ricordami + biometria).
///
/// Non è un provider: lo si interroga in momenti precisi
/// (avvio nello splash, login, logout). Vedi nota architetturale nel piano.
class AuthStorage {
  AuthStorage._();
  static final AuthStorage instance = AuthStorage._();

  // Su Android forza l'uso di EncryptedSharedPreferences (più robusto).
  static const _secure = FlutterSecureStorage(
    aOptions: AndroidOptions(encryptedSharedPreferences: true),
  );

  final LocalAuthentication _localAuth = LocalAuthentication();

  // Chiavi interne della cassaforte.
  static const _kToken = 'auth_token';
  static const _kRemember = 'pref_remember_me';
  static const _kBiometric = 'pref_biometric';

  // ---------------------------------------------------------------------------
  // PREFERENZE
  // ---------------------------------------------------------------------------

  /// "Ricordami" attivo? (default: false)
  Future<bool> isRememberMeEnabled() async {
    return (await _secure.read(key: _kRemember)) == 'true';
  }

  /// Biometria attiva? (default: false)
  Future<bool> isBiometricEnabled() async {
    return (await _secure.read(key: _kBiometric)) == 'true';
  }

  /// Imposta "ricordami". Se lo spegni, cancella anche il token salvato:
  /// senza ricordami non ha senso tenere un token in cassaforte.
  Future<void> setRememberMe(bool value) async {
    await _secure.write(key: _kRemember, value: value ? 'true' : 'false');
    if (!value) {
      await _secure.delete(key: _kToken);
      // Biometria senza token salvato non ha nulla da proteggere: spegnila.
      await _secure.write(key: _kBiometric, value: 'false');
    }
  }

  /// Imposta la biometria. Ha senso solo se "ricordami" è attivo.
  Future<void> setBiometric(bool value) async {
    await _secure.write(key: _kBiometric, value: value ? 'true' : 'false');
  }

  // ---------------------------------------------------------------------------
  // TOKEN
  // ---------------------------------------------------------------------------

  /// Salva il token SOLO se "ricordami" è attivo. Chiamato dopo il login.
  Future<void> saveTokenIfRemember(String token) async {
    if (await isRememberMeEnabled()) {
      await _secure.write(key: _kToken, value: token);
    }
  }

  /// Cancella tutto: token + preferenze. Chiamato al logout.
  Future<void> clear() async {
    await _secure.delete(key: _kToken);
    await _secure.delete(key: _kRemember);
    await _secure.delete(key: _kBiometric);
  }

  /// Lettura grezza del token (senza biometria). Uso interno.
  Future<String?> _readToken() => _secure.read(key: _kToken);

  // ---------------------------------------------------------------------------
  // BIOMETRIA
  // ---------------------------------------------------------------------------

  /// Il dispositivo può fare biometria E ha almeno un metodo registrato?
  Future<bool> canUseBiometrics() async {
    try {
      final supported = await _localAuth.isDeviceSupported();
      if (!supported) return false;
      final canCheck = await _localAuth.canCheckBiometrics;
      if (!canCheck) return false;
      final enrolled = await _localAuth.getAvailableBiometrics();
      return enrolled.isNotEmpty;
    } on PlatformException {
      return false;
    }
  }

  /// Chiede l'impronta/volto. true = passata, false = fallita/annullata.
  Future<bool> _promptBiometric() async {
    try {
      return await _localAuth.authenticate(
        localizedReason: 'Sblocca YogiBook per accedere al tuo account',
        options: const AuthenticationOptions(
          biometricOnly: true, // niente fallback al PIN del telefono (scelta B)
          stickyAuth: true, // regge se l'app va in background durante il prompt
        ),
      );
    } on PlatformException catch (e) {
      // Casi tipici: nessuna impronta registrata, hardware assente, troppi tentativi.
      if (e.code == auth_error.notAvailable ||
          e.code == auth_error.notEnrolled ||
          e.code == auth_error.lockedOut ||
          e.code == auth_error.permanentlyLockedOut) {
        return false;
      }
      return false;
    }
  }

  // ---------------------------------------------------------------------------
  // SBLOCCO ALL'AVVIO (chiamato dallo splash)
  // ---------------------------------------------------------------------------

  /// Decide cosa fare all'avvio dell'app.
  /// Restituisce l'esito; il token (se disponibile) è in [tokenOut].
  Future<({UnlockResult result, String? token})> tryUnlockAtStartup() async {
    // 1. Ricordami spento o nessun token → login normale.
    if (!await isRememberMeEnabled()) {
      return (result: UnlockResult.noSavedLogin, token: null);
    }
    final token = await _readToken();
    if (token == null || token.isEmpty) {
      return (result: UnlockResult.noSavedLogin, token: null);
    }

    // 2. Biometria spenta → entra diretto col token salvato.
    if (!await isBiometricEnabled()) {
      return (result: UnlockResult.success, token: token);
    }

    // 3. Biometria accesa ma non disponibile → fallback a password (scelta B).
    if (!await canUseBiometrics()) {
      return (result: UnlockResult.biometricUnavailable, token: null);
    }

    // 4. Biometria accesa e disponibile → chiedi l'impronta.
    final ok = await _promptBiometric();
    if (ok) {
      return (result: UnlockResult.success, token: token);
    }
    return (result: UnlockResult.biometricFailed, token: null);
  }
}
