//! Parser-specific error types and utilities
//!
//! This module defines error types that are specific to the parser subsystem,
//! providing detailed information about parsing failures with context.

use super::traits::SourcePosition;
use crate::error::Error;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Parser-specific error type
#[derive(Error, Debug, Clone)]
pub enum ParserError {
    /// Syntax error during parsing
    #[error("Syntax error at {position}: {message}")]
    SyntaxError {
        message: String,
        position: SourcePosition,
        expected: Option<Vec<String>>,
    },

    /// Semantic validation error
    #[error("Semantic error: {message}")]
    SemanticError {
        message: String,
        position: Option<SourcePosition>,
    },

    /// Lexical analysis error
    #[error("Lexical error at {position}: {message}")]
    LexicalError {
        message: String,
        position: SourcePosition,
    },

    /// Parser backend error
    #[error("Parser backend error ({backend}): {message}")]
    BackendError {
        backend: String,
        message: String,
        position: Option<SourcePosition>,
    },

    /// Type validation error
    #[error("Type error: {message}")]
    TypeError {
        message: String,
        expected_type: Option<String>,
        actual_type: Option<String>,
        position: Option<SourcePosition>,
    },

    /// Configuration error
    #[error("Configuration error: {message}")]
    ConfigurationError { message: String },

    /// Unsupported feature error
    #[error("Unsupported feature '{feature}' for backend '{backend}': {message}")]
    UnsupportedFeature {
        backend: String,
        feature: String,
        message: String,
    },

    /// Timeout error
    #[error("Parsing timeout: {message}")]
    Timeout {
        message: String,
        timeout_duration: std::time::Duration,
    },

    /// Resource limit exceeded
    #[error("Resource limit exceeded: {message}")]
    ResourceLimit {
        message: String,
        limit_type: String,
        current_value: u64,
        max_value: u64,
    },

    /// Internal parser error
    #[error("Internal parser error: {message}")]
    InternalError {
        message: String,
        cause: Option<String>,
    },
}

impl ParserError {
    /// Create a syntax error
    pub fn syntax(message: impl Into<String>, position: SourcePosition) -> Self {
        Self::SyntaxError {
            message: message.into(),
            position,
            expected: None,
        }
    }

    /// Create a syntax error with expected tokens
    pub fn syntax_with_expected(
        message: impl Into<String>,
        position: SourcePosition,
        expected: Vec<String>,
    ) -> Self {
        Self::SyntaxError {
            message: message.into(),
            position,
            expected: Some(expected),
        }
    }

    /// Create a semantic error
    pub fn semantic(message: impl Into<String>) -> Self {
        Self::SemanticError {
            message: message.into(),
            position: None,
        }
    }

    /// Create a semantic error with position
    pub fn semantic_at(message: impl Into<String>, position: SourcePosition) -> Self {
        Self::SemanticError {
            message: message.into(),
            position: Some(position),
        }
    }

    /// Create a lexical error
    pub fn lexical(message: impl Into<String>, position: SourcePosition) -> Self {
        Self::LexicalError {
            message: message.into(),
            position,
        }
    }

    /// Create a backend error
    pub fn backend(backend: impl Into<String>, message: impl Into<String>) -> Self {
        Self::BackendError {
            backend: backend.into(),
            message: message.into(),
            position: None,
        }
    }

    /// Create a backend error with position
    pub fn backend_at(
        backend: impl Into<String>,
        message: impl Into<String>,
        position: SourcePosition,
    ) -> Self {
        Self::BackendError {
            backend: backend.into(),
            message: message.into(),
            position: Some(position),
        }
    }

    /// Create a type error
    pub fn type_error(message: impl Into<String>) -> Self {
        Self::TypeError {
            message: message.into(),
            expected_type: None,
            actual_type: None,
            position: None,
        }
    }

    /// Create a type error with expected and actual types
    pub fn type_mismatch(
        expected: impl Into<String>,
        actual: impl Into<String>,
        position: Option<SourcePosition>,
    ) -> Self {
        let expected_str = expected.into();
        let actual_str = actual.into();
        Self::TypeError {
            message: format!("Expected {}, found {}", expected_str, actual_str),
            expected_type: Some(expected_str),
            actual_type: Some(actual_str),
            position,
        }
    }

    /// Create a configuration error
    pub fn configuration(message: impl Into<String>) -> Self {
        Self::ConfigurationError {
            message: message.into(),
        }
    }

