//! JSON-RPC Request/Response Types for ACP Server
//!
//! This module contains all the parameter and result types used in
//! the ACP JSON-RPC protocol.

use serde::{Deserialize, Serialize};

use crate::error::AcpServerError;
use crate::sse::ConfigOption;

// ============================================================================
// Session and Connector Info Types
// ============================================================================

/// Information about a session
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionInfo {
    /// The session ID
    pub session_id: String,

    /// Optional title for the session
    pub title: Option<String>,

    /// The connector handling this session
    pub connector_id: String,

    /// Working directory for this session
    pub cwd: Option<String>,

    /// When the session was created (ISO 8601 format)
    pub created_at: String,

    /// Available models and current model (optional, connector-dependent)
    pub models: Option<dirigent_protocol::SessionModelState>,

    /// Available modes and current mode (optional, connector-dependent)
    pub modes: Option<dirigent_protocol::SessionModeState>,
}

/// Information about a connector
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConnectorInfo {
    /// Unique identifier for this connector
    pub id: String,

    /// Human-readable name
    pub name: String,

    /// Type of connector (e.g., "opencode", "gateway")
    pub connector_type: String,

    /// Whether the connector is currently available
    pub available: bool,
}

// ============================================================================
// Initialize Types
// ============================================================================

/// Parameters for the initialize request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
    /// Client capabilities
    #[serde(default)]
    pub capabilities: Option<serde_json::Value>,

    /// Client name
    #[serde(default)]
    pub client_name: Option<String>,

    /// Client version
    #[serde(default)]
    pub client_version: Option<String>,
}

/// Result of the initialize request (ACP spec compliant)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
    /// Protocol version (integer)
    pub protocol_version: u32,

    /// Agent capabilities
    pub agent_capabilities: AgentCapabilities,

    /// Agent info (name, title, version)
    pub agent_info: AgentInfo,

    /// Authentication methods (empty for now)
    pub auth_methods: Vec<AuthMethod>,

    /// Client ID for HTTP-based transports (required for SSE routing)
    /// Note: This is not in the official ACP spec but is required for stateless
    /// HTTP connections where the client needs an ID to subscribe to SSE events.
    pub client_id: String,
}

/// Agent information
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentInfo {
    /// Agent name (programmatic)
    pub name: String,

    /// Agent title (human-readable)
    pub title: String,

    /// Agent version
    pub version: String,
}

/// Agent capabilities advertised to clients
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentCapabilities {
    /// Whether session/load is supported
    #[serde(skip_serializing_if = "Option::is_none")]
    pub load_session: Option<bool>,

    /// Whether session/list is supported
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_sessions: Option<bool>,

    /// Session capabilities (resume, fork, etc.)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_capabilities: Option<SessionCapabilities>,

    /// Prompt capabilities (content types supported)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prompt_capabilities: Option<PromptCapabilities>,

    /// MCP server support
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mcp: Option<McpCapabilities>,
}

/// Extended session capabilities
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCapabilities {
    /// Whether session/list is supported (empty object = supported)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list: Option<serde_json::Value>,

    /// Whether session/resume is supported (empty object = supported)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resume: Option<serde_json::Value>,
}

/// Prompt capabilities (content types)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptCapabilities {
    /// Image content support
    #[serde(skip_serializing_if = "Option::is_none")]
    pub image: Option<bool>,

    /// Audio content support
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio: Option<bool>,

    /// Embedded context support
    #[serde(skip_serializing_if = "Option::is_none")]
    pub embedded_context: Option<bool>,
}

/// MCP capabilities
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpCapabilities {
    /// HTTP transport support
    #[serde(skip_serializing_if = "Option::is_none")]
    pub http: Option<bool>,

    /// SSE transport support (deprecated)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sse: Option<bool>,
}

/// Authentication method
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthMethod {
    /// Method ID
    pub id: String,

    /// Method name
    pub name: String,

    /// Method description
    pub description: String,
}

impl Default for AgentCapabilities {
    fn default() -> Self {
        Self {
            load_session: Some(true),
            list_sessions: Some(true),
            session_capabilities: Some(SessionCapabilities {
                list: Some(serde_json::json!({})),
                resume: Some(serde_json::json!({})),
            }),
            prompt_capabilities: Some(PromptCapabilities {
                image: Some(true),
                audio: None,
                embedded_context: Some(true),
            }),
            mcp: Some(McpCapabilities {
                http: Some(true),
                sse: Some(true),
            }),
        }
    }
}

// ============================================================================
// Session/New Types
// ============================================================================

/// Parameters for session/new request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionNewParams {
    /// Optional connector ID to create the session on
    #[serde(default)]
    pub connector_id: Option<String>,

    /// Optional working directory for the session
    #[serde(default)]
    pub cwd: Option<String>,

    /// Optional client-provided session ID
    #[serde(default)]
    pub session_id: Option<String>,
}

