<?php
/**
 * Security Client - Módulo de Segurança para Sites Clientes
 * 
 * Este arquivo gerencia todas as proteções de segurança locais:
 * - Rate Limiting
 * - Anti-Bot
 * - Proteção DDoS
 * - Bloqueio de IPs
 * - Bloqueio de DevTools
 * 
 * Usa configurações do servidor admin remoto (cacheadas localmente)
 * 
 * @version 1.0.0
 */

// Bloquear acesso direto
if (basename($_SERVER['SCRIPT_FILENAME'] ?? '') === basename(__FILE__)) {
    http_response_code(403);
    die('Access denied');
}

require_once __DIR__ . '/client-config.php';

// ============================================
// CONSTANTES
// ============================================

define('SECURITY_CACHE_FILE', CACHE_DIR . '/security_config.json');
define('SECURITY_CACHE_TTL', 300); // 5 minutos
define('RATE_LIMIT_DIR', CACHE_DIR . '/rate_limits');
define('IP_BLOCK_FILE', CACHE_DIR . '/blocked_ips.json');

// ============================================
// CLASSE PRINCIPAL DE SEGURANÇA
// ============================================

class SecurityClient
{
    private static ?array $config = null;
    private static ?array $blockedIps = null;
    private static ?array $whitelistedIps = null;
    
    /**
     * Inicializa e aplica todas as proteções
     * Chamar no início de cada página
     * 
     * @param string|null $siteId ID do site (para buscar config específica)
     * @param bool $isPageMode Se true, é página HTML; se false, é API
     * @return array Resultado da proteção com info útil
     */
    public static function protect(?string $siteId = null, bool $isPageMode = false): array
    {
        $ip = self::getClientIP();
        
        // 1. Verificar whitelist (sempre permitir)
        if (self::isWhitelisted($ip)) {
            return [
                'status' => 'ok',
                'ip' => $ip,
                'site_id' => $siteId,
                'whitelisted' => true,
                'config_loaded' => true
            ];
        }
        
        // 2. Verificar blacklist (sempre bloquear)
        if (self::isBlocked($ip)) {
            self::blockAccess('IP bloqueado', $isPageMode);
        }
        
        // Carregar configurações
        $config = self::getConfig($siteId);
        
        // 3. Rate Limiting
        if ($config['rate_limit_enabled'] ?? false) {
            if (!self::checkRateLimit($ip, $config)) {
                self::reportEvent('rate_limit', $ip);
                self::blockAccess('Muitas requisições. Aguarde alguns minutos.', $isPageMode);
            }
        }
        
        // 4. Anti-Bot
        if ($config['antibot_enabled'] ?? false) {
            $botCheck = self::checkAntiBot($config);
            if ($botCheck !== true) {
                self::reportEvent('bot_detected', $ip, ['reason' => $botCheck]);
                self::blockAccess('Acesso não autorizado', $isPageMode);
            }
        }
        
        // 5. Proteção DDoS
        if ($config['ddos_enabled'] ?? false) {
            if (!self::checkDDoS($ip, $config)) {
                self::reportEvent('ddos_attack', $ip);
                self::blockAccess('Tráfego suspeito detectado', $isPageMode);
            }
        }
        
        return [
            'status' => 'ok',
            'ip' => $ip,
            'site_id' => $siteId,
            'config_loaded' => !empty($config)
        ];
    }
    
    /**
     * Obtém configurações de segurança (do cache ou servidor)
     * 
     * @param string|null $siteId ID do site para buscar configuração específica
     */
    public static function getConfig(?string $siteId = null): array
    {
        if (self::$config !== null) {
            return self::$config;
        }
        
        // Tentar cache local primeiro
        if (file_exists(SECURITY_CACHE_FILE)) {
            $cached = json_decode(file_get_contents(SECURITY_CACHE_FILE), true);
            if ($cached && ($cached['expires'] ?? 0) > time()) {
                self::$config = $cached['data']['security'] ?? [];
                self::$blockedIps = $cached['data']['blocked_ips'] ?? [];
                self::$whitelistedIps = $cached['data']['whitelisted_ips'] ?? [];
                return self::$config;
            }
        }
        
        // Buscar do servidor
        $response = adminServerRequest('site-config');
        
        if ($response && $response['status'] === 'success') {
            self::$config = $response['data']['security'] ?? [];
            self::$blockedIps = $response['data']['blocked_ips'] ?? [];
            self::$whitelistedIps = $response['data']['whitelisted_ips'] ?? [];
            
            // Salvar no cache
            self::saveConfigCache($response['data']);
            
            return self::$config;
        }
        
        // Fallback: configuração padrão
        self::$config = self::getDefaultConfig();
        return self::$config;
    }
    
