// engine/detectors/gitops-detector.ts — GitOps and Policy as Code detection (SPEC-026b)
// Detects ArgoCD, Flux, Spinnaker, OPA, Kyverno, Gatekeeper, Conftest.
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
// === GitOps detection signals ===
const GITOPS_SIGNALS = [
    {
        tool: 'argocd',
        paths: [],
        globPatterns: ['argocd/**/*.yaml', 'argocd/**/*.yml', '.argocd/**/*.yaml'],
        contentPatterns: ['kind: Application', 'argoproj.io'],
    },
    {
        tool: 'flux',
        paths: ['flux-system/'],
        globPatterns: ['flux-system/**/*.yaml', 'flux-system/**/*.yml'],
        contentPatterns: ['kind: HelmRelease', 'kind: Kustomization', 'fluxcd.io'],
    },
    {
        tool: 'spinnaker',
        paths: ['spinnakerconfig.yml', '.spinnaker/'],
        globPatterns: ['.spinnaker/**/*.yml', '.spinnaker/**/*.yaml'],
        contentPatterns: [],
    },
];
// === Policy as Code detection signals ===
const POLICY_SIGNALS = [
    {
        tool: 'opa',
        paths: [],
        globPatterns: ['**/*.rego', 'policy/**/*.rego', 'opa/**/*.rego'],
        contentPatterns: [],
    },
    {
        tool: 'kyverno',
        paths: [],
        globPatterns: ['policy/**/*.yaml', 'kyverno/**/*.yaml'],
        contentPatterns: ['apiVersion: kyverno.io', 'kind: ClusterPolicy'],
    },
    {
        tool: 'gatekeeper',
        paths: [],
        globPatterns: ['policy/**/*.yaml', 'gatekeeper/**/*.yaml'],
        contentPatterns: ['templates.gatekeeper.sh', 'kind: ConstraintTemplate'],
    },
    {
        tool: 'conftest',
        paths: ['.conftest.yaml', '.conftest.yml', 'conftest.yaml'],
        globPatterns: ['policy/**/*.rego'],
        contentPatterns: [],
    },
];
// === Public API ===
/**
 * Detects GitOps tooling in a project.
 * Checks for ArgoCD, Flux, and Spinnaker configurations.
 */
export async function detectGitOps(projectPath) {
    const configFiles = [];
    for (const signal of GITOPS_SIGNALS) {
        const found = await findGitOpsFiles(projectPath, signal);
        if (found.length > 0) {
            return {
                tool: signal.tool,
                configFiles: found,
            };
        }
    }
    return { tool: null, configFiles };
}
/**
 * Detects Policy as Code tools in a project.
 * Checks for OPA, Kyverno, Gatekeeper, and Conftest.
 */
export async function detectPolicyAsCode(projectPath) {
    const tools = [];
    const configFiles = [];
    for (const signal of POLICY_SIGNALS) {
        const found = await findPolicyFiles(projectPath, signal);
        if (found.length > 0 && !tools.includes(signal.tool)) {
            tools.push(signal.tool);
            configFiles.push(...found);
        }
    }
    return { tools, configFiles: [...new Set(configFiles)] };
}
// === Internal helpers ===
async function findGitOpsFiles(projectPath, signal) {
    const found = [];
    // Check explicit paths
    for (const p of signal.paths) {
        if (await fileExists(join(projectPath, p))) {
            found.push(p);
        }
    }
    // Check glob patterns
    if (signal.globPatterns) {
        for (const pattern of signal.globPatterns) {
            try {
                const matches = await glob(pattern, { cwd: projectPath, nodir: true });
                // Verify content patterns if required
                if (signal.contentPatterns && signal.contentPatterns.length > 0) {
                    for (const match of matches) {
                        if (await fileMatchesContent(projectPath, match, signal.contentPatterns)) {
                            found.push(match);
                        }
                    }
                }
                else {
                    found.push(...matches);
                }
            }
            catch {
                // ignore glob errors
            }
        }
    }
    return found;
}
async function findPolicyFiles(projectPath, signal) {
    const found = [];
    // Check explicit paths
    for (const p of signal.paths) {
        if (await fileExists(join(projectPath, p))) {
            found.push(p);
        }
    }
    // Check glob patterns
    if (signal.globPatterns) {
        for (const pattern of signal.globPatterns) {
            try {
                const matches = await glob(pattern, { cwd: projectPath, nodir: true });
                if (signal.contentPatterns && signal.contentPatterns.length > 0) {
                    for (const match of matches) {
                        if (await fileMatchesContent(projectPath, match, signal.contentPatterns)) {
                            found.push(match);
                        }
                    }
                }
                else {
                    found.push(...matches);
                }
            }
            catch {
                // ignore glob errors
            }
        }
    }
    return found;
}
async function fileMatchesContent(projectPath, filePath, patterns) {
    try {
        const content = await readFile(join(projectPath, filePath), 'utf-8');
        return patterns.some((p) => content.includes(p));
    }
    catch {
        return false;
    }
}
async function fileExists(filePath) {
    try {
        await readFile(filePath, 'utf-8');
        return true;
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=gitops-detector.js.map