use std::sync::Arc;

use crate::agent::{AgentError, AgentLoop, CompositeSystemPromptProvider};
use crate::config::LLMProviderConfig;
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
use crate::gateway::todo_prompt_provider::TodoPromptProvider;
use crate::skills::{SkillPromptProvider, SkillRuntime};
use crate::storage::persistent_session_id;
use crate::storage::PromptInjectionRepository;
use crate::tools::{ToolContext, ToolRegistry};

#[derive(Clone)]
pub(crate) struct AgentFactory {
    tools: Arc<ToolRegistry>,
    skills: Arc<SkillRuntime>,
    reinject_every: usize,
    prompt_repository: Arc<dyn PromptInjectionRepository>,
}

pub(crate) struct AgentBuildRequest<'a> {
    pub(crate) channel_name: &'a str,
    pub(crate) session_chat_id: &'a str,
    pub(crate) notification_chat_id: Option<&'a str>,
    pub(crate) sender_id: Option<&'a str>,
    pub(crate) message_id: Option<&'a str>,
    pub(crate) provider_config: LLMProviderConfig,
    /// 当前话题 ID（可选）：用于 todo 等按 topic 隔离的工具
    pub(crate) topic_id: Option<String>,
    /// 取消信号接收端（可选）：Agent 在每次迭代时检查是否被取消
    pub(crate) cancel_token: Option<tokio::sync::watch::Receiver<()>>,
}

impl AgentFactory {
    pub(crate) fn new(
        tools: Arc<ToolRegistry>,
        skills: Arc<SkillRuntime>,
        reinject_every: usize,
        prompt_repository: Arc<dyn PromptInjectionRepository>,
    ) -> Self {
        Self {
            tools,
            skills,
            reinject_every,
            prompt_repository,
        }
    }

    pub(crate) fn create(&self, request: AgentBuildRequest<'_>) -> Result<AgentLoop, AgentError> {
        let session_id = persistent_session_id(request.channel_name, request.session_chat_id);

        // 创建组合的系统提示词提供者
        let system_prompt_provider = Arc::new(CompositeSystemPromptProvider::new(vec![
            Box::new(AgentPromptProvider::new(
                self.reinject_every,
                request.provider_config.clone(),
                self.prompt_repository.clone(),
            )),
            Box::new(SkillPromptProvider::new(self.skills.clone())),
            Box::new(TodoPromptProvider::new()),
        ]));

        AgentLoop::with_tools_and_system_prompt_provider(
            request.provider_config,
            self.tools.clone(),
            system_prompt_provider,
            Some(self.skills.clone()),
        )
        .map(|agent| {
            // notification_chat_id 优先，否则使用 session_chat_id
            let tool_chat_id = request
                .notification_chat_id
                .unwrap_or(request.session_chat_id);
            let mut agent = agent.with_tool_context(ToolContext {
                channel_name: Some(request.channel_name.to_string()),
                sender_id: request.sender_id.map(str::to_string),
                chat_id: Some(tool_chat_id.to_string()),
                session_id: Some(session_id),
                topic_id: request.topic_id.clone(),
                message_id: request.message_id.map(str::to_string),
                message_seq: None,
                subagent_description: None,
            });
            // 如果有取消信号接收端，注入 Agent
            if let Some(token) = request.cancel_token {
                agent = agent.with_cancel_token(token);
            }
            agent
        })
    }
}