    /**
     * Configuração padrão (fallback)
     */
    private static function getDefaultConfig(): array
    {
        return [
            'rate_limit_enabled' => true,
            'rate_limit_requests' => 60,
            'rate_limit_window' => 60,
            'rate_limit_block_duration' => 300,
            'antibot_enabled' => true,
            'antibot_check_useragent' => true,
            'antibot_check_referrer' => false,
            'antibot_check_javascript' => true,
            'ddos_enabled' => true,
            'ddos_max_connections' => 50,
            'ddos_suspicious_threshold' => 10,
            'ddos_block_duration' => 3600,
            'block_devtools' => false,
            'block_right_click' => false,
            'block_view_source' => false,
            'block_copy' => false
        ];
    }
    
    /**
     * Salva configurações no cache local
     */
    private static function saveConfigCache(array $data): void
    {
        if (!is_dir(CACHE_DIR)) {
            @mkdir(CACHE_DIR, 0755, true);
        }
        
        $cacheData = [
            'data' => $data,
            'expires' => time() + SECURITY_CACHE_TTL,
            'cached_at' => date('Y-m-d H:i:s')
        ];
        
        @file_put_contents(SECURITY_CACHE_FILE, json_encode($cacheData));
    }
    
    /**
     * Verifica se IP está na whitelist
     */
    public static function isWhitelisted(string $ip): bool
    {
        self::getConfig(); // Garante que as listas foram carregadas
        return in_array($ip, self::$whitelistedIps ?? []);
    }
    
    /**
     * Verifica se IP está bloqueado
     */
    public static function isBlocked(string $ip): bool
    {
        self::getConfig(); // Garante que as listas foram carregadas
        
        // Verificar lista do servidor
        if (in_array($ip, self::$blockedIps ?? [])) {
            return true;
        }
        
        // Verificar bloqueios locais temporários
        $localBlocks = self::getLocalBlockedIps();
        if (isset($localBlocks[$ip])) {
            if ($localBlocks[$ip] > time()) {
                return true;
            }
            // Expirou, remover
            unset($localBlocks[$ip]);
            self::saveLocalBlockedIps($localBlocks);
        }
        
        return false;
    }
    
    /**
     * Bloqueia um IP localmente (temporário)
     */
    public static function blockIpLocally(string $ip, int $duration = 3600): void
    {
        $blocks = self::getLocalBlockedIps();
        $blocks[$ip] = time() + $duration;
        self::saveLocalBlockedIps($blocks);
    }
    
    /**
     * Obtém IPs bloqueados localmente
     */
    private static function getLocalBlockedIps(): array
    {
        if (!file_exists(IP_BLOCK_FILE)) {
            return [];
        }
        return json_decode(file_get_contents(IP_BLOCK_FILE), true) ?: [];
    }
    
    /**
     * Salva IPs bloqueados localmente
     */
    private static function saveLocalBlockedIps(array $blocks): void
    {
        if (!is_dir(CACHE_DIR)) {
            @mkdir(CACHE_DIR, 0755, true);
        }
        @file_put_contents(IP_BLOCK_FILE, json_encode($blocks));
    }
    
