//! Core JSON-RPC 2.0 and MCP protocol types
//!
//! Extracted from crimson/dictator implementations and enhanced with patterns
//! from the Go mcphost implementation.

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// JSON-RPC 2.0 request
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct JsonRpcRequest {
    /// JSON-RPC version (always "2.0")
    pub jsonrpc: String,
    /// Request ID (null for notifications)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<Value>,
    /// Method name
    pub method: String,
    /// Method parameters
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
}

impl JsonRpcRequest {
    /// Create a new request with ID
    pub fn new(id: Value, method: impl Into<String>, params: Option<Value>) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            id: Some(id),
            method: method.into(),
            params,
        }
    }

    /// Create a notification (no ID)
    pub fn notification(method: impl Into<String>, params: Option<Value>) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            id: None,
            method: method.into(),
            params,
        }
    }

    /// Check if this is a notification
    pub fn is_notification(&self) -> bool {
        self.id.is_none()
    }
}

/// JSON-RPC 2.0 response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
    /// JSON-RPC version (always "2.0")
    pub jsonrpc: String,
    /// Request ID
    pub id: Value,
    /// Successful result
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<Value>,
    /// Error object
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<JsonRpcError>,
}

impl JsonRpcResponse {
    /// Create a successful response
    pub fn success(id: Value, result: Value) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            id,
            result: Some(result),
            error: None,
        }
    }

    /// Create an error response
    pub fn error(id: Value, error: JsonRpcError) -> Self {
        Self {
            jsonrpc: "2.0".to_string(),
            id,
            result: None,
            error: Some(error),
        }
    }
}

/// JSON-RPC 2.0 error object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcError {
    /// Error code
    pub code: i32,
    /// Error message
    pub message: String,
    /// Additional error data
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<Value>,
}

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

    /// Add additional data to the error
    pub fn with_data(mut self, data: Value) -> Self {
        self.data = Some(data);
        self
    }

    /// Parse error (-32700)
    pub fn parse_error(message: impl Into<String>) -> Self {
        Self::new(-32700, message)
    }

    /// Invalid request (-32600)
    pub fn invalid_request(message: impl Into<String>) -> Self {
        Self::new(-32600, message)
    }

    /// Method not found (-32601)
    pub fn method_not_found(message: impl Into<String>) -> Self {
        Self::new(-32601, message)
    }

    /// Invalid params (-32602)
    pub fn invalid_params(message: impl Into<String>) -> Self {
        Self::new(-32602, message)
    }

    /// Internal error (-32603)
    pub fn internal_error(message: impl Into<String>) -> Self {
        Self::new(-32603, message)
    }
}

/// Server/Client implementation information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Implementation {
    /// Implementation name
    pub name: String,
    /// Implementation version
    pub version: String,
}

impl Implementation {
    /// Create new implementation info
    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            version: version.into(),
        }
    }
}

/// Client information with version validation
#[derive(Debug, Clone, Default)]
pub struct ClientInfo {
    /// Client name
    pub name: String,
    /// Client version
    pub version: String,
}

impl ClientInfo {
    /// Create new client info
    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            version: version.into(),
        }
    }

    /// Check if client version meets minimum requirements
    ///
    /// Uses the configurable MIN_CLIENT_VERSIONS from version module
    pub fn is_supported(&self, min_versions: &[(&str, &str)]) -> bool {
        for (name, min_version) in min_versions {
            if self.name == *name {
                return version_gte(&self.version, min_version);
            }
        }
        // Unknown clients are allowed by default
        true
    }
}

/// Compare semantic versions (simple: major.minor.patch)
///
/// Returns true if version >= min
pub fn version_gte(version: &str, min: &str) -> bool {
    let parse = |v: &str| -> (u32, u32, u32) {
        let parts: Vec<u32> = v.split('.').filter_map(|p| p.parse().ok()).collect();
        (
            parts.first().copied().unwrap_or(0),
            parts.get(1).copied().unwrap_or(0),
            parts.get(2).copied().unwrap_or(0),
        )
    };

    let (v_maj, v_min, v_patch) = parse(version);
    let (m_maj, m_min, m_patch) = parse(min);

    (v_maj, v_min, v_patch) >= (m_maj, m_min, m_patch)
}

