/**
 * 设计产物包真实复验。
 *
 * 本模块读取用户项目中的真实设计资产，检查权威文件存在性、
 * API 字段级合同、SQL 显著不可执行语法和跨文档关键冲突。
 */
import fs from "node:fs";
import path from "node:path";
import { createHash } from "node:crypto";
import YAML from "yaml";
import { auditDesignImplementationTraceability, } from "./traceability.js";
import { getDesignLifecycleArtifactAliases, getDesignLifecycleArtifactPaths } from "./design_lifecycle_contract.js";
export function defaultDesignArtifactPaths() {
    return getDesignLifecycleArtifactPaths();
}
export function createDesignArtifactPack(taskId, paths = {}) {
    return {
        task_id: taskId,
        standard_version: "design-artifact-pack/v1",
        status: "draft",
        paths: { ...defaultDesignArtifactPaths(), ...paths },
        baseline_hashes: {},
        findings: [],
    };
}
function resolveTraceabilityMatrixPath(paths) {
    return paths.traceability_matrix ?? getDesignLifecycleArtifactPaths().traceability_matrix;
}
function readText(rootDir, relativePath) {
    const absolutePath = path.join(rootDir, relativePath);
    return fs.existsSync(absolutePath) ? fs.readFileSync(absolutePath, "utf-8") : undefined;
}
function resolveReadableArtifactPath(rootDir, canonicalPath) {
    for (const candidate of [canonicalPath, ...getDesignLifecycleArtifactAliases(canonicalPath)]) {
        if (readText(rootDir, candidate) !== undefined)
            return candidate;
    }
    return undefined;
}
function addMissing(findings, relativePath) {
    findings.push({ code: "DESIGN_ARTIFACT_MISSING", severity: "hard_fail", file: relativePath, message_zh: `缺少设计产物: ${relativePath}` });
}
function collectSqlFiles(rootDir, roots) {
    const results = [];
    for (const relativeRoot of roots) {
        const absoluteRoot = path.join(rootDir, relativeRoot);
        if (!fs.existsSync(absoluteRoot))
            continue;
        const stack = [absoluteRoot];
        while (stack.length > 0) {
            const current = stack.pop();
            for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
                const absolutePath = path.join(current, entry.name);
                if (entry.isDirectory())
                    stack.push(absolutePath);
                else if (entry.name.endsWith(".sql"))
                    results.push(path.relative(rootDir, absolutePath));
            }
        }
    }
    return [...new Set(results)].sort();
}
function verifyApiFieldContract(content, file, findings) {
    const endpointCount = (content.match(/(?:^|\n)#{2,5}\s+.*(?:GET|POST|PUT|PATCH|DELETE)\s+\/|(?:^|\n)\s*(?:GET|POST|PUT|PATCH|DELETE)\s+\/\S+/g) ?? []).length;
    const requestTables = (content.match(/请求(?:体|参数| Body)?.{0,20}\n\s*\|/g) ?? []).length;
    const responseTables = (content.match(/响应(?:体|字段| Body)?.{0,20}\n\s*\|/g) ?? []).length;
    if (endpointCount === 0) {
        findings.push({ code: "API_ENDPOINTS_NOT_IDENTIFIED", severity: "hard_fail", file, message_zh: "API 规格未识别到 endpoint 定义，无法验证字段级契约" });
        return;
    }
    if (requestTables < endpointCount || responseTables < endpointCount) {
        findings.push({
            code: "API_FIELD_TABLE_INCOMPLETE",
            severity: "hard_fail",
            file,
            message_zh: `API 规格存在 ${endpointCount} 个 endpoint，但字段表不足（请求 ${requestTables}，响应 ${responseTables}）；每个接口必须具备请求与响应字段说明表`,
        });
    }
}
function extractApiOperations(content) {
    const matches = content.matchAll(/\b(GET|POST|PUT|PATCH|DELETE)\s+(\/api\/\S+)/gi);
    return [...matches].map((match) => ({
        method: match[1].toLowerCase(),
        path: match[2].replace(/[，。),;`]+$/, ""),
    }));
}
function verifyOpenApiCoverage(apiContent, openapiContent, file, findings) {
    let parsed;
    try {
        parsed = YAML.parse(openapiContent);
    }
    catch {
        findings.push({ code: "OPENAPI_PARSE_FAILED", severity: "hard_fail", file, message_zh: "OpenAPI YAML 无法解析，不得作为接口字段权威资产" });
        return;
    }
    const paths = parsed?.paths ?? {};
    const operations = extractApiOperations(apiContent);
    if (operations.length === 0 || Object.keys(paths).length === 0) {
        findings.push({ code: "OPENAPI_OPERATIONS_MISSING", severity: "hard_fail", file, message_zh: "API 文档或 OpenAPI 未提供可对账的 endpoint 操作" });
        return;
    }
    for (const operation of operations) {
        if (!paths[operation.path]?.[operation.method]) {
            findings.push({
                code: "OPENAPI_MARKDOWN_DRIFT",
                severity: "hard_fail",
                file,
                message_zh: `API 文档中的 ${operation.method.toUpperCase()} ${operation.path} 未在 OpenAPI 中定义同名操作`,
            });
        }
    }
}
export function verifyDesignArtifactPack(rootDir, contract) {
    const findings = [];
    const checkedFiles = [];
    const hashes = {};
    const requiredDocuments = [
        contract.paths.decision_record,
        contract.paths.architecture_document,
        contract.paths.database_document,
        contract.paths.api_document,
        contract.paths.slice_plan,
        contract.paths.consistency_report,
        resolveTraceabilityMatrixPath(contract.paths),
        contract.paths.openapi,
    ];
    const contents = new Map();
    for (const relativePath of requiredDocuments) {
        const actualPath = relativePath === contract.paths.slice_plan
            ? resolveReadableArtifactPath(rootDir, relativePath)
            : relativePath;
        const text = actualPath ? readText(rootDir, actualPath) : undefined;
        if (!actualPath || text === undefined) {
            addMissing(findings, relativePath);
        }
        else {
            if (relativePath === contract.paths.slice_plan && actualPath !== relativePath) {
                findings.push({
                    code: "DESIGN_ARTIFACT_LEGACY_SLICE_PLAN_PATH",
                    severity: "advisory",
                    file: actualPath,
                    message_zh: `检测到旧切片计划路径 ${actualPath}，当前标准路径为 ${relativePath}`,
                    recovery_zh: `将 ${actualPath} 迁移或复制为 ${relativePath}，保留旧文件仅作为历史兼容引用。`,
                    recovery_command: "soloforge upgrade-design-artifacts --apply --confirm",
                });
            }
            contents.set(relativePath, text);
            checkedFiles.push(actualPath);
            hashes[actualPath] = createHash("sha256").update(text).digest("hex");
        }
    }
    const apiContent = contents.get(contract.paths.api_document);
    if (apiContent) {
        verifyApiFieldContract(apiContent, contract.paths.api_document, findings);
        const openapiContent = contents.get(contract.paths.openapi);
        if (openapiContent) {
            verifyOpenApiCoverage(apiContent, openapiContent, contract.paths.openapi, findings);
        }
    }
    const dbContent = contents.get(contract.paths.database_document) ?? "";
    const sqlFiles = collectSqlFiles(rootDir, contract.paths.sql_roots);
    if (sqlFiles.length === 0) {
        findings.push({ code: "SQL_AUTHORITY_MISSING", severity: "hard_fail", message_zh: "缺少可执行 SQL/migration 权威资产，数据库 Markdown 不得单独作为物理结构结论" });
    }
    for (const sqlFile of sqlFiles) {
        const sql = readText(rootDir, sqlFile) ?? "";
        checkedFiles.push(sqlFile);
        hashes[sqlFile] = createHash("sha256").update(sql).digest("hex");
        if (/CONSTRAINT\s+\S+\s+UNIQUE\s*\([^)]*\)\s+WHERE\b/i.test(sql)) {
            findings.push({ code: "POSTGRES_PARTIAL_UNIQUE_INVALID", severity: "hard_fail", file: sqlFile, message_zh: "检测到 PostgreSQL 不支持的 CONSTRAINT UNIQUE ... WHERE 语法，应使用部分唯一索引并重新真实验证" });
        }
    }
    if (/PostgreSQL/i.test(dbContent) && /CONSTRAINT\s+\S+\s+UNIQUE\s*\([^)]*\)\s+WHERE\b/i.test(dbContent)) {
        findings.push({ code: "POSTGRES_PARTIAL_UNIQUE_INVALID", severity: "hard_fail", file: contract.paths.database_document, message_zh: "数据库设计文档包含 PostgreSQL 不可执行的条件唯一约束语法" });
    }
    const architectureContent = contents.get(contract.paths.architecture_document) ?? "";
    if (/"code"\s*:\s*200/.test(architectureContent) && /"code"\s*:\s*0/.test(apiContent ?? "")) {
        findings.push({ code: "CROSS_DOC_RESPONSE_CODE_CONFLICT", severity: "hard_fail", message_zh: "架构文档与 API 规格的统一响应业务码定义冲突" });
    }
    if (/\balert\b|告警/i.test(apiContent ?? "") && !/\balert\b|告警/i.test(dbContent)) {
        findings.push({ code: "CROSS_DOC_ENTITY_MISSING", severity: "hard_fail", message_zh: "API 规格引用告警实体，但数据库设计未定义对应实体或映射" });
    }
    const traceabilityReport = auditDesignImplementationTraceability(rootDir, resolveTraceabilityMatrixPath(contract.paths));
    for (const traceFinding of traceabilityReport.findings) {
        findings.push({
            code: traceFinding.code,
            severity: traceFinding.severity,
            file: traceFinding.file,
            message_zh: traceFinding.message_zh,
            recovery_zh: traceFinding.recovery_zh,
            recovery_command: traceFinding.recovery_command,
            template_path: traceFinding.template_path,
        });
    }
    for (const checkedFile of traceabilityReport.checked_files) {
        if (!checkedFiles.includes(checkedFile))
            checkedFiles.push(checkedFile);
    }
    const passed = findings.every((finding) => finding.severity !== "hard_fail");
    return { passed, status: passed ? "implementation_ready" : "verification_failed", findings, checked_files: checkedFiles, hashes };
}
//# sourceMappingURL=design_artifact_pack.js.map