import { containsAny } from './pattern-matcher.js';
import { ERROR_HANDLING_KEYWORDS, SECURITY_KEYWORDS, PERFORMANCE_KEYWORDS, ACCESSIBILITY_KEYWORDS, DATA_VALIDATION_KEYWORDS, OBSERVABILITY_KEYWORDS, EDGE_CASE_KEYWORDS, ROLLBACK_KEYWORDS, } from './constants.js';
/** Generates a sequential ID for a suggestion (e.g. "GAP-01"). */
function nextId(ctx, prefix) {
    const counter = ctx.idCounter;
    counter.value += 1;
    return `${prefix}-${String(counter.value).padStart(2, '0')}`;
}
/** Checks whether any existing AC already covers the given keywords. */
function acCovers(acLines, keywords) {
    const combined = acLines.join(' ').toLowerCase();
    return containsAny(combined, keywords);
}
// ---------------------------------------------------------------------------
// Individual gap rules
// ---------------------------------------------------------------------------
export const ruleErrorHandling = (ctx) => {
    if (acCovers(ctx.acLines, ERROR_HANDLING_KEYWORDS)) {
        return null;
    }
    return {
        id: nextId(ctx, 'EH'),
        category: 'error-handling',
        priority: 'high',
        suggestion: 'Add ACs covering error and failure scenarios',
        rationale: 'No existing AC addresses error handling. Every user-facing operation should specify behavior on failure.',
        example: 'When the operation fails due to a server error, the system shows an actionable error message and does not corrupt state.',
    };
};
export const ruleSecurityAuth = (ctx) => {
    if (acCovers(ctx.acLines, SECURITY_KEYWORDS)) {
        return null;
    }
    // Only flag if the spec text mentions anything that could be user-facing or data-mutating
    const mutableKeywords = ['create', 'update', 'delete', 'submit', 'write', 'post', 'put'];
    if (!containsAny(ctx.fullText, mutableKeywords)) {
        return null;
    }
    return {
        id: nextId(ctx, 'SEC'),
        category: 'security',
        priority: 'critical',
        suggestion: 'Add ACs specifying authentication and authorization requirements',
        rationale: 'The spec describes mutable operations but no AC defines who is allowed to perform them.',
        example: 'When an unauthenticated user attempts the action, the system returns 401 and logs the attempt.',
    };
};
export const ruleDataValidation = (ctx) => {
    if (acCovers(ctx.acLines, DATA_VALIDATION_KEYWORDS)) {
        return null;
    }
    const inputKeywords = ['input', 'form', 'field', 'parameter', 'payload', 'body', 'request'];
    if (!containsAny(ctx.fullText, inputKeywords)) {
        return null;
    }
    return {
        id: nextId(ctx, 'DV'),
        category: 'data-validation',
        priority: 'high',
        suggestion: 'Add ACs covering input validation boundaries and constraints',
        rationale: 'The spec references inputs or form fields but no AC defines validation rules, required fields, or format constraints.',
        example: 'When the user submits a required field as empty, the system highlights the field and prevents submission.',
    };
};
export const rulePerformance = (ctx) => {
    if (acCovers(ctx.acLines, PERFORMANCE_KEYWORDS)) {
        return null;
    }
    const heavyKeywords = ['list', 'search', 'query', 'load', 'export', 'report', 'bulk', 'batch'];
    if (!containsAny(ctx.fullText, heavyKeywords)) {
        return null;
    }
    return {
        id: nextId(ctx, 'PERF'),
        category: 'performance',
        priority: 'medium',
        suggestion: 'Add ACs defining acceptable response time or throughput',
        rationale: 'The spec involves potentially expensive operations (search/list/bulk) but no AC defines performance expectations.',
        example: 'When the user triggers a list/search, the response completes in under 2 seconds for up to 10,000 records.',
    };
};
export const ruleAccessibility = (ctx) => {
    if (!ctx.hasWebUi) {
        return null;
    }
    if (acCovers(ctx.acLines, ACCESSIBILITY_KEYWORDS)) {
        return null;
    }
    return {
        id: nextId(ctx, 'A11Y'),
        category: 'accessibility',
        priority: 'medium',
        suggestion: 'Add ACs verifying keyboard navigation and screen reader compatibility',
        rationale: 'This spec appears to include UI components but no AC ensures WCAG accessibility compliance.',
        example: 'When a keyboard-only user navigates the form, all interactive elements are reachable via Tab and operable via Enter/Space.',
    };
};
export const ruleEdgeCases = (ctx) => {
    if (acCovers(ctx.acLines, EDGE_CASE_KEYWORDS)) {
        return null;
    }
    // Flag when there are fewer than 3 ACs — sparse specs likely miss edge cases
    if (ctx.acLines.length >= 3) {
        return null;
    }
    return {
        id: nextId(ctx, 'EDGE'),
        category: 'edge-cases',
        priority: 'medium',
        suggestion: 'Add ACs covering empty/null/boundary input conditions',
        rationale: 'The spec has few acceptance criteria and likely misses edge cases such as empty collections, null values, or boundary inputs.',
        example: 'When the dataset is empty, the system renders a clear empty-state message instead of an error.',
    };
};
export const ruleObservability = (ctx) => {
    if (acCovers(ctx.acLines, OBSERVABILITY_KEYWORDS)) {
        return null;
    }
    const criticalKeywords = ['payment', 'billing', 'order', 'transaction', 'deploy', 'migration'];
    if (!containsAny(ctx.fullText, criticalKeywords)) {
        return null;
    }
    return {
        id: nextId(ctx, 'OBS'),
        category: 'observability',
        priority: 'high',
        suggestion: 'Add ACs specifying logging and audit trail requirements',
        rationale: 'The spec involves business-critical operations that typically require audit logs or metrics.',
        example: 'When the operation completes (or fails), an audit log entry is written with actor, timestamp, and outcome.',
    };
};
export const ruleRollback = (ctx) => {
    if (acCovers(ctx.acLines, ROLLBACK_KEYWORDS)) {
        return null;
    }
    const migrationKeywords = ['migration', 'schema change', 'deploy', 'upgrade', 'rollout'];
    if (!containsAny(ctx.fullText, migrationKeywords)) {
        return null;
    }
    return {
        id: nextId(ctx, 'RB'),
        category: 'rollback',
        priority: 'high',
        suggestion: 'Add ACs covering rollback or undo behavior if the operation fails midway',
        rationale: 'The spec describes a multi-step or migration-like operation but no AC defines the rollback strategy.',
        example: 'When the migration fails partway through, the system rolls back all changes and the database is left in its original state.',
    };
};
/** All rules, ordered from highest to lowest default priority. */
export const ALL_RULES = [
    ruleSecurityAuth,
    ruleErrorHandling,
    ruleDataValidation,
    ruleRollback,
    ruleObservability,
    rulePerformance,
    ruleAccessibility,
    ruleEdgeCases,
];
/** Map from category name to its rule for targeted execution. */
export const RULE_BY_CATEGORY = {
    'error-handling': ruleErrorHandling,
    security: ruleSecurityAuth,
    'data-validation': ruleDataValidation,
    performance: rulePerformance,
    accessibility: ruleAccessibility,
    'edge-cases': ruleEdgeCases,
    observability: ruleObservability,
    rollback: ruleRollback,
};
/** Returns the rules to run given an optional category filter. */
export function selectRules(categories) {
    if (!categories || categories.length === 0) {
        return ALL_RULES;
    }
    return categories
        .map((cat) => RULE_BY_CATEGORY[cat])
        .filter((r) => r !== undefined);
}
//# sourceMappingURL=gap-rules.js.map