    /// Create an unsupported feature error
    pub fn unsupported_feature(backend: impl Into<String>, feature: impl Into<String>) -> Self {
        let backend = backend.into();
        let feature = feature.into();
        let message = format!(
            "Feature '{}' is not supported by backend '{}'",
            feature, backend
        );
        Self::UnsupportedFeature {
            backend,
            feature,
            message,
        }
    }

    /// Create an internal error
    pub fn internal(message: impl Into<String>) -> Self {
        Self::InternalError {
            message: message.into(),
            cause: None,
        }
    }

    /// Create an internal error with cause
    pub fn internal_with_cause(message: impl Into<String>, cause: impl std::fmt::Display) -> Self {
        Self::InternalError {
            message: message.into(),
            cause: Some(cause.to_string()),
        }
    }

    /// Create a timeout error
    pub fn timeout(duration_ms: u64) -> Self {
        Self::Timeout {
            message: format!("Parser timeout after {}ms", duration_ms),
            timeout_duration: std::time::Duration::from_millis(duration_ms),
        }
    }

    /// Create a resource limit exceeded error
    pub fn resource_limit(resource: impl Into<String>, limit: u64, actual: u64) -> Self {
        let limit_type = resource.into();
        let message = format!("Resource '{}' limit exceeded", limit_type);
        Self::ResourceLimit {
            message,
            limit_type,
            current_value: actual,
            max_value: limit,
        }
    }

    /// Get the position associated with this error (if any)
    pub fn position(&self) -> Option<&SourcePosition> {
        match self {
            Self::SyntaxError { position, .. } => Some(position),
            Self::SemanticError { position, .. } => position.as_ref(),
            Self::LexicalError { position, .. } => Some(position),
            Self::BackendError { position, .. } => position.as_ref(),
            Self::TypeError { position, .. } => position.as_ref(),
            _ => None,
        }
    }

    /// Get the error message
    pub fn message(&self) -> String {
        match self {
            Self::SyntaxError { message, .. } => message.clone(),
            Self::SemanticError { message, .. } => message.clone(),
            Self::LexicalError { message, .. } => message.clone(),
            Self::BackendError { message, .. } => message.clone(),
            Self::TypeError { message, .. } => message.clone(),
            Self::ConfigurationError { message } => message.clone(),
            Self::UnsupportedFeature { message, .. } => message.clone(),
            Self::InternalError { message, .. } => message.clone(),
            Self::Timeout { message, .. } => message.clone(),
            Self::ResourceLimit { message, .. } => message.clone(),
        }
    }

    /// Check if this error is recoverable.
    ///
    /// Recoverable errors include those where switching backends, retrying with
    /// a longer timeout, or raising a resource limit may allow progress.
    pub fn is_recoverable(&self) -> bool {
        matches!(
            self,
            Self::BackendError { .. }
                | Self::ConfigurationError { .. }
                | Self::UnsupportedFeature { .. }
                | Self::Timeout { .. }
                | Self::ResourceLimit { .. }
        )
    }

    /// Get the error category
    pub fn category(&self) -> &ErrorCategory {
        match self {
            Self::SyntaxError { .. } | Self::LexicalError { .. } => &ErrorCategory::Syntax,
            Self::SemanticError { .. } => &ErrorCategory::Semantic,
            Self::TypeError { .. } => &ErrorCategory::Type,
            Self::ConfigurationError { .. } => &ErrorCategory::Configuration,
            Self::BackendError { .. } | Self::UnsupportedFeature { .. } => &ErrorCategory::Backend,
            Self::InternalError { .. } | Self::Timeout { .. } | Self::ResourceLimit { .. } => {
                &ErrorCategory::Internal
            }
        }
    }

    /// Get the error severity
    pub fn severity(&self) -> &ErrorSeverity {
        match self {
            Self::SyntaxError { .. }
            | Self::SemanticError { .. }
            | Self::LexicalError { .. }
            | Self::TypeError { .. } => &ErrorSeverity::Error,
            Self::ConfigurationError { .. } | Self::UnsupportedFeature { .. } => {
                &ErrorSeverity::Warning
            }
            Self::BackendError { .. } | Self::Timeout { .. } | Self::ResourceLimit { .. } => {
                &ErrorSeverity::Error
            }
            Self::InternalError { .. } => &ErrorSeverity::Fatal,
        }
    }

