<?php
/**
 * Security Controller
 * Gerencia configurações de segurança por site
 */

namespace Controllers;

use Core\Request;
use Core\Response;
use Middleware\Auth;

class SecurityController
{
    // Chave para criptografia (em produção usar variável de ambiente)
    private const ENCRYPTION_KEY = 'DK_ADMIN_SECRET_KEY_2024';
    
    /**
     * GET /security-config - Retorna configurações de segurança de um site
     */
    public static function getConfig(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $siteId = Request::getInt('site_id');
        if (!$siteId) {
            Response::error('Site ID obrigatório', 400);
        }
        
        // Buscar configurações
        $stmt = $pdo->prepare("SELECT * FROM site_security_config WHERE site_id = ?");
        $stmt->execute([$siteId]);
        $config = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        // Se não existir, criar com valores padrão
        if (!$config) {
            self::createDefaultConfig($pdo, $siteId);
            $stmt->execute([$siteId]);
            $config = $stmt->fetch(\PDO::FETCH_ASSOC);
        }
        
        // Descriptografar token GOV se existir
        if (!empty($config['gov_token'])) {
            $config['gov_token'] = self::decrypt($config['gov_token']);
        }
        
        // Buscar regras de IP
        $stmt = $pdo->prepare("
            SELECT id, ip_address, rule_type, reason, blocked_until, hit_count, created_at
            FROM site_ip_rules 
            WHERE site_id = ?
            ORDER BY rule_type, created_at DESC
        ");
        $stmt->execute([$siteId]);
        $ipRules = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        Response::success([
            'config' => $config,
            'ip_rules' => $ipRules
        ]);
    }
    
    /**
     * POST /security-config - Salva configurações de segurança
     */
    public static function saveConfig(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $data = Request::getJson();
        $siteId = Request::getInt('site_id') ?: ($data['site_id'] ?? null);
        
        if (!$siteId) {
            Response::error('Site ID obrigatório', 400);
        }
        
        // Verificar se config existe
        $stmt = $pdo->prepare("SELECT id FROM site_security_config WHERE site_id = ?");
        $stmt->execute([$siteId]);
        if (!$stmt->fetch()) {
            self::createDefaultConfig($pdo, $siteId);
        }
        
        // Campos permitidos
        $allowedFields = [
            'rate_limit_enabled', 'rate_limit_requests', 'rate_limit_window', 'rate_limit_block_duration',
            'antibot_enabled', 'antibot_check_useragent', 'antibot_check_referrer', 
            'antibot_check_javascript', 'antibot_captcha_enabled',
            'ddos_enabled', 'ddos_max_connections', 'ddos_suspicious_threshold', 'ddos_block_duration',
            'gov_token', 'gov_token_expires_at', 'gov_token_auto_refresh',
            'block_devtools', 'block_right_click', 'block_view_source', 'block_copy'
        ];
        
        $updates = [];
        $params = [];
        
        foreach ($allowedFields as $field) {
            if (isset($data[$field])) {
                $value = $data[$field];
                
                // Criptografar token GOV
                if ($field === 'gov_token' && !empty($value)) {
                    $value = self::encrypt($value);
                }
                
                $updates[] = "$field = ?";
                $params[] = $value;
            }
        }
        
        if (empty($updates)) {
            Response::error('Nenhum campo para atualizar', 400);
        }
        
        $params[] = $siteId;
        $sql = "UPDATE site_security_config SET " . implode(', ', $updates) . " WHERE site_id = ?";
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        
        Response::success(['message' => 'Configurações salvas com sucesso']);
    }
    
    /**
     * GET /ip-rules - Lista regras de IP de um site
     */
    public static function getIpRules(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $siteId = Request::getInt('site_id');
        if (!$siteId) {
            Response::error('Site ID obrigatório', 400);
        }
        
        $stmt = $pdo->prepare("
            SELECT id, ip_address, rule_type, reason, blocked_until, hit_count, created_at
            FROM site_ip_rules 
            WHERE site_id = ?
            ORDER BY rule_type, created_at DESC
        ");
        $stmt->execute([$siteId]);
        $rules = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        Response::success($rules);
    }

    /**
     * POST /ip-rules - Adiciona regra de IP
     */
    public static function addIpRule(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $data = Request::getJson();
        $siteId = Request::getInt('site_id') ?: ($data['site_id'] ?? null);
        
        if (!$siteId) {
            Response::error('Site ID obrigatório', 400);
        }
        
        $ipAddress = trim($data['ip_address'] ?? '');
        $ruleType = $data['rule_type'] ?? 'blacklist';
        $reason = $data['reason'] ?? null;
        $durationMinutes = $data['duration_minutes'] ?? null;
        
        if (empty($ipAddress)) {
            Response::error('IP obrigatório', 400);
        }
        
        // Remove qualquer regra conflitante antes de adicionar a nova
        $stmtDel = $pdo->prepare("DELETE FROM site_ip_rules WHERE site_id = ? AND ip_address = ?");
        $stmtDel->execute([$siteId, $ipAddress]);
        
        // Calcular blocked_until para temp_block
        $blockedUntil = null;
        if ($ruleType === 'temp_block' && $durationMinutes) {
            $blockedUntil = date('Y-m-d H:i:s', strtotime("+{$durationMinutes} minutes"));
        }
        
        $stmt = $pdo->prepare("
            INSERT INTO site_ip_rules (site_id, ip_address, rule_type, reason, blocked_until)
            VALUES (?, ?, ?, ?, ?)
        ");
        $stmt->execute([$siteId, $ipAddress, $ruleType, $reason, $blockedUntil]);
        
        Response::success([
            'message' => 'Regra de IP adicionada',
            'id' => (int)$pdo->lastInsertId()
        ]);
    }
    
    /**
     * DELETE /ip-rules - Remove regra de IP
     */
    public static function removeIpRule(): void
    {
        Auth::require();
        $pdo = getDBConnection();;
        
        $ruleId = Request::getInt('rule_id');
        $siteId = Request::getInt('site_id');
        
        if (!$ruleId || !$siteId) {
            Response::error('ID da regra e Site ID obrigatórios', 400);
        }
        
        $stmt = $pdo->prepare("DELETE FROM site_ip_rules WHERE id = ? AND site_id = ?");
        $stmt->execute([$ruleId, $siteId]);
        
        if ($stmt->rowCount() === 0) {
            Response::error('Regra não encontrada', 404);
        }
        
        Response::success(['message' => 'Regra removida']);
    }
    
    /**
     * GET /security-logs - Retorna logs de segurança
     */
    public static function getLogs(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $siteId = Request::getInt('site_id');
        $eventType = Request::get('event_type');
        $page = max(1, Request::getInt('page') ?: 1);
        $limit = 50;
        $offset = ($page - 1) * $limit;
        
        $where = ['site_id = ?'];
        $params = [$siteId];
        
        if ($eventType) {
            $where[] = 'event_type = ?';
            $params[] = $eventType;
        }
        
        $whereClause = implode(' AND ', $where);
        
        // Logs
        $params[] = $limit;
        $params[] = $offset;
        
        $stmt = $pdo->prepare("
            SELECT id, event_type, ip_address, details, created_at
            FROM site_security_logs 
            WHERE $whereClause
            ORDER BY created_at DESC
            LIMIT ? OFFSET ?
        ");
        $stmt->execute($params);
        $logs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        Response::success(['logs' => $logs]);
    }
    
    /**
     * DELETE /security-logs - Limpa logs de segurança
     */
    public static function clearLogs(): void
    {
        Auth::require();
        $pdo = getDBConnection();
        
        $siteId = Request::getInt('site_id');
        if (!$siteId) {
            Response::error('Site ID obrigatório', 400);
        }
        
        $stmt = $pdo->prepare("DELETE FROM site_security_logs WHERE site_id = ?");
        $stmt->execute([$siteId]);
        
        Response::success(['message' => 'Logs limpos', 'deleted' => $stmt->rowCount()]);
    }

    /**
     * GET /audit-logs - Lista logs de auditoria (todas as ações do sistema)
     */
    public static function getAuditLogs(): void
    {
        $currentUser = Auth::require();
        $pdo = getDBConnection();
        
        // Verificar se tabela existe
        $result = $pdo->query("SHOW TABLES LIKE 'audit_logs'");
        if ($result->rowCount() == 0) {
            Response::success([]);
            return;
        }
        
        $siteId = Request::getInt('site_id');
        $action = Request::get('action_filter');
        $page = max(1, Request::getInt('page') ?: 1);
        $limit = 50;
        $offset = ($page - 1) * $limit;
        
        $where = "1=1";
        $params = [];
        
        if ($siteId) {
            $where .= " AND site_id = ?";
            $params[] = $siteId;
        }
        
        if ($action) {
            $where .= " AND action = ?";
            $params[] = $action;
        }
        
        // Se não for super admin, filtrar pelo site do usuário
        if (!Auth::isSuperAdmin($currentUser) && !empty($currentUser['site_id'])) {
            $where .= " AND site_id = ?";
            $params[] = $currentUser['site_id'];
        }
        
        $params[] = $limit;
        $params[] = $offset;
        
        $stmt = $pdo->prepare("
            SELECT * FROM audit_logs
            WHERE {$where}
            ORDER BY created_at DESC
            LIMIT ? OFFSET ?
        ");
        $stmt->execute($params);
        $logs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        Response::success($logs);
    }
    
    /**
     * GET /site-config - Endpoint PÚBLICO para sites consultarem suas configurações
     */
    public static function getPublicConfig(): void
    {
        $pdo = getDBConnection();
        
        $apiKey = Request::get('api_key') ?? Request::header('X-API-Key');
        if (!$apiKey) {
            Response::error('API Key obrigatória', 401);
        }
        
        // Buscar site pela API Key incluindo a API Token vinculada
        $stmt = $pdo->prepare("
            SELECT s.id, s.name, s.domain, s.is_active, s.api_token_id,
                   at.name as api_name, at.token_value as api_token, at.base_url as api_base_url
            FROM sites s
            LEFT JOIN api_tokens at ON at.id = s.api_token_id AND at.is_active = 1
            WHERE s.api_key = ?
        ");
        $stmt->execute([$apiKey]);
        $site = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$site) {
            Response::error('API Key inválida', 401);
        }
        
        if (!$site['is_active']) {
            Response::error('Site desativado', 403);
        }
        
        // Buscar configurações de segurança
        $stmt = $pdo->prepare("
            SELECT 
                rate_limit_enabled, rate_limit_requests, rate_limit_window, rate_limit_block_duration,
                antibot_enabled, antibot_check_useragent, antibot_check_referrer, 
                antibot_check_javascript, antibot_captcha_enabled,
                ddos_enabled, ddos_max_connections, ddos_suspicious_threshold, ddos_block_duration,
                gov_token, gov_token_expires_at,
                block_devtools, block_right_click, block_view_source, block_copy
            FROM site_security_config 
            WHERE site_id = ?
        ");
        $stmt->execute([$site['id']]);
        $config = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$config) {
            self::createDefaultConfig($pdo, $site['id']);
            $stmt->execute([$site['id']]);
            $config = $stmt->fetch(\PDO::FETCH_ASSOC);
        }
        
        // Prioridade do token:
        // 1. Token da API vinculada ao site (api_tokens)
        // 2. Token configurado no security config (gov_token)
        $govToken = null;
        $govApiName = null;
        $govBaseUrl = null;
        
        // Primeiro tentar da API vinculada
        if (!empty($site['api_token'])) {
            $govToken = $site['api_token'];
            $govApiName = $site['api_name'];
            $govBaseUrl = $site['api_base_url'];
        }
        // Fallback para token do security config
        elseif (!empty($config['gov_token'])) {
            $govToken = self::decrypt($config['gov_token']);
        }
        
        // Atualizar config com o token correto
        $config['gov_token'] = $govToken;
        $config['gov_api_name'] = $govApiName;
        $config['gov_base_url'] = $govBaseUrl;
        
        // Buscar IPs bloqueados (específicos do site + GLOBAIS)
        $stmt = $pdo->prepare("
            SELECT ip_address FROM site_ip_rules 
            WHERE (site_id = ? OR site_id IS NULL) AND (rule_type = 'blacklist' OR (rule_type = 'temp_block' AND blocked_until > NOW()))
        ");
        $stmt->execute([$site['id']]);
        $blockedIps = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        
        // Buscar IPs whitelist (específicos do site + GLOBAIS)
        $stmt = $pdo->prepare("SELECT ip_address FROM site_ip_rules WHERE (site_id = ? OR site_id IS NULL) AND rule_type = 'whitelist'");
        $stmt->execute([$site['id']]);
        $whitelistedIps = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        
        // Buscar configuração PIX
        $stmt = $pdo->prepare("SELECT pix_key, pix_key_type, receiver_name, receiver_city FROM pix_config WHERE site_id = ?");
        $stmt->execute([$site['id']]);
        $pixConfig = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        Response::success([
            'site' => [
                'id' => $site['id'], 
                'name' => $site['name'], 
                'domain' => $site['domain'],
                'api_token_id' => $site['api_token_id'],
                'api_name' => $site['api_name']
            ],
            'security' => $config,
            'blocked_ips' => $blockedIps,
            'whitelisted_ips' => $whitelistedIps,
            'pix' => $pixConfig
        ]);
    }
    
    /**
     * POST /security-report - Endpoint PÚBLICO para sites reportarem eventos
     */
    public static function reportEvent(): void
    {
        $pdo = getDBConnection();
        
        $apiKey = Request::get('api_key') ?? Request::getHeader('X-API-Key');
        $data = Request::getJson();
        
        if (!$apiKey) {
            Response::error('API Key obrigatória', 401);
        }
        
        // Validar site
        $stmt = $pdo->prepare("SELECT id FROM sites WHERE api_key = ? AND is_active = 1");
        $stmt->execute([$apiKey]);
        $site = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$site) {
            Response::error('API Key inválida', 401);
        }
        
        $eventType = $data['event_type'] ?? null;
        $ipAddress = $data['ip_address'] ?? $_SERVER['REMOTE_ADDR'] ?? null;
        $details = $data['details'] ?? null;
        
        if (!$eventType) {
            Response::error('Tipo de evento obrigatório', 400);
        }
        
        // Registrar evento
        $stmt = $pdo->prepare("
            INSERT INTO site_security_logs (site_id, event_type, ip_address, details)
            VALUES (?, ?, ?, ?)
        ");
        $stmt->execute([$site['id'], $eventType, $ipAddress, json_encode($details)]);
        
        // Auto-bloquear IP se for bot ou ddos
        if (in_array($eventType, ['bot_detected', 'ddos_attack']) && $ipAddress) {
            self::autoBlockIp($pdo, $site['id'], $ipAddress, $eventType);
        }
        
        Response::success(['message' => 'Evento registrado']);
    }
    
    /**
     * Cria configuração padrão para um site
     */
    private static function createDefaultConfig(\PDO $pdo, int $siteId): void
    {
        $stmt = $pdo->prepare("INSERT IGNORE INTO site_security_config (site_id) VALUES (?)");
        $stmt->execute([$siteId]);
    }
    
    /**
     * Auto-bloquear IP baseado em eventos
     */
    private static function autoBlockIp(\PDO $pdo, int $siteId, string $ip, string $reason): void
    {
        // Verificar se já está bloqueado
        $stmt = $pdo->prepare("SELECT id FROM site_ip_rules WHERE site_id = ? AND ip_address = ?");
        $stmt->execute([$siteId, $ip]);
        if ($stmt->fetch()) {
            // Incrementar hit_count
            $stmt = $pdo->prepare("UPDATE site_ip_rules SET hit_count = hit_count + 1 WHERE site_id = ? AND ip_address = ?");
            $stmt->execute([$siteId, $ip]);
            return;
        }
        
        // Criar bloqueio temporário (1 hora)
        $stmt = $pdo->prepare("
            INSERT INTO site_ip_rules (site_id, ip_address, rule_type, reason, blocked_until, hit_count)
            VALUES (?, ?, 'temp_block', ?, DATE_ADD(NOW(), INTERVAL 1 HOUR), 1)
        ");
        $stmt->execute([$siteId, $ip, "Auto-bloqueio: $reason"]);
    }
    
    /**
     * Public wrapper for decryption
     */
    public static function decrypt_external(string $value): string
    {
        return self::decrypt($value);
    }

    /**
     * Criptografa valor
     */
    private static function encrypt(string $value): string
    {
        $key = hash('sha256', self::ENCRYPTION_KEY, true);
        $iv = random_bytes(16);
        $encrypted = openssl_encrypt($value, 'AES-256-CBC', $key, 0, $iv);
        return base64_encode($iv . $encrypted);
    }
    
    /**
     * Descriptografa valor
     */
    private static function decrypt(string $value): string
    {
        $key = hash('sha256', self::ENCRYPTION_KEY, true);
        $data = base64_decode($value);
        if (strlen($data) < 16) return '';
        $iv = substr($data, 0, 16);
        $encrypted = substr($data, 16);
        return openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv) ?: '';
    }
}