    /**
     * Verifica Rate Limiting
     */
    private static function checkRateLimit(string $ip, array $config): bool
    {
        $maxRequests = $config['rate_limit_requests'] ?? 60;
        $window = $config['rate_limit_window'] ?? 60;
        $blockDuration = $config['rate_limit_block_duration'] ?? 300;
        
        if (!is_dir(RATE_LIMIT_DIR)) {
            @mkdir(RATE_LIMIT_DIR, 0755, true);
        }
        
        $file = RATE_LIMIT_DIR . '/' . md5($ip) . '.json';
        $now = time();
        $windowStart = $now - $window;
        
        $requests = [];
        
        // Usar lock para evitar race conditions
        $lockFile = $file . '.lock';
        $lock = @fopen($lockFile, 'w');
        
        if ($lock && flock($lock, LOCK_EX | LOCK_NB)) {
            try {
                if (file_exists($file)) {
                    $data = @json_decode(file_get_contents($file), true);
                    if (is_array($data)) {
                        $requests = array_filter($data, fn($ts) => $ts > $windowStart);
                    }
                }
                
                if (count($requests) >= $maxRequests) {
                    // Bloquear IP localmente
                    self::blockIpLocally($ip, $blockDuration);
                    flock($lock, LOCK_UN);
                    fclose($lock);
                    return false;
                }
                
                $requests[] = $now;
                @file_put_contents($file, json_encode(array_values($requests)));
                
            } finally {
                flock($lock, LOCK_UN);
                fclose($lock);
            }
            
            return true;
        }
        
        if ($lock) fclose($lock);
        return true; // Se não conseguir lock, permitir
    }
    
    /**
     * Verifica Anti-Bot
     */
    private static function checkAntiBot(array $config): bool|string
    {
        // Verificar User-Agent
        if ($config['antibot_check_useragent'] ?? false) {
            $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
            
            if (empty($ua)) {
                return 'no_useragent';
            }
            
            // Bots conhecidos
            $botPatterns = [
                '/curl/i', '/wget/i', '/python/i', '/scrapy/i',
                '/httpclient/i', '/libwww/i', '/lwp/i', '/java\//i',
                '/phantom/i', '/headless/i', '/selenium/i', '/puppeteer/i'
            ];
            
            foreach ($botPatterns as $pattern) {
                if (preg_match($pattern, $ua)) {
                    return 'bot_useragent';
                }
            }
        }
        
        // Verificar Referrer (se habilitado)
        if ($config['antibot_check_referrer'] ?? false) {
            $referer = $_SERVER['HTTP_REFERER'] ?? '';
            // Requisições diretas (sem referrer) em páginas internas podem ser suspeitas
            // Mas na página inicial é normal não ter referrer
            // Implementação básica: apenas registrar
        }
        
        return true;
    }
    
    /**
     * Verifica proteção DDoS
     */
    private static function checkDDoS(string $ip, array $config): bool
    {
        $threshold = $config['ddos_suspicious_threshold'] ?? 10;
        $blockDuration = $config['ddos_block_duration'] ?? 3600;
        
        // Contar requisições no último segundo
        $file = RATE_LIMIT_DIR . '/ddos_' . md5($ip) . '.json';
        $now = time();
        
        $requests = [];
        if (file_exists($file)) {
            $data = @json_decode(file_get_contents($file), true);
            if (is_array($data)) {
                // Manter apenas último segundo
                $requests = array_filter($data, fn($ts) => $ts >= $now);
            }
        }
        
        if (count($requests) >= $threshold) {
            self::blockIpLocally($ip, $blockDuration);
            return false;
        }
        
        $requests[] = $now;
        @file_put_contents($file, json_encode(array_values($requests)));
        
        return true;
    }
    
    /**
     * Reporta evento de segurança para o servidor
     */
    public static function reportEvent(string $eventType, string $ip, array $details = []): void
    {
        // Reportar de forma assíncrona para não bloquear a página
        $payload = [
            'event_type' => $eventType,
            'ip_address' => $ip,
            'details' => array_merge($details, [
                'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
                'url' => $_SERVER['REQUEST_URI'] ?? '',
                'timestamp' => date('Y-m-d H:i:s')
            ])
        ];
        
        // Tentar enviar (com timeout curto)
        try {
            adminServerRequest('security-report', 'POST', $payload);
        } catch (Exception $e) {
            // Ignorar erros de envio
            error_log("Security report failed: " . $e->getMessage());
        }
    }
    
    /**
     * Bloqueia acesso e encerra a requisição
     */
    private static function blockAccess(string $message = 'Acesso negado', bool $isPageMode = false): void
    {
        http_response_code(403);
        
        // Se for requisição AJAX ou modo API, retornar JSON
        if (
            !$isPageMode && (
                !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && 
                strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'
            )
        ) {
            header('Content-Type: application/json');
            echo json_encode(['status' => 'error', 'message' => $message]);
            exit;
        }
        
        // HTML simples para requisições normais
        echo "<!DOCTYPE html><html><head><title>Acesso Negado</title>";
        echo "<style>body{font-family:Arial,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#f5f5f5;}";
        echo ".error{text-align:center;padding:40px;background:white;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}";
        echo "h1{color:#e74c3c;margin:0 0 10px;}p{color:#666;margin:0;}</style></head>";
        echo "<body><div class='error'><h1>Acesso Bloqueado</h1><p>{$message}</p></div></body></html>";
        exit;
    }
    
