// ABOUTME: This file defines authentication method types and configurations
// ABOUTME: Includes biometric authentication, passwords, and authentication state management

import 'package:equatable/equatable.dart';

/// Available authentication methods for securing the app
enum AuthMethod {
  /// No authentication required (not recommended for sensitive data)
  none,
  /// Password-based authentication
  password,
  /// Biometric authentication (fingerprint, face, etc.)
  biometric,
  /// Both password and biometric (most secure)
  biometricWithPassword,
}

extension AuthMethodExtension on AuthMethod {
  /// Human-readable name for the authentication method
  String get displayName {
    switch (this) {
      case AuthMethod.none:
        return 'None';
      case AuthMethod.password:
        return 'Password';
      case AuthMethod.biometric:
        return 'Biometric';
      case AuthMethod.biometricWithPassword:
        return 'Biometric + Password';
    }
  }

  /// Description of the authentication method
  String get description {
    switch (this) {
      case AuthMethod.none:
        return 'No authentication required';
      case AuthMethod.password:
        return 'Secure with a password';
      case AuthMethod.biometric:
        return 'Use fingerprint or face unlock';
      case AuthMethod.biometricWithPassword:
        return 'Biometric with password fallback';
    }
  }

  /// Icon representation for UI
  String get icon {
    switch (this) {
      case AuthMethod.none:
        return '🔓';
      case AuthMethod.password:
        return '🔑';
      case AuthMethod.biometric:
        return '👆';
      case AuthMethod.biometricWithPassword:
        return '🔐';
    }
  }

  /// Security level (higher is more secure)
  int get securityLevel {
    switch (this) {
      case AuthMethod.none:
        return 0;
      case AuthMethod.password:
        return 2;
      case AuthMethod.biometric:
        return 3;
      case AuthMethod.biometricWithPassword:
        return 4;
    }
  }

  /// Whether this method requires biometric hardware
  bool get requiresBiometric {
    return this == AuthMethod.biometric || 
           this == AuthMethod.biometricWithPassword;
  }

  /// Whether this method requires password setup
  bool get requiresPassword {
    return this == AuthMethod.password || 
           this == AuthMethod.biometricWithPassword;
  }
}

/// Biometric authentication types available on the device
enum BiometricType {
  /// Fingerprint authentication
  fingerprint,
  /// Face recognition
  face,
  /// Iris scanning
  iris,
  /// Generic biometric (when specific type is unknown)
  unknown,
}

extension BiometricTypeExtension on BiometricType {
  String get displayName {
    switch (this) {
      case BiometricType.fingerprint:
        return 'Fingerprint';
      case BiometricType.face:
        return 'Face ID';
      case BiometricType.iris:
        return 'Iris';
      case BiometricType.unknown:
        return 'Biometric';
    }
  }

  String get icon {
    switch (this) {
      case BiometricType.fingerprint:
        return '👆';
      case BiometricType.face:
        return '👤';
      case BiometricType.iris:
        return '👁';
      case BiometricType.unknown:
        return '🔐';
    }
  }
}

/// Authentication configuration and state
class AuthConfig extends Equatable {
  final AuthMethod method;
  final bool isEnabled;
  final DateTime? lastAuthenticationAt;
  final int failedAttempts;
  final DateTime? lockedUntil;
  final List<BiometricType> availableBiometrics;

  const AuthConfig({
    required this.method,
    required this.isEnabled,
    this.lastAuthenticationAt,
    this.failedAttempts = 0,
    this.lockedUntil,
    this.availableBiometrics = const [],
  });

  /// Creates a default authentication configuration
  factory AuthConfig.defaultConfig() {
    return const AuthConfig(
      method: AuthMethod.none,
      isEnabled: false,
    );
  }

  /// Whether authentication is currently required
  bool get requiresAuth => isEnabled && method != AuthMethod.none;

  /// Whether the app is currently locked due to failed attempts
  bool get isLocked {
    if (lockedUntil == null) return false;
    return DateTime.now().isBefore(lockedUntil!);
  }

  /// Whether biometric authentication is available and configured
  bool get canUseBiometric {
    return method.requiresBiometric && 
           availableBiometrics.isNotEmpty &&
           !isLocked;
  }

  /// Whether password authentication is required
  bool get needsPassword {
    return method.requiresPassword || 
           (method.requiresBiometric && !canUseBiometric);
  }

  /// Time remaining until unlock (if locked)
  Duration? get lockTimeRemaining {
    if (!isLocked) return null;
    return lockedUntil!.difference(DateTime.now());
  }

