//! Error types for provisioning generation.
//!
//! Follows M-ERRORS-CANONICAL-STRUCTS guideline: specific error types instead of generic enums.

use std::path::PathBuf;
use thiserror::Error;

pub type Result<T> = std::result::Result<T, ProvisioningGenError>;

/// Root error type combining all provisioning generation errors.
#[derive(Debug, Error)]
pub enum ProvisioningGenError {
    /// Cargo.toml introspection failed
    #[error("Cargo introspection failed: {0}")]
    CargoIntrospection(#[from] CargoIntrospectionError),

    /// Project spec loading failed
    #[error("Config loading failed: {0}")]
    ConfigLoading(#[from] ConfigLoadingError),

    /// Nickel schema loading failed
    #[error("Nickel schema loading failed: {0}")]
    NickelSchemaLoading(#[from] NickelSchemaLoadingError),

    /// Template rendering failed
    #[error("Template rendering failed: {0}")]
    TemplateRender(#[from] TemplateRenderError),

    /// Schema generation failed
    #[error("Schema generation failed: {0}")]
    SchemaGeneration(#[from] SchemaGenerationError),

    /// Fragment generation failed
    #[error("Fragment generation failed: {0}")]
    FragmentGeneration(#[from] FragmentGenerationError),

    /// Validator generation failed
    #[error("Validator generation failed: {0}")]
    ValidatorGeneration(#[from] ValidatorGenerationError),

    /// Constraint generation failed
    #[error("Constraint generation failed: {0}")]
    ConstraintGeneration(#[from] ConstraintGenerationError),

    /// File I/O error
    #[error("File I/O error: {0}")]
    Io(#[from] std::io::Error),

    /// JSON serialization/deserialization error
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),

    /// TOML serialization/deserialization error
    #[error("TOML error: {0}")]
    Toml(#[from] toml::de::Error),

    /// Generic error
    #[error("{0}")]
    Other(String),
}

/// Cargo.toml introspection errors.
#[derive(Debug)]
pub struct CargoIntrospectionError {
    pub cargo_path: PathBuf,
    pub reason: String,
}

impl std::fmt::Display for CargoIntrospectionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to introspect {}: {}",
            self.cargo_path.display(),
            self.reason
        )
    }
}

impl std::error::Error for CargoIntrospectionError {}

/// Config loading errors.
#[derive(Debug)]
pub struct ConfigLoadingError {
    pub config_path: PathBuf,
    pub reason: String,
}

impl std::fmt::Display for ConfigLoadingError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to load config from {}: {}",
            self.config_path.display(),
            self.reason
        )
    }
}

impl std::error::Error for ConfigLoadingError {}

/// Nickel schema loading errors.
#[derive(Debug)]
pub struct NickelSchemaLoadingError {
    pub schema_path: PathBuf,
    pub reason: String,
}

impl std::fmt::Display for NickelSchemaLoadingError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to load Nickel schema from {}: {}",
            self.schema_path.display(),
            self.reason
        )
    }
}

impl std::error::Error for NickelSchemaLoadingError {}

/// Template rendering errors.
#[derive(Debug)]
pub struct TemplateRenderError {
    pub template_name: String,
    pub reason: String,
}

impl std::fmt::Display for TemplateRenderError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to render template '{}': {}",
            self.template_name, self.reason
        )
    }
}

impl std::error::Error for TemplateRenderError {}

/// Schema generation errors.
#[derive(Debug)]
pub struct SchemaGenerationError {
    pub feature_name: String,
    pub reason: String,
}

impl std::fmt::Display for SchemaGenerationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to generate schema for feature '{}': {}",
            self.feature_name, self.reason
        )
    }
}

impl std::error::Error for SchemaGenerationError {}

/// Fragment generation errors.
#[derive(Debug)]
pub struct FragmentGenerationError {
    pub fragment_name: String,
    pub reason: String,
}

impl std::fmt::Display for FragmentGenerationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to generate fragment '{}': {}",
            self.fragment_name, self.reason
        )
    }
}

impl std::error::Error for FragmentGenerationError {}

/// Validator generation errors.
#[derive(Debug)]
pub struct ValidatorGenerationError {
    pub feature_name: String,
    pub reason: String,
}

impl std::fmt::Display for ValidatorGenerationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Failed to generate validators for feature '{}': {}",
            self.feature_name, self.reason
        )
    }
}

impl std::error::Error for ValidatorGenerationError {}

/// Constraint generation errors.
#[derive(Debug)]
pub struct ConstraintGenerationError {
    pub reason: String,
}

impl std::fmt::Display for ConstraintGenerationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Failed to generate constraints: {}", self.reason)
    }
}

impl std::error::Error for ConstraintGenerationError {}
