// engine/ambiguity-scorer.ts — SPEC-632: Detect undefined units and vague triggers in criteria
import { readFile } from 'node:fs/promises';
import { stripFrontmatter } from './frontmatter-parser.js';
const VAGUE_UNITS = {
    fast: 'No latency target specified (e.g. "< 200ms")',
    slow: 'No latency target specified (e.g. "> 5s")',
    quick: 'No latency target specified (e.g. "< 100ms")',
    large: 'No size threshold specified (e.g. "> 10MB")',
    small: 'No size threshold specified (e.g. "< 1KB")',
    big: 'No size threshold specified',
    huge: 'No size threshold specified',
    tiny: 'No size threshold specified',
    many: 'No quantity threshold specified (e.g. "> 1000")',
    few: 'No quantity threshold specified (e.g. "< 5")',
    several: 'No quantity threshold specified',
    numerous: 'No quantity threshold specified',
    high: 'No numeric threshold specified',
    low: 'No numeric threshold specified',
    soon: 'No time window specified (e.g. "within 2 minutes")',
    efficient: 'No efficiency metric specified (e.g. "< 50ms CPU")',
    scalable: 'No scale target specified (e.g. "1000 req/s")',
    responsive: 'No latency target specified (e.g. "< 100ms")',
};
const VAGUE_TRIGGERS = {
    sometimes: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    often: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    usually: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    rarely: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    frequently: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    occasionally: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    typically: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    generally: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    normally: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
    mostly: 'Trigger condition is non-deterministic — use WHEN + concrete condition',
};
const UNIT_PENALTY = 10;
const TRIGGER_PENALTY = 15;
const MAX_PENALTY = 100;
function buildSuggestion(term, criterion) {
    const clean = criterion.slice(0, 80);
    return `Replace "${term}" with a concrete measurable value, e.g.: "${clean.replace(new RegExp(`\\b${term}\\b`, 'gi'), `<${term}_value>`)}"`;
}
function hasAdjacentNumber(line, term) {
    // Check if a number appears within 4 words of the vague term
    const words = line.toLowerCase().split(/\s+/);
    const idx = words.indexOf(term);
    if (idx === -1) {
        return false;
    }
    const window = words.slice(Math.max(0, idx - 4), idx + 5);
    return window.some((w) => /\d/.test(w));
}
/**
 * SPEC-632: Score spec criteria for ambiguity.
 * Returns score 0-100 and a list of violations with rewrite suggestions.
 * Score < 70 blocks the approved transition.
 */
export function scoreAmbiguity(criteriaLines) {
    const violations = [];
    let penalty = 0;
    for (const line of criteriaLines) {
        if (line === 'bdd-scenario') {
            continue;
        }
        const lower = line.toLowerCase();
        // Check vague units (skip if adjacent number is present)
        for (const [term, reason] of Object.entries(VAGUE_UNITS)) {
            const pattern = new RegExp(`\\b${term}\\b`);
            if (pattern.test(lower) && !hasAdjacentNumber(lower, term)) {
                violations.push({
                    term,
                    criterion: line.slice(0, 120),
                    reason,
                    suggestion: buildSuggestion(term, line),
                });
                penalty += UNIT_PENALTY;
            }
        }
        // Check vague triggers
        for (const [term, reason] of Object.entries(VAGUE_TRIGGERS)) {
            const pattern = new RegExp(`\\b${term}\\b`);
            if (pattern.test(lower)) {
                violations.push({
                    term,
                    criterion: line.slice(0, 120),
                    reason,
                    suggestion: `Replace "${term}" with a deterministic WHEN condition`,
                });
                penalty += TRIGGER_PENALTY;
            }
        }
    }
    const score = Math.max(0, 100 - Math.min(penalty, MAX_PENALTY));
    const blockers = score < 70
        ? violations.map((v) => `Ambiguity: "${v.term}" in criterion — ${v.reason}.\n  Suggestion: ${v.suggestion}`)
        : [];
    return { score, violations, blockers };
}
/** Minimal criteria extractor — same logic as readiness-checker without the full dependency chain. */
function extractLines(body) {
    const lines = body.split('\n');
    const result = [];
    let inSection = false;
    for (const raw of lines) {
        const line = raw.trim();
        if (/^#{1,6}\s+/.test(line)) {
            inSection = /^#{1,6}\s+(acceptance\s+criteria|criteria|criterios)\b/i.test(line);
            continue;
        }
        if (/^- \[[ x]\]/i.test(line) || /^- .*\bgiven\b.*\bwhen\b.*\bthen\b/i.test(line)) {
            result.push(line);
            continue;
        }
        if (inSection && /^- \S/.test(line)) {
            result.push(line);
        }
    }
    return result;
}
/**
 * SPEC-632: Read spec file at path, extract criteria, and return an AmbiguityReport.
 * Returns null if file cannot be read (best-effort, never blocks).
 */
export async function scoreAmbiguityFromPath(specPath) {
    try {
        const raw = await readFile(specPath, 'utf-8');
        const body = stripFrontmatter(raw);
        const lines = extractLines(body);
        if (lines.length === 0) {
            return null;
        }
        return scoreAmbiguity(lines);
    }
    catch {
        return null;
    }
}
//# sourceMappingURL=ambiguity-scorer.js.map