// SPEC-597-EXEMPT: canonical mapping (npm-package↔framework, keyword catalogue)
// — must enumerate framework names by design; no detection logic to migrate.
// engine/model-router/complexity-analyzer.ts — Extract and normalise complexity signals.
// SPEC-076 AC-01
// ---------------------------------------------------------------------------
// Normalisation ceilings (signals are clamped to 0-1 using these maximums)
// ---------------------------------------------------------------------------
const MAX_CRITERIA = 40;
const MAX_FILES = 30;
const MAX_DEPTH = 6;
const MAX_CROSS_LAYER = 5;
const MAX_HOURS = 80;
/** Clamp a value into [0, 1] by dividing by its ceiling. */
function norm(value, ceiling) {
    if (ceiling <= 0) {
        return 0;
    }
    return Math.min(Math.max(value / ceiling, 0), 1);
}
// ---------------------------------------------------------------------------
// Signal extraction from text
// ---------------------------------------------------------------------------
const TASK_KEYWORDS = {
    search: ['search', 'find', 'explore', 'list', 'read', 'summarize', 'grep'],
    generation: ['generate', 'create', 'implement', 'write', 'build', 'add', 'scaffold'],
    architecture: ['architect', 'design', 'restructure', 'system', 'plan', 'rfc'],
    debugging: ['debug', 'fix', 'investigate', 'trace', 'diagnose', 'bug', 'error'],
    refactor: ['refactor', 'rename', 'extract', 'simplify', 'clean', 'reorganize', 'move'],
};
const SECURITY_KEYWORDS = [
    'security',
    'auth',
    'permission',
    'rbac',
    'encryption',
    'token',
    'jwt',
    'cors',
    'csrf',
    'xss',
    'injection',
    'sanitize',
    'pii',
    'gdpr',
];
const DB_KEYWORDS = [
    'database',
    'migration',
    'schema',
    'table',
    'column',
    'index',
    'query',
    'sql',
    'postgres',
    'mysql',
    'mongo',
    'redis',
    'prisma',
    'drizzle',
];
/** Keywords that indicate ultra-deep reasoning is required (SPEC-259). */
const ULTRATHINK_KEYWORDS = [
    'architecture',
    'migration',
    'redesign',
    'security audit',
    'performance bottleneck',
    'race condition',
    'distributed system',
    'refactor entire',
    'redesign the',
];
/** Infer a TaskType from a text description. */
function inferTaskType(text) {
    const lower = text.toLowerCase();
    let bestType = 'generation';
    let bestScore = 0;
    for (const [type, keywords] of Object.entries(TASK_KEYWORDS)) {
        const score = keywords.filter((kw) => lower.includes(kw)).length;
        if (score > bestScore) {
            bestScore = score;
            bestType = type;
        }
    }
    return bestType;
}
/** Count how many keywords from a list appear in a text. */
function keywordHits(text, keywords) {
    const lower = text.toLowerCase();
    return keywords.filter((kw) => lower.includes(kw)).length;
}
/** Count acceptance criteria by looking for AC- or numbered list patterns. */
function countCriteria(text) {
    const acMatches = text.match(/\bAC-\d+/g);
    if (acMatches) {
        return acMatches.length;
    }
    // Fallback: count markdown list items that look like criteria
    const listItems = text.match(/^[-*]\s+/gm);
    return listItems ? Math.ceil(listItems.length / 2) : 0;
}
/** Estimate file count from technical sheet patterns. */
function countFiles(text) {
    const fileRefs = text.match(/`[^`]+\.(ts|js|json|md|tsx|jsx|py|go|rs)`/g);
    return fileRefs ? fileRefs.length : 0;
}
/** Count distinct architectural layers referenced. */
function countLayers(text) {
    const layers = ['types/', 'engine/', 'tools/', 'storage/', 'config/', 'resources/'];
    return layers.filter((l) => text.includes(l)).length;
}
// ---------------------------------------------------------------------------
// Internal helpers to reduce extractSignals complexity
// ---------------------------------------------------------------------------
/** Extract boolean and categorical signals from text, applying overrides. */
function extractQualitativeSignals(text, overrides) {
    return {
        hasNewTypes: overrides?.hasNewTypes ?? text.includes('types/'),
        hasDBChanges: overrides?.hasDBChanges ?? keywordHits(text, DB_KEYWORDS) >= 2,
        hasSecurityImplications: overrides?.hasSecurityImplications ?? keywordHits(text, SECURITY_KEYWORDS) >= 2,
        isReadOnly: overrides?.isReadOnly ?? false,
        taskType: overrides?.taskType ?? inferTaskType(text),
    };
}
/** Extract raw signal values from text, applying overrides. */
function extractRawSignals(text, overrides) {
    const criteriaCount = overrides?.criteriaCount ?? countCriteria(text);
    const fileCount = overrides?.fileCount ?? countFiles(text);
    const crossLayerCount = overrides?.crossLayerCount ?? countLayers(text);
    const dependencyDepth = overrides?.dependencyDepth ?? Math.min(crossLayerCount, MAX_DEPTH);
    const estimatedHours = overrides?.estimatedHours ?? criteriaCount * 0.5;
    const qualitative = extractQualitativeSignals(text, overrides);
    return {
        criteriaCount,
        fileCount,
        dependencyDepth,
        crossLayerCount,
        estimatedHours,
        ...qualitative,
    };
}
// ---------------------------------------------------------------------------
// Effort suggestion computation (SPEC-259)
// ---------------------------------------------------------------------------
/**
 * Compute the suggested effort level for a task based on its signals and description.
 * Returns 'ultrathink' for architectural/high-risk tasks, scaling down to 'low' for simple ones.
 */
export function computeEffortSuggestion(signals, text) {
    const lower = text.toLowerCase();
    // Ultrathink conditions
    const hasUltrathinkKeyword = ULTRATHINK_KEYWORDS.some((kw) => lower.includes(kw));
    const isArchitectural = signals.taskType === 'architecture';
    const isHighRisk = signals.hasSecurityImplications;
    const isDifficultyUltra = signals.criteriaCount >= 20;
    if (isArchitectural || isHighRisk || isDifficultyUltra || hasUltrathinkKeyword) {
        return 'ultrathink';
    }
    // High: difficulty >= 3 (criteriaCount >= 12) or cross-module (crossLayerCount >= 3)
    if (signals.criteriaCount >= 12 || signals.crossLayerCount >= 3) {
        return 'high';
    }
    // Medium: difficulty >= 2 (criteriaCount >= 6)
    if (signals.criteriaCount >= 6) {
        return 'medium';
    }
    return 'low';
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Extract complexity signals from a text description (spec content or task description).
 * Missing signals are filled with sensible defaults.
 */
export function extractSignals(text, overrides) {
    const raw = extractRawSignals(text, overrides);
    const normalised = normaliseSignals(raw);
    const overallScore = computeOverallScore(normalised);
    const effortSuggestion = computeEffortSuggestion(raw, text);
    const requiresDeepReasoning = effortSuggestion === 'ultrathink' ||
        raw.taskType === 'architecture' ||
        text.toLowerCase().includes('architecture');
    return { ...raw, overallScore, requiresDeepReasoning, effortSuggestion };
}
/** Build signals entirely from an explicit partial object (no text analysis). */
export function buildSignalsFromPartial(partial) {
    return extractSignals('', partial);
}
/** Normalise raw numeric signals to the 0-1 range. */
export function normaliseSignals(raw) {
    return {
        criteriaCount: norm(raw.criteriaCount, MAX_CRITERIA),
        fileCount: norm(raw.fileCount, MAX_FILES),
        dependencyDepth: norm(raw.dependencyDepth, MAX_DEPTH),
        crossLayerCount: norm(raw.crossLayerCount, MAX_CROSS_LAYER),
        estimatedHours: norm(raw.estimatedHours, MAX_HOURS),
        hasNewTypes: raw.hasNewTypes ? 1 : 0,
        hasDBChanges: raw.hasDBChanges ? 1 : 0,
        hasSecurityImplications: raw.hasSecurityImplications ? 1 : 0,
        isReadOnly: raw.isReadOnly ? 1 : 0,
    };
}
/**
 * Compute a weighted overall complexity score (0-1) from normalised signals.
 * Higher = more complex.
 */
export function computeOverallScore(n) {
    const weights = {
        criteriaCount: 0.25,
        fileCount: 0.15,
        dependencyDepth: 0.1,
        crossLayerCount: 0.15,
        estimatedHours: 0.1,
        hasNewTypes: 0.05,
        hasDBChanges: 0.05,
        hasSecurityImplications: 0.1,
        isReadOnly: -0.05, // read-only reduces complexity
    };
    let score = 0;
    for (const [key, weight] of Object.entries(weights)) {
        score += n[key] * weight;
    }
    return Math.min(Math.max(score, 0), 1);
}
//# sourceMappingURL=complexity-analyzer.js.map