import { debug } from "../core/logger.js";
/**
 * 对任务进行拆解规划，生成子任务序列和执行顺序。
 * 不满足拆解条件时返回空计划。
 * @param input - 规划输入，包含意图、分类结果、项目配置和知识索引
 * @returns 计划结果，包含子任务列表、执行顺序和并行分组
 */
export function planTask(input) {
    debug("任务规划", "planTask() 开始, intent=${intent.substring(0, 60)}");
    const { intent, classification, config, knowledgeIndex } = input;
    if (!shouldPlan(classification)) {
        debug("任务规划", "不满足拆解条件, complexity=${classification.complexity}, strategy=${classification.strategy}");
        return {
            task_id: "",
            original_intent: intent,
            sub_tasks: [],
            execution_order: [],
            parallel_groups: [],
            summary: `不拆解: ${classification.complexity === "high" ? "非全流程策略" : "复杂度不足以触发拆解"}`,
            skipped_reason: `触发条件不满足（complexity=${classification.complexity}, strategy=${classification.strategy}）`,
            current_step_index: 0,
        };
    }
    // 查询 pipeline_procedure 知识（流程指引）
    const stages = input.task_stage ? [input.task_stage] : undefined;
    const pipelineProcedures = knowledgeIndex.query({
        scope: classification.affected_repos,
        products: [config.product_profile],
        type: "pipeline_procedure",
        keywords: extractKeywords(intent),
        stages,
    });
    // 查询普通 procedure 知识（流程补充）
    const procedures = knowledgeIndex.query({
        scope: classification.affected_repos,
        products: [config.product_profile],
        type: "procedure",
        keywords: extractKeywords(intent),
        stages,
    });
    // 查询可用的 acceptance_template（输出格式模板）
    const templates = knowledgeIndex.query({
        scope: classification.affected_repos,
        products: [config.product_profile],
        type: "acceptance_template",
        keywords: [],
        stages,
    });
    // 根据知识情况选择拆分策略
    if (pipelineProcedures.length > 0) {
        debug("任务规划", "发现 ${pipelineProcedures.length} 个 pipeline_procedure 知识，走知识驱动拆解");
        // 有 pipeline_procedure → 知识驱动的多阶段计划
        return buildKnowledgeDrivenPlan(intent, classification, config, pipelineProcedures, templates);
    }
    // 无 pipeline_procedure → 按仓库边界拆分（原有逻辑）
    debug("任务规划", "无 pipeline_procedure，按仓库边界拆解");
    const subTasks = splitByRepoBoundary(intent, classification, config);
    if (procedures.length > 0) {
        mergeProcedureHints(subTasks, procedures.map((p) => p.name));
    }
    debug("任务规划", "拆解结果 subTasks.length=${subTasks.length}");
    if (subTasks.length <= 1) {
        const single = subTasks[0] ?? {
            id: "st-1",
            title: intent,
            description: "无法自动拆解，建议人工细化",
            intent,
            depends_on: [],
            affected_repos: classification.affected_repos,
            estimated_complexity: "medium",
        };
        return {
            task_id: "",
            original_intent: intent,
            sub_tasks: [single],
            execution_order: [single.id],
            parallel_groups: [[single.id]],
            summary: `无法自动拆解，建议人工细化子任务`,
            current_step_index: 0,
        };
    }
    const { order, parallelGroups } = buildExecutionOrder(subTasks, classification.affected_repos);
    return {
        task_id: "",
        original_intent: intent,
        sub_tasks: subTasks,
        execution_order: order,
        parallel_groups: parallelGroups,
        summary: `拆解为 ${subTasks.length} 个子任务，${parallelGroups.length} 个执行阶段`,
        current_step_index: 0,
    };
}
/**
 * 基于知识驱动的多阶段计划生成。
 * 读取 pipeline_procedure 知识条目的步骤定义，结合可用模板，生成带模板绑定的子任务。
 */
