/// Base sealed class for all application exceptions.
///
/// Provides type-safe error handling with user-friendly message keys.
/// Technical details are captured for logging but never shown to users.
sealed class AppException implements Exception {
  const AppException({
    required this.code,
    this.technicalMessage,
  });

  /// Unique error code for logging/tracking (e.g., "AUTH_001")
  final String code;

  /// Technical details for developers (never shown to users)
  final String? technicalMessage;

  /// Returns the localization key for user-friendly message
  String get messageKey;

  @override
  String toString() => 'AppException($code): $technicalMessage';
}

// ============================================================
// AUTH EXCEPTIONS
// ============================================================

/// Base class for authentication-related exceptions.
sealed class AuthException extends AppException {
  const AuthException({required super.code, super.technicalMessage});
}

/// Thrown when email/password combination is incorrect.
class InvalidCredentialsException extends AuthException {
  const InvalidCredentialsException({String? technicalMessage})
      : super(code: 'AUTH_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.invalid_credentials';
}

/// Thrown when attempting to register with an email that already exists.
class AccountExistsException extends AuthException {
  const AccountExistsException({String? technicalMessage})
      : super(code: 'AUTH_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.account_exists';
}

/// Thrown when the user session has expired.
class SessionExpiredException extends AuthException {
  const SessionExpiredException({String? technicalMessage})
      : super(code: 'AUTH_003', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.session_expired';
}

/// Thrown when user profile is not found in database after Firebase auth.
class UserNotFoundException extends AuthException {
  const UserNotFoundException({String? technicalMessage})
      : super(code: 'AUTH_004', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.user_not_found';
}

/// Thrown when user is not authorized for the current app (wrong role).
class UnauthorizedAppException extends AuthException {
  const UnauthorizedAppException({String? technicalMessage})
      : super(code: 'AUTH_005', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.unauthorized_app';
}

/// Thrown when password doesn't meet security requirements.
class WeakPasswordException extends AuthException {
  const WeakPasswordException({String? technicalMessage})
      : super(code: 'AUTH_006', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.weak_password';
}

/// Thrown when sign-up process fails.
class SignUpFailedException extends AuthException {
  const SignUpFailedException({String? technicalMessage})
      : super(code: 'AUTH_007', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.sign_up_failed';
}

/// Thrown when sign-in process fails.
class SignInFailedException extends AuthException {
  const SignInFailedException({String? technicalMessage})
      : super(code: 'AUTH_008', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.sign_in_failed';
}

/// Thrown when email exists but password doesn't match.
class PasswordMismatchException extends AuthException {
  const PasswordMismatchException({String? technicalMessage})
      : super(code: 'AUTH_009', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.password_mismatch';
}

/// Thrown when account exists only with Google provider (no password).
class GoogleOnlyAccountException extends AuthException {
  const GoogleOnlyAccountException({String? technicalMessage})
      : super(code: 'AUTH_010', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.google_only_account';
}

// ============================================================
// HUB EXCEPTIONS
// ============================================================

/// Base class for hub-related exceptions.
sealed class HubException extends AppException {
  const HubException({required super.code, super.technicalMessage});
}

/// Thrown when attempting to delete a hub that has active orders.
class HubHasOrdersException extends HubException {
  const HubHasOrdersException({String? technicalMessage})
      : super(code: 'HUB_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.hub.has_orders';
}

/// Thrown when hub is not found.
class HubNotFoundException extends HubException {
  const HubNotFoundException({String? technicalMessage})
      : super(code: 'HUB_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.hub.not_found';
}

/// Thrown when hub creation fails.
class HubCreationFailedException extends HubException {
  const HubCreationFailedException({String? technicalMessage})
      : super(code: 'HUB_003', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.hub.creation_failed';
}

// ============================================================
// ORDER EXCEPTIONS
// ============================================================

/// Base class for order-related exceptions.
sealed class OrderException extends AppException {
  const OrderException({required super.code, super.technicalMessage});
}

/// Thrown when order creation is attempted without a hub.
class OrderMissingHubException extends OrderException {
  const OrderMissingHubException({String? technicalMessage})
      : super(code: 'ORDER_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.order.missing_hub';
}

/// Thrown when order creation is attempted without a vendor.
class OrderMissingVendorException extends OrderException {
  const OrderMissingVendorException({String? technicalMessage})
      : super(code: 'ORDER_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.order.missing_vendor';
}

/// Thrown when order creation fails.
class OrderCreationFailedException extends OrderException {
  const OrderCreationFailedException({String? technicalMessage})
      : super(code: 'ORDER_003', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.order.creation_failed';
}

/// Thrown when shift creation fails.
class ShiftCreationFailedException extends OrderException {
  const ShiftCreationFailedException({String? technicalMessage})
      : super(code: 'ORDER_004', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.order.shift_creation_failed';
}

/// Thrown when order is missing required business context.
class OrderMissingBusinessException extends OrderException {
  const OrderMissingBusinessException({String? technicalMessage})
      : super(code: 'ORDER_005', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.order.missing_business';
}

// ============================================================
// PROFILE EXCEPTIONS
// ============================================================

/// Base class for profile-related exceptions.
sealed class ProfileException extends AppException {
  const ProfileException({required super.code, super.technicalMessage});
}

/// Thrown when staff profile is not found.
class StaffProfileNotFoundException extends ProfileException {
  const StaffProfileNotFoundException({String? technicalMessage})
      : super(code: 'PROFILE_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.profile.staff_not_found';
}

/// Thrown when business profile is not found.
class BusinessNotFoundException extends ProfileException {
  const BusinessNotFoundException({String? technicalMessage})
      : super(code: 'PROFILE_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.profile.business_not_found';
}

/// Thrown when profile update fails.
class ProfileUpdateFailedException extends ProfileException {
  const ProfileUpdateFailedException({String? technicalMessage})
      : super(code: 'PROFILE_003', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.profile.update_failed';
}

// ============================================================
// SHIFT EXCEPTIONS
// ============================================================

/// Base class for shift-related exceptions.
sealed class ShiftException extends AppException {
  const ShiftException({required super.code, super.technicalMessage});
}

/// Thrown when no open roles are available for a shift.
class NoOpenRolesException extends ShiftException {
  const NoOpenRolesException({String? technicalMessage})
      : super(code: 'SHIFT_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.shift.no_open_roles';
}

/// Thrown when application for shift is not found.
class ApplicationNotFoundException extends ShiftException {
  const ApplicationNotFoundException({String? technicalMessage})
      : super(code: 'SHIFT_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.shift.application_not_found';
}

/// Thrown when no active shift is found for clock out.
class NoActiveShiftException extends ShiftException {
  const NoActiveShiftException({String? technicalMessage})
      : super(code: 'SHIFT_003', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.shift.no_active_shift';
}

// ============================================================
// NETWORK/GENERIC EXCEPTIONS
// ============================================================

/// Thrown when there is no network connection.
class NetworkException extends AppException {
  const NetworkException({String? technicalMessage})
      : super(code: 'NET_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.generic.no_connection';
}

/// Thrown when an unexpected error occurs.
class UnknownException extends AppException {
  const UnknownException({String? technicalMessage})
      : super(code: 'UNKNOWN', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.generic.unknown';
}

/// Thrown when the server returns an error (500, etc.).
class ServerException extends AppException {
  const ServerException({String? technicalMessage})
      : super(code: 'SRV_001', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.generic.server_error';
}

/// Thrown when the service is unavailable (Data Connect down).
class ServiceUnavailableException extends AppException {
  const ServiceUnavailableException({String? technicalMessage})
      : super(code: 'SRV_002', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.generic.service_unavailable';
}

/// Thrown when user is not authenticated.
class NotAuthenticatedException extends AppException {
  const NotAuthenticatedException({String? technicalMessage})
      : super(code: 'AUTH_NOT_LOGGED', technicalMessage: technicalMessage);

  @override
  String get messageKey => 'errors.auth.not_authenticated';
}
