//! Error types for TypeAgent
//!
//! Following M-ERRORS-CANONICAL-STRUCTS: situation-specific error struct with kind enum

use std::error::Error as StdError;
use std::fmt;

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

/// TypeAgent error with context and source chaining
#[derive(Debug)]
pub struct Error {
    pub kind: ErrorKind,
    pub context: String,
    pub source: Option<Box<dyn StdError + Send + Sync>>,
}

/// Error kind taxonomy
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
    /// MDX/Markdown parsing failed
    Parse {
        line: Option<usize>,
        column: Option<usize>,
    },
    /// AST to Nickel transpilation failed
    Transpile { detail: String },
    /// Nickel type checking or evaluation failed
    NickelEval { detail: String },
    /// Agent execution failed
    Execution { stage: String },
    /// I/O operation failed
    Io,
    /// JSON/YAML serialization failed
    Serialization,
    /// Cache operation failed
    Cache { operation: String },
    /// Output validation failed
    Validation { rule: String },
    /// File format not supported
    UnsupportedFormat { extension: String },
    /// File not found
    NotFound { path: String },
}

impl Error {
    /// Create parse error with location
    pub fn parse(message: impl Into<String>, line: Option<usize>, column: Option<usize>) -> Self {
        Self {
            kind: ErrorKind::Parse { line, column },
            context: message.into(),
            source: None,
        }
    }

    /// Create transpile error
    pub fn transpile(message: impl Into<String>, detail: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::Transpile {
                detail: detail.into(),
            },
            context: message.into(),
            source: None,
        }
    }

    /// Create Nickel evaluation error
    pub fn nickel_eval(message: impl Into<String>, detail: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::NickelEval {
                detail: detail.into(),
            },
            context: message.into(),
            source: None,
        }
    }

    /// Create execution error
    pub fn execution(message: impl Into<String>, stage: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::Execution {
                stage: stage.into(),
            },
            context: message.into(),
            source: None,
        }
    }

    /// Create I/O error
    pub fn io(message: impl Into<String>, detail: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::Io,
            context: format!("{}: {}", message.into(), detail.into()),
            source: None,
        }
    }

    /// Create cache error
    pub fn cache(message: impl Into<String>, operation: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::Cache {
                operation: operation.into(),
            },
            context: message.into(),
            source: None,
        }
    }

    /// Create validation error
    pub fn validation(message: impl Into<String>, rule: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::Validation { rule: rule.into() },
            context: message.into(),
            source: None,
        }
    }

    /// Create unsupported format error
    pub fn unsupported_format(extension: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::UnsupportedFormat {
                extension: extension.into(),
            },
            context: "Unsupported agent file format".to_string(),
            source: None,
        }
    }

    /// Create file not found error
    pub fn not_found(path: impl Into<String>) -> Self {
        Self {
            kind: ErrorKind::NotFound { path: path.into() },
            context: "Agent file not found".to_string(),
            source: None,
        }
    }

    /// Add source error for chaining
    pub fn with_source(mut self, source: impl StdError + Send + Sync + 'static) -> Self {
        self.source = Some(Box::new(source));
        self
    }

    /// Check if error is parse error
    pub fn is_parse(&self) -> bool {
        matches!(self.kind, ErrorKind::Parse { .. })
    }

    /// Check if error is transpile error
    pub fn is_transpile(&self) -> bool {
        matches!(self.kind, ErrorKind::Transpile { .. })
    }

    /// Check if error is Nickel evaluation error
    pub fn is_nickel_eval(&self) -> bool {
        matches!(self.kind, ErrorKind::NickelEval { .. })
    }

    /// Check if error is execution error
    pub fn is_execution(&self) -> bool {
        matches!(self.kind, ErrorKind::Execution { .. })
    }

    /// Check if error is validation error
    pub fn is_validation(&self) -> bool {
        matches!(self.kind, ErrorKind::Validation { .. })
    }

    /// Get parse location if available
    pub fn parse_location(&self) -> Option<(Option<usize>, Option<usize>)> {
        if let ErrorKind::Parse { line, column } = &self.kind {
            Some((*line, *column))
        } else {
            None
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.kind {
            ErrorKind::Parse { line, column } => {
                write!(f, "Parse error")?;
                if let Some(l) = line {
                    write!(f, " at line {}", l)?;
                }
                if let Some(c) = column {
                    write!(f, ", column {}", c)?;
                }
                write!(f, ": {}", self.context)
            }
            ErrorKind::Transpile { detail } => {
                write!(f, "Transpile error ({}): {}", detail, self.context)
            }
            ErrorKind::NickelEval { detail } => {
                write!(f, "Nickel evaluation error ({}): {}", detail, self.context)
            }
            ErrorKind::Execution { stage } => {
                write!(f, "Execution error at {}: {}", stage, self.context)
            }
            ErrorKind::Io => {
                write!(f, "I/O error: {}", self.context)
            }
            ErrorKind::Serialization => {
                write!(f, "Serialization error: {}", self.context)
            }
            ErrorKind::Cache { operation } => {
                write!(f, "Cache error during {}: {}", operation, self.context)
            }
            ErrorKind::Validation { rule } => {
                write!(f, "Validation failed ({}): {}", rule, self.context)
            }
            ErrorKind::UnsupportedFormat { extension } => {
                write!(f, "Unsupported format '{}': {}", extension, self.context)
            }
            ErrorKind::NotFound { path } => {
                write!(f, "File not found '{}': {}", path, self.context)
            }
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.source.as_ref().map(|e| e.as_ref() as &dyn StdError)
    }
}

/// Conversion from std::io::Error
impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Self {
        Self {
            kind: ErrorKind::Io,
            context: err.to_string(),
            source: Some(Box::new(err)),
        }
    }
}

/// Conversion from serde_json::Error
impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Self {
            kind: ErrorKind::Serialization,
            context: err.to_string(),
            source: Some(Box::new(err)),
        }
    }
}

/// Conversion from serde_yaml::Error
impl From<serde_yaml::Error> for Error {
    fn from(err: serde_yaml::Error) -> Self {
        Self {
            kind: ErrorKind::Serialization,
            context: err.to_string(),
            source: Some(Box::new(err)),
        }
    }
}