function buildKnowledgeDrivenPlan(intent, classification, _config, pipelineProcedures, _templates) {
    const procedure = pipelineProcedures[0];
    // 从 pipeline_procedure body 中提取步骤
    const steps = parsePipelineSteps(procedure.body || procedure.when);
    const subTasks = steps.map((step, idx) => {
        return {
            id: `st-${idx + 1}`,
            title: step.title,
            description: step.description,
            intent: step.title,
            depends_on: idx > 0 ? [`st-${idx}`] : [],
            affected_repos: classification.affected_repos,
            estimated_complexity: inferComplexity(intent),
            output_template: step.template_name || undefined,
            output_path: step.output_path || `.soloforge/output/${String(idx + 1).padStart(2, "0")}-${step.title}.md`,
            suggested_tools: step.tools || undefined,
        };
    });
    const { order, parallelGroups } = buildExecutionOrder(subTasks, classification.affected_repos);
    return {
        task_id: "",
        original_intent: intent,
        sub_tasks: subTasks,
        execution_order: order,
        parallel_groups: parallelGroups,
        summary: `按 "${procedure.name}" 流程拆解为 ${subTasks.length} 个阶段`,
        current_step_index: 0,
    };
}
/**
 * 从 pipeline_procedure 知识条目的 Markdown body 中提取步骤定义。
 * 识别 "### 第X步" 或 "### 步骤X" 格式的标题，提取：
 * - 标题
 * - 描述（标题下的文字）
 * - 模板引用（"模板：XXX" 或 "template: XXX"）
 * - 输出路径（"产出保存到：XXX" 或 "output: XXX"）
 * - 工具建议（"使用 XXX 工具" 或 "tools: XXX"）
 */
