//! Error types for Córtex API

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
use thiserror::Error;

/// Result type for Córtex operations
pub type Result<T> = std::result::Result<T, Error>;

/// Córtex API error types
#[derive(Debug, Error)]
pub enum Error {
    /// Triple not found
    #[error("Triple not found: {0}")]
    NotFound(String),

    /// Invalid input
    #[error("Invalid input: {0}")]
    InvalidInput(String),

    /// Validation error
    #[error("Validation failed: {0}")]
    ValidationError(String),

    /// Authentication error
    #[error("Authentication failed: {0}")]
    AuthError(String),

    /// Authorization error
    #[error("Not authorized: {0}")]
    Forbidden(String),

    /// Rate limit exceeded
    #[error("Rate limit exceeded: {0}")]
    RateLimitExceeded(String),

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

    /// SPARQL parse error
    #[error("SPARQL parse error: {0}")]
    SparqlParseError(String),

    /// Unbound variable in FILTER expression
    #[error("Unbound variable: {0}")]
    UnboundVariable(String),

    /// Unsupported FILTER expression
    #[error("Unsupported expression")]
    UnsupportedExpression,

    /// Invalid regular expression
    #[error("Invalid regex: {0}")]
    InvalidRegex(String),

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

    /// Proof verification failed
    #[error("Proof verification failed: {0}")]
    ProofVerificationFailed(String),

    /// Graph database error
    #[error("Graph error: {0}")]
    GraphError(#[from] aingle_graph::Error),

    /// Logic validation error
    #[error("Logic error: {0}")]
    LogicError(#[from] aingle_logic::Error),

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

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

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

    /// Timeout error
    #[error("Operation timed out: {0}")]
    Timeout(String),

    /// Bad request
    #[error("Bad request: {0}")]
    BadRequest(String),

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

/// Error response body
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
    pub error: String,
    pub code: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<String>,
}

impl Error {
    /// Get HTTP status code for this error
    pub fn status_code(&self) -> StatusCode {
        match self {
            Error::NotFound(_) => StatusCode::NOT_FOUND,
            Error::InvalidInput(_) => StatusCode::BAD_REQUEST,
            Error::ValidationError(_) => StatusCode::UNPROCESSABLE_ENTITY,
            Error::AuthError(_) => StatusCode::UNAUTHORIZED,
            Error::Forbidden(_) => StatusCode::FORBIDDEN,
            Error::RateLimitExceeded(_) => StatusCode::TOO_MANY_REQUESTS,
            Error::QueryError(_) => StatusCode::BAD_REQUEST,
            Error::SparqlParseError(_) => StatusCode::BAD_REQUEST,
            Error::UnboundVariable(_) => StatusCode::BAD_REQUEST,
            Error::UnsupportedExpression => StatusCode::BAD_REQUEST,
            Error::InvalidRegex(_) => StatusCode::BAD_REQUEST,
            Error::ProofNotFound(_) => StatusCode::NOT_FOUND,
            Error::ProofVerificationFailed(_) => StatusCode::UNPROCESSABLE_ENTITY,
            Error::GraphError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Error::LogicError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Error::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Error::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Error::Serialization(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Error::Timeout(_) => StatusCode::REQUEST_TIMEOUT,
            Error::BadRequest(_) => StatusCode::BAD_REQUEST,
            Error::Conflict(_) => StatusCode::CONFLICT,
        }
    }

    /// Get error code string
    pub fn error_code(&self) -> &'static str {
        match self {
            Error::NotFound(_) => "NOT_FOUND",
            Error::InvalidInput(_) => "INVALID_INPUT",
            Error::ValidationError(_) => "VALIDATION_ERROR",
            Error::AuthError(_) => "AUTH_ERROR",
            Error::Forbidden(_) => "FORBIDDEN",
            Error::RateLimitExceeded(_) => "RATE_LIMIT_EXCEEDED",
            Error::QueryError(_) => "QUERY_ERROR",
            Error::SparqlParseError(_) => "SPARQL_PARSE_ERROR",
            Error::UnboundVariable(_) => "UNBOUND_VARIABLE",
            Error::UnsupportedExpression => "UNSUPPORTED_EXPRESSION",
            Error::InvalidRegex(_) => "INVALID_REGEX",
            Error::ProofNotFound(_) => "PROOF_NOT_FOUND",
            Error::ProofVerificationFailed(_) => "PROOF_VERIFICATION_FAILED",
            Error::GraphError(_) => "GRAPH_ERROR",
            Error::LogicError(_) => "LOGIC_ERROR",
            Error::Internal(_) => "INTERNAL_ERROR",
            Error::Io(_) => "IO_ERROR",
            Error::Serialization(_) => "SERIALIZATION_ERROR",
            Error::Timeout(_) => "TIMEOUT",
            Error::BadRequest(_) => "BAD_REQUEST",
            Error::Conflict(_) => "CONFLICT",
        }
    }
}

impl IntoResponse for Error {
    fn into_response(self) -> Response {
        let status = self.status_code();
        let body = ErrorResponse {
            error: self.to_string(),
            code: self.error_code().to_string(),
            details: None,
        };

        (status, axum::Json(body)).into_response()
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Error::Serialization(err.to_string())
    }
}
