//! 任务相关命令

use serde::Deserialize;
use tauri::State;

use df_types::types::{new_id, TaskStatus};
use df_storage::models::TaskRecord;

use crate::state::AppState;

use super::{err_str, now_millis};

/// 创建任务入参
#[derive(Debug, Deserialize)]
pub struct CreateTaskInput {
    pub project_id: String,
    pub title: String,
    #[serde(default)]
    pub description: String,
    #[serde(default = "default_priority")]
    pub priority: i32,
    pub branch_name: Option<String>,
    pub assignee: Option<String>,
}

fn default_priority() -> i32 {
    2 // medium — 新任务默认中优先级(非 high),符合常识
}

/// 列出未删除任务(deleted_at IS NULL),可按 project_id 过滤。
///
/// 软删对标 projects:默认过滤回收站(语义与 list_projects 一致)。
/// 无 project_id 时调 list_active(WHERE deleted_at IS NULL);有 project_id 时
/// 先 list_active 再内存过滤 project_id(任务量小,无需 SQL 下推,避免新增专用查询方法)。
/// 注意:不能用通用 query 宏——它不带 deleted_at 过滤,会把回收站任务也返回。
#[tauri::command]
pub async fn list_tasks(
    state: State<'_, AppState>,
    project_id: Option<String>,
) -> Result<Vec<TaskRecord>, String> {
    let mut tasks = state.tasks.list_active().await.map_err(err_str)?;
    if let Some(pid) = project_id {
        tasks.retain(|t| t.project_id == pid);
    }
    Ok(tasks)
}

/// 按 id 查任务，找不到返回 Err(供前端详情页)
#[tauri::command]
pub async fn get_task_by_id(
    state: State<'_, AppState>,
    id: String,
) -> Result<TaskRecord, String> {
    state
        .tasks
        .get_by_id(&id)
        .await
        .map_err(err_str)?
        .ok_or_else(|| format!("任务 {} 不存在", id))
}

/// 创建任务，返回完整记录
#[tauri::command]
pub async fn create_task(
    state: State<'_, AppState>,
    input: CreateTaskInput,
) -> Result<TaskRecord, String> {
    let now = now_millis();
    let record = TaskRecord {
        id: new_id(),
        project_id: input.project_id,
        title: input.title,
        description: input.description,
        status: "todo".to_string(),
        priority: input.priority,
        branch_name: input.branch_name,
        assignee: input.assignee,
        workflow_def_id: None,
        base_branch: None,
        review_rounds: 0,
        output_json: None,
        created_at: now.clone(),
        updated_at: now,
    };
    state
        .tasks
        .insert(record.clone())
        .await
        .map_err(err_str)?;
    Ok(record)
}

/// 更新任务单个字段（字段名走 df-storage 白名单校验；status 值走枚举校验）
#[tauri::command]
pub async fn update_task(
    state: State<'_, AppState>,
    id: String,
    field: String,
    value: String,
) -> Result<bool, String> {
    // status 值校验保留：仅对「非法值」(拼写错 in-progess / "in progress" / 大小写错 / 越界)
    // 给出友好早错误(先于 crud.rs 白名单那串冷冰冰的「字段不在白名单」拒)。合法 status 值
    // 不可经本 IPC 写入——crud.rs tasks 白名单已收口移除 status(F-03 batch64 b94e74a /
    // D-260616-04)，所有 status 改动须走 advance_task_atomic 状态机(CAS + can_transition +
    // review_rounds 累加,唯一 status 写入路径)。即此 is_valid 校验的「通过」分支永不会触达
    // update_field 的 status 写入(白名单会先拒)；它只为非法值兜底 UX，不承担合法值写入职责。
    // priority 值校验同理补在下方：拦截 "abc" / 999 等脏数据静默落库。
    if field == "status" && !TaskStatus::is_valid(&value) {
        return Err(format!(
            "非法 status 值 {:?}，合法值: {:?}",
            value,
            TaskStatus::valid_values()
        ));
    }
    // priority 值域 0..=3（0=critical, 1=high, 2=medium, 3=low），与前端 <select> 及
    // constants/project.ts 的 PRIORITY_LABELS 一致。拦截 "abc" / 999 等脏数据。
    if field == "priority" {
        match value.parse::<i32>() {
            Ok(p) if (0..=3).contains(&p) => {}
            _ => {
                return Err(format!(
                    "非法 priority 值 {:?}，合法值: 整数 0..=3（0=critical, 1=high, 2=medium, 3=low）",
                    value
                ));
            }
        }
    }
    // project_id 跨表存在性校验(B-260616-16 收尾)。
    // crud.rs tasks 白名单保留 project_id(支持跨项目移动),跨表约束由本命令层兜底。
    // 拦截移动到不存在的项目(手输脏 id / 已物理删除的项目),避免 tasks.project_id 悬空。
    if field == "project_id" {
        let exists = state.projects.get_by_id(&value).await.map_err(err_str)?;
        if exists.is_none() {
            return Err(format!("非法 project_id 值 {:?}，目标项目不存在", value));
        }
    }
    state
        .tasks
        .update_field(&id, &field, &value)
        .await
        .map_err(err_str)
}

/// 删除任务(软删 → 回收站,可恢复)。对标 delete_project(SET deleted_at=now)。
#[tauri::command]
pub async fn delete_task(state: State<'_, AppState>, id: String) -> Result<bool, String> {
    state.tasks.soft_delete(&id).await.map_err(err_str)
}

/// 恢复任务(从回收站还原,清 deleted_at)。对标 restore_project。
#[tauri::command]
pub async fn restore_task(state: State<'_, AppState>, id: String) -> Result<bool, String> {
    state.tasks.restore(&id).await.map_err(err_str)
}

/// 推进任务状态(任务推进链 F-260616-02,推进链唯一 status 写入路径)。
///
/// thin 入口(D-260616-03):业务逻辑(状态机校验 + 原子 CAS + review_rounds 累加)
/// 落 df-nodes::task_advance_node::advance_task_atomic,本命令只做参数转发与错误串化。
///
/// 流程:读当前态 → can_transition 校验 → 下沉 SQL `WHERE id AND status=expected`
/// 防 TOCTOU → 退回转换一并 review_rounds+=1。失败均返回 Err(状态机/TOCTOU/任务不存在)。
///
/// 返回:推进成功后的最新 TaskRecord(含新 status / 累加后的 review_rounds)。
#[tauri::command]
pub async fn advance_task(
    state: State<'_, AppState>,
    id: String,
    target_status: String,
) -> Result<TaskRecord, String> {
    df_nodes::task_advance_node::advance_task_atomic(&state.tasks, &id, &target_status)
        .await
        .map_err(err_str)
}
