// engine/hooks/handlers/on-push-check.ts — on-push-check hook handler (SPEC-138)
// Drift detection on spec-related commits during push events.
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const SPEC_PATH_PREFIX = 'planu/specs/';
const SPEC_FILE_PATTERNS = [
    /spec\.md$/,
    /technical\.md$/,
    /progress\.md$/,
    /SPEC-\d+/,
    /\.spec\.(ts|js|tsx|jsx)$/,
];
const SECURITY_KEYWORDS = [
    '.env',
    'auth',
    'config',
    'secret',
    'credential',
    'password',
    'token',
    'key',
    'private',
];
/** Default drift threshold: 10% */
export const DEFAULT_DRIFT_THRESHOLD = 0.1;
// ---------------------------------------------------------------------------
// Module-level cache (keyed by projectPath for per-project isolation)
// ---------------------------------------------------------------------------
const lastPushCheckResultMap = new Map();
// ---------------------------------------------------------------------------
// Path classification
// ---------------------------------------------------------------------------
/**
 * Returns true if the given path is related to a spec file.
 * Matches planu/specs/** or files matching common spec patterns.
 */
export function isSpecRelatedFile(path) {
    if (path.startsWith(SPEC_PATH_PREFIX)) {
        return true;
    }
    return SPEC_FILE_PATTERNS.some((pattern) => pattern.test(path));
}
/**
 * Returns true if the given path is security-sensitive.
 * Detects .env files, auth/config/secrets paths, etc.
 */
export function isSecuritySensitiveFilePath(path) {
    const lower = path.toLowerCase();
    return SECURITY_KEYWORDS.some((keyword) => lower.includes(keyword));
}
// ---------------------------------------------------------------------------
// Drift computation
// ---------------------------------------------------------------------------
/**
 * Compute drift score as a ratio of changed spec criteria to total criteria.
 * specCriteria: total number of acceptance criteria
 * fileIndex: number of spec-related changed files
 */
export function computeDriftScore(specCriteria, fileIndex) {
    if (specCriteria <= 0) {
        return 0;
    }
    return Math.min(1, fileIndex / specCriteria);
}
// ---------------------------------------------------------------------------
// Security detection
// ---------------------------------------------------------------------------
/**
 * Find security-sensitive paths among changed files.
 */
export function detectSecuritySensitivePaths(changedFiles) {
    return changedFiles.filter((f) => isSecuritySensitiveFilePath(f));
}
// ---------------------------------------------------------------------------
// Cache accessors
// ---------------------------------------------------------------------------
/**
 * Get the cached result from the last push check run.
 * If projectPath is omitted, returns the most recently set result across all projects.
 */
export function getLastPushCheckResult(projectPath) {
    if (projectPath !== undefined) {
        return lastPushCheckResultMap.get(projectPath) ?? null;
    }
    // Return the last value inserted (most recent across all projects)
    let last = null;
    for (const v of lastPushCheckResultMap.values()) {
        /* v8 ignore start -- map only stores WorkerRunResult objects, never null */
        last = v ?? null;
        /* v8 ignore stop */
    }
    return last;
}
/**
 * Reset the push check result cache.
 * If projectPath is provided, clears only that project's cache.
 * If omitted, clears all projects.
 * Useful for testing.
 */
export function resetPushCheckCache(projectPath) {
    if (projectPath !== undefined) {
        lastPushCheckResultMap.delete(projectPath);
    }
    else {
        lastPushCheckResultMap.clear();
    }
}
/**
 * Reset push check result cache for all projects.
 * Useful for testing.
 */
export function resetAllPushCheckCaches() {
    lastPushCheckResultMap.clear();
}
// ---------------------------------------------------------------------------
// Handler
// ---------------------------------------------------------------------------
/**
 * Handle on-push-check event.
 * Performs drift detection and security-sensitive path detection.
 * Caches results as WorkerRunResult and emits WorkerFindings via console.warn.
 * Fire-and-forget — all errors are caught with console.warn.
 */
export function handleOnPushCheck(event, projectPath, driftThreshold = DEFAULT_DRIFT_THRESHOLD) {
    const startedAt = new Date().toISOString();
    const startMs = Date.now();
    try {
        const specRelated = isSpecRelatedFile(event.path);
        console.warn(`[hooks] on-push-check: path=${event.path} type=${event.type} specRelated=${String(specRelated)} project=${projectPath}`);
        const findings = [];
        const changedFiles = [event.path];
        // Drift detection
        if (specRelated) {
            console.warn(`[hooks] on-push-check: spec-related file detected, drift check triggered for ${event.path}`);
            const driftScore = computeDriftScore(10, changedFiles.filter(isSpecRelatedFile).length);
            if (driftScore >= driftThreshold) {
                const driftPct = Math.round(driftScore * 100);
                findings.push({
                    severity: 'warning',
                    file: event.path,
                    message: `Drift detected: ${String(driftPct)}% of spec criteria affected`,
                    suggestion: 'Review and reconcile spec documentation',
                    category: 'drift',
                });
                console.warn(`[hooks] on-push-check: drift threshold exceeded (${String(driftPct)}%) for ${event.path}`);
            }
        }
        // Security detection
        const sensitivePaths = detectSecuritySensitivePaths(changedFiles);
        for (const sensitivePath of sensitivePaths) {
            findings.push({
                severity: 'high',
                file: sensitivePath,
                message: `Security-sensitive file changed: ${sensitivePath}`,
                suggestion: 'Review changes to security-sensitive files carefully',
                category: 'security',
            });
            console.warn(`[hooks] on-push-check: security-sensitive file detected: ${sensitivePath}`);
        }
        const completedAt = new Date().toISOString();
        lastPushCheckResultMap.set(projectPath, {
            workerName: 'on-push-check',
            startedAt,
            completedAt,
            durationMs: Date.now() - startMs,
            status: 'success',
            findings,
            analyzedFiles: changedFiles.length,
        });
    }
    catch (err) {
        console.warn(`[hooks] on-push-check: unexpected error for ${event.path}`, err);
        const completedAt = new Date().toISOString();
        lastPushCheckResultMap.set(projectPath, {
            workerName: 'on-push-check',
            startedAt,
            completedAt,
            durationMs: Date.now() - startMs,
            status: 'error',
            findings: [],
            error: String(err),
            analyzedFiles: 0,
        });
    }
    return Promise.resolve();
}
//# sourceMappingURL=on-push-check.js.map