use crate::config::{AgentWorkerConfig, Config, LlmProviderConfig};
use crate::types::{LlmResponse, LlmToolCall, ToolDefinition};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Provider 注册表
#[derive(Clone)]
pub struct ProviderRegistry {
    pub(crate) providers: HashMap<String, LlmProviderConfig>,
    default_provider: String,
}

impl ProviderRegistry {
    pub fn new(config: &Config) -> Self {
        let mut providers = config.providers.clone();
        let default_provider = config
            .default_provider
            .clone()
            .unwrap_or_else(|| "deepseek".to_string());

        // If no providers configured but deepseek_api_key exists, create default
        if providers.is_empty() {
            if let Some(ref key) = config.deepseek_api_key {
                providers.insert(
                    "deepseek".to_string(),
                    LlmProviderConfig {
                        name: "deepseek".to_string(),
                        base_url: "https://api.deepseek.com".to_string(),
                        api_key: key.clone(),
                        model: "deepseek-v4-flash".to_string(),
                        api_format: Some("openai".to_string()),
                        max_tokens: None,
                        temperature: None,
                        params: None,
                    },
                );
            }
        }

        Self {
            providers,
            default_provider,
        }
    }

    pub fn get_client(&self, provider_name: Option<&str>) -> Option<LlmClient> {
        let name = provider_name.unwrap_or(&self.default_provider);
        let provider = self.providers.get(name)?;
        Some(LlmClient::from_config(provider))
    }

    pub fn get_client_for_task(
        &self,
        task_type: &str,
        workers: &[AgentWorkerConfig],
    ) -> Option<LlmClient> {
        let provider_name = workers
            .iter()
            .find(|w| w.task_type == task_type)
            .map(|w| w.provider.as_str());
        self.get_client(provider_name)
    }
}

/// LLM 客户端
pub struct LlmClient {
    client: Client,
    api_key: String,
    model: String,
    base_url: String,
}

impl LlmClient {
    /// 创建新的 LLM 客户端（legacy）
    pub fn new(api_key: String) -> Self {
        Self {
            client: Client::builder()
                .timeout(std::time::Duration::from_secs(120))
                .connect_timeout(std::time::Duration::from_secs(10))
                .build()
                .expect("Failed to build HTTP client"),
            api_key,
            model: "deepseek-v4-flash".to_string(),
            base_url: "https://api.deepseek.com".to_string(),
        }
    }

    /// 从配置创建 LLM 客户端
    pub fn from_config(config: &LlmProviderConfig) -> Self {
        Self {
            client: Client::builder()
                .timeout(std::time::Duration::from_secs(120))
                .connect_timeout(std::time::Duration::from_secs(10))
                .build()
                .expect("Failed to build HTTP client"),
            api_key: config.api_key.clone(),
            model: config.model.clone(),
            base_url: config.base_url.clone(),
        }
    }

    /// 发送消息并获取响应（支持工具调用）
    pub async fn chat(
        &self,
        messages: Vec<ChatMessage>,
        tools: Option<Vec<ToolDefinition>>,
        temperature: f64,
        max_tokens: u32,
    ) -> Result<LlmResponse, LlmError> {
        let mut body = serde_json::json!({
            "model": self.model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens
        });

        // 添加工具定义
        if let Some(tools) = tools {
            let tools_json: Vec<serde_json::Value> = tools
                .iter()
                .map(|t| {
                    serde_json::json!({
                        "type": "function",
                        "function": {
                            "name": t.name,
                            "description": t.description,
                            "parameters": t.parameters
                        }
                    })
                })
                .collect();
            body.as_object_mut()
                .unwrap()
                .insert("tools".to_string(), serde_json::json!(tools_json));
        }

        let res = self
            .client
            .post(format!("{}/chat/completions", self.base_url))
            .header("Authorization", format!("Bearer {}", self.api_key))
            .json(&body)
            .send()
            .await
            .map_err(|e| LlmError::RequestFailed(e.to_string()))?;

        let status = res.status();
        if !status.is_success() {
            let body_text = res.text().await.unwrap_or_default();
            return Err(LlmError::ApiError {
                status: status.as_u16(),
                message: body_text,
            });
        }

        let body: serde_json::Value = res
            .json()
            .await
            .map_err(|e| LlmError::ParseError(e.to_string()))?;

        // 解析响应
        let message = &body["choices"][0]["message"];
        let content = message["content"].as_str().map(String::from);
        let finish_reason = body["choices"][0]["finish_reason"]
            .as_str()
            .unwrap_or("stop")
            .to_string();

        // 解析工具调用
        let tool_calls = if let Some(calls) = message["tool_calls"].as_array() {
            calls
                .iter()
                .filter_map(|call| {
                    let id = call["id"].as_str()?.to_string();
                    let name = call["function"]["name"].as_str()?.to_string();
                    let args_str = call["function"]["arguments"].as_str()?;
                    let arguments = serde_json::from_str(args_str).ok()?;
                    Some(LlmToolCall {
                        id,
                        name,
                        arguments,
                    })
                })
                .collect()
        } else {
            Vec::new()
        };

        Ok(LlmResponse {
            content,
            tool_calls,
            finish_reason,
        })
    }
}

/// 聊天消息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
    pub role: String,
    pub content: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_calls: Option<Vec<LlmToolCall>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_call_id: Option<String>,
}

impl ChatMessage {
    pub fn system(content: impl Into<String>) -> Self {
        Self {
            role: "system".to_string(),
            content: Some(content.into()),
            tool_calls: None,
            tool_call_id: None,
        }
    }

    pub fn user(content: impl Into<String>) -> Self {
        Self {
            role: "user".to_string(),
            content: Some(content.into()),
            tool_calls: None,
            tool_call_id: None,
        }
    }

    pub fn assistant(
        content: Option<String>,
        tool_calls: Option<Vec<LlmToolCall>>,
    ) -> Self {
        Self {
            role: "assistant".to_string(),
            content,
            tool_calls,
            tool_call_id: None,
        }
    }

    pub fn tool(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
        Self {
            role: "tool".to_string(),
            content: Some(content.into()),
            tool_calls: None,
            tool_call_id: Some(tool_call_id.into()),
        }
    }
}

/// LLM 错误类型
#[derive(Debug)]
pub enum LlmError {
    RequestFailed(String),
    ApiError { status: u16, message: String },
    ParseError(String),
}

impl std::fmt::Display for LlmError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LlmError::RequestFailed(e) => write!(f, "Request failed: {}", e),
            LlmError::ApiError { status, message } => {
                write!(f, "API error {}: {}", status, message)
            }
            LlmError::ParseError(e) => write!(f, "Parse error: {}", e),
        }
    }
}