    /// Get suggested recovery actions
    pub fn recovery_suggestions(&self) -> Vec<String> {
        match self {
            Self::BackendError { backend, .. } => {
                vec![format!(
                    "Try switching from '{}' parser backend to another",
                    backend
                )]
            }
            Self::UnsupportedFeature {
                backend, feature, ..
            } => {
                vec![
                    format!(
                        "Switch from '{}' backend to one that supports '{}'",
                        backend, feature
                    ),
                    format!("Remove or modify the '{}' feature usage", feature),
                ]
            }
            Self::Timeout {
                timeout_duration, ..
            } => {
                vec![
                    format!(
                        "Increase parser timeout (current: {}ms)",
                        timeout_duration.as_millis()
                    ),
                    "Simplify the query to reduce parsing complexity".to_string(),
                ]
            }
            Self::ResourceLimit {
                limit_type,
                max_value,
                ..
            } => {
                vec![
                    format!("Increase '{}' limit (current: {})", limit_type, max_value),
                    format!("Reduce usage of '{}' in the query", limit_type),
                ]
            }
            Self::ConfigurationError { .. } => {
                vec!["Check parser configuration settings".to_string()]
            }
            _ => vec![],
        }
    }
}

impl From<ParserError> for Error {
    fn from(err: ParserError) -> Self {
        match err {
            ParserError::SyntaxError { message, .. }
            | ParserError::SemanticError { message, .. }
            | ParserError::LexicalError { message, .. } => Error::cql_parse(message),
            ParserError::BackendError { message, .. }
            | ParserError::InternalError { message, .. } => Error::internal(message),
            ParserError::TypeError { message, .. } => Error::type_conversion(message),
            ParserError::ConfigurationError { message } => Error::configuration(message),
            ParserError::UnsupportedFeature {
                backend, feature, ..
            } => Error::invalid_operation(format!(
                "Feature '{}' not supported by backend '{}'",
                feature, backend
            )),
            ParserError::Timeout {
                timeout_duration, ..
            } => Error::internal(format!(
                "Parser timeout after {}ms",
                timeout_duration.as_millis()
            )),
            ParserError::ResourceLimit {
                limit_type,
                current_value,
                max_value,
                ..
            } => Error::internal(format!(
                "Resource '{}' limit exceeded: {} > {}",
                limit_type, current_value, max_value
            )),
        }
    }
}

/// Error severity levels
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ErrorSeverity {
    /// Information level
    Info,
    /// Warning level
    Warning,
    /// Error level
    Error,
    /// Fatal error level
    Fatal,
}

/// Error categories
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ErrorCategory {
    /// Syntax errors
    Syntax,
    /// Semantic errors
    Semantic,
    /// Type errors
    Type,
    /// Configuration errors
    Configuration,
    /// Backend errors
    Backend,
    /// Internal errors
    Internal,
}

/// Parser warning type
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ParserWarning {
    /// Warning message
    pub message: String,
    /// Position in source (if available)
    pub position: Option<SourcePosition>,
    /// Warning category
    pub category: ErrorCategory,
}

impl ParserWarning {
    /// Create a new warning
    pub fn new(message: String, category: ErrorCategory) -> Self {
        Self {
            message,
            position: None,
            category,
        }
    }

    /// Create a warning with position
    pub fn with_position(
        message: String,
        category: ErrorCategory,
        position: SourcePosition,
    ) -> Self {
        Self {
            message,
            position: Some(position),
            category,
        }
    }
}

/// Specialized result type for parser operations
pub type ParserResult<T> = std::result::Result<T, ParserError>;

/// Error context for providing additional information about parsing failures
#[derive(Debug, Clone)]
pub struct ErrorContext {
    /// Input text that was being parsed
    pub input: String,
    /// Current parser backend
    pub backend: String,
    /// Parser configuration at time of error
    pub config: Option<String>,
    /// Stack trace or call stack if available
    pub stack_trace: Option<Vec<String>>,
}

impl ErrorContext {
    /// Create a new error context
    pub fn new(input: String, backend: String) -> Self {
        Self {
            input,
            backend,
            config: None,
            stack_trace: None,
        }
    }

    /// Add configuration information
    pub fn with_config(mut self, config: String) -> Self {
        self.config = Some(config);
        self
    }

    /// Add stack trace information
    pub fn with_stack_trace(mut self, stack_trace: Vec<String>) -> Self {
        self.stack_trace = Some(stack_trace);
        self
    }

    /// Get a snippet of the input around the error position
    pub fn get_error_snippet(&self, position: &SourcePosition, context_lines: usize) -> String {
        let lines: Vec<&str> = self.input.lines().collect();
        let error_line = position.line as usize;

        if error_line == 0 || error_line > lines.len() {
            return self.input.clone();
        }

        let start_line = error_line.saturating_sub(context_lines + 1);
        let end_line = std::cmp::min(error_line + context_lines, lines.len());

        let mut snippet = String::new();

        for (i, line) in lines[start_line..end_line].iter().enumerate() {
            let line_num = start_line + i + 1;
            let marker = if line_num == error_line {
                ">>> "
            } else {
                "    "
            };
            snippet.push_str(&format!("{}{:4}: {}\n", marker, line_num, line));

            if line_num == error_line {
                let col = position.column as usize;
                if col > 0 && col <= line.len() {
                    snippet.push_str(&format!("{}     {}\n", marker, " ".repeat(col - 1) + "^"));
                }
            }
        }

        snippet
    }
}

