// engine/spec-source-sync.ts — Spec-as-Source sync logic (SPEC-349)
import { readFile } from 'node:fs/promises';
// ---------------------------------------------------------------------------
// Marker detection
// ---------------------------------------------------------------------------
const MARKER_PATTERNS = [
    { regex: /\/\/\s*@spec\s+(SPEC-\d+)/i, style: 'slashslash' },
    { regex: /#\s*@spec\s+(SPEC-\d+)/i, style: 'hash' },
    { regex: /--\s*@spec\s+(SPEC-\d+)/i, style: 'doubledash' },
];
export function detectMarkers(content) {
    const results = [];
    const lines = content.split('\n');
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i] ?? '';
        for (const { regex, style } of MARKER_PATTERNS) {
            const match = regex.exec(line);
            if (match?.[1] !== undefined) {
                results.push({ specId: match[1], line: i + 1, style });
                break;
            }
        }
    }
    return results;
}
export function detectLanguage(filePath) {
    const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
    const MAP = {
        ts: 'typescript',
        js: 'javascript',
        tsx: 'typescript',
        jsx: 'javascript',
        py: 'python',
        rb: 'ruby',
        go: 'go',
        rs: 'rust',
        java: 'java',
        kt: 'kotlin',
        swift: 'swift',
        cs: 'csharp',
        php: 'php',
        sh: 'shell',
        sql: 'sql',
    };
    return MAP[ext] ?? 'unknown';
}
// ---------------------------------------------------------------------------
// Sync from code (pure — receives existing bindings, returns new state)
// ---------------------------------------------------------------------------
export async function computeSyncFromCode(projectPath, filePaths, existingBindings) {
    const now = new Date().toISOString();
    const errors = [];
    let bindingsFound = 0;
    let bindingsCreated = 0;
    let bindingsUpdated = 0;
    const newBindings = [];
    for (const filePath of filePaths) {
        let content;
        try {
            content = await readFile(filePath, 'utf-8');
        }
        catch (err) {
            errors.push(`Cannot read ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
            continue;
        }
        const markers = detectMarkers(content);
        bindingsFound += markers.length;
        for (const { specId, line, style } of markers) {
            const existing = existingBindings.find((b) => b.filePath === filePath && b.specId === specId);
            if (existing !== undefined) {
                const updated = {
                    ...existing,
                    lineNumber: line,
                    markerStyle: style,
                    status: 'active',
                    updatedAt: now,
                };
                newBindings.push(updated);
                bindingsUpdated++;
            }
            else {
                const created = {
                    id: `bind-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
                    specId,
                    filePath,
                    lineNumber: line,
                    language: detectLanguage(filePath),
                    markerStyle: style,
                    status: 'active',
                    createdAt: now,
                    updatedAt: now,
                };
                newBindings.push(created);
                bindingsCreated++;
            }
        }
    }
    // Mark existing bindings not found in scanned files as stale
    const scannedSet = new Set(filePaths);
    const preserved = existingBindings.filter((b) => !scannedSet.has(b.filePath));
    const stale = preserved.map((b) => ({ ...b, status: 'stale' }));
    const result = {
        projectPath,
        syncedAt: now,
        scannedFiles: filePaths.length,
        bindingsFound,
        bindingsCreated,
        bindingsUpdated,
        bindingsRemoved: 0,
        errors,
    };
    return { result, newBindings: [...newBindings, ...stale] };
}
// ---------------------------------------------------------------------------
// Mark as source (pure — returns binding to save)
// ---------------------------------------------------------------------------
export function buildMarkerComment(specId, language) {
    const hashLangs = new Set(['python', 'ruby', 'shell', 'yaml', 'toml', 'r']);
    const dashLangs = new Set(['sql', 'haskell', 'lua']);
    if (hashLangs.has(language)) {
        return `# @spec ${specId}`;
    }
    if (dashLangs.has(language)) {
        return `-- @spec ${specId}`;
    }
    return `// @spec ${specId}`;
}
export function buildSourceBinding(specId, filePath, lineNumber) {
    const now = new Date().toISOString();
    const language = detectLanguage(filePath);
    const markerStyle = language === 'python' || language === 'ruby' || language === 'shell'
        ? 'hash'
        : language === 'sql'
            ? 'doubledash'
            : 'slashslash';
    return {
        id: `bind-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
        specId,
        filePath,
        lineNumber,
        language,
        markerStyle,
        status: 'active',
        createdAt: now,
        updatedAt: now,
    };
}
//# sourceMappingURL=spec-source-sync.js.map