//! Persistence layer error types
//!
//! Provides database-specific error handling for the persistence layer.
//! These errors are converted from/to the main `LifecycleError` type.

use thiserror::Error;

/// Result type for persistence operations
pub type PersistenceResult<T> = std::result::Result<T, PersistenceError>;

/// Persistence-specific errors
#[derive(Debug, Error)]
pub enum PersistenceError {
    /// SQLite-specific error
    #[error("SQLite error: {0}")]
    SqliteError(String),

    /// SurrealDB-specific error
    #[error("SurrealDB error: {0}")]
    SurrealDbError(String),

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

    /// Invalid data
    #[error("Invalid data: {0}")]
    InvalidData(String),

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

    /// Schema initialization error
    #[error("Schema initialization error: {0}")]
    SchemaError(String),

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

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

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

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

impl PersistenceError {
    /// Check if error is a "not found" error
    pub fn is_not_found(&self) -> bool {
        matches!(self, PersistenceError::NotFound(_))
    }

    /// Check if error is a connection error
    pub fn is_connection_error(&self) -> bool {
        matches!(self, PersistenceError::ConnectionError(_))
    }

    /// Convert to main LifecycleError
    pub fn to_lifecycle_error(self) -> crate::error::LifecycleError {
        match self {
            PersistenceError::SqliteError(msg)
            | PersistenceError::SurrealDbError(msg)
            | PersistenceError::Other(msg) => crate::error::LifecycleError::Database(msg),
            PersistenceError::NotFound(msg) => {
                crate::error::LifecycleError::Database(format!("Not found: {}", msg))
            }
            PersistenceError::InvalidData(msg) => {
                crate::error::LifecycleError::Database(format!("Invalid data: {}", msg))
            }
            PersistenceError::ConnectionError(msg) => {
                crate::error::LifecycleError::Database(format!("Connection error: {}", msg))
            }
            PersistenceError::SchemaError(msg) => {
                crate::error::LifecycleError::Database(format!("Schema error: {}", msg))
            }
            PersistenceError::SerializationError(msg) => {
                crate::error::LifecycleError::Database(format!("Serialization error: {}", msg))
            }
            PersistenceError::MigrationError(msg) => {
                crate::error::LifecycleError::MigrationError(msg)
            }
            PersistenceError::ConfigError(msg) => crate::error::LifecycleError::Config(msg),
        }
    }
}

/// Convert SQLx errors to PersistenceError
impl From<sqlx::Error> for PersistenceError {
    fn from(err: sqlx::Error) -> Self {
        match err {
            sqlx::Error::RowNotFound => PersistenceError::NotFound("Record not found".to_string()),
            sqlx::Error::Configuration(_) => PersistenceError::ConfigError(err.to_string()),
            sqlx::Error::Io(_) => PersistenceError::ConnectionError(err.to_string()),
            _ => PersistenceError::SqliteError(err.to_string()),
        }
    }
}

/// Convert IO errors to PersistenceError
impl From<std::io::Error> for PersistenceError {
    fn from(err: std::io::Error) -> Self {
        PersistenceError::ConnectionError(err.to_string())
    }
}

/// Convert JSON errors to PersistenceError
impl From<serde_json::Error> for PersistenceError {
    fn from(err: serde_json::Error) -> Self {
        PersistenceError::SerializationError(err.to_string())
    }
}

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

    #[test]
    fn test_not_found_detection() {
        let err = PersistenceError::NotFound("user".to_string());
        assert!(err.is_not_found());
    }

    #[test]
    fn test_connection_error_detection() {
        let err = PersistenceError::ConnectionError("timeout".to_string());
        assert!(err.is_connection_error());
    }

    #[test]
    fn test_error_display() {
        let err = PersistenceError::InvalidData("empty name".to_string());
        let msg = err.to_string();
        assert!(msg.contains("Invalid data"));
    }
}