function parsePipelineSteps(body) {
    const steps = [];
    const sections = body.split(/^### /m).slice(1);
    for (const section of sections) {
        const lines = section.split("\n");
        const title = lines[0].replace(/^第\d+[步阶段][:：]\s*/, "").replace(/^步骤\d+[:：]\s*/, "").trim();
        if (!title)
            continue;
        const description = [];
        let templateName;
        let outputPath;
        const tools = [];
        for (const line of lines.slice(1)) {
            const trimmed = line.trim();
            if (!trimmed || trimmed.startsWith("#"))
                continue;
            // 匹配模板引用
            const templateMatch = trimmed.match(/(?:模板|template)[：:]\s*(.+)/i);
            if (templateMatch) {
                templateName = templateMatch[1].trim();
                continue;
            }
            // 匹配输出路径
            const outputMatch = trimmed.match(/(?:产出保存到|输出到|output)[：:]\s*(.+)/i);
            if (outputMatch) {
                outputPath = outputMatch[1].trim();
                continue;
            }
            // 匹配工具建议
            const toolsMatch = trimmed.match(/(?:使用|工具|tools)[：:]\s*(.+)/i);
            if (toolsMatch) {
                tools.push(...toolsMatch[1].split(/[,，、]/).map((t) => t.trim()).filter(Boolean));
                continue;
            }
            description.push(trimmed);
        }
        steps.push({
            title,
            description: description.join("\n"),
            template_name: templateName,
            output_path: outputPath,
            tools: tools.length > 0 ? tools : undefined,
        });
    }
    return steps;
}
/**
 * 判断是否需要拆解任务。
 * 新逻辑: route_decision.execution_shape === "multi_stage_plan" 时才允许生成完整多阶段计划。
 * 兼容: 无 route_decision 时回退到旧 complexity + strategy 判断。
 * single_artifact 即使命中 pipeline_procedure 也只作为阶段模板/知识注入。
 */
function shouldPlan(classification) {
    const routeDecision = classification.route_decision;
    if (routeDecision?.execution_shape) {
        return routeDecision.execution_shape === "multi_stage_plan";
    }
    return classification.complexity === "high" && classification.strategy === "full_pipeline";
}
/**
 * 按仓库边界拆分任务: 每个受影响仓库生成一个子任务。
 * 前端依赖后端时自动添加 depends_on 关系。
 * @param intent - 用户意图
 * @param classification - 分类结果
 * @param config - 项目配置
 * @returns 子任务数组，单仓库时返回空数组
 */
function splitByRepoBoundary(intent, classification, _config) {
    const repos = classification.affected_repos;
    if (repos.length <= 1)
        return [];
    const tasks = [];
    let idx = 1;
    for (const repo of repos) {
        const repoLabel = repo === "backend" ? "后端" : repo === "frontend" ? "前端" : repo;
        const repoIntent = buildRepoIntent(intent, repo, repos);
        tasks.push({
            id: `st-${idx++}`,
            title: `${repoLabel}: ${repoIntent}`,
            description: `${intent} 的 ${repoLabel} 部分`,
            intent: repoIntent,
            depends_on: repo === "frontend" && repos.includes("backend") ? ["st-1"] : [],
            affected_repos: [repo],
            estimated_complexity: inferComplexity(repoIntent),
        });
    }
    return tasks;
}
/**
 * 为特定仓库生成意图描述: 将"前后端/联调/全链路"等跨域词替换为仓库视角的描述。
 * @param intent - 原始意图
 * @param repo - 目标仓库名称
 * @param allRepos - 所有受影响的仓库
 * @returns 仓库视角的意图描述
 */
function buildRepoIntent(intent, repo, allRepos) {
    if (repo === "backend" && allRepos.includes("frontend")) {
        return intent.replace(/前后端|联调|全链路/g, "后端接口");
    }
    if (repo === "frontend" && allRepos.includes("backend")) {
        return intent.replace(/前后端|联调|全链路/g, "前端对接");
    }
    return intent;
}
/**
 * 将 procedure 知识名称作为参考提示合并到子任务描述中。
 * @param tasks - 子任务列表（原地修改）
 * @param procedureNames - procedure 知识名称列表
 */
function mergeProcedureHints(tasks, procedureNames) {
    if (tasks.length === 0)
        return;
    const hint = `参考流程: ${procedureNames.join(", ")}`;
    for (const task of tasks) {
        task.description += `\n${hint}`;
    }
}
/**
 * 基于依赖关系构建执行顺序和并行分组。
 * 使用拓扑排序确定执行顺序，无依赖或依赖已满足的任务归入同一并行组。
 * @param subTasks - 子任务列表
 * @param _affectedRepos - 受影响仓库（预留参数）
 * @returns 拓扑排序后的执行顺序和并行分组
 */
function buildExecutionOrder(subTasks, _affectedRepos) {
    const visited = new Set();
    const visiting = new Set();
    const order = [];
    const taskMap = new Map(subTasks.map((t) => [t.id, t]));
    function visit(id) {
        if (visited.has(id))
            return;
        if (visiting.has(id))
            return; // 检测到循环依赖 — 跳过该边避免死循环
        visiting.add(id);
        const task = taskMap.get(id);
        if (task) {
            for (const dep of task.depends_on)
                visit(dep);
        }
        visiting.delete(id);
        visited.add(id);
        order.push(id);
    }
    for (const task of subTasks)
        visit(task.id);
    const parallelGroups = [];
    const scheduled = new Set();
    for (const id of order) {
        const task = taskMap.get(id);
        if (!task)
            continue;
        const depsReady = task.depends_on.every((d) => scheduled.has(d));
        if (depsReady) {
            const lastGroup = parallelGroups[parallelGroups.length - 1];
            // 任务可加入最后一个并行组的条件: 无依赖 或 所有依赖均不在最后一个并行组中
            // （即依赖已在更早阶段完成）。这使得 B(依赖A) 和 C(依赖A) 可在 A 完成后并行执行。
            const hasNoDeps = task.depends_on.length === 0;
            const noDepsInLastGroup = lastGroup ? !task.depends_on.some((d) => lastGroup.includes(d)) : true;
            const canParallel = lastGroup && (hasNoDeps || noDepsInLastGroup);
            if (canParallel) {
                lastGroup.push(id);
            }
            else {
                parallelGroups.push([id]);
            }
            scheduled.add(id);
        }
    }
    return { order, parallelGroups };
}
/**
 * 从意图文本中提取关键词，用于知识库查询。
 * @param intent - 意图文本
 * @returns 关键词数组（过滤长度不足 2 的词）
 */
function extractKeywords(intent) {
    return intent
        .split(/[\s,，、：:]+/)
        .filter((w) => w.length >= 2);
}
/**
 * 基于意图关键词推断子任务复杂度。
 * @param intent - 意图文本
 * @returns 复杂度: "low" / "medium" / "high"
 */
function inferComplexity(intent) {
    if (/加.*查询|改.*名称|修.*typo/.test(intent))
        return "low";
    if (/多个|批量|跨|联调|前后端|全链路/.test(intent))
        return "high";
    return "medium";
}
//# sourceMappingURL=task_planner.js.map