//! Error types for the ACP Server
//!
//! This module defines error types used throughout the ACP Server implementation,
//! including conversions to JSON-RPC error format for client responses.

use std::fmt;

use serde::{Deserialize, Serialize};

/// Standard JSON-RPC error codes as defined in the specification
pub mod error_codes {
    /// Parse error - Invalid JSON was received by the server
    pub const PARSE_ERROR: i32 = -32700;

    /// Invalid Request - The JSON sent is not a valid Request object
    pub const INVALID_REQUEST: i32 = -32600;

    /// Method not found - The method does not exist / is not available
    pub const METHOD_NOT_FOUND: i32 = -32601;

    /// Invalid params - Invalid method parameter(s)
    pub const INVALID_PARAMS: i32 = -32602;

    /// Internal error - Internal JSON-RPC error
    pub const INTERNAL_ERROR: i32 = -32603;

    // Server errors reserved for implementation-defined errors (-32000 to -32099)

    /// Session not found error
    pub const SESSION_NOT_FOUND: i32 = -32001;

    /// Connector not found error
    pub const CONNECTOR_NOT_FOUND: i32 = -32002;

    /// Invalid session error
    pub const INVALID_SESSION: i32 = -32003;

    /// Transport error
    pub const TRANSPORT_ERROR: i32 = -32004;

    /// Client not found error
    pub const CLIENT_NOT_FOUND: i32 = -32005;
}

/// ACP Server error enum representing all possible error conditions
#[derive(Debug, Clone, PartialEq)]
pub enum AcpServerError {
    /// The session ID provided is invalid or malformed
    InvalidSession,

    /// An RPC-related error occurred
    ///
    /// Contains a description of the RPC error, such as method not found,
    /// invalid params, or parse errors.
    RpcError(String),

    /// A transport-level error occurred
    ///
    /// Contains a description of the transport error, such as connection
    /// failures, timeout, or network issues.
    TransportError(String),

    /// The requested session was not found
    ///
    /// The session ID does not correspond to any known session in the
    /// session manager.
    SessionNotFound,

    /// The requested connector was not found
    ///
    /// The connector ID does not correspond to any registered connector
    /// in the runtime. Contains the connector ID that was not found.
    ConnectorNotFound(String),

    /// The connector is not in a ready state
    ///
    /// The connector exists but is not available to handle requests
    /// (e.g., still connecting, error state, or stopped).
    /// Contains the connector ID that is not ready.
    ConnectorNotReady(String),

    /// Operation timed out
    ///
    /// Contains a description of what timed out.
    Timeout(String),

    /// An internal server error occurred
    ///
    /// Contains a description of the internal error. Used for unexpected
    /// conditions that don't fit other categories.
    Internal(String),

    /// The requested client was not found
    ///
    /// The client ID does not correspond to any connected client in the
    /// session manager. Contains the client ID that was not found.
    ClientNotFound(String),
}

impl fmt::Display for AcpServerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AcpServerError::InvalidSession => {
                write!(f, "Invalid session ID provided")
            }
            AcpServerError::RpcError(msg) => {
                write!(f, "RPC error: {}", msg)
            }
            AcpServerError::TransportError(msg) => {
                write!(f, "Transport error: {}", msg)
            }
            AcpServerError::SessionNotFound => {
                write!(f, "Session not found")
            }
            AcpServerError::ConnectorNotFound(id) => {
                write!(f, "Connector not found: {}", id)
            }
            AcpServerError::ConnectorNotReady(id) => {
                write!(f, "Connector not ready: {}", id)
            }
            AcpServerError::Timeout(msg) => {
                write!(f, "Timeout: {}", msg)
            }
            AcpServerError::Internal(msg) => {
                write!(f, "Internal error: {}", msg)
            }
            AcpServerError::ClientNotFound(id) => {
                write!(f, "Client not found: {}", id)
            }
        }
    }
}

impl std::error::Error for AcpServerError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        // None of our error variants wrap other errors currently
        None
    }
}

