//! B-260615-09: generating 状态 RAII guard —— 从 agentic/mod.rs 抽离(重构第一批,纯结构搬迁)。
//!
//! 行为零变更:仅文件位置移动,逻辑/字段/语义完全保留。
//! 调用方(agentic/mod.rs run_agentic_loop)经 `use super::guard::GeneratingGuard;` 复用。

use std::sync::Arc;

use tauri::{AppHandle, Emitter, Manager};
use tokio::sync::Mutex;

use crate::commands::ai::{AiChatEvent, AiSession};

use super::conv_state::{self, ConvState};

/// generating 复位 RAII guard,取代散布的手动 `session.generating = false`。
///
/// 两路复位:
/// - 正常路径:exit 点显式 `reset().await` 即时复位(emit 前调,保证"复位→emit"顺序,
///   前端收事件时后端已可接下一条)。
/// - 异常路径(panic/未走正常 return):Drop 兜底 spawn 复位,防 generating 永真卡死前端。
///
/// 注:try_continue_agent_loop 不用 guard——其 should_continue=false 路径需保持
/// generating=true(审批等待态),全函数 guard 会误复位;该函数单点 provider-Err 复位保持手动。
///
/// F-260616-09 B 批4:guard 持 `conv_id`,复位改写 `session.conv(&conv_id).generating = false`
/// (per-conv 唯一真相源)。顶层 `session.generating` 字段已在批4 删除,reset/Drop 仅写 per_conv;
/// IPC(ai_is_generating/ai_chat_send 等)亦改读 per_conv,无需双写桥接。
///
/// L2 统一状态机收尾(2026-06-22):本地 `state: Option<ConvState>` 视图层冗余字段已删除。
///
/// 背景:批1 曾在 guard 内嵌 `state` 作「写收敛视图层」(双轨过渡),批2 已把 `ConvState` 落到
/// `PerConvState.conv_state` 字段持久化(读侧真相源)。批2 同时在 `reset`/`drop` 内读
/// `conv.conv_state` 持久化字段做迁移 + emit AiConvStateChanged——故本地 `state` 成纯冗余
/// (双源,且本地 state 在 Drop 无 await 上下文无法持久化,语义本就不完整)。
///
/// 收尾删除后:
/// - `new`:读 `conv.conv_state` 持久化字段,若 Idle→Generating 迁移成功则 emit 通知前端
///   (入口 `run_agentic_loop` 已独立写持久化迁移但**不 emit**,故此处补 emit 防前端漏收生成态)。
/// - `reset`/`drop`:迁移 + emit 均读 `conv.conv_state` 持久化字段(批2 已落地,本批仅删冗余本地块)。
/// - `disarm`:不动 ConvState(审批等待是 Generating 内的暂停点)。
///
/// 开关 `CONV_STATE_ENABLED`(默认 on)门控:off 时跳过 enum 迁移与 emit,纯旧 bool 行为(可回退)。
pub(super) struct GeneratingGuard {
    session: Arc<Mutex<AiSession>>,
    /// guard 所属会话(loop 启动时快照的 conv_id,来自 run_agentic_loop 入参)。
    conv_id: String,
    done: bool,
    /// L2 批2 1b:emit AiConvStateChanged 用的 AppHandle(Clone 廉价,run_agentic_loop 入参传入)。
    /// CONV_STATE_ENABLED 门控:off 时仍持(无副作用),但 new/reset/drop 跳过 emit。
    app_handle: AppHandle,
}

