import { splitLines } from '../../../shared/helpers/index.js';
import { parseDocument } from 'yaml';
import { criarOcorrencia } from '../../../types/index.js';
const SELF_HOSTED_MARKERS = ['self-hosted', 'my-runner', 'custom-runner'];
const DEPRECATED_FEATURES = [{
        pattern: /::set-output\s+name=/i,
        tipo: 'deprecated-set-output',
        mensagem: 'Uso de ::set-output:: depreciado',
        sugestao: 'Use $GITHUB_OUTPUT para definir outputs de step'
    }, {
        pattern: /::add-mask::/i,
        tipo: 'deprecated-add-mask',
        mensagem: '::add-mask:: pode expor secrets em logs',
        sugestao: 'Use actions/create-github-app-token ou secrets mascarados nativamente'
    }, {
        pattern: /::save-state\s+name=/i,
        tipo: 'deprecated-save-state',
        mensagem: '::save-state:: depreciado para comunicação entre jobs',
        sugestao: 'Use artifacts ou cache para compartilhar dados entre jobs'
    }];
export function detectarJobsPassivos(job) {
    if (!job?.steps || job.steps.length <= 1)
        return false;
    const meaningfulSteps = job.steps.filter(string => {
        const uses = string.uses || '';
        const run = string.run || '';
        return !uses.startsWith('actions/checkout') && !uses.startsWith('actions/download-artifact') && !/echo\s+::(set-output|save-state|add-mask)/i.test(run);
    });
    return meaningfulSteps.length === 0;
}
export const detectorWorkflowEstrutura = {
    nome: 'detector-workflow-estrutura',
    categoria: 'workflows',
    descricao: 'Detecta problemas estruturais em GitHub Actions',
    test: (relPath) => /\.github\/workflows\/.*\.ya?ml$/i.test(relPath),
    aplicar: (src, relPath) => {
        if (!src)
            return [];
        const ocorrencias = [];
        const linhas = splitLines(src);
        try {
            const doc = parseDocument(src);
            const workflow = doc.toJS();
            if (!workflow || typeof workflow !== 'object')
                return ocorrencias;
            const jobs = workflow.jobs || {};
            const jobNames = Object.keys(jobs);
            for (const [jobId, job] of Object.entries(jobs)) {
                if (!job)
                    continue;
                if (job.needs) {
                    const needsList = Array.isArray(job.needs) ? job.needs : [job.needs];
                    for (const need of needsList) {
                        if (typeof need === 'string' && !jobNames.includes(need)) {
                            ocorrencias.push(criarOcorrencia({
                                tipo: 'job-orfao',
                                nivel: 'erro',
                                mensagem: `Job '${jobId}' depende de '${need}' que não existe`,
                                relPath,
                                linha: 1,
                                sugestao: `Verifique se o job '${need}' está definido no workflow ou remova a dependência`
                            }));
                        }
                    }
                }
                const runsOn = job['runs-on'];
                if (typeof runsOn === 'string') {
                    if (SELF_HOSTED_MARKERS.some(metrica => runsOn.toLowerCase().includes(metrica)) && !job.name) {
                        ocorrencias.push(criarOcorrencia({
                            tipo: 'job-over-provisioning',
                            nivel: 'info',
                            mensagem: `Job '${jobId}' usa runner self-hosted sem label específico`,
                            relPath,
                            linha: 1,
                            sugestao: 'Use labels específicos para self-hosted runners (ex: self-hosted-linux-gpu)'
                        }));
                    }
                    if (runsOn === 'ubuntu-latest' && job.steps && job.steps.length <= 3 && job.steps.every(string => string.uses?.startsWith('actions/checkout') || string.uses?.startsWith('actions/setup-'))) {
                        ocorrencias.push(criarOcorrencia({
                            tipo: 'job-over-provisioning',
                            nivel: 'info',
                            mensagem: `Job '${jobId}' usa ubuntu-latest apenas para checkout/setup`,
                            relPath,
                            linha: 1,
                            sugestao: 'Considere usar runs-on: ubuntu-22.04 ou ubuntu-20.04 se compatível'
                        }));
                    }
                }
                if (typeof job.container === 'object' && job.container !== null) {
                    const image = job.container.image;
                    if (image && (image.includes('large') || image.includes('xlarge'))) {
                        ocorrencias.push(criarOcorrencia({
                            tipo: 'job-over-provisioning',
                            nivel: 'aviso',
                            mensagem: `Job '${jobId}' usa container de grande porte (${image})`,
                            relPath,
                            linha: 1,
                            sugestao: 'Verifique se realmente precisa de um container grande; considere usar o runner direto'
                        }));
                    }
                }
                const hasHeavyOps = job.steps?.some(string => {
                    const run = string.run || '';
                    const uses = string.uses || '';
                    return /\b(docker\s+build|make|cmake|bazel)\b/.test(run) || /docker\/build-push-action/.test(uses);
                });
                if (hasHeavyOps && typeof runsOn === 'string' && /^(ubuntu-20\.04|ubuntu-18\.04)$/.test(runsOn)) {
                    ocorrencias.push(criarOcorrencia({
                        tipo: 'job-under-provisioning',
                        nivel: 'aviso',
                        mensagem: `Job '${jobId}' realiza operações pesadas em runner antigo (${runsOn})`,
                        relPath,
                        linha: 1,
                        sugestao: 'Considere usar ubuntu-22.04, ubuntu-24.04 ou runner com mais recursos'
                    }));
                }
            }
            const seenNames = new Set();
            for (const [, job] of Object.entries(jobs)) {
                if (!job?.name)
                    continue;
                const displayName = job.name.toLowerCase().trim();
                if (seenNames.has(displayName)) {
                    ocorrencias.push(criarOcorrencia({
                        tipo: 'workflow-duplicado',
                        nivel: 'aviso',
                        mensagem: `Nome de job duplicado detectado: "${job.name}"`,
                        relPath,
                        linha: 1,
                        sugestao: 'Renomeie jobs para evitar confusão nos logs e na interface do GitHub Actions'
                    }));
                }
                seenNames.add(displayName);
            }
            for (const dep of DEPRECATED_FEATURES) {
                if (dep.pattern.test(src)) {
                    ocorrencias.push(criarOcorrencia({
                        tipo: dep.tipo,
                        nivel: 'aviso',
                        mensagem: dep.mensagem,
                        relPath,
                        linha: 1,
                        sugestao: dep.sugestao
                    }));
                }
            }
            for (let indice = 0; indice < linhas.length; indice++) {
                if (/continue-on-error:\s*true/i.test(linhas[indice])) {
                    const hasIfCondition = /if:/.test(linhas[indice]) || indice > 0 && /if:/.test(linhas[indice - 1]);
                    if (!hasIfCondition) {
                        ocorrencias.push(criarOcorrencia({
                            tipo: 'step-fragil',
                            nivel: 'info',
                            mensagem: 'Step com continue-on-error sem condição de fallback',
                            relPath,
                            linha: indice + 1,
                            sugestao: 'Adicione uma condição "if:" para tornar o fallback mais explícito e controlado'
                        }));
                    }
                }
            }
            for (const [jobId, job] of Object.entries(jobs)) {
                if (detectarJobsPassivos(job)) {
                    ocorrencias.push(criarOcorrencia({
                        tipo: 'job-passivo',
                        nivel: 'info',
                        mensagem: `Job '${jobId}' não realiza operações significativas`,
                        relPath,
                        linha: 1,
                        sugestao: 'Considere remover o job ou consolidá-lo com outro'
                    }));
                }
            }
        }
        catch {
            for (const dep of DEPRECATED_FEATURES) {
                if (dep.pattern.test(src)) {
                    ocorrencias.push(criarOcorrencia({
                        tipo: dep.tipo,
                        nivel: 'aviso',
                        mensagem: dep.mensagem,
                        relPath,
                        linha: 1,
                        sugestao: dep.sugestao
                    }));
                }
            }
        }
        return ocorrencias;
    }
};
//# sourceMappingURL=detector-workflow-structure.js.map