const CONTRACT_HINTS = {
    api: /\b(api|rest|endpoint|openapi|http route|controller)\b/i,
    graphql: /\b(graphql|gql|schema\.graphql|resolver)\b/i,
    event: /\b(event|webhook|queue|topic|message broker|kafka|rabbitmq|pubsub)\b/i,
    ui: /\b(ui contract|component contract|screen contract|design contract)\b/i,
    mcp: /\b(mcp|tool contract|model context protocol|registerTool|server tool)\b/i,
};
function isNonTrivial(spec) {
    return spec.scope !== 'trivial';
}
function normalizeCriterion(value) {
    return value
        .replace(/^-\s*\[[ x]\]\s*/i, '')
        .replace(/\s+/g, ' ')
        .trim()
        .toLowerCase();
}
function hasText(value) {
    return value !== undefined && value.trim().length > 0;
}
export function inferRequiredContractEvidence(spec, criteria) {
    const text = `${spec.title} ${spec.tags.join(' ')} ${criteria.join(' ')}`;
    const required = [];
    for (const [kind, pattern] of Object.entries(CONTRACT_HINTS)) {
        if (pattern.test(text)) {
            required.push(kind);
        }
    }
    return required;
}
export function checkDiscoveryGate(spec, artifacts) {
    if (!isNonTrivial(spec)) {
        return [];
    }
    const issues = [];
    if (!artifacts.discovery) {
        issues.push({
            code: 'discovery_missing',
            message: 'Discovery evidence is required before approval. Add discovery.json with rules, examples, open questions, out-of-scope boundaries, and glossary.',
        });
        return issues;
    }
    const unresolved = artifacts.discovery.openQuestions.filter((q) => q.status === 'open');
    if (unresolved.length > 0) {
        issues.push({
            code: 'discovery_unresolved_questions',
            message: `Discovery has unresolved open questions: ${unresolved.map((q) => q.question).join('; ')}`,
        });
    }
    return issues;
}
export function checkTaskPlanGate(spec, criteria, artifacts) {
    if (!isNonTrivial(spec)) {
        return [];
    }
    if (!artifacts.taskPlan) {
        return [
            {
                code: 'task_plan_missing',
                message: 'Task plan evidence is required before implementation. Add task-plan.json with tasks mapped to acceptance criteria.',
            },
        ];
    }
    const covered = new Set(artifacts.taskPlan.tasks.flatMap((task) => task.acceptanceCriteria.map(normalizeCriterion)));
    const uncovered = criteria.filter((criterion) => !covered.has(normalizeCriterion(criterion)));
    if (uncovered.length === 0) {
        return [];
    }
    return [
        {
            code: 'task_plan_uncovered_criteria',
            message: `Task plan does not cover acceptance criteria: ${uncovered.join('; ')}`,
        },
    ];
}
function rowHasEvidence(row) {
    return (hasText(row.scenario) ||
        (row.testEvidence !== undefined && row.testEvidence.length > 0) ||
        (row.contractEvidence !== undefined && row.contractEvidence.length > 0) ||
        hasText(row.manualEvidence));
}
export function checkDoneEvidenceGate(spec, criteria, artifacts) {
    const issues = [];
    const matrix = artifacts.traceabilityMatrix;
    if (!matrix) {
        issues.push({
            code: 'traceability_missing',
            message: 'Traceability matrix evidence is required before done. Add traceability-matrix.json covering each acceptance criterion.',
        });
    }
    else {
        const covered = new Set(matrix.rows.map((row) => normalizeCriterion(row.acceptanceCriterion)));
        const uncovered = criteria.filter((criterion) => !covered.has(normalizeCriterion(criterion)));
        if (uncovered.length > 0) {
            issues.push({
                code: 'traceability_uncovered_criteria',
                message: `Traceability matrix does not cover acceptance criteria: ${uncovered.join('; ')}`,
            });
        }
        const incompleteRows = matrix.rows.filter((row) => !rowHasEvidence(row) ||
            row.changedFiles.length === 0 ||
            !hasText(row.validationEvidence) ||
            !hasText(row.reviewerEvidence));
        if (incompleteRows.length > 0) {
            issues.push({
                code: 'traceability_incomplete_rows',
                message: 'Traceability rows must include scenario/test/contract/manual evidence, changed files, validation evidence, and reviewer evidence.',
            });
        }
    }
    const requiredKinds = inferRequiredContractEvidence(spec, criteria);
    const passedKinds = new Set(artifacts.contractValidations
        .filter((artifact) => artifact.passed)
        .map((artifact) => artifact.kind));
    const failedKinds = artifacts.contractValidations
        .filter((artifact) => !artifact.passed)
        .map((artifact) => artifact.kind);
    const missingKinds = requiredKinds.filter((kind) => !passedKinds.has(kind));
    if (missingKinds.length > 0) {
        issues.push({
            code: 'contract_validation_missing',
            message: `Contract validation evidence is required for: ${missingKinds.join(', ')}`,
        });
    }
    if (failedKinds.length > 0) {
        issues.push({
            code: 'contract_validation_failed',
            message: `Contract validation evidence failed for: ${failedKinds.join(', ')}`,
        });
    }
    return issues;
}
export function checkLifecycleEvidenceGate(args) {
    const issues = args.artifacts.invalidArtifacts.map((artifact) => ({
        code: 'evidence_artifact_invalid',
        message: artifact,
    }));
    if (args.transition === 'approved') {
        issues.push(...checkDiscoveryGate(args.spec, args.artifacts));
    }
    else if (args.transition === 'implementing') {
        issues.push(...checkTaskPlanGate(args.spec, args.criteria, args.artifacts));
    }
    else {
        issues.push(...checkDoneEvidenceGate(args.spec, args.criteria, args.artifacts));
    }
    return {
        passed: issues.length === 0,
        issues,
        requiredContractKinds: inferRequiredContractEvidence(args.spec, args.criteria),
    };
}
//# sourceMappingURL=lifecycle-gate.js.map