// ABOUTME: Error handling utilities for consistent error management and user-friendly messaging
// ABOUTME: Provides centralized error handling, logging, and error message translation services
import 'dart:developer' as developer;
import 'package:sqflite/sqflite.dart';

/// Enum representing different types of errors in the app
enum ErrorType {
  database,
  network,
  validation,
  authentication,
  fileSystem,
  unknown,
}

/// Class representing a standardized app error
class AppError {
  final ErrorType type;
  final String code;
  final String message;
  final String? details;
  final DateTime timestamp;
  final StackTrace? stackTrace;

  AppError({
    required this.type,
    required this.code,
    required this.message,
    this.details,
    DateTime? timestamp,
    this.stackTrace,
  }) : timestamp = timestamp ?? DateTime.now();

  @override
  String toString() {
    return 'AppError(type: $type, code: $code, message: $message, details: $details)';
  }

  /// Convert to JSON for logging
  Map<String, dynamic> toJson() {
    return {
      'type': type.toString(),
      'code': code,
      'message': message,
      'details': details,
      'timestamp': timestamp.toIso8601String(),
      'stackTrace': stackTrace?.toString(),
    };
  }
}

/// Centralized error handler for the health tracker app
class ErrorHandler {
  static ErrorHandler? _instance;

  // Private constructor for singleton pattern
  ErrorHandler._();

  /// Get singleton instance of ErrorHandler
  static ErrorHandler get instance {
    _instance ??= ErrorHandler._();
    return _instance!;
  }

  /// Log an error with context
  void logError(
    Object error, {
    String? context,
    StackTrace? stackTrace,
    Map<String, dynamic>? additionalData,
  }) {
    final timestamp = DateTime.now();
    final logEntry = {
      'timestamp': timestamp.toIso8601String(),
      'error': error.toString(),
      'context': context,
      'stackTrace': stackTrace?.toString(),
      'additionalData': additionalData,
    };

    // Log to console in debug mode
    developer.log(
      'ERROR: ${error.toString()}',
      name: 'HealthTracker',
      time: timestamp,
      error: error,
      stackTrace: stackTrace,
    );

    // In production, you could send this to a logging service like Firebase Crashlytics
    // For now, we just log to console
    print('ERROR LOG: $logEntry');
  }

  /// Handle database errors and convert to AppError
  AppError handleDatabaseError(Object error, {String? operation}) {
    String code = 'DB_UNKNOWN';
    String message = 'A database error occurred';
    String? details;

    if (error is DatabaseException) {
      details = error.toString();
      final errorStr = error.toString().toLowerCase();
      
      // Map common database errors to user-friendly messages by checking error message
      if (errorStr.contains('corrupt') || errorStr.contains('malformed')) {
        code = 'DB_CORRUPT';
        message = 'Database is corrupted. Please restart the app.';
      } else if (errorStr.contains('no such table')) {
        code = 'DB_NO_TABLE';
        message = 'Database structure error. Please restart the app.';
      } else if (errorStr.contains('syntax error')) {
        code = 'DB_SYNTAX';
        message = 'Database query error. Please try again.';
      } else if (errorStr.contains('read-only') || errorStr.contains('readonly')) {
        code = 'DB_READONLY';
        message = 'Database is read-only. Cannot save data.';
      } else if (errorStr.contains('database is closed')) {
        code = 'DB_CLOSED';
        message = 'Database connection lost. Please restart the app.';
      } else if (errorStr.contains('unique constraint')) {
        code = 'DB_DUPLICATE';
        message = 'This entry already exists.';
      } else if (errorStr.contains('foreign key constraint')) {
        code = 'DB_FOREIGN_KEY';
        message = 'Cannot complete operation due to data relationship constraints.';
      }
    } else if (error.toString().contains('disk I/O error')) {
      code = 'DB_IO_ERROR';
      message = 'Storage error. Please check available storage space.';
    } else if (error.toString().contains('permission denied')) {
      code = 'DB_PERMISSION';
      message = 'Permission denied. Cannot access database.';
    }

    if (operation != null) {
      message = '$message (Operation: $operation)';
    }

    final appError = AppError(
      type: ErrorType.database,
      code: code,
      message: message,
      details: details,
    );

    logError(error, context: 'Database operation: $operation');
    return appError;
  }