// =============================================================================
// ROOTS - Client Context Management
// =============================================================================

/// Represents a root URI provided by the client
///
/// Roots are top-level directories or resources that the client wants
/// the server to be aware of for context.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Root {
    /// URI of the root (e.g., file:// or other protocol)
    pub uri: String,
    /// Optional human-readable name for the root
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

/// Result of listing roots
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListRootsResult {
    /// List of roots provided by the client
    pub roots: Vec<Root>,
}

/// Notification that the client's root list has changed
///
/// Servers should re-request the roots list when receiving this
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RootsListChangedNotification {}

// =============================================================================
// SAMPLING - LLM Message Creation
// =============================================================================

/// Role in a conversation
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
    /// User message
    User,
    /// Assistant message
    Assistant,
}

/// A message in a sampling conversation
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SamplingMessage {
    /// Role of the message sender
    pub role: Role,
    /// Message content (reuses Content from content module)
    pub content: Value, // Will be Content type from content module
}

/// Specifies how much context should be included in sampling requests
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ContextInclusion {
    /// No context
    None,
    /// Context from this server only
    ThisServer,
    /// Context from all connected servers
    AllServers,
}

/// Model hint for LLM selection
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelHint {
    /// Preferred model name (e.g., "claude-3-sonnet", "gpt-4")
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

/// Model selection preferences
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelPreferences {
    /// Model name hints
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hints: Option<Vec<ModelHint>>,
    /// Cost priority (0.0-1.0, higher means prefer cheaper models)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cost_priority: Option<f64>,
    /// Speed priority (0.0-1.0, higher means prefer faster models)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub speed_priority: Option<f64>,
    /// Intelligence priority (0.0-1.0, higher means prefer smarter models)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub intelligence_priority: Option<f64>,
}

/// Request to create an LLM message/completion
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateMessageRequest {
    /// Conversation history
    pub messages: Vec<SamplingMessage>,
    /// Model selection preferences
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_preferences: Option<ModelPreferences>,
    /// System prompt to use
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system_prompt: Option<String>,
    /// How much context to include
    #[serde(skip_serializing_if = "Option::is_none")]
    pub include_context: Option<ContextInclusion>,
    /// Temperature (0.0-1.0+)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f64>,
    /// Maximum tokens to generate
    pub max_tokens: u32,
    /// Stop sequences
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stop_sequences: Option<Vec<String>>,
    /// Additional metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Value>,
}

/// Result of creating an LLM message
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateMessageResult {
    /// Generated message
    pub message: SamplingMessage,
    /// Model used for generation
    pub model: String,
    /// Why generation stopped
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stop_reason: Option<String>, // "endTurn", "stopSequence", "maxTokens"
}

impl CreateMessageResult {
    /// Stop reason: natural end of turn
    pub const STOP_REASON_END_TURN: &'static str = "endTurn";
    /// Stop reason: hit a stop sequence
    pub const STOP_REASON_STOP_SEQUENCE: &'static str = "stopSequence";
    /// Stop reason: reached max tokens
    pub const STOP_REASON_MAX_TOKENS: &'static str = "maxTokens";
}

// =============================================================================
// ELICITATION - Structured User Input
// =============================================================================

/// Action taken by user in response to elicitation
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ElicitationAction {
    /// User approved and provided data
    Accept,
    /// User explicitly declined
    Decline,
    /// User dismissed without decision
    Cancel,
}

/// Request to elicit structured input from user
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateElicitationRequest {
    /// Message to show user
    pub message: String,
    /// Schema for requested data (imported from elicitation module)
    pub requested_schema: Value, // Will be ElicitationSchema
}

/// Result of elicitation request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateElicitationResult {
    /// User's action
    pub action: ElicitationAction,
    /// User-provided content (only present if action is Accept)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content: Option<Value>,
}

// ============================================================================
// Tasks
// ============================================================================