/// Utility functions for error handling
pub mod utils {
    use super::*;

    /// Convert nom parsing errors to ParserError
    pub fn from_nom_error<I>(error: nom::Err<nom::error::Error<I>>, _input: &str) -> ParserError
    where
        I: std::fmt::Debug,
    {
        match error {
            nom::Err::Error(e) | nom::Err::Failure(e) => {
                ParserError::backend("nom", format!("Parse error: {:?}", e))
            }
            nom::Err::Incomplete(_) => ParserError::backend("nom", "Incomplete input"),
        }
    }

    /// Convert pest parsing errors to ParserError
    #[cfg(feature = "pest")]
    pub fn from_pest_error(error: Box<dyn std::error::Error>) -> ParserError {
        ParserError::backend("pest", format!("Parse error: {}", error))
    }

    /// Create a helpful error message with context
    pub fn create_contextual_error(error: ParserError, context: &ErrorContext) -> String {
        let mut message = format!("Parser Error: {}\n", error.message());

        if let Some(position) = error.position() {
            message.push_str(&format!(
                "Location: line {}, column {}\n",
                position.line, position.column
            ));

            let snippet = context.get_error_snippet(position, 2);
            if !snippet.is_empty() {
                message.push_str("Context:\n");
                message.push_str(&snippet);
            }
        }

        message.push_str(&format!("Backend: {}\n", context.backend));

        if let Some(config) = &context.config {
            message.push_str(&format!("Configuration: {}\n", config));
        }

        let suggestions = error.recovery_suggestions();
        if !suggestions.is_empty() {
            message.push_str("Suggestions:\n");
            for suggestion in suggestions {
                message.push_str(&format!("  - {}\n", suggestion));
            }
        }

        message
    }

    /// Chain multiple parser errors into a single error
    pub fn chain_errors(mut errors: Vec<ParserError>) -> ParserError {
        match errors.len() {
            0 => ParserError::internal("No errors to chain"),
            1 => errors.remove(0),
            _ => {
                let messages: Vec<String> = errors.iter().map(|e| e.message()).collect();
                ParserError::internal(format!("Multiple errors: {}", messages.join("; ")))
            }
        }
    }
}

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

    #[test]
    fn test_parser_error_creation() {
        let pos = SourcePosition::new(10, 5, 100, 20);

        let syntax_err = ParserError::syntax("Expected ';'", pos.clone());
        assert!(matches!(syntax_err, ParserError::SyntaxError { .. }));
        assert_eq!(syntax_err.position(), Some(&pos));

        let semantic_err = ParserError::semantic("Table does not exist");
        assert!(matches!(semantic_err, ParserError::SemanticError { .. }));
        assert_eq!(semantic_err.position(), None);

        let backend_err = ParserError::backend("nom", "Parse failed");
        assert!(matches!(backend_err, ParserError::BackendError { .. }));
        assert!(backend_err.is_recoverable());
    }

    #[test]
    fn test_error_recovery_suggestions() {
        let timeout_err = ParserError::timeout(5000);
        let suggestions = timeout_err.recovery_suggestions();
        assert!(!suggestions.is_empty());
        assert!(suggestions[0].contains("timeout"));

        let feature_err = ParserError::unsupported_feature("nom", "streaming");
        let suggestions = feature_err.recovery_suggestions();
        assert!(!suggestions.is_empty());
        assert!(suggestions[0].contains("backend"));
    }

    #[test]
    fn test_error_context() {
        let input = "SELECT * FROM users\nWHERE id = ?".to_string();
        let context = ErrorContext::new(input, "nom".to_string());

        let pos = SourcePosition::new(2, 10, 25, 1);
        let snippet = context.get_error_snippet(&pos, 1);

        assert!(snippet.contains("WHERE"));
        assert!(snippet.contains(">>>"));
        assert!(snippet.contains("^"));
    }

    #[test]
    fn test_error_conversion() {
        let parser_err = ParserError::syntax("Expected token", SourcePosition::start());
        let core_err: Error = parser_err.into();

        assert!(matches!(core_err, Error::CqlParse(_)));
    }
}
