/**
 * 陈旧任务检测器 — 问题六十五。
 *
 * 解决真实项目暴露的问题：旧 current-task 误阻塞新任务。
 *
 * 核心规则:
 * - stale current task 不得误阻塞新任务分类
 * - 不得让 AI 说"无需 SoloForge 状态"然后直接分析
 * - 必须输出 resume/archive/start_new 选择
 * - 旧任务不得静默删除，只能提示归档或在用户确认后归档指针
 * - task 文件保留审计痕迹，默认只处理 current-task 指针
 */
import fs from "node:fs";
import path from "node:path";
import { debug } from "../core/logger.js";
import { safeJsonParse } from "../core/helpers.js";
/** 旧任务判定阈值（毫秒）: 30 分钟未推进视为陈旧 */
const STALE_AGE_MS = 30 * 60 * 1000;
/**
 * 读取 current-task 指针指向的原始任务（不过滤终态）。
 * 与 TaskContextManager.getCurrentTask() 的区别在于：
 * - getCurrentTask 过滤 done/failed 任务
 * - 本函数读取指针并加载原始任务用于陈旧分析
 */
async function loadRawCurrentTask(stateDir) {
    const pointerPath = path.join(stateDir, "current-task.json");
    try {
        const data = fs.readFileSync(pointerPath, "utf-8");
        const { task_id } = safeJsonParse(data) ?? {};
        if (!task_id)
            return null;
        const taskPath = path.join(stateDir, `${task_id}.json`);
        if (!fs.existsSync(taskPath)) {
            // 指针指向不存在的任务文件
            return { pointerTaskId: task_id, ctx: null };
        }
        const ctx = safeJsonParse(fs.readFileSync(taskPath, "utf-8"));
        if (!ctx)
            return null;
        return { pointerTaskId: task_id, ctx };
    }
    catch {
        return null;
    }
}
/**
 * 检测当前任务的陈旧状态。
 *
 * 识别条件:
 * 1. current-task.json 指向不存在的任务文件
 * 2. 任务处于 classifying 但已有 classification 且长期未推进
 * 3. 任务含 missing_authorization / hard_fail invocation_violations
 * 4. 任务处于 done/failed 但指针仍存在
 * 5. 任务长时间未更新（> STALE_AGE_MS）
 */
export async function detectStaleCurrentTask(stateDir) {
    debug("陈旧检测", "开始检测", stateDir);
    const empty = {
        is_stale: false,
        task_id: null,
        reason_zh: "",
        stale_indicators: [],
        suggested_resolutions: [],
    };
    if (!fs.existsSync(stateDir)) {
        return empty;
    }
    const raw = await loadRawCurrentTask(stateDir);
    if (!raw) {
        return empty;
    }
    const { pointerTaskId, ctx } = raw;
    // 情况 1: 指针指向不存在的任务文件
    if (!ctx) {
        return {
            is_stale: true,
            task_id: pointerTaskId,
            reason_zh: `current-task.json 指向的任务文件 ${pointerTaskId}.json 不存在`,
            stale_indicators: ["pointer_target_missing"],
            suggested_resolutions: ["archive"],
        };
    }
    const indicators = [];
    const reasons = [];
    // 情况 2: 任务处于终态但指针仍存在
    if (ctx.status === "done" || ctx.status === "failed") {
        indicators.push("terminal_status_with_pointer");
        reasons.push(`任务状态为 ${ctx.status}（终态），但 current-task 指针仍指向该任务`);
    }
    // 情况 3: 任务处于 classifying 且有 classification 但长期未推进
    if (ctx.status === "classifying" && ctx.classification) {
        const updatedAt = new Date(ctx.updated_at ?? ctx.created_at).getTime();
        const age = Date.now() - updatedAt;
        if (age > STALE_AGE_MS) {
            indicators.push("classifying_with_classification_stale");
            reasons.push(`任务处于 classifying 但已有分类结果，且超过 ${Math.round(STALE_AGE_MS / 60000)} 分钟未推进`);
        }
    }
    // 情况 4: 任务含 missing_authorization / hard_fail invocation_violations
    const violations = ctx.invocation_violations ?? [];
    const hardFailViolations = violations.filter((v) => v.severity === "hard_fail" || v.violation_type === "missing_authorization");
    if (hardFailViolations.length > 0) {
        indicators.push("hard_fail_violations");
        reasons.push(`任务包含 ${hardFailViolations.length} 个 hard_fail 级别的调用违规（如 missing_authorization）`);
    }
    // 情况 5: 长时间未更新
    const idleMs = Date.now() - new Date(ctx.updated_at ?? ctx.created_at).getTime();
    if (idleMs > STALE_AGE_MS && !["done", "failed"].includes(ctx.status)) {
        indicators.push("long_idle");
        reasons.push(`任务超过 ${Math.round(idleMs / 60000)} 分钟未更新`);
    }
    if (indicators.length === 0) {
        return {
            ...empty,
            task_id: pointerTaskId,
            current_task_context: ctx,
        };
    }
    // 计算建议操作
    const resolutions = [];
    // 如果任务处于非终态且有实质进展，建议恢复
    if (!["done", "failed"].includes(ctx.status) && (ctx.classification || ctx.expansion || ctx.execution)) {
        resolutions.push("resume");
    }
    // 总是可以归档
    resolutions.push("archive");
    // 如果有严重违规或长期未推进，也建议开始新任务
    if (hardFailViolations.length > 0 || indicators.includes("classifying_with_classification_stale")) {
        resolutions.push("start_new");
    }
    // 确保至少有一个选项
    if (resolutions.length === 0) {
        resolutions.push("archive", "start_new");
    }
    return {
        is_stale: true,
        task_id: pointerTaskId,
        reason_zh: reasons.join("；"),
        stale_indicators: indicators,
        suggested_resolutions: resolutions,
        current_task_context: ctx,
    };
}
/**
 * 归档陈旧任务的 current-task 指针（不删除任务文件）。
 * 必须在用户确认后调用。
 */
export async function archiveStaleCurrentPointer(stateDir, taskId) {
    const pointerPath = path.join(stateDir, "current-task.json");
    try {
        const data = safeJsonParse(fs.readFileSync(pointerPath, "utf-8"));
        if (data?.task_id === taskId) {
            // 将指针文件重命名为归档标记，保留审计痕迹
            const archivePath = path.join(stateDir, `archived-pointer-${taskId}.json`);
            fs.writeFileSync(archivePath, JSON.stringify({
                ...data,
                archived_at: new Date().toISOString(),
                archive_reason: "stale_task_cleanup",
            }, null, 2));
            fs.unlinkSync(pointerPath);
            debug("陈旧检测", `已归档 current-task 指针: ${taskId}`);
            return true;
        }
        return false;
    }
    catch (e) {
        debug("陈旧检测", "归档指针失败", e);
        return false;
    }
}
//# sourceMappingURL=stale_current_task_detector.js.map