  /// Handle validation errors
  AppError handleValidationError(List<String> validationErrors, {String? field}) {
    final message = validationErrors.isNotEmpty 
        ? validationErrors.first
        : 'Validation failed';
    
    final details = validationErrors.length > 1 
        ? validationErrors.join(', ')
        : null;

    final appError = AppError(
      type: ErrorType.validation,
      code: 'VALIDATION_FAILED',
      message: field != null ? '$field: $message' : message,
      details: details,
    );

    logError(appError, context: 'Validation error for field: $field');
    return appError;
  }

  /// Handle file system errors
  AppError handleFileSystemError(Object error, {String? operation}) {
    String code = 'FS_UNKNOWN';
    String message = 'File operation failed';
    final details = error.toString();

    if (error.toString().contains('No such file or directory')) {
      code = 'FS_NOT_FOUND';
      message = 'File not found';
    } else if (error.toString().contains('Permission denied')) {
      code = 'FS_PERMISSION';
      message = 'Permission denied to access file';
    } else if (error.toString().contains('No space left on device')) {
      code = 'FS_NO_SPACE';
      message = 'Not enough storage space available';
    } else if (error.toString().contains('File exists')) {
      code = 'FS_EXISTS';
      message = 'File already exists';
    }

    if (operation != null) {
      message = '$message (Operation: $operation)';
    }

    final appError = AppError(
      type: ErrorType.fileSystem,
      code: code,
      message: message,
      details: details,
    );

    logError(error, context: 'File system operation: $operation');
    return appError;
  }

  /// Handle network errors
  AppError handleNetworkError(Object error, {String? operation}) {
    String code = 'NET_UNKNOWN';
    String message = 'Network error occurred';
    final details = error.toString();

    if (error.toString().contains('No address associated with hostname')) {
      code = 'NET_NO_INTERNET';
      message = 'No internet connection available';
    } else if (error.toString().contains('Connection refused')) {
      code = 'NET_REFUSED';
      message = 'Server connection refused';
    } else if (error.toString().contains('timeout')) {
      code = 'NET_TIMEOUT';
      message = 'Connection timeout. Please try again.';
    } else if (error.toString().contains('certificate')) {
      code = 'NET_SSL';
      message = 'Secure connection error';
    }

    if (operation != null) {
      message = '$message (Operation: $operation)';
    }

    final appError = AppError(
      type: ErrorType.network,
      code: code,
      message: message,
      details: details,
    );

    logError(error, context: 'Network operation: $operation');
    return appError;
  }

  /// Handle authentication errors
  AppError handleAuthenticationError(Object error, {String? operation}) {
    String code = 'AUTH_UNKNOWN';
    String message = 'Authentication error occurred';
    final details = error.toString();

    if (error.toString().contains('biometric')) {
      code = 'AUTH_BIOMETRIC_FAILED';
      message = 'Biometric authentication failed';
    } else if (error.toString().contains('passcode')) {
      code = 'AUTH_PASSCODE_FAILED';
      message = 'Passcode authentication failed';
    } else if (error.toString().contains('not enrolled')) {
      code = 'AUTH_NOT_ENROLLED';
      message = 'Biometric authentication not set up on device';
    } else if (error.toString().contains('cancelled')) {
      code = 'AUTH_CANCELLED';
      message = 'Authentication was cancelled';
    }

    final appError = AppError(
      type: ErrorType.authentication,
      code: code,
      message: message,
      details: details,
    );

    logError(error, context: 'Authentication operation: $operation');
    return appError;
  }

