//! Configuration error types and result handling.
//!
//! This module defines comprehensive error types for configuration management
//! operations, including database errors, vault integration failures, parsing
//! errors, and validation issues. Uses thiserror for ergonomic error handling.

use thiserror::Error;

/// Comprehensive error type for configuration management operations.
///
/// Covers all possible error conditions that can occur during configuration
/// loading, validation, and management operations. Each variant provides
/// specific context about the failure to aid in debugging and error handling.
#[derive(Error, Debug)]
#[allow(clippy::module_name_repetitions)]
pub enum ConfigError {
    /// Database operation failed (connection, query, or transaction error)
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    /// HashiCorp Vault integration error (authentication, secret retrieval, etc.)
    #[error("Vault error: {0}")]
    Vault(String),

    /// Configuration parsing error (invalid TOML, JSON, or environment variables)
    #[error("Parse error: {0}")]
    Parse(String),

    /// Requested configuration key or resource was not found
    #[error("Not found: {0}")]
    NotFound(String),

    /// Configuration validation failed (invalid values, missing required fields)
    #[error("Invalid configuration: {0}")]
    Invalid(String),
}

/// Result type alias for configuration operations.
///
/// Provides a convenient Result type that uses ConfigError as the error type.
///
/// Used throughout the configuration system for consistent error handling.
pub type ConfigResult<T> = Result<T, ConfigError>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vault_error_display() {
        let error = ConfigError::Vault("Connection failed".to_owned());
        assert_eq!(format!("{}", error), "Vault error: Connection failed");
    }

    #[test]
    fn test_parse_error_display() {
        let error = ConfigError::Parse("Invalid JSON".to_owned());
        assert_eq!(format!("{}", error), "Parse error: Invalid JSON");
    }

    #[test]
    fn test_not_found_error_display() {
        let error = ConfigError::NotFound("config_key".to_owned());
        assert_eq!(format!("{}", error), "Not found: config_key");
    }

    #[test]
    fn test_invalid_error_display() {
        let error = ConfigError::Invalid("Missing required field".to_owned());
        assert_eq!(
            format!("{}", error),
            "Invalid configuration: Missing required field"
        );
    }

    #[test]
    fn test_error_debug_format() {
        let error = ConfigError::Vault("Test error".to_owned());
        let debug_output = format!("{:?}", error);
        assert!(debug_output.contains("Vault"));
        assert!(debug_output.contains("Test error"));
    }

    #[test]
    fn test_config_result_ok() {
        let result: ConfigResult<i32> = Ok(42);
        assert!(result.is_ok());
        match result {
            Ok(value) => assert_eq!(value, 42),
            Err(_) => panic!("Expected Ok value"),
        }
    }

    #[test]
    fn test_config_result_err() {
        let result: ConfigResult<i32> = Err(ConfigError::NotFound("test".to_owned()));
        assert!(result.is_err());
    }

    #[test]
    fn test_error_type_matching() {
        let error = ConfigError::Parse("syntax error".to_owned());
        match error {
            ConfigError::Parse(msg) => assert_eq!(msg, "syntax error"),
            _ => panic!("Expected Parse error"),
        }
    }

    #[test]
    fn test_vault_error_creation() {
        let error = ConfigError::Vault("Token expired".to_owned());
        if let ConfigError::Vault(msg) = error {
            assert_eq!(msg, "Token expired");
        } else {
            panic!("Expected Vault error");
        }
    }
}
