/// Base class for all exceptions in the application
/// Exceptions are thrown during runtime and should be caught and converted to failures
abstract class AppException implements Exception {
  final String message;
  final String? code;

  const AppException(this.message, {this.code});

  @override
  String toString() => 'AppException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when there's a server-related error
/// This includes HTTP errors, API response errors, etc.
class ServerException extends AppException {
  const ServerException(super.message, {super.code});

  @override
  String toString() => 'ServerException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when there's a network-related error
/// This includes connection timeouts, no internet connection, etc.
class NetworkException extends AppException {
  const NetworkException(super.message, {super.code});

  @override
  String toString() => 'NetworkException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when there's a local storage error
/// This includes Hive errors, file system errors, etc.
class CacheException extends AppException {
  const CacheException(super.message, {super.code});

  @override
  String toString() => 'CacheException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when input validation fails
class ValidationException extends AppException {
  final Map<String, String>? fieldErrors;

  const ValidationException(
    super.message, {
    super.code,
    this.fieldErrors,
  });

  @override
  String toString() {
    var result = 'ValidationException: $message${code != null ? ' (Code: $code)' : ''}';
    if (fieldErrors != null && fieldErrors!.isNotEmpty) {
      result += '\nField errors: ${fieldErrors.toString()}';
    }
    return result;
  }
}

/// Exception thrown when a required permission is denied
class PermissionException extends AppException {
  final String permissionType;

  const PermissionException(
    super.message,
    this.permissionType, {
    super.code,
  });

  @override
  String toString() =>
      'PermissionException: $message (Permission: $permissionType)${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when scanning operation fails
class ScannerException extends AppException {
  const ScannerException(super.message, {super.code});

  @override
  String toString() => 'ScannerException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown when printing operation fails
class PrintException extends AppException {
  const PrintException(super.message, {super.code});

  @override
  String toString() => 'PrintException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown for JSON parsing errors
class JsonException extends AppException {
  const JsonException(super.message, {super.code});

  @override
  String toString() => 'JsonException: $message${code != null ? ' (Code: $code)' : ''}';
}

/// Exception thrown for format-related errors (e.g., invalid barcode format)
class FormatException extends AppException {
  final String expectedFormat;
  final String receivedFormat;

  const FormatException(
    super.message,
    this.expectedFormat,
    this.receivedFormat, {
    super.code,
  });

  @override
  String toString() =>
      'FormatException: $message (Expected: $expectedFormat, Received: $receivedFormat)${code != null ? ' (Code: $code)' : ''}';
}

/// Generic exception for unexpected errors
class UnknownException extends AppException {
  const UnknownException([super.message = 'An unexpected error occurred', String? code])
      : super(code: code);

  @override
  String toString() => 'UnknownException: $message${code != null ? ' (Code: $code)' : ''}';
}