/// Result of session/new request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionNewResult {
    /// The client-facing session ID
    pub session_id: String,

    /// Optional title for the session
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// The connector handling this session
    pub connector_id: String,

    /// When the session was created (ISO 8601 format)
    pub created_at: String,

    /// Available models and current model (UNSTABLE in ACP spec)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub models: Option<dirigent_protocol::SessionModelState>,

    /// Available modes and current mode
    #[serde(skip_serializing_if = "Option::is_none")]
    pub modes: Option<dirigent_protocol::SessionModeState>,

    /// Configuration options (modes, models as unified config)
    /// This is the preferred way to expose configuration in ACP
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config_options: Option<Vec<ConfigOption>>,
}

// ============================================================================
// Session/Load Types
// ============================================================================

/// Parameters for session/load request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionLoadParams {
    /// The session ID to load
    pub session_id: String,

    /// The connector ID where the session exists (optional; resolved automatically if omitted)
    #[serde(default)]
    pub connector_id: Option<String>,

    /// Optional working directory (standard ACP field sent by clients like Zed)
    #[serde(default)]
    pub cwd: Option<String>,

    /// Optional MCP server configurations (standard ACP field)
    #[serde(default)]
    pub mcp_servers: Option<Vec<serde_json::Value>>,
}

/// Result of session/load request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionLoadResult {
    /// The client-facing session ID
    pub session_id: String,

    /// Optional title for the session
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// The connector handling this session
    pub connector_id: String,

    /// When the session was created (ISO 8601 format)
    pub created_at: String,

    /// Available models and current model (UNSTABLE in ACP spec)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub models: Option<dirigent_protocol::SessionModelState>,

    /// Available modes and current mode
    #[serde(skip_serializing_if = "Option::is_none")]
    pub modes: Option<dirigent_protocol::SessionModeState>,

    /// Configuration options (modes, models as unified config)
    /// This is the preferred way to expose configuration in ACP
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config_options: Option<Vec<ConfigOption>>,
}

// ============================================================================
// Session/List Types
// ============================================================================

/// Parameters for session/list request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionListParams {
    /// Optional connector ID to list sessions from
    #[serde(default)]
    pub connector_id: Option<String>,

    /// Optional working directory filter
    #[serde(default)]
    pub cwd: Option<String>,

    /// Optional pagination cursor from previous response
    #[serde(default)]
    pub cursor: Option<String>,
}

/// A session entry in a session/list response (ACP spec)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionListEntry {
    /// Unique session identifier
    pub session_id: String,

    /// Working directory for this session
    pub cwd: String,

    /// Human-readable title
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// Last activity timestamp (ISO 8601)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated_at: Option<String>,

    /// Agent-specific metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "_meta")]
    pub meta: Option<serde_json::Value>,
}

/// Result of session/list request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionListResult {
    /// List of available sessions
    pub sessions: Vec<SessionListEntry>,

    /// Pagination cursor for next page (absent when no more results)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_cursor: Option<String>,
}

// ============================================================================
// Session/Resume Types
// ============================================================================

/// Parameters for session/resume request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionResumeParams {
    /// The session ID to resume
    pub session_id: String,

    /// The connector ID where the session exists (optional; resolved automatically if omitted)
    #[serde(default)]
    pub connector_id: Option<String>,

    /// Optional working directory (standard ACP field sent by clients like Zed)
    #[serde(default)]
    pub cwd: Option<String>,

    /// Optional MCP server configurations (standard ACP field)
    #[serde(default)]
    pub mcp_servers: Option<Vec<serde_json::Value>>,
}

/// Result of session/resume request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionResumeResult {
    /// The client-facing session ID
    pub session_id: String,

    /// Optional title for the session
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// The connector handling this session
    pub connector_id: String,

    /// When the session was created (ISO 8601 format)
    pub created_at: String,

    /// Available models and current model
    #[serde(skip_serializing_if = "Option::is_none")]
    pub models: Option<dirigent_protocol::SessionModelState>,

    /// Available modes and current mode
    #[serde(skip_serializing_if = "Option::is_none")]
    pub modes: Option<dirigent_protocol::SessionModeState>,

    /// Configuration options
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config_options: Option<Vec<crate::sse::ConfigOption>>,
}

// ============================================================================
// Session/Prompt Types
// ============================================================================

/// Parameters for session/prompt request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionPromptParams {
    /// The session ID to send the prompt to
    pub session_id: String,

    /// The prompt content (array of content blocks)
    pub prompt: PromptContent,
}

/// Content for a prompt - either simple text or structured blocks
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PromptContent {
    /// Simple text content
    Text(String),

    /// Structured content blocks
    Blocks(Vec<ContentBlock>),
}

impl PromptContent {
    /// Convert to plain text representation
    pub fn to_text(&self) -> String {
        match self {
            PromptContent::Text(text) => text.clone(),
            PromptContent::Blocks(blocks) => blocks
                .iter()
                .filter_map(|b| {
                    if let ContentBlock::Text { text } = b {
                        Some(text.clone())
                    } else {
                        None
                    }
                })
                .collect::<Vec<_>>()
                .join("\n"),
        }
    }
}

/// A content block in a prompt
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ContentBlock {
    /// Text content
    #[serde(rename = "text")]
    Text { text: String },

    /// Image content (base64 encoded)
    #[serde(rename = "image")]
    Image { data: String, media_type: String },
}

