//! Mode and model mapping module for Gateway connector
//!
//! This module translates between Gateway's standardized modes/models and agent-specific values.
//! Gateway advertises generic modes like "simple", "dailydriver", "high" which map to agent-specific
//! values like "haiku", "sonnet", "opus" for Claude.
//!
//! # Architecture
//!
//! The Gateway connector serves as a session entry point for incoming ACP connections. When
//! sessions are transferred to target agents (Claude, Codex, Gemini), the Gateway's standardized
//! mode/model identifiers must be translated to agent-specific identifiers.
//!
//! # Mapping Direction
//!
//! - **Forward mapping** (`map_mode`, `map_model`): Gateway → Agent
//!   Used when setting mode/model on a target agent after session transfer.
//!   Example: User clicks "High" in editor → send "opus" to Claude.
//!
//! - **Reverse mapping** (`reverse_map_mode`, `reverse_map_model`): Agent → Gateway
//!   Used in legacy mode when reporting agent state back to editor.
//!   Example: Claude reports "opus" → display "High" in editor.
//!
//! # Legacy vs Config Options
//!
//! These mappings only apply in legacy mode (without Zed's `acp-beta` flag).
//! When using the new `config_options` system, values pass through unchanged.
//!
//! # Implementation
//!
//! All vendor-specific mappings are delegated to the vendor registry at
//! `dirigent_core::vendors::VENDOR_REGISTRY`. Each vendor implements the
//! `VendorInfo` trait with its specific mappings.
//!
//! # Example
//!
//! ```rust
//! use dirigent_core::connectors::gateway::mappings::{map_mode, map_model, gateway_modes, gateway_models};
//! use dirigent_core::connectors::gateway::mappings::{reverse_map_mode, reverse_map_model};
//! use dirigent_core::connectors::acp::config::ConnectorAgentType;
//!
//! // Forward mapping: Gateway mode to Claude mode
//! let result = map_mode(gateway_modes::WRITE, ConnectorAgentType::Claude);
//! assert_eq!(result.mapped_id, "acceptEdits");
//! assert!(result.warning.is_none());
//!
//! // Forward mapping: Gateway model to Claude model
//! let result = map_model(gateway_models::SIMPLE, ConnectorAgentType::Claude);
//! assert_eq!(result.mapped_id, "haiku");
//! assert!(result.warning.is_none());
//!
//! // Reverse mapping: Claude model to Gateway model
//! let result = reverse_map_model("opus", ConnectorAgentType::Claude);
//! assert_eq!(result.mapped_id, "high");
//! assert!(result.warning.is_none());
//! ```

use crate::connectors::acp::config::ConnectorAgentType;
use crate::vendors::VENDOR_REGISTRY;

/// Gateway mode identifiers advertised to clients
pub mod gateway_modes {
    /// Plan mode - agent creates execution plans but doesn't execute
    pub const PLAN: &str = "plan";
    /// Read-only mode - agent can read but not modify files
    pub const READONLY: &str = "readonly";
    /// Ask mode - agent asks before every action
    pub const ASK: &str = "ask";
    /// Write mode - agent can modify files with explicit permission
    pub const WRITE: &str = "write";
    /// YOLO mode - agent can perform any action without asking
    pub const YOLO: &str = "yolo";
}

/// Gateway model identifiers advertised to clients
pub mod gateway_models {
    /// Simple model - fast, lightweight, lower capability
    pub const SIMPLE: &str = "simple";
    /// Daily driver model - balanced performance and capability, the go-to model for everyday tasks
    pub const DAILYDRIVER: &str = "dailydriver";
    /// High model - most capable, slower, higher cost
    pub const HIGH: &str = "high";
}

/// Claude-specific mode identifiers
///
/// Note: These are re-exported for backwards compatibility.
/// Prefer using `dirigent_core::vendors::claude::modes` for new code.
pub mod claude_modes {
    pub use crate::vendors::claude::modes::*;
}

/// Claude-specific model identifiers
///
/// Note: These are re-exported for backwards compatibility.
/// Prefer using `dirigent_core::vendors::claude::models` for new code.
pub mod claude_models {
    pub use crate::vendors::claude::models::*;
}

