use serde_json::{json, Value};
use super::types::ExternalSystem;

/// Maps FURNACE sync operations to external MCP server tool names and arguments.
///
/// Each method returns a `(&'static str, Value)` tuple of the tool name to
/// invoke on the external MCP server and the JSON arguments to pass.
pub struct ToolMapping;

impl ToolMapping {
    /// Get the tool name and arguments for creating an issue on an external system.
    pub fn map_create(
        system: &ExternalSystem,
        title: &str,
        description: &str,
        status: &str,
    ) -> (&'static str, Value) {
        match system {
            ExternalSystem::Linear => (
                "create_issue",
                json!({
                    "title": title,
                    "description": description,
                    "status": status
                }),
            ),
            ExternalSystem::Jira => (
                "create_issue",
                json!({
                    "summary": title,
                    "description": description,
                    "status": status
                }),
            ),
            ExternalSystem::Notion => (
                "create_page",
                json!({
                    "title": title,
                    "content": description,
                    "status": status
                }),
            ),
        }
    }

    /// Get the tool name and arguments for updating an issue's status.
    pub fn map_update_status(
        system: &ExternalSystem,
        external_id: &str,
        status: &str,
    ) -> (&'static str, Value) {
        match system {
            ExternalSystem::Linear => (
                "update_issue",
                json!({
                    "id": external_id,
                    "status": status
                }),
            ),
            ExternalSystem::Jira => (
                "transition_issue",
                json!({
                    "issue_key": external_id,
                    "status": status
                }),
            ),
            ExternalSystem::Notion => (
                "update_page",
                json!({
                    "page_id": external_id,
                    "status": status
                }),
            ),
        }
    }

    /// Get the tool name and arguments for fetching an issue from an external system.
    pub fn map_get(
        system: &ExternalSystem,
        external_id: &str,
    ) -> (&'static str, Value) {
        match system {
            ExternalSystem::Linear => (
                "get_issue",
                json!({ "id": external_id }),
            ),
            ExternalSystem::Jira => (
                "get_issue",
                json!({ "issue_key": external_id }),
            ),
            ExternalSystem::Notion => (
                "get_page",
                json!({ "page_id": external_id }),
            ),
        }
    }
}

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

    // ---- map_create tests ----

    #[test]
    fn test_map_create_linear() {
        let (tool, args) = ToolMapping::map_create(
            &ExternalSystem::Linear,
            "Fix login bug",
            "Users cannot log in after password reset",
            "in_progress",
        );
        assert_eq!(tool, "create_issue");
        assert_eq!(args["title"], "Fix login bug");
        assert_eq!(args["description"], "Users cannot log in after password reset");
        assert_eq!(args["status"], "in_progress");
    }

    #[test]
    fn test_map_create_jira() {
        let (tool, args) = ToolMapping::map_create(
            &ExternalSystem::Jira,
            "Fix login bug",
            "Users cannot log in after password reset",
            "In Progress",
        );
        assert_eq!(tool, "create_issue");
        // Jira uses "summary" instead of "title"
        assert_eq!(args["summary"], "Fix login bug");
        assert_eq!(args["description"], "Users cannot log in after password reset");
        assert_eq!(args["status"], "In Progress");
    }

    #[test]
    fn test_map_create_notion() {
        let (tool, args) = ToolMapping::map_create(
            &ExternalSystem::Notion,
            "Fix login bug",
            "Users cannot log in after password reset",
            "In Progress",
        );
        assert_eq!(tool, "create_page");
        assert_eq!(args["title"], "Fix login bug");
        // Notion uses "content" instead of "description"
        assert_eq!(args["content"], "Users cannot log in after password reset");
        assert_eq!(args["status"], "In Progress");
    }

    // ---- map_update_status tests ----

    #[test]
    fn test_map_update_status_linear() {
        let (tool, args) = ToolMapping::map_update_status(
            &ExternalSystem::Linear,
            "LIN-42",
            "done",
        );
        assert_eq!(tool, "update_issue");
        assert_eq!(args["id"], "LIN-42");
        assert_eq!(args["status"], "done");
    }

    #[test]
    fn test_map_update_status_jira() {
        let (tool, args) = ToolMapping::map_update_status(
            &ExternalSystem::Jira,
            "PROJ-99",
            "Done",
        );
        assert_eq!(tool, "transition_issue");
        assert_eq!(args["issue_key"], "PROJ-99");
        assert_eq!(args["status"], "Done");
    }

    #[test]
    fn test_map_update_status_notion() {
        let (tool, args) = ToolMapping::map_update_status(
            &ExternalSystem::Notion,
            "page-abc-123",
            "Done",
        );
        assert_eq!(tool, "update_page");
        assert_eq!(args["page_id"], "page-abc-123");
        assert_eq!(args["status"], "Done");
    }

    // ---- map_get tests ----

    #[test]
    fn test_map_get_linear() {
        let (tool, args) = ToolMapping::map_get(
            &ExternalSystem::Linear,
            "LIN-42",
        );
        assert_eq!(tool, "get_issue");
        assert_eq!(args["id"], "LIN-42");
    }

    #[test]
    fn test_map_get_jira() {
        let (tool, args) = ToolMapping::map_get(
            &ExternalSystem::Jira,
            "PROJ-99",
        );
        assert_eq!(tool, "get_issue");
        assert_eq!(args["issue_key"], "PROJ-99");
    }

    #[test]
    fn test_map_get_notion() {
        let (tool, args) = ToolMapping::map_get(
            &ExternalSystem::Notion,
            "page-abc-123",
        );
        assert_eq!(tool, "get_page");
        assert_eq!(args["page_id"], "page-abc-123");
    }

    // ---- cross-cutting tests ----

    #[test]
    fn test_all_create_return_valid_json_objects() {
        for system in &[ExternalSystem::Linear, ExternalSystem::Jira, ExternalSystem::Notion] {
            let (_, args) = ToolMapping::map_create(system, "t", "d", "s");
            assert!(args.is_object(), "map_create for {} should return a JSON object", system);
        }
    }

    #[test]
    fn test_all_update_return_valid_json_objects() {
        for system in &[ExternalSystem::Linear, ExternalSystem::Jira, ExternalSystem::Notion] {
            let (_, args) = ToolMapping::map_update_status(system, "id-1", "status");
            assert!(args.is_object(), "map_update_status for {} should return a JSON object", system);
        }
    }

    #[test]
    fn test_all_get_return_valid_json_objects() {
        for system in &[ExternalSystem::Linear, ExternalSystem::Jira, ExternalSystem::Notion] {
            let (_, args) = ToolMapping::map_get(system, "id-1");
            assert!(args.is_object(), "map_get for {} should return a JSON object", system);
        }
    }
}
