//! 对话管线执行器（被 HTTP 层直接调用）
//!
//! 为什么不在 AiPlugin 里执行：HTTP 需要同步响应，而 Plugin trait 的
//! handle_message 是异步消息系统，不便返回同步结果。改用 HTTP 层在
//! spawn_blocking 里直接调用此模块，Arc<Mutex> 共享状态。

use crate::core::message::{ChatInput, ChatRequest, ChatResponse};
use crate::plugins::ai::backend::AiBackend;
use crate::plugins::ai::model_manager::ModelManager;
use anyhow::Result;
use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, Mutex};

/// 会话上下文
pub struct SessionContext {
    pub persona_prompt: String,
    pub history: Vec<(String, String)>,
}

/// 对话管线执行器（共享状态）
pub struct ChatPipeline {
    pub backend: Mutex<Box<dyn AiBackend>>,
    pub models: Arc<Mutex<ModelManager>>,
    pub sessions: Mutex<HashMap<String, SessionContext>>,
    pub context_window: usize,
    pub tmp_dir: std::path::PathBuf,
    /// 互斥锁：同一时刻只允许一个回合
    pub busy: Mutex<()>,
}

impl ChatPipeline {
    pub fn new(
        backend: Box<dyn AiBackend>,
        models: ModelManager,
        tmp_dir: std::path::PathBuf,
        context_window: usize,
    ) -> Arc<Self> {
        Arc::new(Self {
            backend: Mutex::new(backend),
            models: Arc::new(Mutex::new(models)),
            sessions: Mutex::new(HashMap::new()),
            context_window,
            tmp_dir,
            busy: Mutex::new(()),
        })
    }

    /// 执行对话回合（同步阻塞，调用方应在 spawn_blocking 里调）
    pub fn run(&self, req: &ChatRequest) -> ChatResponse {
        // 互斥：拿不到锁说明有进行中的回合
        let _guard = match self.busy.try_lock() {
            Ok(g) => g,
            Err(_) => {
                return ChatResponse {
                    session_id: req.session_id.clone(),
                    transcription: None,
                    reply_text: String::new(),
                    reply_audio_path: None,
                    error: Some("设备忙，上一个回合未结束".to_string()),
                }
            }
        };

        let result = self.run_pipeline(req);

        match result {
            Ok((transcription, reply_text, audio_path)) => {
                // 更新会话上下文
                if let Ok(mut sessions) = self.sessions.lock() {
                    let session = sessions
                        .entry(req.session_id.clone())
                        .or_insert_with(|| SessionContext {
                            persona_prompt: req.persona_prompt.clone(),
                            history: Vec::new(),
                        });
                    if session.persona_prompt != req.persona_prompt {
                        session.persona_prompt = req.persona_prompt.clone();
                        session.history.clear();
                    }
                    if let Some(t) = &transcription {
                        session.history.push(("user".to_string(), t.clone()));
                    }
                    session.history.push(("assistant".to_string(), reply_text.clone()));
                    let max = self.context_window * 2;
                    if session.history.len() > max {
                        let drain = session.history.len() - max;
                        session.history.drain(..drain);
                    }
                }
                ChatResponse {
                    session_id: req.session_id.clone(),
                    transcription,
                    reply_text,
                    reply_audio_path: audio_path,
                    error: None,
                }
            }
            Err(e) => ChatResponse {
                session_id: req.session_id.clone(),
                transcription: None,
                reply_text: String::new(),
                reply_audio_path: None,
                error: Some(e.to_string()),
            },
        }
    }

    fn run_pipeline(&self, req: &ChatRequest) -> Result<(Option<String>, String, Option<String>)> {
        // 1. ASR
        let transcription = match &req.input {
            ChatInput::Text { content } => Some(content.clone()),
            ChatInput::Audio { path } => {
                let models = self.models.lock().unwrap();
                let asr_model = models.active_asr_model_path()?;
                drop(models);
                let backend = self.backend.lock().unwrap();
                Some(backend.asr(Path::new(path), &asr_model)?)
            }
        };

        let user_text = transcription
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("ASR 返回空结果"))?;
        if user_text.trim().is_empty() {
            anyhow::bail!("输入为空（ASR 未识别到语音）");
        }

        // 2. LLM
        let models = self.models.lock().unwrap();
        let llm_model = models.active_llm_model_path()?;
        drop(models);

        let history: Vec<(String, String)> = {
            let sessions = self.sessions.lock().unwrap();
            sessions
                .get(&req.session_id)
                .map(|s| s.history.clone())
                .unwrap_or_default()
        };

        let backend = self.backend.lock().unwrap();
        let reply_text = backend.llm_chat(
            &llm_model,
            &req.persona_prompt,
            user_text,
            &history,
            req.max_tokens,
        )?;
        drop(backend);

        // 3. TTS
        let models = self.models.lock().unwrap();
        let tts_model = models.active_tts_model_path()?;
        drop(models);

        let audio_path = self.tmp_dir.join(format!(
            "tts_{}.wav",
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_millis())
                .unwrap_or(0)
        ));
        let backend = self.backend.lock().unwrap();
        backend.tts(&reply_text, &tts_model, &audio_path)?;

        Ok((
            transcription,
            reply_text,
            Some(audio_path.to_string_lossy().into_owned()),
        ))
    }

    /// 清空指定会话上下文（切角色时调用）
    pub fn clear_session(&self, session_id: &str) {
        if let Ok(mut sessions) = self.sessions.lock() {
            sessions.remove(session_id);
        }
    }
}