/// Result of a mode/model mapping operation.
///
/// This type is re-exported from the vendors module for backwards compatibility.
pub use crate::vendors::MappingResult;

// =============================================================================
// FORWARD MAPPING: Gateway → Agent
// =============================================================================

/// Map a Gateway mode to an agent-specific mode.
///
/// # Arguments
///
/// * `gateway_mode` - The Gateway mode identifier to map
/// * `agent_type` - The target agent type
///
/// # Returns
///
/// A `MappingResult` containing the mapped agent-specific mode and optional warning.
///
/// # Example
///
/// ```rust
/// use dirigent_core::connectors::gateway::mappings::{map_mode, gateway_modes};
/// use dirigent_core::connectors::acp::config::ConnectorAgentType;
///
/// let result = map_mode(gateway_modes::WRITE, ConnectorAgentType::Claude);
/// assert_eq!(result.mapped_id, "acceptEdits");
/// ```
pub fn map_mode(gateway_mode: &str, agent_type: ConnectorAgentType) -> MappingResult {
    VENDOR_REGISTRY
        .get_by_agent_type(agent_type)
        .map(|vendor| vendor.map_mode(gateway_mode))
        .unwrap_or_else(|| MappingResult::exact(gateway_mode))
}

/// Map a Gateway model to an agent-specific model.
///
/// # Arguments
///
/// * `gateway_model` - The Gateway model identifier to map
/// * `agent_type` - The target agent type
///
/// # Returns
///
/// A `MappingResult` containing the mapped agent-specific model and optional warning.
///
/// # Example
///
/// ```rust
/// use dirigent_core::connectors::gateway::mappings::{map_model, gateway_models};
/// use dirigent_core::connectors::acp::config::ConnectorAgentType;
///
/// let result = map_model(gateway_models::SIMPLE, ConnectorAgentType::Claude);
/// assert_eq!(result.mapped_id, "haiku");
/// ```
pub fn map_model(gateway_model: &str, agent_type: ConnectorAgentType) -> MappingResult {
    VENDOR_REGISTRY
        .get_by_agent_type(agent_type)
        .map(|vendor| vendor.map_model(gateway_model))
        .unwrap_or_else(|| MappingResult::exact(gateway_model))
}

// =============================================================================
// REVERSE MAPPING: Agent → Gateway
// =============================================================================

/// Reverse map an agent-specific mode to a Gateway mode.
///
/// Used in legacy mode when reporting agent state back to the editor.
/// The editor only understands Gateway modes (plan, readonly, ask, write, yolo),
/// so agent-specific modes must be translated back.
///
/// # Arguments
///
/// * `agent_mode` - The agent-specific mode identifier to map
/// * `agent_type` - The agent type to map from
///
/// # Returns
///
/// A `MappingResult` containing the mapped Gateway mode and optional warning.
///
/// # Example
///
/// ```rust
/// use dirigent_core::connectors::gateway::mappings::reverse_map_mode;
/// use dirigent_core::connectors::acp::config::ConnectorAgentType;
///
/// let result = reverse_map_mode("acceptEdits", ConnectorAgentType::Claude);
/// assert_eq!(result.mapped_id, "write");
/// ```
pub fn reverse_map_mode(agent_mode: &str, agent_type: ConnectorAgentType) -> MappingResult {
    VENDOR_REGISTRY
        .get_by_agent_type(agent_type)
        .map(|vendor| vendor.reverse_map_mode(agent_mode))
        .unwrap_or_else(|| MappingResult::exact(agent_mode))
}