    /**
     * Obtém o IP real do cliente
     */
    public static function getClientIP(): string
    {
        $headers = [
            'HTTP_CF_CONNECTING_IP',
            'HTTP_X_REAL_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_CLIENT_IP',
            'REMOTE_ADDR'
        ];
        
        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                if (strpos($ip, ',') !== false) {
                    $ips = explode(',', $ip);
                    $ip = trim($ips[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }
        
        return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    }
    
    /**
     * Gera o JavaScript de proteção DevTools
     * Incluir no HTML das páginas
     */
    public static function getDevToolsProtectionJS(): string
    {
        $config = self::getConfig();
        
        if (!($config['block_devtools'] ?? false)) {
            return '';
        }
        
        $js = "<script>(function(){";
        
        // Bloquear F12 e atalhos de DevTools
        $js .= "document.addEventListener('keydown',function(e){";
        $js .= "if(e.key==='F12'||(e.ctrlKey&&e.shiftKey&&(e.key==='I'||e.key==='J'||e.key==='C'))||(e.ctrlKey&&e.key==='U')){";
        $js .= "e.preventDefault();return false;}";
        $js .= "});";
        
        // Bloquear botão direito
        if ($config['block_right_click'] ?? false) {
            $js .= "document.addEventListener('contextmenu',function(e){e.preventDefault();return false;});";
        }
        
        // Bloquear copiar
        if ($config['block_copy'] ?? false) {
            $js .= "document.addEventListener('copy',function(e){e.preventDefault();return false;});";
            $js .= "document.addEventListener('cut',function(e){e.preventDefault();return false;});";
        }
        
        // Detectar DevTools aberto
        $js .= "var devtools={open:false,orientation:null};";
        $js .= "var threshold=160;";
        $js .= "setInterval(function(){";
        $js .= "var widthThreshold=window.outerWidth-window.innerWidth>threshold;";
        $js .= "var heightThreshold=window.outerHeight-window.innerHeight>threshold;";
        $js .= "if(widthThreshold||heightThreshold){";
        $js .= "if(!devtools.open){devtools.open=true;";
        // Ação quando detecta DevTools (pode redirecionar ou mostrar aviso)
        $js .= "console.clear();console.log('%c⚠️ DevTools detectado','color:red;font-size:20px;');";
        $js .= "}}else{devtools.open=false;}";
        $js .= "},500);";
        
        $js .= "})();</script>";
        
        return $js;
    }
    
    /**
     * Obtém o Token GOV (DETRAN etc) das configurações
     */
    public static function getGovToken(): ?string
    {
        $config = self::getConfig();
        return $config['gov_token'] ?? null;
    }
    
    /**
     * Limpa caches de segurança
     */
    public static function clearCaches(): void
    {
        if (file_exists(SECURITY_CACHE_FILE)) {
            @unlink(SECURITY_CACHE_FILE);
        }
        
        if (is_dir(RATE_LIMIT_DIR)) {
            $files = glob(RATE_LIMIT_DIR . '/*');
            foreach ($files as $file) {
                @unlink($file);
            }
        }
        
        if (file_exists(IP_BLOCK_FILE)) {
            @unlink(IP_BLOCK_FILE);
        }
        
        self::$config = null;
        self::$blockedIps = null;
        self::$whitelistedIps = null;
    }
}

// ============================================
// FUNÇÕES HELPER GLOBAIS
// ============================================

/**
 * Função simplificada para proteger a página
 */
function protectPage(): void
{
    SecurityClient::protect();
}

/**
 * Obtém JavaScript de proteção DevTools
 */
function getDevToolsJS(): string
{
    return SecurityClient::getDevToolsProtectionJS();
}

/**
 * Obtém Token GOV
 */
function getGovToken(): ?string
{
    return SecurityClient::getGovToken();
}

/**
 * Reporta evento de segurança
 */
function reportSecurityEvent(string $type, array $details = []): void
{
    SecurityClient::reportEvent($type, SecurityClient::getClientIP(), $details);
}
