//! API request and response types

use crate::assistant::{AssistantResponse, ConfigAssistant, FieldSuggestion, GeneratedConfig};
use crate::storage::SurrealDbClient;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};

/// Request to start a new conversation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateConversationRequest {
    /// Schema identifier or name
    pub schema_id: String,
}

/// Response when conversation is created
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateConversationResponse {
    /// Unique conversation identifier
    pub conversation_id: String,

    /// Schema identifier
    pub schema_id: String,

    /// Human-readable message
    pub message: String,
}

/// Request to send a message to the assistant
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendMessageRequest {
    /// User message text
    pub message: String,
}

/// Response from sending a message
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendMessageResponse {
    /// Assistant's text response
    pub text: String,

    /// Field suggestions extracted from response
    pub suggestions: Vec<FieldSuggestion>,

    /// Message ID for tracking
    pub message_id: String,

    /// RAG context used for generation
    pub rag_context: Option<String>,
}

impl From<AssistantResponse> for SendMessageResponse {
    fn from(resp: AssistantResponse) -> Self {
        SendMessageResponse {
            text: resp.text,
            suggestions: resp.suggestions,
            message_id: resp.message_id,
            rag_context: resp.rag_context,
        }
    }
}

/// Request to get field suggestions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuggestionsRequest {
    /// Field name to get suggestions for
    pub field: String,
}

/// Response with field suggestions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuggestionsResponse {
    /// Field name
    pub field: String,

    /// List of suggestions
    pub suggestions: Vec<FieldSuggestion>,
}

/// Request to generate final configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateConfigRequest {
    /// Output format: "json", "yaml", or "toml"
    pub format: String,
}

/// Response with generated configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateConfigResponse {
    /// Generated configuration content
    pub content: String,

    /// Configuration format
    pub format: String,

    /// Whether configuration is valid
    pub is_valid: bool,

    /// Validation errors if any
    pub errors: Vec<String>,
}

impl From<GeneratedConfig> for GenerateConfigResponse {
    fn from(config: GeneratedConfig) -> Self {
        GenerateConfigResponse {
            content: config.content,
            format: config.format,
            is_valid: config.is_valid,
            errors: config.errors,
        }
    }
}

/// Conversation metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConversationInfo {
    /// Conversation ID
    pub id: String,

    /// Schema ID
    pub schema_id: String,

    /// Conversation status
    pub status: String,

    /// Created timestamp
    pub created_at: String,

    /// Updated timestamp
    pub updated_at: String,
}

/// Health check response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
    /// Service status
    pub status: String,

    /// Service version
    pub version: String,

    /// Uptime in seconds
    pub uptime: u64,
}

/// Shared application state
#[derive(Clone)]
pub struct AppState {
    /// Database client
    pub db: Arc<SurrealDbClient>,

    /// Currently active assistants by conversation ID
    pub assistants: Arc<Mutex<std::collections::HashMap<String, ConfigAssistant>>>,

    /// Server start time for uptime tracking
    pub start_time: std::time::Instant,
}

/// Error response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
    /// Error message
    pub error: String,

    /// Error code (for clients to handle specific errors)
    pub code: String,

    /// Optional additional details
    pub details: Option<String>,
}

impl ErrorResponse {
    /// Create a new error response
    pub fn new(error: impl Into<String>, code: impl Into<String>) -> Self {
        ErrorResponse {
            error: error.into(),
            code: code.into(),
            details: None,
        }
    }

    /// Add details to error response
    pub fn with_details(mut self, details: impl Into<String>) -> Self {
        self.details = Some(details.into());
        self
    }
}

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

    #[test]
    fn test_create_conversation_request_serialization() {
        let req = CreateConversationRequest {
            schema_id: "test-schema".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: CreateConversationRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.schema_id, "test-schema");
    }

    #[test]
    fn test_send_message_request_serialization() {
        let req = SendMessageRequest {
            message: "What port should I use?".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: SendMessageRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.message, "What port should I use?");
    }

    #[test]
    fn test_error_response_creation() {
        let error = ErrorResponse::new("Invalid request", "INVALID_REQUEST");
        assert_eq!(error.error, "Invalid request");
        assert_eq!(error.code, "INVALID_REQUEST");
    }

    #[test]
    fn test_error_response_with_details() {
        let error = ErrorResponse::new("Invalid request", "INVALID_REQUEST")
            .with_details("Field 'message' is required");
        assert!(error.details.is_some());
    }

    #[test]
    fn test_generate_config_request_serialization() {
        let req = GenerateConfigRequest {
            format: "json".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let deserialized: GenerateConfigRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.format, "json");
    }
}