/// Reverse map an agent-specific model to a Gateway model.
///
/// Used in legacy mode when reporting agent state back to the editor.
/// The editor only understands Gateway models (simple, daily, high),
/// so agent-specific models must be translated back.
///
/// # Arguments
///
/// * `agent_model` - The agent-specific model identifier to map
/// * `agent_type` - The agent type to map from
///
/// # Returns
///
/// A `MappingResult` containing the mapped Gateway model and optional warning.
///
/// # Example
///
/// ```rust
/// use dirigent_core::connectors::gateway::mappings::reverse_map_model;
/// use dirigent_core::connectors::acp::config::ConnectorAgentType;
///
/// let result = reverse_map_model("opus", ConnectorAgentType::Claude);
/// assert_eq!(result.mapped_id, "high");
/// ```
pub fn reverse_map_model(agent_model: &str, agent_type: ConnectorAgentType) -> MappingResult {
    VENDOR_REGISTRY
        .get_by_agent_type(agent_type)
        .map(|vendor| vendor.reverse_map_model(agent_model))
        .unwrap_or_else(|| MappingResult::exact(agent_model))
}

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

    // ==========================================================================
    // MappingResult tests
    // ==========================================================================

    #[test]
    fn test_mapping_result_exact() {
        let result = MappingResult::exact("test");
        assert_eq!(result.mapped_id, "test");
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_mapping_result_approximate() {
        let result = MappingResult::approximate("test", "warning message");
        assert_eq!(result.mapped_id, "test");
        assert_eq!(result.warning, Some("warning message".to_string()));
    }

    // ==========================================================================
    // Forward mapping tests: Gateway → Agent
    // ==========================================================================

    #[test]
    fn test_map_mode_claude_exact() {
        let result = map_mode(gateway_modes::ASK, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::DEFAULT);
        assert!(result.warning.is_none());

        let result = map_mode(gateway_modes::PLAN, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::PLAN);
        assert!(result.warning.is_none());

        let result = map_mode(gateway_modes::WRITE, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::ACCEPT_EDITS);
        assert!(result.warning.is_none());

        let result = map_mode(gateway_modes::YOLO, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::BYPASS_PERMISSIONS);
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_map_mode_claude_approximate() {
        let result = map_mode(gateway_modes::READONLY, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::PLAN);
        assert!(result.warning.is_some());
        assert!(result.warning.unwrap().contains("readonly"));
    }

    #[test]
    fn test_map_mode_claude_unknown() {
        let result = map_mode("unknown-mode", ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_modes::DEFAULT);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_map_mode_custom_passthrough() {
        let result = map_mode("custom-mode", ConnectorAgentType::Custom);
        assert_eq!(result.mapped_id, "custom-mode");
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_map_mode_codex_passthrough() {
        // Codex currently passes through with warning
        let result = map_mode(gateway_modes::ASK, ConnectorAgentType::Codex);
        assert_eq!(result.mapped_id, gateway_modes::ASK);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_map_mode_gemini_passthrough() {
        // Gemini currently passes through with warning
        let result = map_mode(gateway_modes::ASK, ConnectorAgentType::Gemini);
        assert_eq!(result.mapped_id, gateway_modes::ASK);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_map_model_claude_exact() {
        let result = map_model(gateway_models::SIMPLE, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_models::HAIKU);
        assert!(result.warning.is_none());

        let result = map_model(gateway_models::DAILYDRIVER, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_models::SONNET);
        assert!(result.warning.is_none());

        let result = map_model(gateway_models::HIGH, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_models::OPUS);
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_map_model_claude_unknown() {
        let result = map_model("unknown-model", ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, claude_models::SONNET);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_map_model_custom_passthrough() {
        let result = map_model("custom-model", ConnectorAgentType::Custom);
        assert_eq!(result.mapped_id, "custom-model");
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_map_model_codex_passthrough() {
        let result = map_model(gateway_models::SIMPLE, ConnectorAgentType::Codex);
        assert_eq!(result.mapped_id, gateway_models::SIMPLE);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_map_model_gemini_passthrough() {
        let result = map_model(gateway_models::SIMPLE, ConnectorAgentType::Gemini);
        assert_eq!(result.mapped_id, gateway_models::SIMPLE);
        assert!(result.warning.is_some());
    }

    // ==========================================================================
    // Reverse mapping tests: Agent → Gateway
    // ==========================================================================

    #[test]
    fn test_reverse_map_mode_claude_exact() {
        let result = reverse_map_mode(claude_modes::DEFAULT, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_modes::ASK);
        assert!(result.warning.is_none());

        let result = reverse_map_mode(claude_modes::PLAN, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_modes::PLAN);
        assert!(result.warning.is_none());

        let result = reverse_map_mode(claude_modes::ACCEPT_EDITS, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_modes::WRITE);
        assert!(result.warning.is_none());

        let result = reverse_map_mode(claude_modes::BYPASS_PERMISSIONS, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_modes::YOLO);
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_reverse_map_mode_claude_unknown() {
        let result = reverse_map_mode("unknown-mode", ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_modes::ASK);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_reverse_map_mode_custom_passthrough() {
        let result = reverse_map_mode("custom-mode", ConnectorAgentType::Custom);
        assert_eq!(result.mapped_id, "custom-mode");
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_reverse_map_model_claude_exact() {
        let result = reverse_map_model(claude_models::HAIKU, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_models::SIMPLE);
        assert!(result.warning.is_none());

        let result = reverse_map_model(claude_models::SONNET, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_models::DAILYDRIVER);
        assert!(result.warning.is_none());

        let result = reverse_map_model(claude_models::OPUS, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_models::HIGH);
        assert!(result.warning.is_none());

        // Claude's "default" also maps to dailydriver
        let result = reverse_map_model(claude_models::DEFAULT, ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_models::DAILYDRIVER);
        assert!(result.warning.is_none());
    }

    #[test]
    fn test_reverse_map_model_claude_unknown() {
        let result = reverse_map_model("unknown-model", ConnectorAgentType::Claude);
        assert_eq!(result.mapped_id, gateway_models::DAILYDRIVER);
        assert!(result.warning.is_some());
    }

    #[test]
    fn test_reverse_map_model_custom_passthrough() {
        let result = reverse_map_model("custom-model", ConnectorAgentType::Custom);
        assert_eq!(result.mapped_id, "custom-model");
        assert!(result.warning.is_none());
    }

    // ==========================================================================
    // Round-trip tests: Gateway → Agent → Gateway
    // ==========================================================================

    #[test]
    fn test_mode_round_trip_claude() {
        // Test that we can map Gateway → Claude → Gateway without losing meaning
        for (gateway_mode, expected_claude, expected_gateway) in [
            (
                gateway_modes::ASK,
                claude_modes::DEFAULT,
                gateway_modes::ASK,
            ),
            (gateway_modes::PLAN, claude_modes::PLAN, gateway_modes::PLAN),
            (
                gateway_modes::WRITE,
                claude_modes::ACCEPT_EDITS,
                gateway_modes::WRITE,
            ),
            (
                gateway_modes::YOLO,
                claude_modes::BYPASS_PERMISSIONS,
                gateway_modes::YOLO,
            ),
        ] {
            let forward = map_mode(gateway_mode, ConnectorAgentType::Claude);
            assert_eq!(
                forward.mapped_id, expected_claude,
                "Forward mapping failed for {}",
                gateway_mode
            );

            let reverse = reverse_map_mode(&forward.mapped_id, ConnectorAgentType::Claude);
            assert_eq!(
                reverse.mapped_id, expected_gateway,
                "Reverse mapping failed for {}",
                gateway_mode
            );
        }
    }

    #[test]
    fn test_model_round_trip_claude() {
        // Test that we can map Gateway → Claude → Gateway without losing meaning
        for (gateway_model, expected_claude, expected_gateway) in [
            (
                gateway_models::SIMPLE,
                claude_models::HAIKU,
                gateway_models::SIMPLE,
            ),
            (
                gateway_models::DAILYDRIVER,
                claude_models::SONNET,
                gateway_models::DAILYDRIVER,
            ),
            (
                gateway_models::HIGH,
                claude_models::OPUS,
                gateway_models::HIGH,
            ),
        ] {
            let forward = map_model(gateway_model, ConnectorAgentType::Claude);
            assert_eq!(
                forward.mapped_id, expected_claude,
                "Forward mapping failed for {}",
                gateway_model
            );

            let reverse = reverse_map_model(&forward.mapped_id, ConnectorAgentType::Claude);
            assert_eq!(
                reverse.mapped_id, expected_gateway,
                "Reverse mapping failed for {}",
                gateway_model
            );
        }
    }
}