impl GeneratingGuard {
    pub(super) fn new(session: Arc<Mutex<AiSession>>, conv_id: String, app_handle: AppHandle) -> Self {
        // L2 收尾:入口 `run_agentic_loop` 已独立完成 Idle→Generating 的持久化迁移(写 conv.conv_state),
        // 但**不 emit** AiConvStateChanged。此处补 emit 通知前端进入生成态——读 conv.conv_state
        // 持久化字段(真相源)做迁移校验,迁移成功则 emit,非法记 warn 不阻断。
        //
        // 注:new 是同步函数(无 await),无法持 session 锁读 conv.conv_state;此处经
        // tauri::async_runtime::spawn 异步补 emit(非阻塞 guard 创建,入口持久化已发生故 emit 有据)。
        // spawn 失败(运行时不可用,理论场景)仅漏一次 emit,前端 watchdog 兜底(对齐失败不阻断核心生成)。
        if conv_state::CONV_STATE_ENABLED {
            let session = session.clone();
            let app_handle = app_handle.clone();
            let cid = conv_id.clone();
            tauri::async_runtime::spawn(async move {
                let new_state = {
                    let mut session = session.lock().await;
                    let conv = session.conv(&cid);
                    match conv.conv_state.transition_to(ConvState::Generating) {
                        Ok(ns) => {
                            conv.conv_state = ns;
                            ns
                        }
                        Err(e) => {
                            tracing::warn!(
                                conv_id = %cid,
                                error = %e,
                                "[ai] guard.new ConvState→Generating 非法(持久化层,不阻断核心生成)"
                            );
                            return;
                        }
                    }
                };
                // 持久化层迁移成功后 emit 前端(同步,失败忽略)。锁已随作用域 drop,可安全 emit。
                // L3 emit 双写:提变量避免构造两次,publish 到事件总线(EVENT_BUS_ENABLED 门控在 publish 内)。
                let ev = AiChatEvent::AiConvStateChanged {
                    conv_state: new_state,
                    conversation_id: Some(cid),
                };
                let _ = app_handle.emit("ai-chat-event", ev.clone());
                let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
            });
        }
        Self { session, conv_id, done: false, app_handle }
    }

    /// 显式复位 generating=false。emit 前调用保证顺序。幂等。
    ///
    /// 仅写 per_conv.conv_id.generating(唯一真相源)。
    /// L2:同步迁移 `conv.conv_state → Idle`(持久化层,CONV_STATE_ENABLED 门控)。
    pub(super) async fn reset(&mut self) {
        if !self.done {
            let mut session = self.session.lock().await;
            let conv = session.conv(&self.conv_id);
            conv.generating = false;
            // L2 批2:ConvState 持久化(Generating→Idle 写收敛,CONV_STATE_ENABLED 门控)。
            if conv_state::CONV_STATE_ENABLED {
                match conv.conv_state.transition_to(ConvState::Idle) {
                    Ok(ns) => {
                        conv.conv_state = ns;
                        // L2 批2 1b:持久化层 Idle 收敛成功后 emit 前端(同步,失败忽略)。
                        // L3 emit 双写:提变量避免构造两次,publish 到事件总线(EVENT_BUS_ENABLED 门控在 publish 内)。
                        let ev = AiChatEvent::AiConvStateChanged {
                            conv_state: ns,
                            conversation_id: Some(self.conv_id.clone()),
                        };
                        let _ = self.app_handle.emit("ai-chat-event", ev.clone());
                        let _ = self.app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
                    }
                    Err(e) => tracing::warn!(
                        conv_id = %self.conv_id,
                        error = %e,
                        "[ai] guard.reset ConvState→Idle 非法(持久化层,不阻断核心复位)"
                    ),
                }
            }
            self.done = true;
        }
    }

    /// 解除 Drop 兜底复位但不复位 generating。审批等待 return 路径调用:
    /// 保持 generating=true 留 try_continue 续生成,同时 Drop 因 done=true 跳过复位 spawn。
    /// (B-260615-26: 修复审批执行后对话不续生成回归)
    ///
    /// L2:disarm 不迁移 ConvState(审批等待是 Generating 内的暂停点,ConvState 仍 Generating,
    /// 与「generating 保持 true」语义一致)。续跑时新 guard.new 重新迁移(Generating 自环幂等)。
    pub(super) fn disarm(&mut self) {
        self.done = true;
    }
}