/// JSON-RPC error representation for wire format
///
/// This struct is used in JSON-RPC responses when an error occurs.
/// It follows the JSON-RPC 2.0 specification for error objects.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct JsonRpcErrorObject {
    /// A Number that indicates the error type that occurred
    pub code: i32,

    /// A String providing a short description of the error
    pub message: String,

    /// Optional additional information about the error
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<serde_json::Value>,
}

impl JsonRpcErrorObject {
    /// Create a new JSON-RPC error object
    pub fn new(code: i32, message: impl Into<String>) -> Self {
        Self {
            code,
            message: message.into(),
            data: None,
        }
    }

    /// Create a new JSON-RPC error object with additional data
    pub fn with_data(code: i32, message: impl Into<String>, data: serde_json::Value) -> Self {
        Self {
            code,
            message: message.into(),
            data: Some(data),
        }
    }

    /// Create a parse error
    pub fn parse_error(message: impl Into<String>) -> Self {
        Self::new(error_codes::PARSE_ERROR, message)
    }

    /// Create an invalid request error
    pub fn invalid_request(message: impl Into<String>) -> Self {
        Self::new(error_codes::INVALID_REQUEST, message)
    }

    /// Create a method not found error
    pub fn method_not_found(method: impl Into<String>) -> Self {
        Self::new(
            error_codes::METHOD_NOT_FOUND,
            format!("Method not found: {}", method.into()),
        )
    }

    /// Create an invalid params error
    pub fn invalid_params(message: impl Into<String>) -> Self {
        Self::new(error_codes::INVALID_PARAMS, message)
    }

    /// Create an internal error
    pub fn internal_error(message: impl Into<String>) -> Self {
        Self::new(error_codes::INTERNAL_ERROR, message)
    }
}

