//! Error types for lifecycle management

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

/// Result type for lifecycle operations
pub type Result<T> = std::result::Result<T, LifecycleError>;

/// Main error type for lifecycle operations
#[derive(Debug, Error)]
pub enum LifecycleError {
    /// Configuration error
    #[error("Configuration error: {0}")]
    Config(String),

    /// Invalid phase transition
    #[error("Cannot transition from {from:?} to {to:?}: {reason}")]
    InvalidTransition {
        /// Source phase
        from: String,
        /// Target phase
        to: String,
        /// Reason for invalidity
        reason: String,
    },

    /// Tool not found
    #[error("Tool not found: {0}")]
    ToolNotFound(String),

    /// Tool dependency error
    #[error("Tool dependency error: {reason}")]
    ToolDependencyError {
        /// Reason for dependency error
        reason: String,
    },

    /// Template error
    #[error("Template error: {0}")]
    TemplateError(String),

    /// Checklist error
    #[error("Checklist error: {0}")]
    ChecklistError(String),

    /// IO error
    #[error("IO error: {0}")]
    Io(#[from] io::Error),

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

    /// TOML serialization error
    #[error("TOML serialization error: {0}")]
    TomlSeError(String),

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

    /// File not found
    #[error("File not found: {0}")]
    FileNotFound(PathBuf),

    /// Invalid configuration schema
    #[error("Invalid configuration: {0}")]
    InvalidConfig(String),

    /// Database error
    #[error("Database error: {0}")]
    Database(String),

    /// Provisioning error
    #[error("Provisioning error: {0}")]
    ProvisioningError(String),

    /// Publication error
    #[error("Publication error: {0}")]
    PublicationError(String),

    /// Integration error
    #[error("Integration error: {0}")]
    IntegrationError(String),

    /// Database migration error
    #[error("Database migration error: {0}")]
    MigrationError(String),

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

/// Convert sqlx errors to LifecycleError
impl From<sqlx::Error> for LifecycleError {
    fn from(err: sqlx::Error) -> Self {
        LifecycleError::Database(err.to_string())
    }
}

impl LifecycleError {
    /// Create a configuration error
    pub fn config(msg: impl Into<String>) -> Self {
        Self::Config(msg.into())
    }

    /// Create an invalid transition error
    pub fn invalid_transition(
        from: impl Into<String>,
        to: impl Into<String>,
        reason: impl Into<String>,
    ) -> Self {
        Self::InvalidTransition {
            from: from.into(),
            to: to.into(),
            reason: reason.into(),
        }
    }

    /// Create a tool not found error
    pub fn tool_not_found(name: impl Into<String>) -> Self {
        Self::ToolNotFound(name.into())
    }

    /// Create a template error
    pub fn template(msg: impl Into<String>) -> Self {
        Self::TemplateError(msg.into())
    }

    /// Create a checklist error
    pub fn checklist(msg: impl Into<String>) -> Self {
        Self::ChecklistError(msg.into())
    }

    /// Create a file not found error
    pub fn file_not_found(path: PathBuf) -> Self {
        Self::FileNotFound(path)
    }

    /// Create an invalid config error
    pub fn invalid_config(msg: impl Into<String>) -> Self {
        Self::InvalidConfig(msg.into())
    }

    /// Create a database error
    pub fn database(msg: impl Into<String>) -> Self {
        Self::Database(msg.into())
    }

    /// Create a provisioning error
    pub fn provisioning(msg: impl Into<String>) -> Self {
        Self::ProvisioningError(msg.into())
    }

    /// Create a publication error
    pub fn publication(msg: impl Into<String>) -> Self {
        Self::PublicationError(msg.into())
    }

    /// Create an integration error
    pub fn integration(msg: impl Into<String>) -> Self {
        Self::IntegrationError(msg.into())
    }

    /// Create a custom error
    pub fn custom(msg: impl Into<String>) -> Self {
        Self::Other(msg.into())
    }
}
