/// Exception thrown when a requested setting is not found.
///
/// This occurs when:
/// - Accessing a setting that doesn't exist in the group
/// - Using an invalid storage key format
/// - Referencing a setting before it's been registered
///
/// Example:
/// ```dart
/// try {
///   Settings.getBool('nonexistent.setting');
/// } catch (e) {
///   if (e is SettingNotFoundException) {
///     print('Setting not found: ${e.message}');
///   }
/// }
/// ```
class SettingNotFoundException implements Exception {
  /// Descriptive error message explaining what setting was not found.
  final String message;

  /// Creates a new [SettingNotFoundException] with the given [message].
  const SettingNotFoundException(this.message);

  @override
  String toString() => 'SettingNotFoundException: $message';
}

/// Exception thrown when attempting to modify a non-configurable setting.
///
/// Settings can be marked as non-configurable by setting `userConfigurable: false`.
/// This is useful for system settings or read-only configuration values.
///
/// Example:
/// ```dart
/// final systemSetting = BoolSetting(
///   key: 'systemFlag',
///   defaultValue: true,
///   userConfigurable: false, // This setting cannot be modified by users
/// );
/// ```
class SettingNotConfigurableException implements Exception {
  /// Descriptive error message explaining which setting cannot be configured.
  final String message;

  /// Creates a new [SettingNotConfigurableException] with the given [message].
  const SettingNotConfigurableException(this.message);

  @override
  String toString() => 'SettingNotConfigurableException: $message';
}

/// Exception thrown when a setting value fails validation.
///
/// This occurs when a validator function returns false for a given value.
/// Validators are useful for ensuring data integrity and business rules.
///
/// Example:
/// ```dart
/// final volumeSetting = DoubleSetting(
///   key: 'volume',
///   defaultValue: 0.5,
///   validator: (value) => value >= 0.0 && value <= 1.0,
/// );
///
/// // This will throw SettingValidationException
/// await Settings.setDouble('audio.volume', 1.5);
/// ```
class SettingValidationException implements Exception {
  /// Descriptive error message explaining the validation failure.
  final String message;

  /// Creates a new [SettingValidationException] with the given [message].
  const SettingValidationException(this.message);

  @override
  String toString() => 'SettingValidationException: $message';
}

/// Exception thrown when attempting to access settings before initialization.
///
/// The settings framework requires asynchronous initialization before use.
/// Always await `Settings.init()` or individual `readyFuture` properties
/// before accessing setting values.
///
/// Example:
/// ```dart
/// // Wrong - may throw SettingsNotReadyException
/// bool value = Settings.getBool('game.sound');
///
/// // Correct - wait for initialization
/// await Settings.init();
/// bool value = Settings.getBool('game.sound');
/// ```
class SettingsNotReadyException implements Exception {
  /// Descriptive error message explaining the readiness issue.
  final String message;

  /// Creates a new [SettingsNotReadyException] with the given [message].
  const SettingsNotReadyException(this.message);

  @override
  String toString() => 'SettingsNotReadyException: $message';
}
