//! 项目事件流相关命令(知识图谱 Phase 2 V30,对标设计 §2.4 + §五 get_project_timeline)
//!
//! 统一事件流 project_events 的查询入口。供 AI / Dashboard 项目时间线视图使用
//! (设计 §10.3.4 模式 B 事件流 + §五 get_project_timeline)。
//!
//! 筛选策略:Repo::get_by_project 取最近 N 条(上限 200,对标设计 §10.5 单用户桌面应用
//! 全量查询无压力),应用层按 event_type / 时间范围过滤。复杂多维筛选不进 SQL(数据量小,
//! 应用层过滤足够 + 保持 Repo 专用方法精简,对标 TaskLinkRepo 全专用路径)。

use serde::{Deserialize, Serialize};
use tauri::State;

use df_storage::models::ProjectEventRecord;

use crate::state::AppState;

use super::err_str;

/// get_project_timeline 查询入参(对标设计 §五 get_project_timeline:time 范围/event_type 筛选)。
///
/// 所有字段可选:
/// - `project_id`:必填(查询某项目的事件流)。
/// - `event_type`:可选,按事件类型精确过滤(如 "task_advanced");空/None = 不过滤。
/// - `limit`:可选,返回上限(默认 50,Repo 内部钳制最大 200)。
#[derive(Debug, Deserialize)]
pub struct GetTimelineInput {
    pub project_id: String,
    /// 事件类型过滤(精确匹配,如 "task_created" / "idea_promoted")。None/空串 = 不过滤。
    pub event_type: Option<String>,
    /// 返回条数上限(默认 50)。Repo 内部钳制 ≤ 200。
    #[serde(default = "default_limit")]
    pub limit: u32,
}

fn default_limit() -> u32 {
    50
}

/// 时间线条目(对标设计 §10.3.4 模式 B 事件流)。直接回 ProjectEventRecord(Serialize 已派生),
/// 外层补 total 便于前端分页/计数。
#[derive(Debug, Serialize)]
pub struct TimelineResult {
    /// 过滤后的事件列表(时间倒序,Repo 已排;按 input.limit 截断)
    pub items: Vec<ProjectEventRecord>,
    /// 过滤后**真实总数**(M18:不受 input.limit 截断,反映某项目某 event_type 的完整事件量,
    /// 供前端分页计数;items.len ≤ total,只有当 total ≤ limit 时两者相等)
    pub total: usize,
    /// 查询的项目 ID(回显)
    pub project_id: String,
}

/// 查询项目事件流(对标设计 §五 get_project_timeline + §2.4)。
///
/// 支持按 event_type 过滤 + limit 上限。时间范围过滤暂未实现(设计 §2.4 列出 time 范围筛选,
/// 但当前 get_by_project 仅返回最近 N 条倒序,时间范围筛选用例可后续按需加 Repo 方法;
/// 单用户桌面应用事件量小,最近 N 条 + event_type 过滤已覆盖主要场景)。
#[tauri::command]
pub async fn get_project_timeline(
    state: State<'_, AppState>,
    input: GetTimelineInput,
) -> Result<TimelineResult, String> {
    let project_id = input.project_id.trim().to_string();
    if project_id.is_empty() {
        return Err("project_id 不能为空".to_string());
    }
    // event_type 过滤值规范化(None/空串 = 不过滤)
    let event_filter = input
        .event_type
        .as_deref()
        .map(str::trim)
        .filter(|s| !s.is_empty())
        .map(|s| s.to_string());

    // Repo 取较宽窗口(内部钳制 ≤ 200),时间倒序。M18:不再用 input.limit 直接作 SQL LIMIT——
    // 应用层 event_type 过滤在 SQL LIMIT 之后,若直接 LIMIT N 会先取 N 条混合类型再过滤,
    // 过滤后可能远少于 N(类型稀疏时几乎全空),total 也失真。改为取较宽窗口(上限 200,与 Repo
    // 钳制上限一致),先 event_type 过滤,再按 input.limit 截断,使 total 反映过滤后真实总数
    // (而非过滤后被 limit 截断的 items.len)。
    let fetch_window = 200u32;
    let mut events = state
        .project_events
        .get_by_project(&project_id, fetch_window)
        .await
        .map_err(err_str)?;

    // 应用层按 event_type 过滤(SQL 下推不值得——单用户事件量小,保持 Repo 方法精简)。
    if let Some(et) = &event_filter {
        events.retain(|e| e.event_type == *et);
    }

    // M18:total = 过滤后真实总数(反映某项目某类型的完整事件量,供前端分页计数),
    // 不受 input.limit 截断影响。
    let total = events.len();
    // 再按 input.limit 截断返回的 items(默认 50,与文档一致)。
    let limit = input.limit.min(200) as usize;
    if events.len() > limit {
        events.truncate(limit);
    }

    Ok(TimelineResult {
        items: events,
        total,
        project_id,
    })
}