impl Drop for GeneratingGuard {
    fn drop(&mut self) {
        // 未 done 即异常退出(panic/未走正常 return):核心 generating 经 spawn 复位,
        // ConvState 持久化迁移 + emit 也在同一 spawn 内异步完成(无 await 上下文,对齐核心复位路径)。
        // 开关 off / done=true(正常 reset/disarm 已走) → 跳过。
        if !self.done {
            let session = self.session.clone();
            let conv_id = self.conv_id.clone();
            // L2 批2 1b:Drop 无 await 上下文,持久化迁移 + emit 在 spawn 内异步完成(对齐核心复位 spawn 路径)。
            // app_handle Clone 廉价,随 session/conv_id 一起 move 进 async 块。
            let app_handle = self.app_handle.clone();
            tauri::async_runtime::spawn(async move {
                let mut s = session.lock().await;
                let conv = s.conv(&conv_id);
                conv.generating = false;
                // L2 批2:ConvState 持久化(异常退出兜底→Idle,CONV_STATE_ENABLED 门控)。
                if conv_state::CONV_STATE_ENABLED {
                    if let Ok(ns) = conv.conv_state.transition_to(ConvState::Idle) {
                        conv.conv_state = ns;
                        // 1b:持久化 Idle 收敛后 emit 前端(异常退出兜底也需通知前端退出生成态)。
                        // L3 emit 双写:提变量避免构造两次,publish 到事件总线(EVENT_BUS_ENABLED 门控在 publish 内)。
                        let ev = AiChatEvent::AiConvStateChanged {
                            conv_state: ns,
                            conversation_id: Some(conv_id.clone()),
                        };
                        let _ = app_handle.emit("ai-chat-event", ev.clone());
                        let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
                    }
                }
            });
        }
    }
}

// ============================================================
// 单元测试(guard ConvState 迁移语义,纯逻辑路径验证)
// ============================================================

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

    /// guard.new 路径:持久化层 Idle→Generating 迁移(读 conv.conv_state)。
    ///
    /// 注:不构造真实 AiSession(lock/Mutex 建会话状态成本高),仅校验迁移语义——
    // guard 内部 transition_to 是纯函数,直接断言逻辑路径。
    #[test]
    fn test_guard_new_migrates_to_generating() {
        // CONV_STATE_ENABLED on → Idle → Generating(读侧持久化字段迁移路径)。
        assert!(conv_state::CONV_STATE_ENABLED, "开关默认 on,测试假设");
        let mut s = ConvState::Idle;
        s = s.transition_to(ConvState::Generating).unwrap();
        assert_eq!(s, ConvState::Generating);
    }

    /// reset 迁移 Generating→Idle(正常收敛),验证迁移合法。
    #[test]
    fn test_guard_reset_migrates_to_idle() {
        let mut s = ConvState::Generating;
        s = s.transition_to(ConvState::Idle).unwrap();
        assert_eq!(s, ConvState::Idle);
    }

    /// 异常 Drop 路径同样 Generating→Idle(兜底),验证迁移合法。
    #[test]
    fn test_guard_drop_path_migrates_to_idle() {
        // guard.drop 异常路径(!done)迁移 Generating→Idle(读持久化字段)。
        let mut s = ConvState::Generating;
        s = s.transition_to(ConvState::Idle).unwrap();
        assert_eq!(s, ConvState::Idle);
    }

    /// disarm 不迁移状态(审批等待保持 Generating)。
    /// 验证:disarm 后 enum 仍 Generating(下次 new 时 Generating 自环幂等通过)。
    #[test]
    fn test_guard_disarm_keeps_generating_then_self_loop_on_new() {
        let s = ConvState::Generating;
        // disarm 不动 enum;续跑新 guard.new 时 Generating→Generating 自环通过。
        assert_eq!(s.transition_to(ConvState::Generating), Ok(ConvState::Generating));
    }
}
