<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Route;
use Tests\TestCase;

/**
 * Guards de regressão de segurança derivados dos incidentes do Boletim
 * Oficial (jan/2026 e jun/2026) e da auditoria dos módulos IndicadoresTI.
 *
 * Cada teste protege uma mitigação já aplicada: se ele falhar, uma correção
 * de segurança foi revertida. Referência: docs/cybersec/.
 */
class SecurityGuardsTest extends TestCase
{
    /**
     * Variáveis permitidas em saída não escapada ({!! !!}) nos templates PDF
     * do IndicadoresTI: apenas SVGs gerados internamente pelo SvgChartBuilder
     * a partir de dados numéricos (guard V3).
     */
    private const PDF_ECHO_WHITELIST = [
        '$svgDonut',
        '$svgGauge',
        '$svgSetores',
        '$svgBar',
        "\$d['svg']",
    ];

    /**
     * Guard V1/V3: o DomPDF nunca pode voltar a executar PHP embutido
     * globalmente (vetor de RCE ampliado no commit e713282f6).
     */
    public function test_dompdf_enable_php_permanece_desativado(): void
    {
        $this->assertFalse(
            (bool) config('dompdf.options.enable_php'),
            'config/dompdf.php deve manter enable_php => false globalmente. '
                . 'Uso legítimo deve ser ativado por render via setOptions([\'isPhpEnabled\' => true]).'
        );
    }

    /**
     * Guard V3: os templates PDF do IndicadoresTI (renderizados com
     * isPhpEnabled=true) só podem conter {!! !!} para SVGs whitelisted.
     */
    public function test_templates_pdf_indicadores_ti_sem_saida_nao_escapada_de_dados(): void
    {
        $templates = [
            'relatorio_pdf_resumido.blade.php',
            'relatorio_pdf_detalhado.blade.php',
            'relatorio_pdf_matricial.blade.php',
        ];

        foreach ($templates as $template) {
            $path = resource_path('views/admin/indicadoresTi/reports/' . $template);
            $this->assertFileExists($path);

            $conteudo = (string) file_get_contents($path);

            preg_match_all('/\{!!\s*(.*?)\s*!!\}/s', $conteudo, $matches);

            foreach ($matches[1] as $expressao) {
                $this->assertContains(
                    trim($expressao),
                    self::PDF_ECHO_WHITELIST,
                    "Saída não escapada não permitida em {$template}: '{$expressao}'. "
                        . 'Nenhum dado de chamado pode entrar nos PDFs via {!! !!} (guard V3).'
                );
            }
        }
    }

    /**
     * Guard V4: os autocompletes do IndicadoresTI retornam dados pessoais e
     * devem permanecer com rate limiting.
     */
    public function test_rotas_de_autocomplete_indicadores_ti_possuem_throttle(): void
    {
        $rotas = [
            'admin.indicadoresTi.clientes.buscar',
            'admin.indicadoresTi.unidades.buscar',
        ];

        foreach ($rotas as $nome) {
            $rota = Route::getRoutes()->getByName($nome);

            $this->assertNotNull($rota, "Rota {$nome} não encontrada.");
            $this->assertContains(
                'throttle:autocomplete',
                $rota->gatherMiddleware(),
                "A rota {$nome} deve manter o middleware throttle:autocomplete (guard V4)."
            );
        }
    }

    /**
     * Guard do incidente #2: o public/.htaccess deve continuar bloqueando
     * todas as extensões executáveis (a regra de jan/2026 só cobria .php e
     * foi contornada com .phtml/.phar).
     */
    public function test_htaccess_bloqueia_extensoes_executaveis(): void
    {
        $path = public_path('.htaccess');
        $this->assertFileExists($path);

        $conteudo = (string) file_get_contents($path);

        foreach (['phtml', 'phar', 'pht'] as $extensao) {
            $this->assertStringContainsString(
                $extensao,
                $conteudo,
                "public/.htaccess deve bloquear a extensão .{$extensao} (guard do incidente #2)."
            );
        }
    }

    /**
     * Guard: visualização pública de Cronogramas deve permanecer com rate limiting.
     */
    public function test_rota_publica_cronogramas_possui_throttle(): void
    {
        $rota = Route::getRoutes()->getByName('cronogramas.public.show');

        $this->assertNotNull($rota, 'Rota cronogramas.public.show não encontrada.');
        $this->assertContains(
            'throttle:web-public',
            $rota->gatherMiddleware(),
            'A rota cronogramas.public.show deve manter o middleware throttle:web-public.'
        );
    }
}
