//! API error handling

use super::types::ErrorResponse;
use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};

/// API error type
#[derive(Debug)]
pub enum ApiError {
    /// Conversation not found
    ConversationNotFound(String),

    /// Invalid request
    InvalidRequest(String),

    /// Database operation failed
    DatabaseError(String),

    /// LLM operation failed
    LlmError(String),

    /// Schema analysis failed
    SchemaError(String),

    /// Configuration generation failed
    GenerationError(String),

    /// Internal server error
    InternalError(String),
}

impl ApiError {
    /// Get error code for client handling
    fn code(&self) -> &'static str {
        match self {
            ApiError::ConversationNotFound(_) => "NOT_FOUND",
            ApiError::InvalidRequest(_) => "INVALID_REQUEST",
            ApiError::DatabaseError(_) => "DATABASE_ERROR",
            ApiError::LlmError(_) => "LLM_ERROR",
            ApiError::SchemaError(_) => "SCHEMA_ERROR",
            ApiError::GenerationError(_) => "GENERATION_ERROR",
            ApiError::InternalError(_) => "INTERNAL_ERROR",
        }
    }

    /// Get HTTP status code
    fn status(&self) -> StatusCode {
        match self {
            ApiError::ConversationNotFound(_) => StatusCode::NOT_FOUND,
            ApiError::InvalidRequest(_) => StatusCode::BAD_REQUEST,
            ApiError::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            ApiError::LlmError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            ApiError::SchemaError(_) => StatusCode::BAD_REQUEST,
            ApiError::GenerationError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            ApiError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    /// Get error message
    fn message(&self) -> String {
        match self {
            ApiError::ConversationNotFound(id) => format!("Conversation '{}' not found", id),
            ApiError::InvalidRequest(msg) => msg.clone(),
            ApiError::DatabaseError(msg) => format!("Database error: {}", msg),
            ApiError::LlmError(msg) => format!("LLM error: {}", msg),
            ApiError::SchemaError(msg) => format!("Schema error: {}", msg),
            ApiError::GenerationError(msg) => format!("Generation error: {}", msg),
            ApiError::InternalError(msg) => format!("Internal error: {}", msg),
        }
    }
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let status = self.status();
        let error_response = ErrorResponse::new(self.message(), self.code());

        (status, Json(error_response)).into_response()
    }
}

impl From<anyhow::Error> for ApiError {
    fn from(err: anyhow::Error) -> Self {
        ApiError::InternalError(err.to_string())
    }
}

impl From<String> for ApiError {
    fn from(err: String) -> Self {
        ApiError::InternalError(err)
    }
}

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

    #[test]
    fn test_conversation_not_found_code() {
        let err = ApiError::ConversationNotFound("123".to_string());
        assert_eq!(err.code(), "NOT_FOUND");
    }

    #[test]
    fn test_conversation_not_found_status() {
        let err = ApiError::ConversationNotFound("123".to_string());
        assert_eq!(err.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_invalid_request_code() {
        let err = ApiError::InvalidRequest("bad data".to_string());
        assert_eq!(err.code(), "INVALID_REQUEST");
    }

    #[test]
    fn test_llm_error_code() {
        let err = ApiError::LlmError("api failed".to_string());
        assert_eq!(err.code(), "LLM_ERROR");
        assert_eq!(err.status(), StatusCode::INTERNAL_SERVER_ERROR);
    }

    #[test]
    fn test_error_message() {
        let err = ApiError::ConversationNotFound("conv-123".to_string());
        assert!(err.message().contains("conv-123"));
    }
}