/// Task status in lifecycle
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
    /// The request is currently being processed
    Working,
    /// The task is waiting for input (e.g., elicitation or sampling)
    InputRequired,
    /// The request completed successfully and results are available
    Completed,
    /// The associated request did not complete successfully
    Failed,
    /// The request was cancelled before completion
    Cancelled,
}

/// Metadata for augmenting a request with task execution
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskMetadata {
    /// Requested duration in milliseconds to retain task from creation
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ttl: Option<u64>,
}

/// Data associated with a task
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Task {
    /// The task identifier
    pub task_id: String,
    /// Current task state
    pub status: TaskStatus,
    /// Optional human-readable message describing the current task state
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status_message: Option<String>,
    /// ISO 8601 timestamp when the task was created
    pub created_at: String,
    /// ISO 8601 timestamp when the task was last updated
    pub last_updated_at: String,
    /// Actual retention duration from creation in milliseconds, null for unlimited
    pub ttl: Option<u64>,
    /// Suggested polling interval in milliseconds
    #[serde(skip_serializing_if = "Option::is_none")]
    pub poll_interval: Option<u64>,
}

/// A response to a task-augmented request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTaskResult {
    /// The created task
    pub task: Task,
}

/// Request params for tasks/get
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTaskParams {
    /// The task identifier to query
    pub task_id: String,
}

/// Request params for tasks/cancel
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelTaskParams {
    /// The task identifier to cancel
    pub task_id: String,
}

/// Tool execution metadata
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolExecution {
    /// Indicates whether this tool supports task-augmented execution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub task_support: Option<TaskSupport>,
}

/// Task support level for a tool
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum TaskSupport {
    /// Tool does not support task-augmented execution (default when absent)
    Forbidden,
    /// Tool may support task-augmented execution
    Optional,
    /// Tool requires task-augmented execution
    Required,
}

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

    #[test]
    fn test_version_gte_equal() {
        assert!(version_gte("1.0.0", "1.0.0"));
        assert!(version_gte("2.0.56", "2.0.56"));
    }

    #[test]
    fn test_version_gte_major_greater() {
        assert!(version_gte("2.0.0", "1.0.0"));
        assert!(version_gte("3.0.0", "2.9.99"));
    }

    #[test]
    fn test_version_gte_minor_greater() {
        assert!(version_gte("1.2.0", "1.1.0"));
        assert!(version_gte("1.10.0", "1.9.0"));
    }

    #[test]
    fn test_version_gte_patch_greater() {
        assert!(version_gte("1.0.2", "1.0.1"));
        assert!(version_gte("2.0.56", "2.0.55"));
    }

    #[test]
    fn test_version_gte_less_than() {
        assert!(!version_gte("1.0.0", "2.0.0"));
        assert!(!version_gte("2.0.55", "2.0.56"));
        assert!(!version_gte("1.9.0", "2.0.0"));
    }

    #[test]
    fn test_jsonrpc_request_creation() {
        let req = JsonRpcRequest::new(
            Value::from(1),
            "test_method",
            Some(serde_json::json!({"key": "value"})),
        );
        assert_eq!(req.jsonrpc, "2.0");
        assert_eq!(req.method, "test_method");
        assert!(req.id.is_some());
    }

    #[test]
    fn test_jsonrpc_notification() {
        let notif = JsonRpcRequest::notification("test_notif", None);
        assert!(notif.is_notification());
        assert!(notif.id.is_none());
    }

    #[test]
    fn test_jsonrpc_response_success() {
        let resp = JsonRpcResponse::success(Value::from(1), serde_json::json!({"status": "ok"}));
        assert!(resp.result.is_some());
        assert!(resp.error.is_none());
    }

    #[test]
    fn test_jsonrpc_response_error() {
        let error = JsonRpcError::method_not_found("test method not found");
        let resp = JsonRpcResponse::error(Value::from(1), error);
        assert!(resp.result.is_none());
        assert!(resp.error.is_some());
        assert_eq!(resp.error.unwrap().code, -32601);
    }
}