/// Result of session/prompt request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionPromptResult {
    /// The reason the turn stopped
    pub stop_reason: String,
}

// ============================================================================
// Session/Cancel Types
// ============================================================================

/// Parameters for session/cancel request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCancelParams {
    /// The session ID to cancel
    pub session_id: String,
}

/// Result of session/cancel request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCancelResult {
    /// Whether the cancellation was successful
    pub cancelled: bool,
}

// ============================================================================
// Session/Close Types
// ============================================================================

/// Parameters for session/close request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCloseParams {
    /// The session ID to close
    pub session_id: String,
}

/// Result of session/close request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionCloseResult {
    /// Whether the session was successfully closed
    pub closed: bool,
}

// ============================================================================
// Session/SetMode and Session/SetModel Types
// ============================================================================

/// Parameters for session/set_mode request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionSetModeParams {
    /// The session ID to update
    pub session_id: String,

    /// The mode ID to switch to
    pub mode_id: String,
}

/// Parameters for session/set_model request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionSetModelParams {
    /// The session ID to update
    pub session_id: String,

    /// The model ID to switch to
    pub model_id: String,
}

/// Parameters for session/set_config_option request
///
/// This is the unified way to set configuration options (modes, models, etc.)
/// for clients using the new config_options system.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionSetConfigOptionParams {
    /// The session ID to update
    pub session_id: String,

    /// The config option ID (e.g., "mode", "model")
    pub config_id: String,

    /// The value to set
    pub value: String,
}

// ============================================================================
// ConnectorOperations Trait
// ============================================================================

/// Trait for connector operations that will be implemented by the web server
///
/// This trait abstracts the operations that require access to `CoreHandle`,
/// allowing the RPC handler to remain decoupled from `dirigent_core`.
/// The web server implements this trait to bridge between the ACP server
/// and the actual connector implementations.
#[async_trait::async_trait]
pub trait ConnectorOperations: Send + Sync {
    /// Create a new session on the specified connector
    async fn create_session(
        &self,
        connector_id: &str,
        cwd: Option<String>,
        ownership: dirigent_protocol::SessionOwnership,
    ) -> Result<SessionInfo, AcpServerError>;

    /// Load an existing session from a connector
    async fn load_session(
        &self,
        connector_id: &str,
        session_id: &str,
        cwd: Option<String>,
        mcp_servers: Option<serde_json::Value>,
    ) -> Result<SessionInfo, AcpServerError>;

    /// Send a message to a session and wait for completion
    async fn send_message(
        &self,
        connector_id: &str,
        session_id: &str,
        text: String,
    ) -> Result<String, AcpServerError>;

    /// Cancel active generation on a session
    async fn cancel_generation(
        &self,
        connector_id: &str,
        session_id: &str,
    ) -> Result<(), AcpServerError>;

    /// List all available connectors
    async fn list_connectors(&self) -> Result<Vec<ConnectorInfo>, AcpServerError>;

    /// Get the default connector ID (if configured)
    async fn default_connector_id(&self) -> Option<String>;

    /// Get available commands/tools from a connector
    async fn get_connector_commands(
        &self,
        connector_id: &str,
    ) -> Result<Vec<crate::sse::SlashCommand>, AcpServerError>;

    /// Send an agent response back to a connector
    async fn send_agent_response(
        &self,
        connector_id: &str,
        request_id: serde_json::Value,
        response: serde_json::Value,
    ) -> Result<(), AcpServerError>;

    /// Get session metadata (models/modes) from a session
    async fn get_session_metadata(
        &self,
        connector_id: &str,
        session_id: &str,
    ) -> Result<
        (
            Option<dirigent_protocol::SessionModelState>,
            Option<dirigent_protocol::SessionModeState>,
        ),
        AcpServerError,
    >;

    /// Set the session mode
    async fn set_session_mode(
        &self,
        connector_id: &str,
        session_id: &str,
        mode_id: &str,
    ) -> Result<(), AcpServerError>;

    /// Set the session model
    async fn set_session_model(
        &self,
        connector_id: &str,
        session_id: &str,
        model_id: &str,
    ) -> Result<(), AcpServerError>;

    /// Get the agent type for a connector (for mode/model mapping)
    async fn get_connector_agent_type(
        &self,
        connector_id: &str,
    ) -> Result<Option<String>, AcpServerError>;

    /// List all sessions on a connector
    async fn list_sessions(
        &self,
        connector_id: &str,
    ) -> Result<Vec<SessionInfo>, AcpServerError>;

    /// Look up which connector owns a session by its session ID.
    /// Uses archivist to find the connector. Returns connector_id (not UID).
    /// Default: returns None (no archivist available).
    async fn resolve_session_connector(&self, _session_id: &str) -> Option<String> {
        None
    }

    /// List sessions across all connectors (archivist-backed).
    /// Used when the resolved connector is a gateway to provide cross-connector view.
    /// Default: returns empty vec (no archivist available).
    async fn list_all_sessions(&self) -> Result<Vec<SessionInfo>, AcpServerError> {
        Ok(vec![])
    }
}