impl From<AcpServerError> for JsonRpcErrorObject {
    fn from(error: AcpServerError) -> Self {
        match error {
            AcpServerError::InvalidSession => {
                JsonRpcErrorObject::new(error_codes::INVALID_SESSION, "Invalid session ID provided")
            }
            AcpServerError::RpcError(msg) => {
                JsonRpcErrorObject::new(error_codes::INTERNAL_ERROR, msg)
            }
            AcpServerError::TransportError(msg) => {
                JsonRpcErrorObject::new(error_codes::TRANSPORT_ERROR, msg)
            }
            AcpServerError::SessionNotFound => {
                JsonRpcErrorObject::new(error_codes::SESSION_NOT_FOUND, "Session not found")
            }
            AcpServerError::ConnectorNotFound(id) => {
                JsonRpcErrorObject::new(error_codes::CONNECTOR_NOT_FOUND, format!("Connector not found: {}", id))
            }
            AcpServerError::ConnectorNotReady(id) => {
                JsonRpcErrorObject::new(error_codes::CONNECTOR_NOT_FOUND, format!("Connector not ready: {}", id))
            }
            AcpServerError::Timeout(msg) => {
                JsonRpcErrorObject::new(error_codes::TRANSPORT_ERROR, format!("Timeout: {}", msg))
            }
            AcpServerError::Internal(msg) => JsonRpcErrorObject::internal_error(msg),
            AcpServerError::ClientNotFound(id) => {
                JsonRpcErrorObject::new(error_codes::CLIENT_NOT_FOUND, format!("Client not found: {}", id))
            }
        }
    }
}

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

    #[test]
    fn test_error_display() {
        assert_eq!(
            AcpServerError::InvalidSession.to_string(),
            "Invalid session ID provided"
        );
        assert_eq!(
            AcpServerError::RpcError("test".to_string()).to_string(),
            "RPC error: test"
        );
        assert_eq!(
            AcpServerError::TransportError("timeout".to_string()).to_string(),
            "Transport error: timeout"
        );
        assert_eq!(
            AcpServerError::SessionNotFound.to_string(),
            "Session not found"
        );
        assert_eq!(
            AcpServerError::ConnectorNotFound("test-conn".to_string()).to_string(),
            "Connector not found: test-conn"
        );
        assert_eq!(
            AcpServerError::ConnectorNotReady("test-conn".to_string()).to_string(),
            "Connector not ready: test-conn"
        );
        assert_eq!(
            AcpServerError::Timeout("request timed out".to_string()).to_string(),
            "Timeout: request timed out"
        );
        assert_eq!(
            AcpServerError::Internal("something went wrong".to_string()).to_string(),
            "Internal error: something went wrong"
        );
        assert_eq!(
            AcpServerError::ClientNotFound("client-123".to_string()).to_string(),
            "Client not found: client-123"
        );
    }

    #[test]
    fn test_error_to_jsonrpc() {
        let error: JsonRpcErrorObject = AcpServerError::InvalidSession.into();
        assert_eq!(error.code, error_codes::INVALID_SESSION);
        assert_eq!(error.message, "Invalid session ID provided");

        let error: JsonRpcErrorObject = AcpServerError::SessionNotFound.into();
        assert_eq!(error.code, error_codes::SESSION_NOT_FOUND);

        let error: JsonRpcErrorObject = AcpServerError::ConnectorNotFound("conn1".to_string()).into();
        assert_eq!(error.code, error_codes::CONNECTOR_NOT_FOUND);
        assert!(error.message.contains("conn1"));

        let error: JsonRpcErrorObject = AcpServerError::ConnectorNotReady("conn2".to_string()).into();
        assert_eq!(error.code, error_codes::CONNECTOR_NOT_FOUND);
        assert!(error.message.contains("conn2"));

        let error: JsonRpcErrorObject = AcpServerError::Timeout("session creation".to_string()).into();
        assert_eq!(error.code, error_codes::TRANSPORT_ERROR);

        let error: JsonRpcErrorObject = AcpServerError::TransportError("net error".to_string()).into();
        assert_eq!(error.code, error_codes::TRANSPORT_ERROR);
        assert_eq!(error.message, "net error");

        let error: JsonRpcErrorObject = AcpServerError::Internal("internal".to_string()).into();
        assert_eq!(error.code, error_codes::INTERNAL_ERROR);

        let error: JsonRpcErrorObject = AcpServerError::ClientNotFound("client-456".to_string()).into();
        assert_eq!(error.code, error_codes::CLIENT_NOT_FOUND);
        assert!(error.message.contains("client-456"));
    }

    #[test]
    fn test_jsonrpc_error_serialization() {
        let error = JsonRpcErrorObject::new(error_codes::PARSE_ERROR, "Invalid JSON");
        let json = serde_json::to_string(&error).unwrap();
        assert!(json.contains("-32700"));
        assert!(json.contains("Invalid JSON"));

        // With data
        let error = JsonRpcErrorObject::with_data(
            error_codes::INVALID_PARAMS,
            "Missing field",
            serde_json::json!({"field": "session_id"}),
        );
        let json = serde_json::to_string(&error).unwrap();
        assert!(json.contains("session_id"));
    }

    #[test]
    fn test_jsonrpc_error_factories() {
        let error = JsonRpcErrorObject::parse_error("bad json");
        assert_eq!(error.code, error_codes::PARSE_ERROR);

        let error = JsonRpcErrorObject::invalid_request("missing jsonrpc field");
        assert_eq!(error.code, error_codes::INVALID_REQUEST);

        let error = JsonRpcErrorObject::method_not_found("session.unknown");
        assert_eq!(error.code, error_codes::METHOD_NOT_FOUND);
        assert!(error.message.contains("session.unknown"));

        let error = JsonRpcErrorObject::invalid_params("session_id required");
        assert_eq!(error.code, error_codes::INVALID_PARAMS);

        let error = JsonRpcErrorObject::internal_error("panic");
        assert_eq!(error.code, error_codes::INTERNAL_ERROR);
    }

    #[test]
    fn test_error_is_error_trait() {
        fn assert_is_error<T: std::error::Error>() {}
        assert_is_error::<AcpServerError>();
    }

    #[test]
    fn test_error_clone() {
        let error = AcpServerError::Internal("test".to_string());
        let cloned = error.clone();
        assert_eq!(error, cloned);
    }
}
