import "package:flutter/services.dart";
import "package:flutter_secure_storage/flutter_secure_storage.dart";
import "package:local_auth/local_auth.dart";
import "package:local_auth_android/local_auth_android.dart";

/// Biometric ("Face recognition" / "Fingerprint") sign-in.
///
/// The app already persists a JWT in SharedPreferences, so a valid session
/// auto-restores on boot and the login screen is only shown after logout or
/// token expiry. Biometric login covers that case: the user's credentials are
/// kept in Android Keystore-backed secure storage and released only after the
/// system BiometricPrompt succeeds, then replayed against the normal login API
/// (so each biometric sign-in mints a fresh session/token).
class BiometricAuth {
  final LocalAuthentication _auth = LocalAuthentication();
  // Default options: the Jetpack Security backend this used to ask for by name
  // is deprecated by Google and gone in flutter_secure_storage v11, and v10
  // ignores the parameter and migrates what is already stored to its own ciphers
  // on first access. Which is why this app is pinned below v11 — going straight
  // there from v9 would skip that migration and leave the saved credentials
  // unreadable, quietly switching biometric login off for anyone who had it on.
  // Once a build carrying v10 has run on a device, v11 is a free bump.
  final FlutterSecureStorage _store = const FlutterSecureStorage();

  static const _emailKey = "cc_bio_email";
  static const _passwordKey = "cc_bio_password";

  /// Synchronous mirror of [isEnabled], kept fresh by the async calls below so
  /// the app-lifecycle handler (which can't await) can decide whether to lock.
  bool enabledCached = false;

  /// Whether the device has usable biometric hardware with something enrolled.
  Future<bool> isAvailable() async {
    try {
      if (!await _auth.isDeviceSupported()) return false;
      return await _auth.canCheckBiometrics;
    } on PlatformException {
      return false;
    }
  }

  /// Which biometric kinds are enrolled (used to label the sign-in buttons).
  Future<List<BiometricType>> availableTypes() async {
    try {
      return await _auth.getAvailableBiometrics();
    } on PlatformException {
      return const [];
    }
  }

  Future<bool> hasFace() async =>
      (await availableTypes()).contains(BiometricType.face);

  Future<bool> hasFingerprint() async =>
      (await availableTypes()).contains(BiometricType.fingerprint);

  /// The email biometric login was enabled for, or null if it's off.
  Future<String?> enabledEmail() async {
    final v = await _store.read(key: _emailKey);
    enabledCached = v != null;
    return v;
  }

  Future<bool> isEnabled() async => (await enabledEmail()) != null;

  /// Remember credentials for biometric sign-in. Caller should only invoke this
  /// after a successful password login so the stored creds are known-good.
  Future<void> enable(String email, String password) async {
    await _store.write(key: _emailKey, value: email);
    await _store.write(key: _passwordKey, value: password);
    enabledCached = true;
  }

  Future<void> disable() async {
    await _store.delete(key: _emailKey);
    await _store.delete(key: _passwordKey);
    enabledCached = false;
  }

  /// Prompt for biometrics and, on success, return the stored (email, password).
  /// Returns null if the user cancels or nothing is stored; throws
  /// [BiometricException] with a friendly message on a hard error.
  Future<(String, String)?> unlock({String? reason}) async {
    bool ok;
    try {
      ok = await _auth.authenticate(
        localizedReason: reason ?? "Sign in to Car Control",
        options: const AuthenticationOptions(
          biometricOnly: true,
          stickyAuth: true,
        ),
        authMessages: const [
          AndroidAuthMessages(
            signInTitle: "Car Control sign-in",
            biometricHint: "",
            cancelButton: "Use password",
          ),
        ],
      );
    } on PlatformException catch (e) {
      throw BiometricException(e.message ?? "Biometric authentication failed.");
    }
    if (!ok) return null;
    final email = await _store.read(key: _emailKey);
    final password = await _store.read(key: _passwordKey);
    if (email == null || password == null) return null;
    return (email, password);
  }
}

class BiometricException implements Exception {
  final String message;
  BiometricException(this.message);
  @override
  String toString() => message;
}

/// App-wide singleton (mirrors the other services in main.dart).
final biometricAuth = BiometricAuth();
