// engine/detectors/finops-detector.ts — FinOps and cloud cost management detection (SPEC-026b)
// Detects Infracost, OpenCost, Kubecost, and cloud-native billing integrations.
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
// === Detection signals ===
const FINOPS_SIGNALS = [
    {
        tool: 'infracost',
        configFiles: ['infracost.yml', 'infracost.yaml', '.infracost/'],
        globPatterns: ['.infracost/*.yml', '.infracost/*.yaml'],
        ciKeywords: ['infracost', 'infracost breakdown', 'infracost diff'],
    },
    {
        tool: 'opencost',
        configFiles: [],
        globPatterns: ['**/opencost*.yaml', '**/opencost*.yml'],
        ciKeywords: ['opencost'],
    },
    {
        tool: 'kubecost',
        configFiles: [],
        globPatterns: ['**/kubecost*.yaml', '**/helm/kubecost/**'],
        ciKeywords: ['kubecost'],
    },
    {
        tool: 'aws-cost-explorer',
        configFiles: ['.aws/config'],
        globPatterns: [],
        ciKeywords: ['aws ce', 'cost-explorer', 'costExplorer'],
    },
    {
        tool: 'gcp-billing-export',
        configFiles: [],
        globPatterns: ['**/*.tf'],
        ciKeywords: ['bigquery_export', 'google_billing_budget'],
    },
    {
        tool: 'azure-cost-management',
        configFiles: [],
        globPatterns: ['**/*.tf', '**/*.bicep'],
        ciKeywords: ['azurerm_consumption_budget', 'azure cost management'],
    },
];
// IaC files that integrate with cost tools (e.g., Infracost in CI/CD)
const IAC_COST_PATTERNS = ['**/*.tf', '**/*.tfvars', '**/*.bicep'];
// === Public API ===
/**
 * Detects FinOps and cloud cost management tools.
 * Returns list of detected tools and whether IaC cost integration exists.
 */
export async function detectFinOps(projectPath) {
    const tools = [];
    const configFiles = [];
    // Read CI content once for keyword searches
    const ciContent = await readCiContent(projectPath);
    const tfContent = await readTerraformContent(projectPath);
    for (const signal of FINOPS_SIGNALS) {
        const found = await detectFinOpsTool(projectPath, signal, ciContent, tfContent);
        if (found.detected) {
            tools.push(signal.tool);
            configFiles.push(...found.files);
        }
    }
    const hasIacCostIntegration = (await hasIacFiles(projectPath)) && tools.length > 0;
    return {
        tools: [...new Set(tools)],
        hasIacCostIntegration,
        configFiles: [...new Set(configFiles)],
    };
}
// === Internal helpers ===
async function detectFinOpsTool(projectPath, signal, ciContent, tfContent) {
    const files = [];
    // Check static config files
    for (const f of signal.configFiles) {
        try {
            await readFile(join(projectPath, f), 'utf-8');
            files.push(f);
            return { detected: true, files };
        }
        catch {
            // not found
        }
    }
    // Check glob patterns
    if (signal.globPatterns) {
        for (const pattern of signal.globPatterns) {
            if (!pattern.includes('*.tf') && !pattern.includes('*.bicep')) {
                try {
                    const matches = await glob(pattern, { cwd: projectPath, nodir: true });
                    if (matches.length > 0) {
                        files.push(...matches);
                        return { detected: true, files };
                    }
                }
                catch {
                    // ignore
                }
            }
        }
    }
    // Check CI keywords in CI content
    if (signal.ciKeywords) {
        const combined = `${ciContent}\n${tfContent}`.toLowerCase();
        for (const keyword of signal.ciKeywords) {
            if (combined.includes(keyword.toLowerCase())) {
                return { detected: true, files };
            }
        }
    }
    return { detected: false, files: [] };
}
async function readCiContent(projectPath) {
    const contents = [];
    const patterns = [
        '.github/workflows/*.yml',
        '.github/workflows/*.yaml',
        '.gitlab-ci.yml',
        'azure-pipelines.yml',
    ];
    for (const pattern of patterns) {
        try {
            const matches = pattern.includes('*')
                ? await glob(pattern, { cwd: projectPath, nodir: true })
                : [pattern];
            for (const match of matches) {
                try {
                    const content = await readFile(join(projectPath, match), 'utf-8');
                    contents.push(content);
                }
                catch {
                    // ignore
                }
            }
        }
        catch {
            // ignore
        }
    }
    return contents.join('\n');
}
async function readTerraformContent(projectPath) {
    const contents = [];
    try {
        const matches = await glob('**/*.tf', { cwd: projectPath, nodir: true });
        for (const match of matches.slice(0, 10)) {
            // limit to first 10 tf files
            try {
                const content = await readFile(join(projectPath, match), 'utf-8');
                contents.push(content);
            }
            catch {
                // ignore
            }
        }
    }
    catch {
        // ignore
    }
    return contents.join('\n');
}
async function hasIacFiles(projectPath) {
    for (const pattern of IAC_COST_PATTERNS) {
        try {
            const matches = await glob(pattern, { cwd: projectPath, nodir: true });
            if (matches.length > 0) {
                return true;
            }
        }
        catch {
            // ignore
        }
    }
    return false;
}
//# sourceMappingURL=finops-detector.js.map