//! Request and response transformation for Anthropic OAuth
//!
//! Handles:
//! - Tool name prefixing/unprefixing (mcp_ prefix)
//! - System prompt injection (Claude Code identity)
//! - System prompt sanitization (OpenCode → Claude Code)

use serde_json::{json, Value};

use super::TOOL_PREFIX;

/// The system prompt prefix required for Claude Code OAuth
const CLAUDE_CODE_SYSTEM_PREFIX: &str = "You are Claude Code, Anthropic's official CLI for Claude.";

/// Request transformer for Anthropic OAuth compatibility
pub struct RequestTransformer;

impl RequestTransformer {
    /// Transform request body for OAuth compatibility
    ///
    /// - Injects Claude Code system prompt prefix
    /// - Prefixes tool names with `mcp_`
    /// - Ensures tools array exists (even if empty)
    /// - Sanitizes system prompts (replaces "OpenCode" references)
    pub fn transform_request(body: &str) -> Option<String> {
        let mut parsed: Value = serde_json::from_str(body).ok()?;

        // Inject/modify system prompt with Claude Code prefix
        Self::inject_system_prompt(&mut parsed);

        // Ensure tools array exists (required for OAuth)
        if parsed.get("tools").is_none() {
            parsed["tools"] = json!([]);
        }

        // Prefix tool definitions
        if let Some(tools) = parsed.get_mut("tools").and_then(|t| t.as_array_mut()) {
            for tool in tools {
                if let Some(name) = tool.get_mut("name").and_then(|n| n.as_str()) {
                    tool["name"] = Value::String(format!("{}{}", TOOL_PREFIX, name));
                }
            }
        }

        // Prefix tool_use blocks in messages
        if let Some(messages) = parsed.get_mut("messages").and_then(|m| m.as_array_mut()) {
            for msg in messages {
                if let Some(content) = msg.get_mut("content").and_then(|c| c.as_array_mut()) {
                    for block in content {
                        if block.get("type").and_then(|t| t.as_str()) == Some("tool_use") {
                            if let Some(name) = block.get_mut("name").and_then(|n| n.as_str()) {
                                block["name"] = Value::String(format!("{}{}", TOOL_PREFIX, name));
                            }
                        }
                    }
                }
            }
        }

        serde_json::to_string(&parsed).ok()
    }

    /// Inject Claude Code system prompt prefix
    fn inject_system_prompt(parsed: &mut Value) {
        match parsed.get_mut("system") {
            Some(Value::String(s)) => {
                // Sanitize and prepend
                let sanitized = s
                    .replace("OpenCode", "Claude Code")
                    .replace("opencode", "Claude");
                *s = format!("{}\n\n{}", CLAUDE_CODE_SYSTEM_PREFIX, sanitized);
            }
            Some(Value::Array(arr)) => {
                // Sanitize existing items
                for item in arr.iter_mut() {
                    if let Some(text) = item.get_mut("text").and_then(|t| t.as_str()) {
                        let sanitized = text
                            .replace("OpenCode", "Claude Code")
                            .replace("opencode", "Claude");
                        item["text"] = Value::String(sanitized);
                    }
                }
                // Prepend our prefix
                arr.insert(0, json!({
                    "type": "text",
                    "text": CLAUDE_CODE_SYSTEM_PREFIX
                }));
            }
            None => {
                // No system prompt, add one
                parsed["system"] = Value::String(CLAUDE_CODE_SYSTEM_PREFIX.to_string());
            }
            _ => {}
        }
    }

    /// Transform response chunk to unprefix tool names
    ///
    /// Removes the `mcp_` prefix from tool names in streaming responses
    pub fn transform_response(chunk: &str) -> String {
        let mut result = chunk.to_string();

        // Handle with space after colon: "name": "mcp_xxx"
        while let Some(pos) = result.find(&format!("\"name\": \"{}", TOOL_PREFIX)) {
            let start = pos + "\"name\": \"".len();
            result = format!(
                "{}{}",
                &result[..start],
                &result[start + TOOL_PREFIX.len()..]
            );
        }

        // Handle without space after colon
        while let Some(pos) = result.find(&format!("\"name\":\"{}", TOOL_PREFIX)) {
            let start = pos + "\"name\":\"".len();
            result = format!(
                "{}{}",
                &result[..start],
                &result[start + TOOL_PREFIX.len()..]
            );
        }

        result
    }
}

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

    #[test]
    fn test_transform_request_tools() {
        let body = r#"{"tools":[{"name":"read_file","description":"Read a file"}]}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("mcp_read_file"));
        // Should also inject system prompt
        assert!(transformed.contains("Claude Code"));
    }

    #[test]
    fn test_transform_request_tool_use() {
        let body = r#"{"messages":[{"role":"assistant","content":[{"type":"tool_use","name":"read_file","id":"123"}]}]}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("mcp_read_file"));
    }

    #[test]
    fn test_injects_system_prompt() {
        let body = r#"{"messages":[{"role":"user","content":"Hello"}]}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("You are Claude Code"));
    }

    #[test]
    fn test_injects_empty_tools() {
        let body = r#"{"messages":[{"role":"user","content":"Hello"}]}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("\"tools\":[]"));
    }

    #[test]
    fn test_sanitize_system_string() {
        let body = r#"{"system":"You are OpenCode assistant"}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("Claude Code"));
        assert!(!transformed.contains("OpenCode"));
    }

    #[test]
    fn test_sanitize_system_array() {
        let body = r#"{"system":[{"type":"text","text":"Welcome to opencode"}]}"#;
        let transformed = RequestTransformer::transform_request(body).unwrap();
        assert!(transformed.contains("Claude"));
        assert!(!transformed.contains("opencode"));
    }

    #[test]
    fn test_transform_response() {
        let chunk = r#"{"name": "mcp_read_file", "type": "tool_use"}"#;
        let transformed = RequestTransformer::transform_response(chunk);
        assert!(transformed.contains("\"name\": \"read_file\""));
        assert!(!transformed.contains("mcp_"));
    }

    #[test]
    fn test_transform_response_no_space() {
        let chunk = r#"{"name":"mcp_write_file"}"#;
        let transformed = RequestTransformer::transform_response(chunk);
        assert!(transformed.contains("\"name\":\"write_file\""));
    }
}