  /// Handle unknown/generic errors
  AppError handleUnknownError(Object error, {String? context}) {
    final appError = AppError(
      type: ErrorType.unknown,
      code: 'UNKNOWN_ERROR',
      message: 'An unexpected error occurred. Please try again.',
      details: error.toString(),
    );

    logError(error, context: context);
    return appError;
  }

  /// Get user-friendly error message for display
  String getUserFriendlyMessage(AppError error) {
    switch (error.code) {
      case 'DB_CORRUPT':
        return 'The app\'s data storage is corrupted. Please restart the app to fix this issue.';
      case 'DB_NO_SPACE':
      case 'FS_NO_SPACE':
        return 'Not enough storage space. Please free up space on your device and try again.';
      case 'DB_PERMISSION':
      case 'FS_PERMISSION':
        return 'Permission denied. Please check app permissions in your device settings.';
      case 'DB_DUPLICATE':
        return 'This entry already exists. Please check your recent entries.';
      case 'NET_NO_INTERNET':
        return 'No internet connection. Please check your network settings and try again.';
      case 'NET_TIMEOUT':
        return 'Connection timed out. Please check your internet connection and try again.';
      case 'AUTH_BIOMETRIC_FAILED':
        return 'Biometric authentication failed. Please try again or use your passcode.';
      case 'AUTH_NOT_ENROLLED':
        return 'Biometric authentication is not set up. Please enable it in your device settings.';
      case 'VALIDATION_FAILED':
        return error.message;
      default:
        return error.message.isNotEmpty 
            ? error.message 
            : 'Something went wrong. Please try again.';
    }
  }

  /// Get suggested action for the user based on error type
  String? getSuggestedAction(AppError error) {
    switch (error.code) {
      case 'DB_CORRUPT':
      case 'DB_CLOSED':
        return 'Restart the app';
      case 'DB_NO_SPACE':
      case 'FS_NO_SPACE':
        return 'Free up storage space';
      case 'DB_PERMISSION':
      case 'FS_PERMISSION':
        return 'Check app permissions';
      case 'NET_NO_INTERNET':
      case 'NET_TIMEOUT':
        return 'Check internet connection';
      case 'AUTH_NOT_ENROLLED':
        return 'Enable biometrics in settings';
      case 'AUTH_BIOMETRIC_FAILED':
        return 'Try passcode instead';
      case 'DB_DUPLICATE':
        return 'Check recent entries';
      default:
        return 'Try again later';
    }
  }

  /// Check if error is recoverable (user can retry)
  bool isRecoverable(AppError error) {
    switch (error.code) {
      case 'DB_CORRUPT':
      case 'DB_CLOSED':
      case 'DB_NO_TABLE':
      case 'FS_PERMISSION':
      case 'DB_PERMISSION':
        return false; // These need app restart or system-level fixes
      case 'NET_TIMEOUT':
      case 'NET_REFUSED':
      case 'AUTH_BIOMETRIC_FAILED':
      case 'AUTH_CANCELLED':
        return true; // User can try again
      default:
        return true;
    }
  }

  /// Log info message
  void logInfo(String message, {String? context, Map<String, dynamic>? data}) {
    developer.log(
      'INFO: $message',
      name: 'HealthTracker',
      time: DateTime.now(),
    );
  }

  /// Log warning message
  void logWarning(String message, {String? context, Map<String, dynamic>? data}) {
    developer.log(
      'WARNING: $message',
      name: 'HealthTracker',
      time: DateTime.now(),
    );
  }

  /// Static method to get error message for any error object
  static String getErrorMessage(Object error) {
    final handler = ErrorHandler.instance;
    
    if (error is AppError) {
      return handler.getUserFriendlyMessage(error);
    } else if (error is String) {
      return error;
    } else if (error is DatabaseException) {
      final appError = handler.handleDatabaseError(error);
      return handler.getUserFriendlyMessage(appError);
    } else {
      final appError = handler.handleUnknownError(error);
      return handler.getUserFriendlyMessage(appError);
    }
  }
}
