import YAML from "yaml";
import { buildDeliveryEvidenceChain } from "./semantic_evidence.js";
import { classifyCommand, checkSupplyChain } from "./runtime_safety.js";
import { scanTextSensitivity, redactSensitiveText } from "./privacy_secret_contract.js";
import { preDeliveryReview } from "./developer_sovereignty.js";
import { debug } from "../core/logger.js";
import { errorMessage, safeJsonParse } from "../core/helpers.js";
/**
 * 执行完整的交付流程：提交代码、推送到远程、生成 PR 命令和变更日志。
 * 流程按顺序执行，每一步失败后记录错误并继续后续步骤。
 * @param input - 交付输入参数
 * @returns 交付结果，包含已执行操作、跳过操作、错误信息和 commit hash
 */
export async function deliver(input) {
    const { taskContext, config, knowledgeIndex, projectPath, gitOps, skipPush, skipPr, } = input;
    debug("交付", `开始执行任务 ${taskContext.task_id} 的交付流程`);
    // ── 交付前置状态检查 ──
    const ALLOWED_DELIVERY_STATUS = new Set(["verifying", "done", "learning"]);
    if (!ALLOWED_DELIVERY_STATUS.has(taskContext.status)) {
        return {
            task_id: taskContext.task_id,
            actions_performed: [],
            commit_message: "",
            branch: "",
            skipped_actions: [],
            errors: [`交付前置检查失败: 任务状态为 ${taskContext.status}，仅 verifying/done/learning 状态可交付`],
        };
    }
    // ── 交付前置验证检查 ──
    if (taskContext.verification) {
        const v = taskContext.verification;
        const buildOk = v.checks?.build?.length ? true : false;
        const negationPassed = v.triple_negation?.passed !== false;
        const integrationPassed = v.main_path_integration?.passed !== false;
        if (!buildOk || !negationPassed || !integrationPassed) {
            return {
                task_id: taskContext.task_id,
                actions_performed: [],
                commit_message: "",
                branch: "",
                skipped_actions: [],
                errors: [`交付前置检查失败: 验证未通过 build=${buildOk}, triple_negation=${negationPassed}, main_path_integration=${integrationPassed}`],
            };
        }
    }
    const result = {
        task_id: taskContext.task_id,
        actions_performed: [],
        commit_message: "",
        branch: "",
        skipped_actions: [],
        errors: [],
    };
    // 1. 从产品配置文件加载交付行为配置
    const deliveryConfig = resolveDeliveryConfig(knowledgeIndex, config.product_profile);
    // 2. 获取当前分支名称（作为后续推送和 PR 的目标分支）
    try {
        result.branch = await gitOps.getCurrentBranch(projectPath);
    }
    catch (e) {
        const errMsg = `无法获取当前分支: ${errorMessage(e)}`;
        result.errors.push(errMsg);
        debug("交付", `${errMsg}`);
        return result;
    }
    // 3. 根据任务信息构建所有候选 payload（commit_message, changelog, PR command）
    const entry = generateChangelogEntry(taskContext);
    result.commit_message = formatCommitMessage(entry);
    result.changelog = formatChangelog(entry);
    // 3.3. 生成 PR 创建命令候选（在隐私门禁之前，以便统一扫描）
    let prCandidate;
    let prHasRemote = false;
    if (deliveryConfig.auto_pr && !skipPr) {
        const remoteUrl = await gitOps
            .getRemoteUrl(projectPath)
            .catch(() => null);
        if (remoteUrl) {
            const labels = deliveryConfig.pr_labels.length > 0
                ? ` --label ${deliveryConfig.pr_labels.join(",")}`
                : "";
            prCandidate = `gh pr create --title "${shellEscape(entry.type)}(${shellEscape(entry.scope)}): ${shellEscape(entry.description)}" --body "Task: ${taskContext.task_id}"${labels}`;
            prHasRemote = true;
        }
    }
    // 3.5. 运行安全包 — 命令分级 advisory（不 hard block）
    const safetyAdvisories = [];
    // 检查验证命令是否有 destructive 级别
    const verifyCommands = taskContext.verification?.checks
        ? [
            ...taskContext.verification.checks.build,
            ...taskContext.verification.checks.tests,
        ]
        : [];
    for (const cmd of verifyCommands) {
        const classified = classifyCommand(cmd.command);
        if (classified.risk === "destructive") {
            safetyAdvisories.push(`安全建议: 检测到破坏性验证命令: ${cmd.command} — ${classified.advisory}`);
        }
    }
    // 检查推送命令风险
    const pushClassified = classifyCommand(`git push origin ${result.branch}`);
    if (pushClassified.risk !== "read_only") {
        safetyAdvisories.push(`安全建议: push 命令风险级别为 ${pushClassified.risk} — ${pushClassified.advisory}`);
    }
    // 供应链检查: 依赖变更时调用 checkSupplyChain 检查 lockfile/integrity
    const changedFiles = taskContext.execution?.changed_files ?? [];
    const depFiles = changedFiles.filter((f) => f.endsWith("package.json") || f.endsWith("package-lock.json") ||
        f.endsWith("pnpm-lock.yaml") || f.endsWith("yarn.lock") ||
        f.endsWith("go.mod") || f.endsWith("go.sum") ||
        f.endsWith("Cargo.toml") || f.endsWith("Cargo.lock"));
    if (depFiles.length > 0) {
        try {
            // 使用文件系统根目录列表进行 lockfile 检测（非 git status）
            const fss = await import("node:fs");
            const nodePath = await import("node:path");
            const rootEntries = fss.readdirSync(projectPath);
            const rootFiles = rootEntries.filter((e) => e.endsWith(".json") || e.endsWith(".yaml") || e.endsWith(".yarn") ||
                e.endsWith(".lock") || e.endsWith(".sum") || e.endsWith(".toml"));
            // 如果存在 package.json，读取依赖列表
            const fsAsync = await import("node:fs/promises");
            const deps = [];
            const pkgPath = nodePath.join(projectPath, "package.json");
            try {
                const pkg = safeJsonParse(await fsAsync.readFile(pkgPath, "utf-8"));
                if (!pkg)
                    throw new Error("parse failed");
                const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
                for (const [name, version] of Object.entries(allDeps)) {
                    deps.push({ name, version: String(version) });
                }
            }
            catch { /* 无 package.json 或解析错误 */ }
            if (deps.length > 0) {
                const scReport = checkSupplyChain(rootFiles, deps);
                for (const check of scReport.checks) {
                    if (!check.has_lockfile) {
                        safetyAdvisories.push(`安全建议: ${check.package_name}@${check.version} — 缺少 lockfile 或完整性哈希`);
                    }
                }
            }
            else {
                safetyAdvisories.push("安全建议: 依赖文件发生变更 — 建议运行供应链检查 (sf_dependency_scan)");
            }
        }
        catch {
            safetyAdvisories.push("安全建议: 依赖文件发生变更 — 供应链检查执行失败");
        }
    }
    if (safetyAdvisories.length > 0) {
        result.safety_advisories = safetyAdvisories;
        debug("交付", `检测到 ${safetyAdvisories.length} 项安全建议`);
    }
    // 3.6. Developer Sovereignty — 交付前审查（advisory, 不 hard block）
    try {
        const review = await preDeliveryReview(taskContext, gitOps, projectPath);
        result.pre_delivery_review = review;
    }
    catch {
        // 审查失败仅为建议级别，不阻断交付
    }
    // 3.7. 隐私门禁: 统一扫描所有生成的 payload
    const commitLabels = scanTextSensitivity(result.commit_message, "commit_message");
    const changelogLabels = scanTextSensitivity(result.changelog ?? "", "changelog");
    const prLabels = scanTextSensitivity(prCandidate ?? "", "pr_body");
    const allDeliveryLabels = [...commitLabels, ...changelogLabels, ...prLabels];
    const deliverySecrets = allDeliveryLabels.filter((l) => l.label === "secret" || l.label === "credential");
    if (deliverySecrets.length > 0) {
        const errMsg = `隐私门禁阻断: 交付内容包含禁止级别的敏感信息 (${deliverySecrets.map((l) => l.source_ref + ":" + l.label).join(", ")}), 不允许提交`;
        result.errors.push(errMsg);
        debug("交付", `${errMsg}`);
        // 秘密级别硬阻断: 不提交，不设置 pr_url
        return result;
    }
    // 对所有交付 payload 执行 PII 脱敏
    const commitRedacted = redactSensitiveText(result.commit_message, "commit_message");
    if (commitRedacted.records.length > 0) {
        result.commit_message = commitRedacted.redacted;
    }
    const changelogRedacted = redactSensitiveText(result.changelog ?? "", "changelog");
    if (changelogRedacted.records.length > 0 && result.changelog) {
        result.changelog = changelogRedacted.redacted;
    }
    if (prCandidate) {
        const prRedacted = redactSensitiveText(prCandidate, "pr_body");
        if (prRedacted.records.length > 0) {
            prCandidate = prRedacted.redacted;
        }
    }
    // 4. 执行 git commit（如果配置允许自动提交）
    if (deliveryConfig.auto_commit) {
        try {
            const changedFiles = taskContext.execution?.changed_files ?? [];
            // 优先使用任务上下文中记录的变更文件列表
            if (changedFiles.length > 0) {
                await gitOps.add(projectPath, changedFiles);
            }
            else {
                // 无明确变更记录时，暂存所有改动
                await gitOps.add(projectPath, ["."]);
            }
            const commitOutput = await gitOps.commit(projectPath, result.commit_message);
            // 从提交输出中提取 commit hash，回退到 git rev-parse
            const hashMatch = commitOutput.match(/\[[^\s]+ ([0-9a-f]{7,})\]/);
            if (hashMatch) {
                result.commit_hash = hashMatch[1];
            }
            else {
                try {
                    result.commit_hash = await gitOps.getHeadCommitHash(projectPath);
                }
                catch {
                    result.commit_hash = undefined;
                }
            }
            result.actions_performed.push("commit");
            debug("交付", `提交成功 ${result.commit_hash}`);
            // 更新 pre_delivery_review 中的回滚命令，指向当前 commit
            if (result.pre_delivery_review && result.commit_hash) {
                result.pre_delivery_review.rollback_command =
                    `git revert ${result.commit_hash} --no-edit`;
            }
        }
        catch (e) {
            const errMsg = `提交失败: ${errorMessage(e)}`;
            result.errors.push(errMsg);
            debug("交付", `${errMsg}`);
            result.skipped_actions.push({
                action: "commit",
                reason: "git commit 失败",
            });
        }
    }
    else {
        result.skipped_actions.push({
            action: "commit",
            reason: "DeliveryConfig.auto_commit = false",
        });
    }
    // 5. 推送到远程仓库（需要配置了自动推送且用户未跳过）
    if (deliveryConfig.auto_push && !skipPush) {
        try {
            const remoteUrl = await gitOps.getRemoteUrl(projectPath);
            // 仅当存在远程仓库时才执行推送
            if (remoteUrl) {
                await gitOps.push(projectPath, "origin", result.branch);
                result.actions_performed.push("push");
                debug("交付", `推送成功 ${result.branch}`);
            }
            else {
                result.skipped_actions.push({
                    action: "push",
                    reason: "未配置远程仓库",
                });
            }
        }
        catch (e) {
            const errMsg = `推送失败: ${errorMessage(e)}`;
            result.errors.push(errMsg);
            debug("交付", `${errMsg}`);
            result.skipped_actions.push({
                action: "push",
                reason: "git push 失败",
            });
        }
    }
    else if (skipPush) {
        result.skipped_actions.push({
            action: "push",
            reason: "用户跳过",
        });
    }
    else {
        result.skipped_actions.push({
            action: "push",
            reason: "DeliveryConfig.auto_push = false",
        });
    }
    // 6. 设置 PR 命令（已在 3.3 生成候选，此处设置最终结果）
    if (prCandidate) {
        result.pr_url = prCandidate;
        result.actions_performed.push("pr");
        debug("交付", "PR 命令已生成");
    }
    else if (deliveryConfig.auto_pr && !skipPr && !prHasRemote) {
        result.skipped_actions.push({
            action: "pr",
            reason: "未配置远程仓库",
        });
    }
    else if (!deliveryConfig.auto_pr || skipPr) {
        result.skipped_actions.push({
            action: "pr",
            reason: skipPr ? "用户跳过" : "DeliveryConfig.auto_pr = false",
        });
    }
    // 7. 记录 changelog 动作（已在 3 生成）
    result.actions_performed.push("changelog");
    // 8. 构建语义交付证据链（advisory, 不 hard block）
    result.evidence_chain = buildDeliveryEvidenceChain(taskContext) ?? undefined;
    debug("交付", `完成，已执行操作: ${result.actions_performed.join(", ")}, 错误: ${result.errors.length}`);
    return result;
}
// ── Shell 转义辅助函数 ──
/**
 * 对字符串进行 shell 安全转义，防止双引号内注入。
 * 转义双引号、反引号、美元符号和反斜杠。
 * @param str - 需要转义的字符串
 * @returns 转义后的安全字符串
 */
