use std::path::PathBuf;

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ConfigError {
    #[error("Config file not found: {path}")]
    NotFound { path: PathBuf },

    #[error("Invalid config format: {path} (expected .ncl or .toml)")]
    InvalidFormat { path: PathBuf },

    #[error("Nickel export failed: {reason}")]
    NickelExportFailed { reason: String },

    #[error("Nickel CLI not found in PATH. Ensure `nickel` is installed and available.")]
    NickelNotInstalled,

    #[error("Failed to parse TOML config: {reason}")]
    TomlParseFailed { reason: String },

    #[error("Failed to parse JSON config: {reason}")]
    JsonParseFailed { reason: String },

    #[error("Deserialization failed: {reason}")]
    DeserializationFailed { reason: String },

    #[error("Environment variable not found: {key}")]
    EnvVarNotFound { key: String },

    #[error("IO error: {reason}")]
    IoError { reason: String },

    #[error("Config validation failed: {reason}")]
    ValidationFailed { reason: String },
}

impl ConfigError {
    pub fn not_found(path: impl Into<PathBuf>) -> Self {
        Self::NotFound { path: path.into() }
    }

    pub fn invalid_format(path: impl Into<PathBuf>) -> Self {
        Self::InvalidFormat { path: path.into() }
    }

    pub fn nickel_failed(reason: impl Into<String>) -> Self {
        Self::NickelExportFailed {
            reason: reason.into(),
        }
    }

    pub fn toml_failed(reason: impl Into<String>) -> Self {
        Self::TomlParseFailed {
            reason: reason.into(),
        }
    }

    pub fn json_failed(reason: impl Into<String>) -> Self {
        Self::JsonParseFailed {
            reason: reason.into(),
        }
    }

    pub fn deserialization_failed(reason: impl Into<String>) -> Self {
        Self::DeserializationFailed {
            reason: reason.into(),
        }
    }

    pub fn env_var_not_found(key: impl Into<String>) -> Self {
        Self::EnvVarNotFound { key: key.into() }
    }

    pub fn io_error(reason: impl Into<String>) -> Self {
        Self::IoError {
            reason: reason.into(),
        }
    }

    pub fn validation_failed(reason: impl Into<String>) -> Self {
        Self::ValidationFailed {
            reason: reason.into(),
        }
    }
}

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