  /// Creates a copy with updated values
  AuthConfig copyWith({
    AuthMethod? method,
    bool? isEnabled,
    DateTime? lastAuthenticationAt,
    int? failedAttempts,
    DateTime? lockedUntil,
    List<BiometricType>? availableBiometrics,
  }) {
    return AuthConfig(
      method: method ?? this.method,
      isEnabled: isEnabled ?? this.isEnabled,
      lastAuthenticationAt: lastAuthenticationAt ?? this.lastAuthenticationAt,
      failedAttempts: failedAttempts ?? this.failedAttempts,
      lockedUntil: lockedUntil ?? this.lockedUntil,
      availableBiometrics: availableBiometrics ?? this.availableBiometrics,
    );
  }

  /// Increments failed attempts and applies lockout if necessary
  AuthConfig withFailedAttempt() {
    final newFailedAttempts = failedAttempts + 1;
    DateTime? newLockedUntil = lockedUntil;

    // Progressive lockout: 1min, 5min, 15min, 1hr, 24hr
    if (newFailedAttempts >= 3) {
      final lockoutMinutes = [1, 5, 15, 60, 1440][
        (newFailedAttempts - 3).clamp(0, 4)
      ];
      newLockedUntil = DateTime.now().add(Duration(minutes: lockoutMinutes));
    }

    return copyWith(
      failedAttempts: newFailedAttempts,
      lockedUntil: newLockedUntil,
    );
  }

  /// Resets failed attempts after successful authentication
  AuthConfig withSuccessfulAuth() {
    return copyWith(
      failedAttempts: 0,
      lockedUntil: null,
      lastAuthenticationAt: DateTime.now(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'method': method.name,
      'isEnabled': isEnabled,
      'lastAuthenticationAt': lastAuthenticationAt?.toIso8601String(),
      'failedAttempts': failedAttempts,
      'lockedUntil': lockedUntil?.toIso8601String(),
      'availableBiometrics': availableBiometrics.map((b) => b.name).toList(),
    };
  }

  factory AuthConfig.fromJson(Map<String, dynamic> json) {
    return AuthConfig(
      method: AuthMethod.values.firstWhere(
        (m) => m.name == json['method'],
        orElse: () => AuthMethod.none,
      ),
      isEnabled: json['isEnabled'] as bool,
      lastAuthenticationAt: json['lastAuthenticationAt'] != null
          ? DateTime.parse(json['lastAuthenticationAt'])
          : null,
      failedAttempts: json['failedAttempts'] as int? ?? 0,
      lockedUntil: json['lockedUntil'] != null
          ? DateTime.parse(json['lockedUntil'])
          : null,
      availableBiometrics: (json['availableBiometrics'] as List<dynamic>?)
              ?.map((b) => BiometricType.values.firstWhere(
                    (bt) => bt.name == b,
                    orElse: () => BiometricType.unknown,
                  ))
              .toList() ??
          [],
    );
  }

  @override
  List<Object?> get props => [
        method,
        isEnabled,
        lastAuthenticationAt,
        failedAttempts,
        lockedUntil,
        availableBiometrics,
      ];

  @override
  String toString() {
    return 'AuthConfig(method: $method, enabled: $isEnabled, failed: $failedAttempts, locked: $isLocked)';
  }
}

/// Authentication result with context
class AuthResult extends Equatable {
  final bool success;
  final AuthMethod methodUsed;
  final String? errorMessage;
  final DateTime timestamp;

  const AuthResult({
    required this.success,
    required this.methodUsed,
    this.errorMessage,
    required this.timestamp,
  });

  /// Creates AuthResult with default timestamp if not provided
  factory AuthResult.create({
    required bool success,
    required AuthMethod methodUsed,
    String? errorMessage,
    DateTime? timestamp,
  }) {
    return AuthResult(
      success: success,
      methodUsed: methodUsed,
      errorMessage: errorMessage,
      timestamp: timestamp ?? DateTime.now(),
    );
  }

  factory AuthResult.success(AuthMethod method) {
    return AuthResult(
      success: true,
      methodUsed: method,
      timestamp: DateTime.now(),
    );
  }

  factory AuthResult.failure(AuthMethod method, String error) {
    return AuthResult(
      success: false,
      methodUsed: method,
      errorMessage: error,
      timestamp: DateTime.now(),
    );
  }

  @override
  List<Object?> get props => [success, methodUsed, errorMessage, timestamp];

  @override
  String toString() {
    return 'AuthResult(success: $success, method: $methodUsed, error: $errorMessage)';
  }
}