export function shellEscape(str) {
    // 先处理换行符（防止换行注入），再转义双引号内特殊字符
    return str
        .replace(/\r?\n/g, "\\n")
        .replace(/["$`\\!]/g, "\\$&");
}
// ── 变更日志辅助函数 ──
/**
 * 根据任务上下文生成 Conventional Commits 格式的变更日志条目。
 * 任务类型映射：feature→feat, bugfix/hotfix→fix, refactor→refactor, test→test 等。
 * @param taskContext - 当前任务的上下文
 * @returns 变更日志条目，包含类型、作用域、描述和任务 ID
 */
export function generateChangelogEntry(taskContext) {
    // 任务类型到 Conventional Commits 类型的映射表
    const typeMap = {
        feature: "feat",
        bugfix: "fix",
        hotfix: "fix",
        refactor: "refactor",
        test: "test",
        document: "docs",
        scaffold: "feat",
        optimize: "chore",
    };
    const taskType = taskContext.classification?.task_type ?? "feature";
    return {
        type: typeMap[taskType] ?? "chore",
        scope: taskContext.classification?.affected_repos[0] ?? "core",
        description: taskContext.intent.slice(0, 80), // 意图描述截断为 80 字符
        task_id: taskContext.task_id,
    };
}
// ── 内部辅助函数 ──
/**
 * 将变更日志条目格式化为 Git 提交消息。
 * 格式: `type(scope): description\n\nTask: taskId`
 * @param entry - 变更日志条目
 * @returns 格式化的提交消息字符串
 */
function formatCommitMessage(entry) {
    return `${entry.type}(${entry.scope}): ${entry.description}\n\nTask: ${entry.task_id}`;
}
/**
 * 将变更日志条目格式化为 Markdown 格式的变更日志行。
 * @param entry - 变更日志条目
 * @returns Markdown 格式的变更日志字符串
 */
function formatChangelog(entry) {
    const date = new Date().toISOString().split("T")[0];
    return `## [${date}]\n\n- **${entry.type}(${entry.scope})**: ${entry.description} (${entry.task_id})`;
}
/**
 * 从知识索引中解析交付配置。
 * 在产品配置文件（domain 类型知识条目）中查找 delivery 配置块。
 * 找到则使用配置值，否则返回默认配置（仅自动提交，不自动推送/PR）。
 * @param knowledgeIndex - 知识索引管理器
 * @param productProfile - 产品配置文件名
 * @returns 交付行为配置
 */
function resolveDeliveryConfig(knowledgeIndex, productProfile) {
    // 默认配置：仅自动提交，推送和 PR 需手动触发
    const defaults = {
        auto_commit: true,
        auto_push: false,
        auto_pr: false,
        pr_labels: [],
        require_approval: false,
    };
    const profileEntries = knowledgeIndex.query({
        type: "domain",
        products: [productProfile],
        limit: 5,
    });
    for (const entry of profileEntries) {
        if (!entry.body)
            continue;
        try {
            const parsed = YAML.parse(entry.body);
            if (parsed?.delivery) {
                return {
                    auto_commit: parsed.delivery.auto_commit ?? defaults.auto_commit,
                    auto_push: parsed.delivery.auto_push ?? defaults.auto_push,
                    auto_pr: parsed.delivery.auto_pr ?? defaults.auto_pr,
                    pr_labels: parsed.delivery.pr_labels ?? defaults.pr_labels,
                    require_approval: parsed.delivery.require_approval ?? defaults.require_approval,
                };
            }
        }
        catch {
            /* 忽略解析错误 */
        }
    }
    return defaults;
}
//# sourceMappingURL=delivery.js.map