<?php
// 2025 12 29 - 10:37H
namespace BillSecurity\IntegrityScanner;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

//die((string) __LINE__ . '   ' . var_export('x', true));


/*
delete_transient('plugin_scanner_12h_interval');
error_log('✅ Transient deletado para teste');
return;
die();
*/

// define('BILL_DEBUG', true);  // Debug mode ON - ignora "uma vez por dia"
define('BILL_DEBUG', false); // Debug mode OFF - aplica "uma vez por dia"




// Verifica e ajusta memory_limit
try {
    $current_memory = ini_get('memory_limit');

    // Converte para MB de forma simplificada
    $current_mb = (int) $current_memory;
    if (stripos($current_memory, 'G') !== false) {
        $current_mb *= 1024;
    } elseif (stripos($current_memory, 'K') !== false) {
        $current_mb /= 1024;
    }

    if ($current_mb < 256) {

        // Tenta aumentar
        @ini_set("memory_limit", "256M");

        // Pega o novo valor
        $new_memory = ini_get('memory_limit');
        $new_mb = (int) $new_memory;
        if (stripos($new_memory, 'G') !== false) {
            $new_mb *= 1024;
        } elseif (stripos($new_memory, 'K') !== false) {
            $new_mb /= 1024;
        }

        if ($new_mb < 256) {

            error_log("Unable to increase the PHP memory limit for 256M. It is recommended to increase it to at least 256M.");
        }
    }
} catch (Exception $e) {
    // Em caso de erro
}
safe_set_time_limit(300);


// ⚡ FUNÇÃO SEGURA PARA AJUSTAR TIME LIMIT
function safe_set_time_limit($desired_time)
{
    try {
        $current_limit = (int) ini_get('max_execution_time');
        // Debug do limite atual
        if (isset($GLOBALS['site_checkup_bill_debug']) && $GLOBALS['site_checkup_bill_debug']) {
            site_checkup_record_debug("Current PHP max_execution_time: " . $current_limit . "s");
        }
        // Só ajusta se necessário e se permitido
        if ($current_limit < $desired_time && $current_limit !== 0) {
            // Verifica se a função está disponível (pode estar desabilitada)
            if (function_exists('set_time_limit')) {
                @set_time_limit($desired_time);
                // Confirma se foi alterado
                $new_limit = (int) ini_get('max_execution_time');
                if (isset($GLOBALS['site_checkup_bill_debug']) && $GLOBALS['site_checkup_bill_debug']) {
                    if ($new_limit >= $desired_time || $new_limit === 0) {
                        site_checkup_record_debug("PHP time_limit increased to: " . $new_limit . "s");
                    } else {
                        site_checkup_record_debug("Warning: Could not increase time_limit. Current: " . $new_limit . "s");
                    }
                }
            } else {
                if (isset($GLOBALS['site_checkup_bill_debug']) && $GLOBALS['site_checkup_bill_debug']) {
                    site_checkup_record_debug("Warning: set_time_limit() function is disabled");
                }
            }
        } else {
            if (isset($GLOBALS['site_checkup_bill_debug']) && $GLOBALS['site_checkup_bill_debug']) {
                site_checkup_record_debug("PHP time_limit already sufficient: " . $current_limit . "s");
            }
        }
    } catch (Exception $e) {
        // Silenciosamente captura qualquer exceção para não quebrar o scan
        if (isset($GLOBALS['site_checkup_bill_debug']) && $GLOBALS['site_checkup_bill_debug']) {
            site_checkup_record_debug("Exception in time_limit adjustment: " . $e->getMessage());
        }
    }
}

$X =  array(
    // Arquivos de verificação de serviços
    'google*.html',
    'google*.txt',
    'google*.xml',
    'bing*.html',
    'bing*.txt',
    'BingSiteAuth.xml',
    'facebook-domain-verification*.html',
    'yandex_verification*.html',
    'pinterest-site-verification*.html',
    'ahrefs-site-verification*.html',
    'google-site-verification.html',

    // Arquivos de sitemap/SEO
    'sitemap.xml',
    'sitemap_index.xml',
    'sitemap*.xml',
    'sitemap.html',
    'rss.xml',
    'feed.xml',
    'atom.xml',
    'robots.txt',

    // Arquivos de configuração do servidor
    '.htaccess',
    '.htpasswd',
    'web.config',
    '.user.ini',
    'favicon.ico',
    'crossdomain.xml',
    'clientaccesspolicy.xml',

    // Arquivos de log (comuns)
    'error_log',
    'debug.log',
    'access_log',

    // Arquivos de manutenção
    'maintenance.html',
    'maintenance.php',

    // Arquivos de controle de versão (deveriam ser removidos, mas são comuns)
    '.gitignore',
    '.gitattributes',
    'README.md',
    'CHANGELOG.md',
    'LICENSE.md',

    // Outros arquivos comuns
    'cgi-bin/', // Diretório, não arquivo
    '.well-known/', // Diretório para SSL/security

    // Extensões específicas para ignorar
    '*.txt',
    '*.html',
    '*.xml',
    '*.ico',
    '*.md',
);




//   die((string) __LINE__ . '   ' . var_export($current_step, true));$today = date('Y-m-d');return;

/**
 * Creates or updates the site integrity issues table structure
 * Ensures proper indexing for performance and data integrity
 */
/*
 Table Field Documentation
1. IDENTIFICATION FIELDS
id - Primary auto-increment identifier for each issue record

file_path - Relative path from WordPress root (e.g., wp-admin/admin.php)

file_hash - Current SHA-256 hash of the suspicious file for verification

2. ISSUE CLASSIFICATION
issue_type - Type of issue: modified|unknown|missing|malicious|permissions

severity - Issue severity level: low|medium|high|critical

description - Detailed human-readable explanation of the detected problem

3. USER WORKFLOW MANAGEMENT
status - Current status: detected|reviewed|ignored|fixed|quarantined

reviewed - Boolean flag indicating if user has reviewed the issue (Yes/No)

reviewed_at - Timestamp when user reviewed the issue

user_action - Action taken by user: ignore|repair|delete|quarantine

user_notes - User comments or notes about the issue for future reference

4. TEMPORAL TRACKING
first_detected - Initial discovery timestamp (immutable, first occurrence)

last_detected - Most recent detection timestamp (updated on re-scan)

resolved_at - Timestamp when the issue was marked as resolved/fixed

5. TECHNICAL METADATA
expected_hash - Expected SHA-256 hash for WordPress core files (official hash)

file_size - File size in bytes for size anomaly detection

permissions - File system permissions in octal notation (e.g., 0644, 0755)

file_owner - OS-level file owner information (user/group)

last_modified - Actual file system last modification timestamp

6. SCANNER METADATA
scanner_version - Version of the scanner/detection engine that found the issue
 
scan_id - Unique identifier for the scan session (for issue correlation)

detection_method - Method used for detection: hash_check|signature|heuristics

7. OCCURRENCE TRACKING
occurrence_count - Number of times this issue was detected across scans

is_active - Boolean flag indicating if issue is still present/active

8. AUDIT TRAIL
created_at - Record creation timestamp (automatically set on insert)

updated_at - Last modification timestamp (automatically updated on changes)

Issue Type Definitions
modified
Core WordPress file that has been altered from the original version

Detected via hash mismatch comparison with official WordPress files

Example: wp-admin/admin.php has different content than expected

unknown
File not part of the standard WordPress installation

Found in WordPress core directories but not in official distribution

Example: wp-includes/new-file.php that shouldn't exist

missing
Core WordPress file that is absent from the installation

Expected file not found in its proper location

Example: wp-includes/version.php file not found

malicious
File containing known malware signatures or suspicious code patterns

Detected via signature matching or heuristic analysis

Example: PHP file with eval(base64_decode( pattern

permissions
File with insecure or incorrect permission settings

Detected via permission check against security standards

Example: Configuration file with 0777 (world-writable) permissions

Severity Level Guidelines
critical
Malicious files with active malware

Core system files modified with backdoors

Immediate attention required, security breach likely

high
Modified core WordPress files

Unknown files in sensitive directories (wp-admin/, wp-includes/)

High risk of compromise or instability

medium
Unknown files in less sensitive areas

Permission issues on important files

Requires review but not emergency

low
Minor permission issues on non-critical files

Modified non-essential files

Informational issues for awareness

Status Flow Explanation
text
detected → reviewed → [ignored|fixed|quarantined]
    ↑          |
    └──────────┘ (if re-detected)
detected - Initial state when issue is first found

reviewed - User has seen and acknowledged the issue

ignored - User decided to ignore the issue (whitelisted)

fixed - Issue has been resolved (file repaired/restored)

quarantined - File has been moved to quarantine for safety
*/

/*
// Criar a tabela (normalmente no activation hook)
//$scanner->create_integrity_table();

// Usar a classe
use BillSecurity\IntegrityScanner\Bill_Catch_Malware;

$scanner = new Bill_Catch_Malware();


// Executar o scan
$result = $scanner->run_scan();

// Verificar resultado
if ($result['success']) {
    echo "Scan completado! " . $result['issues_found'] . " problemas encontrados.";
}

// Obter estatísticas
$stats = $scanner->get_scan_statistics();
echo "Problemas ativos: " . $stats['active_issues'];
die();
*/


/**
 * Bill_Catch_Malware - WordPress File Integrity Scanner
 * 
 * Scans WordPress core files for integrity issues, detects suspicious files,
 * and stores findings in a database for user review and action.
 * 
 * @package BillSecurity
 * @version 1.0.0
 */



/**
 * Main scanner class that detects and stores file integrity issues
 * Runs once daily to monitor WordPress core file changes and suspicious files
 */
//debug4();

//die((string) __LINE__ . '   ' . var_export('x', true));

class Bill_Catch_Malware
{

    private $plugin_scanner;

    /**
     * @var string Database table name for integrity issues
     */
    private $table_name;


    /**
     * @var string Option name for last scan timestamp
     */
    private $option_name = 'bill_catch_malware_last_scan';

    /**
     * @var string Scanner version
     */
    private $version = '1.0.0';

    /**
     * Constructor - initializes the scanner
     */
    public function __construct()
    {
        global $wpdb;

        // die((string) __LINE__ . '   ' . var_export('x', true));

        // \//debug4();
        // \debug3a("301");


        $this->table_name = $wpdb->prefix . 'bill_site_integrity_issues';

        // Carrega a função plugins_api() para uso futuro
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        // === ADICIONE ESTA LINHA ===
        $this->site_checkup_create_integrity_table();
        // ===========================
        // Inicializa o scanner de plugins
        $this->plugin_scanner = new Plugin_Integrity_Scanner();
        // Manual 
        // > plugins, precisa acertar aqui >> exemplo: $this->plugin_scanner->do_actual_plugin_scan();
        // com cron...
        add_action('init', array($this, 'maybe_run_24h_scan'), 20);
        // \//debug4('🟢 DEBUG: Bill_Catch_Malware construído em ' . date('Y-m-d H:i:s'));
        // die((string) __LINE__ . '   ' . var_export('x', true));
    }


    public function maybe_run_24h_scan()
    {
        $transient_name = 'malware_scanner_24h_interval';
        // \//debug4();
        // \debug3a("326");
        // // //debug4();
        //error_log(var_export(gettype(get_transient($transient_name)), true));
        $transient_value = get_transient($transient_name);
        // Só executa se o transiente NÃO existir (tiver expirado)
        if ($transient_value === false) {
            // die((string) __LINE__ . '   ' . var_export('x', true));

            // $this->execute_integrity_scan();
            set_transient($transient_name, true, 24 * HOUR_IN_SECONDS);
            $this->execute_integrity_scan();
            // \//debug4();
            // \debug3a("335");
        } else {
            //die((string) __LINE__ . '   ' . var_export('x', true));

            // error_log(var_export(__LINE__, true));
            // \//debug4();
            // \debug3a("339");
        }
    }








    public function run_plugin_scan()
    {

        // BLOQUEIO DE EXECUÇÃO DUPLICADA
        static $executed = false;
        if ($executed) {
            return;
        }
        $executed = true;


        // OPTION NAME específica para plugins
        $plugin_scan_option = 'bill_plugin_last_scan_date';
        $last_plugin_scan = get_option($plugin_scan_option);
        $today = date('Y-m-d');

        // Verifica o modo debug
        $bill_debug = defined('BILL_DEBUG') ? BILL_DEBUG : false;

        // Aplica a mesma regra "uma vez por dia" (exceto em debug)
        if (!$bill_debug && $last_plugin_scan && date('Y-m-d', strtotime($last_plugin_scan)) === $today) {
            // error_log('Plugin scan already completed today. Skipping.');
            return; // Já fez scan de plugin hoje
        }

        // Agora executa o scan de UM plugin
        $plugin_to_scan = $this->plugin_scanner->get_next_plugin_to_scan();

        if ($plugin_to_scan) {
            $result = $this->plugin_scanner->scan_plugin($plugin_to_scan);


            if ($result !== false) {

                // ENCONTRA o índice deste plugin na lista
                $plugins = get_plugins();
                $plugin_files = array_keys($plugins);
                $current_index = array_search($plugin_to_scan, $plugin_files);

                if ($current_index !== false) {
                    // ATUALIZA o índice APENAS se encontrou
                    update_option('bill_last_plugin_scanned_index', $current_index);
                    // //  // //debug4("✅ Índice atualizado para: " . $current_index);

                    // Salva timestamp deste plugin
                    update_option('bill_plugin_scan_' . md5($plugin_to_scan), time());
                    // //  // //debug4("✅ Timestamp salvo para plugin");
                } else {
                    // //  // //debug4("❌ Plugin não encontrado na lista: " . $plugin_to_scan);
                }
            } else {
                // //  // //debug4("❌ Scan retornou false - NÃO atualizando índice");
            }
            // === FIM DO BLOCO ===

            // Atualiza timestamp do último scan DESTE plugin específico
            update_option('bill_plugin_scan_' . md5($plugin_to_scan), time());

            // DEBUG
            if (defined('BILL_DEBUG') && BILL_DEBUG) {
                error_log('Plugin scan completed for: ' . $plugin_to_scan);
                error_log('Result: ' . print_r($result, true));
            }
        }
    }









    /**
     * Main method to execute the scan and store results
     * Ensures the scan runs only once per day
     * 
     * @return array Scan results with status and statistics
     */

    /*
    public function run_scan()
    {
        // Check if scan already ran today
        $last_scan = get_option($this->option_name);
        $today = date('Y-m-d');

        // Check if debug mode is enabled
        $bill_debug = defined('BILL_DEBUG') ? BILL_DEBUG : false;

        // Only enforce "once per day" rule if NOT in debug mode
        if (!$bill_debug && $last_scan && date('Y-m-d', strtotime($last_scan)) === $today) {
            return array(
                'success' => false,
                'message' => 'Scan already completed today. Next scan available tomorrow.',
                'last_scan' => $last_scan
            );
        }

        // Execute the scan
        $scan_results = $this->execute_integrity_scan();

        // Update last scan timestamp
        update_option($this->option_name, current_time('mysql'));

        return array_merge($scan_results, array(
            'scan_date' => $today,
            'next_scan_available' => date('Y-m-d', strtotime('+1 day'))
        ));
    }
    */
    /*
    public function run_daily_scan()
    {

        // Mantém lógica "uma vez por dia"
        $last_scan = get_option('bill_core_last_scan_date');
        $today = date('Y-m-d');

        if ($last_scan && date('Y-m-d', strtotime($last_scan)) === $today) {
            return; // Já fez scan do core hoje
        }

        // ... executa scan do core WordPress ...

        update_option('bill_core_last_scan_date', current_time('mysql'));
        $this->run_scan();
    }
    */



    /**
     * Executes the integrity scan by calling WordPress scanner and storing results
     * 
     * @return array Detailed scan results with issue statistics
     */

    private function execute_integrity_scan()
    {
        global $wpdb;

        // Lista de arquivos globais seguros que não devem ser reportados se encontrados na raiz
        $global_safe_ignored = array(
            '.htaccess',
            'robots.txt',
            'sitemap.xml',
            'license.txt',
            'readme.html',
            'wp-config.php',
            'wp-includes/version.php', // Ignorar este pois ele é alterado em cada versão
            'index.php', // Geralmente seguro
        );
        // Usa lowercased para pesquisa rápida na Seção 2 e 3
        $excluded_files = array_flip(array_map('strtolower', $global_safe_ignored));

        // Padrões RegEx para ignorar arquivos na RAIZ que são comumente benignos/logs
        $root_patterns_to_ignore = array(
            '/^google[0-9a-f]{16}\.html$/i', // googlexxxxxx.html
            '/^bing[0-9a-f]{16}\.html$/i',   // bingxxxxxxxx.html
            '/^wp-config-sample\.php$/i',
            '/\.log$/i',                     // Qualquer .log
            '/\.ini$/i',                     // user.ini, php.ini
        );

        try {
            // 1. PEGAR OS CHECKSUMS OFICIAIS
            $wp_version = get_bloginfo('version');
            $wp_locale = get_locale();

            $checksums = get_core_checksums($wp_version, $wp_locale);

            if (false === $checksums || !is_array($checksums)) {
                return array(
                    'success' => false,
                    'message' => 'Could not retrieve WordPress core checksums from API',
                    'issues_found' => 0
                );
            }

            // Filtrar arquivos do wp-content/ (são personalizados e não devem ser checados por hash)
            foreach ($checksums as $file => $checksum) {
                if (false !== strpos($file, 'wp-content/')) {
                    unset($checksums[$file]);
                }
            }

            // 2. FAZER A VERIFICAÇÃO DO CORE (Hash Check)
            $file_results = array();
            $filepath = ABSPATH;

            foreach ($checksums as $file => $expected_checksum) {
                $normalized_file = strtolower($file);

                if (isset($excluded_files[$normalized_file])) {
                    continue;
                }

                $full_path = $filepath . $file;

                if (file_exists($full_path)) {
                    $local_checksum = md5_file($full_path);

                    if ($local_checksum !== $expected_checksum) {
                        // Arquivo MODIFICADO! -> CORE_MODIFIED
                        $reason = esc_html__('Core content modified', 'site-checkup') .
                            ' (Hash mismatch: ' . substr($expected_checksum, 0, 8) . ' vs ' . substr($local_checksum, 0, 8) . ')';
                        $issue_type = 'CORE_MODIFIED';
                        $file_results[] = array($file, $reason, $issue_type); // 3 elementos
                    }
                } else {
                    // Arquivo AUSENTE! -> CORE_MISSING
                    $reason = esc_html__('Core file missing', 'site-checkup');
                    $issue_type = 'CORE_MISSING';
                    $file_results[] = array($file, $reason, $issue_type); // 3 elementos
                }
            }

            // 3. PROCURAR POR ARQUIVOS DESCONHECIDOS (UNKNOWN_FILE)
            if (class_exists('\RecursiveDirectoryIterator')) {
                $directories = array(
                    untrailingslashit(ABSPATH),
                    untrailingslashit(ABSPATH . 'wp-admin'),
                    untrailingslashit(ABSPATH . WPINC),
                );



                // LIMITE CRÍTICO PARA ARQUIVOS DE LOGS
                $CRITICAL_ERROR_LOG_SIZE =  5242880; // 5 MB em bytes  
                $WP_UPLOADS_DIR = wp_upload_dir()['basedir'];

                // Define o limite de 5 MB em bytes
                $CRITICAL_DEBUG_LOG_SIZE = 5242880;
                $WP_CONTENT_DIR = WP_CONTENT_DIR;
                $DEBUG_LOG_PATH = trailingslashit($WP_CONTENT_DIR) . 'debug.log';

                // --- DEFINIÇÕES CRÍTICAS PARA ROBOTS.TXT E HTACCESS ---
                $ROBOTS_PATH = ABSPATH . 'robots.txt'; // Define o caminho
                $ROBOTS_MAX_SIZE = 100 * 1024; // 100 KB
                $ROBOTS_MAX_SITEMAPS = 5; // Limite de Sitemaps

                $HTACCESS_PATH = ABSPATH . '.htaccess'; // Define o caminho
                $HTACCESS_MAX_SIZE = 100 * 1024; // 100 KB
                // Padrões RegEx comuns de malware em .htaccess (Se você não os definiu antes)
                $HTACCESS_MALWARE_PATTERNS = array(
                    '/[A-Z0-9]{32}#END/i',
                    '/RewriteRule\s+\^[^$]*\$\s+http(s)?:\/\/[^\s]+/i',
                    '/php_value\s+auto_prepend_file/i',
                );
                // EXTENSÕES DE ALTO RISCO EXPOSTAS NA RAIZ
                $EXPOSED_DB_EXTS = array('sql', 'sqldump');
                $EXPOSED_BACKUP_EXTS = array('zip', 'tar', 'tgz', 'gz', 'rar', '7z');
                $EXPOSED_LOG_EXTS = array('log', 'txt'); // Focado em logs/arquivos de texto grandes não-padrão


                // ...
                $EXPOSED_BACKUP_EXTS = array('zip', 'tar', 'tgz', 'gz', 'rar', '7z');

                // --- NOVAS PALAVRAS-CHAVE PARA IDENTIFICAR NOME DE BACKUP ---
                $BACKUP_KEYWORDS = array('backup', 'dump', 'copy', 'migrat');




                // ----------------------------------------------------



                foreach ($directories as $directory) {
                    $is_root = (untrailingslashit(ABSPATH) === $directory);

                    if ($is_root) {
                        $iterator = new \DirectoryIterator($directory);
                    } else {
                        // Varre wp-admin e wp-includes recursivamente
                        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
                    }

                    foreach ($iterator as $file) {
                        if (!$file->isFile()) {
                            continue;
                        }

                        $path = str_replace(ABSPATH, '', $file->getPathname());

                        // --- FILTRO GERAL: Ignorar todos os arquivos em wp-content/ ---
                        if (strpos($path, 'wp-content/') === 0) {
                            continue;
                        }

                        $normalized_path = strtolower($path);
                        $filename = $file->getFilename();

                        // --- TRATAMENTO ESPECIAL PARA error_log (na raiz) ---
                        if ($filename === 'error_log' && $is_root) {
                            if ($file->getSize() >= $CRITICAL_ERROR_LOG_SIZE) {
                                $reason = esc_html__('Excessive size (over 5MB) detected. Possible log bomb or data obfuscation.', 'site-checkup');
                                $issue_type = 'LOG_OVERSIZE';
                                $file_results[] = array($path, $reason, $issue_type);
                            }
                            continue;
                        }

                        // --- FILTRO DE ARQUIVOS CONHECIDOS OU SEGUROS (Core/Lista Global) ---
                        if (isset($checksums[$path]) || isset($excluded_files[$normalized_path])) {
                            continue;
                        }

                        // --- TRATAMENTO ESPECÍFICO DA RAIZ (ABSPATH) ---
                        if ($is_root) {

                            // 1. FILTRO DE PADRÕES DA RAIZ (IGNORAR benignos: .log, .ini, google*.html, etc.)
                            $skip_file = false;
                            foreach ($root_patterns_to_ignore as $pattern) {
                                if (preg_match($pattern, $filename)) {
                                    $skip_file = true;
                                    break;
                                }
                            }
                            if ($skip_file) {
                                continue;
                            }

                            // 2. ANÁLISE DE RISCO ESPECÍFICO (BACKUPS/LOGS EXPOSTOS)
                            $file_extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

                            // --- A. ARQUIVOS DE BACKUP DE BANCO DE DADOS EXPOSTOS (CRÍTICO) ---
                            if (in_array($file_extension, $EXPOSED_DB_EXTS)) {
                                $reason = esc_html__('Exposed database backup file (SQL) found in the root directory. High risk of data breach.', 'site-checkup');
                                $issue_type = 'BACKUP_DATABASE_EXPOSED';
                                $file_results[] = array($path, $reason, $issue_type);
                                continue;
                            }

                            // --- B. ARQUIVOS COMPACTADOS/ZIP EXPOSTOS (ALTO RISCO - GENÉRICO) ---
                            // Trata TODOS os ZIPs/TARs como BACKUP_FILES_EXPOSED
                            /*
                            if (in_array($file_extension, $EXPOSED_BACKUP_EXTS)) {
                                $reason = esc_html__('Exposed site backup file (ZIP/TAR) found in the root directory. High risk of sensitive data exposure.', 'site-checkup');
                                $issue_type = 'BACKUP_FILES_EXPOSED';
                                $file_results[] = array($path, $reason, $issue_type);
                                continue;
                            }
                            */

                            if (in_array($file_extension, $EXPOSED_BACKUP_EXTS)) {
                                // Verifica se a substring "backup" (case-insensitive) está no nome do arquivo
                                if (stripos($path, 'backup') !== false) {
                                    $reason = esc_html__('Exposed site backup file (ZIP/TAR) found in the root directory. High risk of sensitive data exposure.', 'site-checkup');
                                    $issue_type = 'BACKUP_FILES_EXPOSED';
                                } else {
                                    $reason = esc_html__('Exposed file (ZIP/TAR) found in the root directory. High risk of sensitive data exposure.', 'site-checkup');
                                    $issue_type = 'COMPRESSED_FILES_EXPOSED';
                                    // $issue_type = 'BACKUP_FILES_EXPOSED';
                                }

                                $file_results[] = array($path, $reason, $issue_type);
                                continue;
                            }

                            // --- C. ARQUIVOS DE LOGS EXPOSTOS (MÉDIO RISCO) ---
                            if (in_array($file_extension, $EXPOSED_LOG_EXTS) && $file->getSize() > 1024 * 10) {
                                $reason = esc_html__('Exposed log or large text file found. May contain sensitive server information.', 'site-checkup');
                                $issue_type = 'LOG_FILE_EXPOSED';
                                $file_results[] = array($path, $reason, $issue_type);
                                continue;
                            }

                            // --- GUARD RAIL: Impedir que arquivos da raiz caiam no UNKNOWN_FILE ---
                            continue;
                        } // FIM do if ($is_root)

                        // 3. CLASSIFICAÇÃO FINAL: Se passou por todos os filtros, é um Arquivo DESCONHECIDO (ÓRFÃO)
                        $reason = esc_html__('Unknown file found in critical WordPress system directory. Potential suspicious script, tool, or unauthorized file.', 'site-checkup');
                        $issue_type = 'UNKNOWN_FILE';
                        $file_results[] = array($path, $reason, $issue_type);
                    }
                } // FIM do foreach ($directories as $directory)

                // 4. PROCURAR POR MUST-USE PLUGINS (mu-plugins)
                // Estes arquivos são carregados automaticamente e são um alvo comum para malware.
                $mu_plugins_dir = untrailingslashit(WPMU_PLUGIN_DIR);
                $mu_plugins_relative_path = str_replace(ABSPATH, '', $mu_plugins_dir) . '/';

                if (class_exists('\RecursiveDirectoryIterator') && file_exists($mu_plugins_dir)) {

                    // Apenas varrer se a pasta mu-plugins existir
                    // Usamos o iterador recursivo para pegar arquivos aninhados (subpastas)
                    $mu_iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($mu_plugins_dir, \FilesystemIterator::SKIP_DOTS));

                    foreach ($mu_iterator as $file) {
                        if (!$file->isFile()) {
                            continue;
                        }

                        $path = str_replace(ABSPATH, '', $file->getPathname());
                        $filename = $file->getFilename();

                        // Filtrar arquivos conhecidos do Core (embora improvável em mu-plugins)
                        if (isset($checksums[$path]) || in_array($path, $excluded_files, true)) {
                            continue;
                        }


                        // Define o caminho completo do arquivo para exclusão
                        $mu_plugin_exclusion = 'wp-content/mu-plugins/bill-catch-errors.php';

                        if ($path === $mu_plugin_exclusion) {
                            continue; // Pula este arquivo, ele não será reportado
                        }

                        // Se for um arquivo na pasta mu-plugins e não for core/excluído, classificamos como oculto
                        // Classificação: Arquivos em mu-plugins devem ser examinados
                        $reason = esc_html__('Hidden file in mu-plugins directory. Requires examination.', 'site-checkup');
                        $issue_type = 'MU_PLUGIN_FILE'; // Novo issue_type

                        // Adicionar ao array de resultados
                        $file_results[] = array($path, $reason, $issue_type);
                    }
                }

                // DEBUG: Mostrar resultados
                //error_log('Bill_Catch_Malware: Total issues found: ' . count($file_results));
                // ... (resto do seu código de debug e Seção 4) ...


                // --- TRATAMENTO ESPECIAL PARA debug.log (em wp-content/) ---
                if (is_file($DEBUG_LOG_PATH)) {
                    $file_size = filesize($DEBUG_LOG_PATH);
                    if ($file_size > $CRITICAL_DEBUG_LOG_SIZE) {
                        $path = str_replace(ABSPATH, '', $DEBUG_LOG_PATH);
                        $reason = esc_html__('Debug log has excessive size (over 5MB) detected. Possible log bomb or data leak/obfuscation.', 'site-checkup');
                        $issue_type = 'LOG_OVERSIZE';
                        $file_results[] = array($path, $reason, $issue_type);
                    }
                }
                // --- TRATAMENTO ESPECIAL PARA robots.txt (Ofuscação/Spam) ---
                if (is_file($ROBOTS_PATH)) {


                    $path = str_replace(ABSPATH, '', $ROBOTS_PATH);

                    if (!$this->is_safe_to_read($ROBOTS_PATH, 102400)) { // Limite de 100KB para robots
                        $reason = esc_html__('robots.txt has excessive size or is unreadable. Potential spam injection.', 'site-checkup');
                        $file_results[] = array($path, $reason, 'SUSPICIOUS_ROBOTS');
                    } else {
                        $content = @file_get_contents($ROBOTS_PATH);
                        // ... resto da tua lógica de sitemaps ...
                    }

                    if ($content === false) {
                        // Não foi possível ler o arquivo (erro de permissão)
                        $path = str_replace(ABSPATH, '', $ROBOTS_PATH);
                        $reason = esc_html__('robots.txt exists but cannot be read (Permission issue). Potential obfuscation.', 'site-checkup');
                        $issue_type = 'ROBOTS_BLOCKED';
                        $file_results[] = array($path, $reason, $issue_type);
                    } else {
                        $path = str_replace(ABSPATH, '', $ROBOTS_PATH);
                        $sitemap_count = 0;
                        $suspicious_flags = false;

                        // 1. Verificar tamanho excessivo (log bomb ou injeção de spam)
                        if (strlen($content) > $ROBOTS_MAX_SIZE) {
                            $reason = esc_html__('robots.txt has excessive size (over 100KB). Potential spam injection.', 'site-checkup');
                            $issue_type = 'SUSPICIOUS_ROBOTS';
                            $file_results[] = array($path, $reason, $issue_type);
                            $suspicious_flags = true; // Define a flag para não duplicar o registro
                        }

                        // 2. Análise de conteúdo (Sitemaps e Disallows suspeitos)
                        $lines = explode("\n", $content);
                        foreach ($lines as $line) {
                            $line = trim($line);

                            // Contar Sitemaps
                            if (stripos($line, 'Sitemap:') === 0) {
                                $sitemap_count++;
                                // Checar Sitemaps Externos Maliciosos





                                // Captura a URL completa do Sitemap. O final da RegEx foi alterado para pegar todo o caminho até o final da linha.
                                if (preg_match('/Sitemap:\s*(https?:\/\/[^\s]+)/i', $line, $matches)) {
                                    $sitemap_url = $matches[1]; // A URL capturada será o primeiro match

                                    // Checar Sitemaps Externos Maliciosos
                                    // O parse_url precisa da URL completa, não apenas do host
                                    $domain = parse_url(home_url(), PHP_URL_HOST);
                                    $sitemap_host = parse_url($sitemap_url, PHP_URL_HOST);

                                    // Se o domínio do Sitemap não for o domínio do site
                                    if ($domain && $sitemap_host && strcasecmp($domain, $sitemap_host) !== 0) {
                                        if (!$suspicious_flags) { // Evita duplicar se já foi pego pelo tamanho
                                            $reason = esc_html__('External Sitemap found in robots.txt. Potential SEO spam/malicious redirection.', 'site-checkup');
                                            $issue_type = 'SUSPICIOUS_ROBOTS';
                                            $file_results[] = array($path, $reason, $issue_type);
                                            $suspicious_flags = true;
                                        }
                                    }
                                }
                            }

                            // Checar Disallows de diretórios críticos injetados (wp-admin, wp-includes, etc. são comuns mas aqui procuramos por padrões não-padrão)
                            // Exemplo: Disallow apontando para /wp-includes/cache/
                            if (stripos($line, 'Disallow:') === 0 && preg_match('/Disallow:\s*\/wp-(includes|admin)\/([^\s]+)/i', $line)) {
                                // Um Disallow específico de subdiretórios que não são padrão pode ser usado para ofuscação.
                                if (!$suspicious_flags) {
                                    $reason = esc_html__('Suspicious "Disallow" pattern found, possibly to hide injected malware from scanners.', 'site-checkup');
                                    $issue_type = 'SUSPICIOUS_ROBOTS';
                                    $file_results[] = array($path, $reason, $issue_type);
                                    $suspicious_flags = true;
                                }
                            }
                        } // Fim do foreach ($lines)

                        // 3. Verificar excesso de Sitemaps
                        if ($sitemap_count > $ROBOTS_MAX_SITEMAPS && !$suspicious_flags) {
                            $reason = esc_html__('Excessive number of Sitemaps detected in robots.txt. Potential spam injection.', 'site-checkup');
                            $issue_type = 'SUSPICIOUS_ROBOTS';
                            $file_results[] = array($path, $reason, $issue_type);
                        }
                    }
                } // Fim da verificação de robots.txt



                // ... [Fim da Verificação do robots.txt inserida aqui] ...

                // --- TRATAMENTO ESPECIAL PARA .htaccess (Redirecionamentos Maliciosos/Injeção) ---

                $HTACCESS_PATH = ABSPATH . '.htaccess';
                $HTACCESS_MAX_SIZE = 100 * 1024; // 100 KB é um limite razoável.
                // Padrões RegEx comuns de malware em .htaccess
                $HTACCESS_MALWARE_PATTERNS = array(
                    '/[A-Z0-9]{32}#END/i', // Padrão de injeção de "código binário"
                    '/RewriteRule\s+\^[^$]*\$\s+http(s)?:\/\/[^\s]+/i', // Regra de redirecionamento global suspeita
                    '/php_value\s+auto_prepend_file/i', // Injeção de auto-execução
                );

                if (is_file($HTACCESS_PATH)) {
                    $path = str_replace(ABSPATH, '', $HTACCESS_PATH);

                    if (!$this->is_safe_to_read($HTACCESS_PATH, 204800)) { // Limite de 200KB para .htaccess
                        $reason = esc_html__('.htaccess has excessive size or is unreadable. Potential malicious redirection.', 'site-checkup');
                        $file_results[] = array($path, $reason, 'SUSPICIOUS_HTACCESS');
                    } else {
                        $content = @file_get_contents($HTACCESS_PATH);
                        // ... resto da tua lógica de patterns ...
                    }

                    if ($content === false) {
                        // Não foi possível ler o arquivo (erro de permissão)
                        $reason = esc_html__('.htaccess exists but cannot be read (Permission issue). Potential obfuscation.', 'site-checkup');
                        $issue_type = 'HTACCESS_BLOCKED';
                        $file_results[] = array($path, $reason, $issue_type);
                    } else {
                        $suspicious_flags = false;

                        // 1. Verificar tamanho excessivo
                        if (strlen($content) > $HTACCESS_MAX_SIZE) {
                            $reason = esc_html__('.htaccess has excessive size (over 100KB). Potential redirection spam injection.', 'site-checkup');
                            $issue_type = 'SUSPICIOUS_HTACCESS';
                            $file_results[] = array($path, $reason, $issue_type);
                            $suspicious_flags = true;
                        }

                        // 2. Análise de padrões de malware conhecidos (Redirecionamentos, Injeção)
                        foreach ($HTACCESS_MALWARE_PATTERNS as $pattern) {
                            if (preg_match($pattern, $content)) {
                                if (!$suspicious_flags) { // Evita duplicar o registro
                                    $reason = esc_html__('Known malware pattern or malicious redirection rule found in .htaccess.', 'site-checkup');
                                    $issue_type = 'SUSPICIOUS_HTACCESS';
                                    $file_results[] = array($path, $reason, $issue_type);
                                    $suspicious_flags = true;
                                }
                                break; // Se um padrão foi encontrado, não precisamos verificar os outros
                            }
                        }

                        // 3. Checar a integridade básica do bloco WordPress padrão
                        // (Esta é uma checagem heurística mais avançada para garantir que o bloco WP não foi removido/modificado)
                        if (strpos($content, '# BEGIN WordPress') === false || strpos($content, '# END WordPress') === false) {
                            // Se o arquivo existe e é maior que 200 bytes, mas não contém os blocos WP padrão, pode ser um arquivo de shell disfarçado.
                            if (strlen($content) > 200 && !$suspicious_flags) {
                                $reason = esc_html__('Missing WordPress block markers (# BEGIN/END WordPress). File may be hijacked.', 'site-checkup');
                                $issue_type = 'SUSPICIOUS_HTACCESS';
                                $file_results[] = array($path, $reason, $issue_type);
                            }
                        }
                    }
                } // Fim da verificação de .htaccess

                // --- VARREDURA CRÍTICA DE UPLOADS PARA .PHP ---
                if (is_dir($WP_UPLOADS_DIR)) {
                    // ... seu código de varredura de uploads aqui
                }

                // --- VARREDURA CRÍTICA DE UPLOADS PARA .PHP ---
                if (is_dir($WP_UPLOADS_DIR)) {
                    // ... seu código de varredura de uploads aqui
                }
                //}


                // --- VARREDURA CRÍTICA DE UPLOADS PARA .PHP ---
                if (is_dir($WP_UPLOADS_DIR)) {
                    $upload_iterator = new \RecursiveIteratorIterator(
                        new \RecursiveDirectoryIterator($WP_UPLOADS_DIR, \FilesystemIterator::SKIP_DOTS),
                        \RecursiveIteratorIterator::SELF_FIRST
                    );

                    foreach ($upload_iterator as $file) {
                        if ($file->isFile()) {
                            $filename = $file->getFilename();
                            $path = str_replace(ABSPATH, '', $file->getPathname());

                            // Ignorar o index.php vazio que alguns plugins criam
                            if ($filename === 'index.php' && $file->getSize() < 100) {
                                continue;
                            }

                            // Encontrou um arquivo PHP no diretório de uploads? ALERTA CRÍTICO!
                            if (strtolower(pathinfo($filename, PATHINFO_EXTENSION)) === 'php') {
                                $reason = esc_html__('Executable PHP file found in public uploads directory. Highly probable Web Shell/Backdoor.', 'site-checkup');
                                $issue_type = 'MALICIOUS_UPLOAD';
                                $file_results[] = array($path, $reason, $issue_type);
                            }
                        }
                    }
                }
            }

            // 4. PROCESSAR OS RESULTADOS
            if (empty($file_results)) {
                // No issues found - mark existing active issues as resolved
                $wpdb->update(
                    $this->table_name,
                    array(
                        'is_active' => false,
                        'status' => 'fixed',
                        'resolved_at' => current_time('mysql'),
                        'updated_at' => current_time('mysql')
                    ),
                    array('is_active' => true)
                );

                return array(
                    'success' => true,
                    'message' => 'No integrity issues detected',
                    'issues_found' => 0,
                    'scan_id' => 'scan_' . time()
                );
            }

            // Após o processamento do core, adicione:
            // 5. EXECUTAR SCAN DE PLUGIN (se for hora)
            if ($this->should_run_plugin_scan()) {
                $this->run_plugin_scan();
            }

            // Generate unique scan ID for this session
            $scan_id = 'scan_' . time() . '_' . uniqid();
            $issues_processed = 0;
            $new_issues = 0;
            $updated_issues = 0;

            // Process each detected issue (AGORA $issue TEM 3 ELEMENTOS)
            foreach ($file_results as $issue) {
                if (count($issue) < 3) {
                    // error_log('Bill_Catch_Malware: Malformed issue data: ' . print_r($issue, true));
                    continue;
                }

                $file_path = sanitize_text_field($issue[0]);
                $description = sanitize_text_field($issue[1]);
                $issue_type = sanitize_text_field($issue[2]); // PEGA O NOVO TIPO DEFINIDO

                if (empty($file_path)) {
                    continue;
                }

                // Determinar a severidade com base no caminho e tipo
                $severity = $this->determine_severity_level($file_path, $issue_type);

                $file_info = $this->get_additional_file_info($file_path);

                // Check if this issue already exists and is active OR ignored
                $existing_issue = $wpdb->get_row($wpdb->prepare(
                    "SELECT id, occurrence_count, status FROM {$this->table_name} 
      WHERE file_path = %s AND issue_type = %s AND (is_active = 1 OR status = 'ignored')",
                    $file_path,
                    $issue_type
                ));

                if ($existing_issue) {
                    // Se o status for 'ignored', não atualizar
                    if ($existing_issue->status === 'ignored') {
                        continue;
                    }

                    // Update existing active issue
                    $wpdb->update(
                        $this->table_name,
                        array(
                            'last_detected' => current_time('mysql'),
                            'occurrence_count' => $existing_issue->occurrence_count + 1,
                            'updated_at' => current_time('mysql'),
                            'description' => $description,
                            'severity' => $severity,
                            'scanner_version' => $this->get_scanner_version_info(),
                            'scan_id' => $scan_id
                        ),
                        array('id' => $existing_issue->id)
                    );
                    $updated_issues++;
                } else {
                    // Insert new issue
                    $inserted = $wpdb->insert(
                        $this->table_name,
                        array(
                            'file_path' => $file_path,
                            'issue_type' => $issue_type,
                            'severity' => $severity,
                            'description' => $description,
                            'status' => 'detected',
                            'reviewed' => false,
                            'first_detected' => current_time('mysql'),
                            'last_detected' => current_time('mysql'),
                            'file_hash' => $file_info['hash'] ?? null,
                            'file_size' => $file_info['size'] ?? null,
                            'permissions' => $file_info['permissions'] ?? null,
                            'file_owner' => $file_info['owner'] ?? null,
                            'last_modified' => $file_info['modified'] ?? null,
                            'scanner_version' => $this->get_scanner_version_info(),
                            'scan_id' => $scan_id,
                            'detection_method' => 'hash_check',
                            'occurrence_count' => 1,
                            'is_active' => true,
                            'created_at' => current_time('mysql'),
                            'updated_at' => current_time('mysql')
                        )
                    );

                    if ($inserted === false) {
                        $bill_debug = defined('BILL_DEBUG') ? BILL_DEBUG : false;
                        if ($bill_debug) {
                            error_log('INSERT FAILED for ' . $file_path . ' | Type: ' . $issue_type . ' | Error: ' . $wpdb->last_error);
                        }
                    } else {
                        $new_issues++;
                    }
                }

                $issues_processed++;
            }

            // Return scan summary
            return array(
                'success' => true,
                'message' => 'Scan completed successfully',
                'issues_found' => count($file_results),
                'issues_processed' => $issues_processed,
                'new_issues' => $new_issues,
                'updated_issues' => $updated_issues,
                'scan_id' => $scan_id,
                'scan_timestamp' => current_time('mysql')
            );
        } catch (\Exception $e) {
            //error_log('Bill_Catch_Malware Error: ' . $e->getMessage());
            return array(
                'success' => false,
                'message' => 'Scanner error: ' . $e->getMessage(),
                'issues_found' => 0
            );
        }
    }


    /**
     * Verifica se um ficheiro é seguro para ser lido para a memória.
     * Limite padrão: 2MB (suficiente para ficheiros de código, perigoso para logs/backups).
     */
    private function is_safe_to_read($filepath, $max_size = 2097152)
    {
        if (!is_file($filepath)) return false;

        $size = @filesize($filepath);

        // Se não conseguir ler o tamanho ou for maior que o limite, bloqueia
        if ($size === false || $size > $max_size) {
            return false;
        }

        return true;
    }


    /**
     * Determines issue type from scanner description
     * 
     * @param string $description Scanner's description of the issue
     * @return string Standardized issue type
     */
    private function determine_issue_type_from_description($description)
    {
        $description_lower = strtolower($description);

        if (strpos($description_lower, 'modified') !== false) {
            return 'modified';
        } elseif (strpos($description_lower, 'missing') !== false) {
            return 'missing';
        } elseif (
            strpos($description_lower, 'not part of') !== false ||
            strpos($description_lower, 'unknown') !== false
        ) {
            return 'unknown';
        } elseif (strpos($description_lower, 'permission') !== false) {
            return 'permissions';
        } elseif (
            strpos($description_lower, 'malicious') !== false ||
            strpos($description_lower, 'suspicious') !== false ||
            strpos($description_lower, 'malware') !== false
        ) {
            return 'malicious';
        }

        return 'unknown';
    }

    /**
     * Determines severity level based on file location and issue type
     * 
     * @param string $file_path Relative file path
     * @param string $issue_type Type of issue detected
     * @return string Severity level
     */
    private function determine_severity_level($file_path, $issue_type)
    {
        // Malicious files are always critical
        if ($issue_type === 'malicious') {
            return 'critical';
        }

        // Core WordPress directories
        $core_dirs = array('wp-admin/', 'wp-includes/');

        foreach ($core_dirs as $dir) {
            if (strpos($file_path, $dir) === 0) {
                return ($issue_type === 'modified') ? 'high' : 'critical';
            }
        }

        // WordPress root files
        $root_files = array('wp-config.php', 'index.php', '.htaccess', 'wp-activate.php');
        $filename = basename($file_path);
        if (in_array($filename, $root_files)) {
            return 'high';
        }

        // Plugin and theme directories
        //$plugin_theme_dirs = array('wp-content/plugins/', 'wp-content/themes/');


        $plugins_base = trim(str_replace(ABSPATH, '', WP_PLUGIN_DIR), '/');
        $themes_base = trim(str_replace(ABSPATH, '', get_theme_root()), '/');

        $plugin_theme_dirs = array(
            $plugins_base . '/',
            $themes_base . '/'
        );




        foreach ($plugin_theme_dirs as $dir) {
            if (strpos($file_path, $dir) === 0) {
                return 'medium';
            }
        }

        // Uploads and other directories
        $uploads_dirs = array('wp-content/uploads/', 'wp-content/cache/', 'wp-content/backup/');
        foreach ($uploads_dirs as $dir) {
            if (strpos($file_path, $dir) === 0) {
                return 'low';
            }
        }

        // Default for other locations
        return 'medium';
    }

    /**
     * Gets additional file information not provided by WordPress scanner
     * 
     * @param string $file_path Relative file path
     * @return array Additional file metadata
     */
    private function get_additional_file_info($file_path)
    {
        $full_path = ABSPATH . $file_path;
        $info = array();

        if (file_exists($full_path)) {
            // Get file hash
            $info['hash'] = @hash_file('sha256', $full_path) ?: null;

            // Get file size
            $info['size'] = @filesize($full_path) ?: null;

            // Get file permissions
            $perms = @fileperms($full_path);
            $info['permissions'] = $perms ? substr(sprintf('%o', $perms), -4) : null;

            // Get last modified time
            $modified = @filemtime($full_path);
            $info['modified'] = $modified ? date('Y-m-d H:i:s', $modified) : null;

            // Get file owner (Unix-like systems only)
            if (function_exists('posix_getpwuid')) {
                $owner = @fileowner($full_path);
                if ($owner !== false) {
                    $owner_info = @posix_getpwuid($owner);
                    $info['owner'] = $owner_info['name'] ?? null;
                }
            }
        }

        return $info;
    }

    /**
     * Gets scanner version information
     * 
     * @return string Scanner version or identifier
     */
    private function get_scanner_version_info()
    {
        // Try to get version from scanner class if available
        if (
            class_exists('site_checkup_File_Integrity_Checker') &&
            method_exists('site_checkup_File_Integrity_Checker', 'get_version')
        ) {
            $checker = new \site_checkup_File_Integrity_Checker();
            return $checker->get_version();
        }

        // Default version
        return $this->version;
    }

    /**
     * Gets the last scan timestamp
     * 
     * @return string|null Last scan timestamp or null if never scanned
     */
    public function get_last_scan_time()
    {
        return get_option($this->option_name);
    }

    /**
     * Gets scan statistics from the database
     * 
     * @return array Scan statistics including issue counts
     */
    public function get_scan_statistics()
    {
        global $wpdb;

        $stats = array(
            'total_issues' => 0,
            'active_issues' => 0,
            'unreviewed_issues' => 0,
            'by_type' => array(),
            'by_severity' => array()
        );

        // Get total issues count
        $stats['total_issues'] = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");

        // Get active issues count
        $stats['active_issues'] = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE is_active = 1"
        );

        // Get unreviewed issues count
        $stats['unreviewed_issues'] = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->table_name} WHERE reviewed = 0 AND is_active = 1"
        );

        // Get issues by type
        $type_results = $wpdb->get_results(
            "SELECT issue_type, COUNT(*) as count FROM {$this->table_name} 
             WHERE is_active = 1 GROUP BY issue_type",
            ARRAY_A
        );

        foreach ($type_results as $row) {
            $stats['by_type'][$row['issue_type']] = (int) $row['count'];
        }

        // Get issues by severity
        $severity_results = $wpdb->get_results(
            "SELECT severity, COUNT(*) as count FROM {$this->table_name} 
             WHERE is_active = 1 GROUP BY severity",
            ARRAY_A
        );

        foreach ($severity_results as $row) {
            $stats['by_severity'][$row['severity']] = (int) $row['count'];
        }

        return $stats;
    }

    /**
     * Cleans up old scan data and unschedules events
     * Use for deactivation or cleanup
     */
    public function cleanup()
    {
        // Clear scheduled event
        wp_clear_scheduled_hook('bill_catch_malware_daily_scan');

        // Optionally remove old data (keep last 90 days)
        global $wpdb;
        $cutoff_date = date('Y-m-d H:i:s', strtotime('-90 days'));
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_name} WHERE first_detected < %s AND is_active = 0",
            $cutoff_date
        ));
    }

    /**
     * Checks if the integrity issues table already exists in the database
     * Returns boolean true if table exists, false if it doesn't
     * 
     * @return bool True if table exists, false otherwise
     */
    private function table_exists()
    {
        global $wpdb;

        // Method 1: Check using SHOW TABLES (most reliable)
        $query = $wpdb->prepare(
            "SHOW TABLES LIKE %s",
            $wpdb->esc_like($this->table_name)
        );

        $result = $wpdb->get_var($query);

        // Explicitly return true or false
        return ($result === $this->table_name);
    }

    /**
     * Creates or updates the site integrity issues table structure
     * Ensures proper indexing for performance and data integrity
     * Checks if table exists before attempting to create it
     * Includes debug output for troubleshooting
     * 
     * @return bool True if table was created or already exists, false on failure
     */
    public function site_checkup_create_integrity_table()
    {
        global $wpdb;

        // //  // //debug4();

        // DEBUG: Start table creation process
        if (defined('WP_DEBUG') && WP_DEBUG) {
            //error_log('Bill_Catch_Malware: Starting table creation for ' . $this->table_name);
            //echo '<div style="background: #f0f0f0; padding: 10px; margin: 10px 0; border: 1px solid #ccc;">';
            //echo '<strong>DEBUG: Bill_Catch_Malware Table Creation</strong><br>';
            //echo 'Table name: ' . $this->table_name . '<br>';
        }

        // Check if table already exists
        $table_exists = $this->table_exists();

        // //  // //debug4($table_exists);


        if (defined('WP_DEBUG') && WP_DEBUG) {
            // echo 'Table exists check: ' . ($table_exists ? 'YES' : 'NO') . '<br>';
        }

        if ($table_exists) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                // echo 'Table already exists, skipping creation.<br>';
                // echo '</div>';
            }

            // NOVO CÓDIGO ADICIONADO: Verificar se o campo plugin existe
            $plugin_field_exists = false;
            $table_structure = $wpdb->get_results("DESCRIBE {$this->table_name}");

            foreach ($table_structure as $column) {
                if ($column->Field == 'plugin') {
                    $plugin_field_exists = true;
                    break;
                }
            }

            // Se o campo plugin não existe, adicioná-lo
            if (!$plugin_field_exists) {
                if (defined('WP_DEBUG') && WP_DEBUG) {
                    // echo 'Plugin field does not exist, adding it...<br>';
                }

                // Adicionar o campo plugin à tabela existente
                $alter_sql = "ALTER TABLE {$this->table_name} 
                              ADD COLUMN plugin VARCHAR(100) NULL COMMENT 'Plugin name for plugin-related issues' 
                              AFTER issue_type";


                $alter_result = $wpdb->query($alter_sql);
            }
            // FIM DO NOVO CÓDIGO

            // error_log('Bill_Catch_Malware: Table ' . $this->table_name . ' already exists');
            return true;
        }



        $charset_collate = $wpdb->get_charset_collate();

        // DEBUG: Show charset/collation
        if (defined('WP_DEBUG') && WP_DEBUG) {
            //echo 'Charset/Collate: ' . $charset_collate . '<br>';
        }

        $sql = "CREATE TABLE {$this->table_name} (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        
        -- File identification and location
        file_path VARCHAR(500) NOT NULL COMMENT 'Relative file path from WordPress root',
        file_hash VARCHAR(64) COMMENT 'Current SHA-256 hash of the file',
        plugin_slug VARCHAR(100) COMMENT 'Plugin slug for plugin-related issues',
        
        -- Issue classification and details
        issue_type VARCHAR(30) NOT NULL,
        
        
        
        severity VARCHAR(10) DEFAULT 'medium' COMMENT 'Issue severity: low|medium|high|critical',
        description TEXT COMMENT 'Detailed description of the issue',
        
        -- User workflow and review status
        status VARCHAR(20) DEFAULT 'detected' COMMENT 'Current status: detected|reviewed|ignored|fixed|quarantined|deleted',
        reviewed BOOLEAN DEFAULT FALSE COMMENT 'Whether user has reviewed the issue',
        reviewed_at DATETIME COMMENT 'Timestamp when user reviewed the issue',
        user_action VARCHAR(20) COMMENT 'Action taken: ignore|repair|delete|quarantine',
        user_notes TEXT COMMENT 'User comments or notes about the issue',
        
        -- Temporal tracking metadata
        first_detected DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'First detection timestamp',
        last_detected DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'Most recent detection timestamp',
        resolved_at DATETIME COMMENT 'When the issue was resolved',
        
        -- Technical data for analysis and repair
        expected_hash VARCHAR(64) COMMENT 'Expected SHA-256 hash for core files',
        file_size INT UNSIGNED COMMENT 'File size in bytes',
        permissions VARCHAR(4) COMMENT 'File permissions (e.g., 0644, 0755)',
        file_owner VARCHAR(50) COMMENT 'File owner user/group',
        last_modified DATETIME COMMENT 'Actual file modification timestamp',
        
        -- Scanner and detection metadata
        scanner_version VARCHAR(20) COMMENT 'Version of scanner that detected issue',
        scan_id VARCHAR(50) COMMENT 'Unique identifier for the scan session',
        detection_method VARCHAR(30) COMMENT 'Method used: hash_check|signature|heuristics',
        
        -- Duplicate prevention and occurrence tracking
        occurrence_count INT UNSIGNED DEFAULT 1 COMMENT 'Number of times this issue was detected',
        is_active BOOLEAN DEFAULT TRUE COMMENT 'Whether issue is still present/active',
        
        -- Audit and maintenance fields
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        
        PRIMARY KEY (id),
        
        -- Performance indexes
        INDEX idx_file_path (file_path(100)),
        INDEX idx_plugin_slug (plugin_slug), 
        INDEX idx_issue_type (issue_type),
        INDEX idx_severity (severity),
        INDEX idx_status (status),
        INDEX idx_reviewed (reviewed),
        INDEX idx_is_active (is_active),
        INDEX idx_first_detected (first_detected),
        INDEX idx_last_detected (last_detected),
        INDEX idx_scan_id (scan_id(20)),
        
        -- Composite indexes for common queries
        INDEX idx_active_unreviewed (is_active, reviewed, severity),
        INDEX idx_type_severity (issue_type, severity, is_active),
        INDEX idx_detection_timeline (first_detected, last_detected, is_active),
        
        -- Unique constraint to prevent exact duplicate active issues
        UNIQUE KEY uniq_active_file_issue (file_path(200), issue_type, is_active),
        
        
        -- Fulltext search for descriptions and user notes
        FULLTEXT KEY ftx_search (description, user_notes)
    ) $charset_collate;";

        // DEBUG: Show SQL (first 500 chars)
        if (defined('WP_DEBUG') && WP_DEBUG) {
            echo 'SQL to execute (truncated): ' . substr($sql, 0, 500) . '...<br>';
        }

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

        // DEBUG: Before dbDelta
        if (defined('WP_DEBUG') && WP_DEBUG) {
            echo 'Calling dbDelta...<br>';
        }

        // Suppress errors temporarily to capture them
        $wpdb->hide_errors();
        $result = dbDelta($sql);
        $wpdb->show_errors();

        // DEBUG: After dbDelta
        if (defined('WP_DEBUG') && WP_DEBUG) {
            echo 'dbDelta completed.<br>';
            echo 'dbDelta result: <pre>' . print_r($result, true) . '</pre>';

            // Check for SQL errors
            if ($wpdb->last_error) {
                echo '<span style="color: red;">SQL Error: ' . esc_html($wpdb->last_error) . '</span><br>';
                echo '<span style="color: red;">Last Query: ' . esc_html($wpdb->last_query) . '</span><br>';
            } else {
                echo '<span style="color: green;">No SQL errors detected.</span><br>';
            }
        }

        // Log any SQL errors
        if ($wpdb->last_error) {
            // error_log('Bill_Catch_Malware SQL Error: ' . $wpdb->last_error);
            // error_log('Bill_Catch_Malware Last Query: ' . $wpdb->last_query);

            if (defined('WP_DEBUG') && WP_DEBUG) {
                // echo '<span style="color: red;">Logged SQL error to error_log.</span><br>';
            }
        }

        // Verify table was created successfully
        $table_created = $this->table_exists();

        if (defined('WP_DEBUG') && WP_DEBUG) {
            echo 'Post-creation table exists check: ' . ($table_created ? 'YES' : 'NO') . '<br>';
        }
        // //  // //debug4($table_created);

        if ($table_created) {
            // Add version tracking for future schema updates
            update_option('site_integrity_table_version', $this->version);

            // Schedule daily scan if not already scheduled
            if (!wp_next_scheduled('bill_catch_malware_daily_scan')) {
                wp_schedule_event(time(), 'daily', 'bill_catch_malware_daily_scan');
            }

            if (defined('WP_DEBUG') && WP_DEBUG) {
                // echo '<span style="color: green;">SUCCESS: Table ' . $this->table_name . ' created successfully!</span><br>';
                // echo '</div>';
            }







            // error_log('Bill_Catch_Malware: Table ' . $this->table_name . ' created successfully');
            return true;
        } else {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                echo '<span style="color: red;">FAILED: Table ' . $this->table_name . ' was not created.</span><br>';

                // Additional debugging: try a simpler table creation
                echo 'Attempting fallback: creating minimal table...<br>';

                $simple_sql = "CREATE TABLE {$this->table_name} (
                id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                file_path VARCHAR(500) NOT NULL,
                issue_type VARCHAR(20) NOT NULL,
                PRIMARY KEY (id)
            ) $charset_collate;";

                $wpdb->query($simple_sql);

                if ($wpdb->last_error) {
                    echo '<span style="color: red;">Fallback also failed: ' . esc_html($wpdb->last_error) . '</span><br>';
                } else {
                    echo '<span style="color: green;">Fallback succeeded with minimal table.</span><br>';
                }

                echo '</div>';
            }

            //error_log('Bill_Catch_Malware: Failed to create table ' . $this->table_name);

            // Try to get more specific error information
            if (!$wpdb->last_error) {
                // error_log('Bill_Catch_Malware: No SQL error captured, but table was not created.');
                // error_log('Bill_Catch_Malware: Check database user permissions for CREATE TABLE.');
            }

            return false;
        }
    }

    // Adicione este método auxiliar
    private function should_run_plugin_scan()
    {
        // Executa scan de plugin a cada 2 horas (ajustável)
        $last_run = get_option('bill_last_plugin_scan_run', 0);
        return (time() - $last_run) > (2 * HOUR_IN_SECONDS);
    }
} // end class Bill_Catch_Malware

/**
 * Plugin Integrity Scanner - Verifica integridade dos plugins
 * Compara arquivos locais com versões oficiais do WordPress.org
 */
class Plugin_Integrity_Scanner
{
    private $table_name;
    private $option_prefix = 'bill_plugin_scan_';

    public function __construct()
    {
        global $wpdb;
        //  // //debug4('🟢 DEBUG: Plugin_Integrity_Scanner construído em ' . date('Y-m-d H:i:s'));
        $this->table_name = $wpdb->prefix . 'bill_site_integrity_issues';
        //  // //debug4($this->table_name);


        //=================
        //add_action('shutdown', array($this, 'maybe_run_12h_scan'));
        //=======================

        // Carrega a função plugins_api() para uso futuro
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }
        // Inicializa o scanner de plugins
        // $this->plugin_scanner = new Plugin_Integrity_Scanner();
        // === ADICIONE ESTA LINHA ===
        //$this->create_integrity_table();
        // $this->plugin_scanner->create_integrity_table();
        // ===========================


        // Manual
        // $this->plugin_scanner->do_actual_plugin_scan();
        // com cron...
        // \debug3a("1682");
        add_action('init', array($this, 'maybe_run_12h_scan'), 20);

        // die((string) __LINE__ . '   ' . var_export('x', true));


        // // \//debug4('🟢 DEBUG: Plugin_Integrity_Scanner construído em ' . date('Y-m-d H:i:s'));
    }



    public function maybe_run_12h_scan()
    {

        // die((string) __LINE__ . '   ' . var_export('x', true));


        // GERAR UM ID ÚNICO PARA ESTA EXECUÇÃO
        static $test_id = null;
        if ($test_id === null) {
            $test_id = 'TEST_' . time() . '_' . rand(1000, 9999);
        }

        //// error_log("=== 🧪 EXPERIMENTO {$test_id} ===");

        $transient_name = 'plugin_scanner_12h_interval';

        // 1. ESTADO INICIAL
        // error_log("🧪 1. Estado INICIAL do transient: " .
        //    (get_transient($transient_name) ? 'EXISTE' : 'NÃO EXISTE'));

        if (get_transient($transient_name)) {
            // error_log("🧪 2. SAIU: transient já existia");
            // error_log("=== FIM {$test_id} ===");
            return;
        }

        // 2. SE CHEGOU AQUI, TRANSIENT NÃO EXISTIA
        // error_log("🧪 2. Entrou: transient NÃO existia");

        // 3. CRIAR TRANSIENT PRIMEIRO (para evitar loops)
        // error_log("🧪 3. Criando transient ANTES do scan...");
        $resultado = set_transient($transient_name, 'scanned', 12 * HOUR_IN_SECONDS);
        // error_log("🧪 4. set_transient() retornou: " . ($resultado ? 'true' : 'false'));

        // 4. EXECUTAR SCAN
        // error_log("🧪 5. Executando do_actual_plugin_scan()...");
        $this->do_actual_plugin_scan();

        // error_log("🧪 6. FIM DO PROCESSO");
        // error_log("=== FIM {$test_id} ===");
    }






    public function do_actual_plugin_scan()
    {
        // Força o carregamento da função get_plugins() e get_plugin_data()
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        // Força o carregamento da função plugins_api() para consultar o repositório oficial
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        // Força o carregamento da função download_url() necessária para baixar o ZIP
        if (!function_exists('download_url')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $plugin_file = $this->get_next_plugin_to_scan();
        // error_log('🟢 [2] get_next_plugin_to_scan() retornou: ' . ($plugin_file ?: 'NULL/FALSE'));

        if (!$plugin_file) {
            // error_log('🔴 [3] SAINDO: plugin_file inválido');
            return;
        }


        // error_log('🟢 [4] Chamando scan_plugin() para: ' . $plugin_file);
        //$result = $this->scan_plugin($plugin_file);
        // error_log('🟢 [5] scan_plugin() retornou: ' . print_r($result, true));

        // ⬇️⬇️⬇️ ATUALIZA ÍNDICE SEMPRE ⬇️⬇️⬇️
        $plugins = get_plugins();
        $plugin_files = array_keys($plugins);
        $current_index = array_search($plugin_file, $plugin_files);

        if ($current_index !== false) {
            // PRÓXIMO índice (não o atual!)
            $next_index = ($current_index + 1) % count($plugin_files);
            update_option('bill_last_plugin_scanned_index', $next_index);
            // error_log('🟢 [6] Índice atualizado: ' . $current_index . ' → ' . $next_index);
        }

        // error_log('🟢 [4] Chamando scan_plugin() para: ' . $plugin_file);
        $result = $this->scan_plugin($plugin_file);
        // error_log('🟢 [5] scan_plugin() retornou: ' . print_r($result, true));


        // ⬇️⬇️⬇️ REMOVA o código antigo duplicado ⬇️⬇️⬇️
        // ❌ NÃO inclua as seções 3 e 4 da versão antiga
        // ❌ NÃO inclua o código duplicado

        // error_log('🟢 [7] Scan finalizado');
    }



    public function scan_plugin($plugin_main_file_path)
    {
        global $wpdb;

        // \debug3a(">>> INICIANDO scan_plugin()");
        // \debug3a("plugin_main_file_path: {$plugin_main_file_path}");

        // Define files to ignore for specific plugins
        $files_to_ignore = [
            [
                'plugin_slug' => 'site-checkup',
                'files' => ['rules.txt', 'rules2.txt']
            ],
            [
                'plugin_slug' => '*',  // Curinga para todos os plugins
                'files' => ['readme.txt']
            ]
        ];

        // \debug3a("files_to_ignore configurado");

        if (!class_exists('ZipArchive')) {
            //   \ // //debug4("❌ ZipArchive não disponível - ABORTANDO");
            error_log('Plugin Scanner Error: PHP ZipArchive extension required');
            return false;
        }

        // \debug3a("✅ ZipArchive disponível");

        // Get plugin data
        $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_main_file_path);
        $local_plugin_version = $plugin_data['Version'];
        $plugin_name = $plugin_data['Name'];

        // \debug3a("Plugin Name: {$plugin_name}");
        // \debug3a("Local Version: {$local_plugin_version}");

        // Determine plugin slug for API
        if (strpos($plugin_main_file_path, '/') !== false) {
            $api_slug = dirname($plugin_main_file_path);
            // \debug3a("API Slug (com diretório): {$api_slug}");
        } else {
            $api_slug = !empty($plugin_data['slug']) ? $plugin_data['slug'] : str_replace('.php', '', basename($plugin_main_file_path));
            // \debug3a("API Slug (single file): {$api_slug}");
        }

        $local_plugin_directory_slug = dirname($plugin_main_file_path);
        // \debug3a("Local Directory Slug: {$local_plugin_directory_slug}");

        // Query WordPress.org API
        $args = [
            'slug'   => $api_slug,
            'fields' => [
                'version'       => true,
                'download_link' => true,
                'versions'      => true,
            ],
        ];

        // \debug3a("Chamando plugins_api() com slug: {$api_slug}");

        // Carrega a função plugins_api() para uso futuro
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        $response = \plugins_api('plugin_information', $args);

        if (is_wp_error($response) || empty($response->download_link)) {
            // \debug3a("❌ Plugin não encontrado no WordPress.org: {$api_slug}");

            return [
                'plugin' => $plugin_name,
                'status' => 'not_in_repo',
                'local_version' => $local_plugin_version,
                'message' => 'Plugin não está no repositório oficial do WordPress'
            ];
        }



        // \debug3a("✅ API respondeu com sucesso");
        // \debug3a("Versão no repositório: " . ($response->version ?? 'N/A'));
        // \debug3a("Download link disponível: Sim");

        // Get correct version download link
        $zip_url_to_download = '';
        $version_to_compare = '';

        if (!empty($response->versions) && isset($response->versions[$local_plugin_version])) {
            // \debug3a("Versão específica {$local_plugin_version} encontrada no repositório");

            if (
                preg_match('#/plugin/([^/.]+)\.(?:[0-9.]+|trunk)\.zip$#i', $response->download_link, $matches) ||
                preg_match('#/plugin/([^/.]+)\.zip$#i', $response->download_link, $matches)
            ) {
                $download_slug_in_link = $matches[1];
                $zip_url_to_download = "https://downloads.wordpress.org/plugin/{$download_slug_in_link}.{$local_plugin_version}.zip";
                $version_to_compare = $local_plugin_version;

                // \debug3a("URL ZIP específica montada: {$zip_url_to_download}");
            }
        } else {
            // \debug3a("Versão específica {$local_plugin_version} NÃO encontrada, usando última versão");


            // Verificação final: se não conseguiu montar a URL da versão exata, ABORTA.
            if (empty($zip_url_to_download)) {
                // \debug3a("❌ ABORTANDO: Não foi possível obter o pacote ZIP da versão exata {$local_plugin_version}.");
                // Você pode logar uma mensagem mais detalhada para o usuário/admin
                // error_log("Plugin Scanner: Cannot verify '{$plugin_name}'. Exact version {$local_plugin_version} not available for download from WordPress.org.");

                // Retorna um array especial indicando o problema, em vez de 'false'
                return [
                    'plugin' => $plugin_name,
                    'status' => 'version_unavailable',
                    'local_version' => $local_plugin_version,
                    'available_version' => $response->version ?? 'unknown',
                    'message' => sprintf(__('Integrity check skipped for %s. The exact version (%s) is not available for comparison in the WordPress repository.', 'site-checkup'), $plugin_name, $local_plugin_version)
                ];
            }
        }

        if (empty($zip_url_to_download)) {
            $zip_url_to_download = $response->download_link;
            $version_to_compare = $response->version;
            //   \ // //debug4("Usando URL padrão: {$zip_url_to_download}");
            //   \ // //debug4("Versão para comparação: {$version_to_compare}");
        }

        // \debug3a("Versão final para comparação: {$version_to_compare}");

        // Download ZIP
        // \debug3a("Iniciando download de: {$zip_url_to_download}");
        $temp_zip_file = download_url($zip_url_to_download, 300);

        if (is_wp_error($temp_zip_file)) {
            //   \ // //debug4("❌ ERRO no download: " . $temp_zip_file->get_error_message());
            // error_log("Plugin Scanner: Download failed for {$api_slug}");
            return false;
        }

        //   \ // //debug4("✅ Download concluído. Arquivo temporário: {$temp_zip_file}");

        // Process ZIP file
        $repo_files_hashes = [];
        $zip = new \ZipArchive();

        // \debug3a("Abrindo arquivo ZIP...");

        if ($zip->open($temp_zip_file) === TRUE) {
            //   \ // //debug4("✅ ZIP aberto com sucesso. Total de arquivos: " . $zip->numFiles);

            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $filename_in_zip = $stat['name'];

                if (substr($filename_in_zip, -1) === '/') continue;

                $path_for_comparison = $filename_in_zip;
                $parts = explode('/', $filename_in_zip);
                $zip_root_dir_or_file = $parts[0];

                // DETERMINAR SE É SINGLE-FILE
                $is_single_file_plugin = (strpos($plugin_main_file_path, '/') === false);

                if (!$is_single_file_plugin) {
                    // Plugin COM PASTA
                    if ($zip_root_dir_or_file !== $api_slug && count($parts) === 1) {
                        $path_for_comparison = $api_slug . '/' . $filename_in_zip;
                    } elseif ($zip_root_dir_or_file !== $api_slug && count($parts) > 1) {
                        continue; // Ignorar arquivos de outras pastas
                    }

                    // REMOVER api_slug/ do início para normalização
                    if (strpos($path_for_comparison, $api_slug . '/') === 0) {
                        $path_for_comparison = substr($path_for_comparison, strlen($api_slug . '/'));
                    }
                    // ⭐⭐ ADICIONE: REMOVER 'trunk/' se existir (estrutura SVN do WordPress.org)
                    if (strpos($path_for_comparison, 'trunk/') === 0) {
                        $path_for_comparison = substr($path_for_comparison, strlen('trunk/'));
                    }

                    // ⭐⭐ ADICIONE: REMOVER 'tags/versao/' se existir
                    if (preg_match('#^tags/[^/]+/#', $path_for_comparison)) {
                        $path_for_comparison = preg_replace('#^tags/[^/]+/#', '', $path_for_comparison);
                    }
                } else {
                    // Plugin SINGLE-FILE: manter como está
                    // O ZIP deve conter apenas o arquivo principal
                    // \debug3a("Single-file ZIP entry: {$filename_in_zip}");
                }

                $file_content = $zip->getFromIndex($i);
                if ($file_content !== false) {
                    $repo_files_hashes[$path_for_comparison] = hash('sha256', $file_content);
                }
                // \ // //debug4($file_content);
            }
            $zip->close();
            //   \ // //debug4("✅ Processamento ZIP concluído. Arquivos no ZIP: " . count($repo_files_hashes));
        } else {
            //   \ // //debug4("❌ Falha ao abrir arquivo ZIP");
        }

        @unlink($temp_zip_file);
        //   \ // //debug4("Arquivo temporário removido");

        // Get local file hashes
        $local_files_hashes = [];
        /*
        if ($local_plugin_directory_slug === '.') {
            // \debug3a("Plugin single-file detectado");
            $full_local_path = WP_PLUGIN_DIR . '/' . $plugin_main_file_path;
            if ($this->is_safe_to_read($full_local_path)) {
                $local_hash = hash_file('sha256', $full_local_path);
            } else {
                // Se o ficheiro for gigante, não calculamos o hash (ou usamos apenas streaming)
                // Para segurança extrema, apenas reportamos o tamanho suspeito
                $issues_found[] = array($plugin_main_file_path, 'File too large to scan safely.', 'LARGE_FILE_SKIPPED');
                continue;
            }
            if (is_file($full_local_path)) {
                $local_files_hashes[basename($plugin_main_file_path)] = hash_file('sha256', $full_local_path);
                //   \ // //debug4("Hash do arquivo único calculado");
            } else {
                //   \ // //debug4("❌ Arquivo local não encontrado: {$full_local_path}");
            }
        } else {
            //   \ // //debug4("Plugin com diretório. Buscando arquivos locais...");
            $local_files_hashes = $this->get_local_files_relative(WP_PLUGIN_DIR . '/' . $local_plugin_directory_slug, $api_slug);
            // \debug3a("Arquivos locais encontrados: " . count($local_files_hashes));
        }
        */

        if ($local_plugin_directory_slug === '.') {
            // \debug3a("Plugin single-file detectado");
            $full_local_path = WP_PLUGIN_DIR . '/' . $plugin_main_file_path;

            // 1. VERIFICAÇÃO DE EXISTÊNCIA E SEGURANÇA (Tudo dentro deste IF)
            if (is_file($full_local_path)) {

                // 2. CHECAGM DE TAMANHO (is_safe_to_read)
                if ($this->is_safe_to_read($full_local_path)) {

                    // O código só chega aqui se o arquivo existir E for pequeno/seguro
                    $hash = hash_file('sha256', $full_local_path);
                    $local_files_hashes[basename($plugin_main_file_path)] = $hash;
                    // \debug3a("Hash do arquivo único calculado com sucesso");

                } else {
                    // Se o arquivo for gigante, apenas reportamos e o código segue adiante
                    $issues_found[] = array(
                        $plugin_main_file_path,
                        'File too large to scan safely (>2MB).',
                        'LARGE_FILE_SKIPPED'
                    );
                }
            } else {
                // \debug3a("❌ Arquivo local não encontrado: {$full_local_path}");
            }
        } else {
            // Plugin com diretório (mantém sua lógica original)
            $local_files_hashes = $this->get_local_files_relative(WP_PLUGIN_DIR . '/' . $local_plugin_directory_slug, $api_slug);
        }

        //   \ // //debug4("Comparando arquivos...");
        //   \ // //debug4("Arquivos no ZIP: " . count($repo_files_hashes));
        //   \ // //debug4("Arquivos locais: " . count($local_files_hashes));

        // Compare files and store issues
        $issues_found = $this->compare_and_store_issues(
            $plugin_main_file_path,
            $plugin_name,
            $api_slug,
            $local_files_hashes,
            $repo_files_hashes,
            $files_to_ignore,
            $version_to_compare
        );

        //   \ // //debug4("✅ Comparação concluída. Issues encontradas: " . count($issues_found));
        //   \ // //debug4("<<< FINALIZANDO scan_plugin()");

        return [
            'plugin' => $plugin_name,
            'issues_found' => count($issues_found),
            'issues' => $issues_found
        ];
    }
    /**
     * Executa o scan de um plugin específico
     */
    public function scan_plugin_bom($plugin_main_file_path)
    {
        global $wpdb;

        // Define files to ignore for specific plugins
        $files_to_ignore = [
            [
                'plugin_slug' => 'site-checkup',
                'files' => ['rules.txt', 'rules2.txt']
            ]
        ];

        if (!class_exists('ZipArchive')) {
            // error_log('Plugin Scanner Error: PHP ZipArchive extension required');
            return false;
        }

        // Get plugin data
        $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_main_file_path);
        $local_plugin_version = $plugin_data['Version'];
        $plugin_name = $plugin_data['Name'];

        // \debug3a($plugin_name);

        // Determine plugin slug for API
        if (strpos($plugin_main_file_path, '/') !== false) {
            $api_slug = dirname($plugin_main_file_path);
        } else {
            $api_slug = !empty($plugin_data['slug']) ? $plugin_data['slug'] : str_replace('.php', '', basename($plugin_main_file_path));
        }

        $local_plugin_directory_slug = dirname($plugin_main_file_path);

        // Query WordPress.org API
        $args = [
            'slug'   => $api_slug,
            'fields' => [
                'version'       => true,
                'download_link' => true,
                'versions'      => true,
            ],
        ];

        $response = plugins_api('plugin_information', $args);

        if (is_wp_error($response) || empty($response->download_link)) {
            // error_log("Plugin Scanner: API error for {$api_slug}");
            return false;
        }

        // Get correct version download link
        $zip_url_to_download = '';
        $version_to_compare = '';

        if (!empty($response->versions) && isset($response->versions[$local_plugin_version])) {
            if (
                preg_match('#/plugin/([^/.]+)\.(?:[0-9.]+|trunk)\.zip$#i', $response->download_link, $matches) ||
                preg_match('#/plugin/([^/.]+)\.zip$#i', $response->download_link, $matches)
            ) {
                $download_slug_in_link = $matches[1];
                $zip_url_to_download = "https://downloads.wordpress.org/plugin/{$download_slug_in_link}.{$local_plugin_version}.zip";
                $version_to_compare = $local_plugin_version;
            }
        }

        if (empty($zip_url_to_download)) {
            $zip_url_to_download = $response->download_link;
            $version_to_compare = $response->version;
        }

        // Download ZIP
        $temp_zip_file = download_url($zip_url_to_download, 300);

        if (is_wp_error($temp_zip_file)) {
            //error_log("Plugin Scanner: Download failed for {$api_slug}");
            return false;
        }

        // Process ZIP file
        $repo_files_hashes = [];
        //  $zip = new ZipArchive();
        $zip = new \ZipArchive();

        if ($zip->open($temp_zip_file) === TRUE) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $filename_in_zip = $stat['name'];

                if (substr($filename_in_zip, -1) === '/') continue;

                $path_for_comparison = $filename_in_zip;
                $parts = explode('/', $filename_in_zip);
                $zip_root_dir_or_file = $parts[0];

                if ($zip_root_dir_or_file !== $api_slug && count($parts) === 1) {
                    if (strpos($plugin_main_file_path, '/') === false) {
                        // Single-file plugin
                    } else {
                        $path_for_comparison = $api_slug . '/' . $filename_in_zip;
                    }
                } elseif ($zip_root_dir_or_file !== $api_slug && count($parts) > 1) {
                    continue;
                }

                $file_content = $zip->getFromIndex($i);
                if ($file_content !== false) {
                    $repo_files_hashes[$path_for_comparison] = hash('sha256', $file_content);
                }
            }
            $zip->close();
        }

        @unlink($temp_zip_file);

        // Get local file hashes
        $local_files_hashes = [];
        if ($local_plugin_directory_slug === '.') {
            $full_local_path = WP_PLUGIN_DIR . '/' . $plugin_main_file_path;
            if (is_file($full_local_path)) {
                $local_files_hashes[basename($plugin_main_file_path)] = hash_file('sha256', $full_local_path);
            }
        } else {
            $local_files_hashes = $this->get_local_files_relative(WP_PLUGIN_DIR . '/' . $local_plugin_directory_slug, $api_slug);
        }

        // Compare files and store issues
        $issues_found = $this->compare_and_store_issues(
            $plugin_main_file_path,
            $plugin_name,
            $api_slug,
            $local_files_hashes,
            $repo_files_hashes,
            $files_to_ignore,
            $version_to_compare
        );

        return [
            'plugin' => $plugin_name,
            'issues_found' => count($issues_found),
            'issues' => $issues_found
        ];
    }

    /**
     * Compare files and store issues in database
     */

    private function compare_and_store_issues(
        $plugin_path,
        $plugin_name,
        $plugin_slug,
        $local_hashes,
        $repo_hashes,
        $ignore_list,
        $version
    ) {
        global $wpdb;

        $issues = [];
        $scan_id = 'plugin_scan_' . time() . '_' . uniqid();

        // ================================================
        // 1. DETERMINAR TIPO DO PLUGIN
        // ================================================
        $is_single_file_plugin = (strpos($plugin_path, '/') === false);
        // \debug3a("=== DETECÇÃO DE TIPO DE PLUGIN ===");
        // \debug3a("Plugin path: {$plugin_path}");
        // \debug3a("Plugin slug: {$plugin_slug}");
        // \debug3a("Tipo: " . ($is_single_file_plugin ? 'SINGLE-FILE (ex: hello-dolly.php)' : 'COM PASTA (ex: plugin-name/plugin-name.php)'));

        // ================================================
        // 2. NORMALIZAR OS PATHS DO REPOSITÓRIO
        // ================================================
        // \debug3a("=== NORMALIZANDO REPOSITÓRIO PATHS ===");
        $normalized_repo_hashes = [];

        foreach ($repo_hashes as $repo_path => $hash) {
            $normalized_path = $repo_path;

            if (!$is_single_file_plugin) {
                // Plugin COM PASTA: remover "plugin_slug/" do início se existir
                if (strpos($repo_path, $plugin_slug . '/') === 0) {
                    $normalized_path = substr($repo_path, strlen($plugin_slug . '/'));
                    // \debug3a("Repo [PASTA]: '{$repo_path}' → '{$normalized_path}'");
                } else {
                    // \debug3a("Repo [PASTA]: '{$repo_path}' → mantido");
                }
            } else {
                // Plugin SINGLE-FILE: o path já está correto (ex: hello-dolly.php)
                // \debug3a("Repo [SINGLE]: '{$repo_path}' → mantido");
            }

            $normalized_repo_hashes[$normalized_path] = $hash;
        }

        // ================================================
        // 3. DEBUG: VERIFICAR KEYS
        // ================================================
        // \debug3a("=== KEYS APÓS NORMALIZAÇÃO ===");
        // \debug3a("Local keys (primeiros 5): " . implode(', ', array_slice(array_keys($local_hashes), 0, 5)));
        // \debug3a("Repo keys norm. (primeiros 5): " . implode(', ', array_slice(array_keys($normalized_repo_hashes), 0, 5)));

        // DEBUG EXTRA: Verificar se há duplicações
        $all_local_keys = array_keys($local_hashes);
        $all_repo_keys = array_keys($normalized_repo_hashes);
        $duplicate_keys = array_intersect($all_local_keys, $all_repo_keys);
        // \debug3a("Chaves em comum (deveria ser todas para plugins OK): " . count($duplicate_keys));

        // ================================================
        // 4. COMBINAR TODOS OS PATHS ÚNICOS
        // ================================================
        $all_paths = array_unique(array_merge(
            array_keys($local_hashes),
            array_keys($normalized_repo_hashes)
        ));
        // \debug3a("Total paths únicos para comparar: " . count($all_paths));

        // ================================================
        // 5. BASE PATH PARA CONSTRUIR file_path
        // ================================================
        $plugins_base = str_replace(ABSPATH, '', WP_PLUGIN_DIR);
        $plugins_base = trim($plugins_base, '/');
        // \debug3a("Base plugins: {$plugins_base}");

        // ================================================
        // 6. PRÉ-PROCESSAR IGNORE LIST PARA EFICIÊNCIA
        // ================================================
        $combined_ignore_files = [];
        foreach ($ignore_list as $ignore_entry) {
            if ($ignore_entry['plugin_slug'] === $plugin_slug || $ignore_entry['plugin_slug'] === '*') {
                $combined_ignore_files = array_merge($combined_ignore_files, $ignore_entry['files']);
            }
        }
        $combined_ignore_files = array_unique($combined_ignore_files);
        // \debug3a("Arquivos ignorados para este plugin: " . implode(', ', $combined_ignore_files));

        // ================================================
        // 7. COMPARAÇÃO DE CADA ARQUIVO
        // ================================================
        foreach ($all_paths as $relative_path) {
            // 7.1. Verificar se arquivo deve ser ignorado
            $basename = basename($relative_path);
            if (in_array($basename, $combined_ignore_files)) {
                // \debug3a("⏭️ IGNORADO: {$relative_path} ({$basename} na lista de ignorados)");
                continue;
            }

            // 7.2. Construir file_path para logging/storage
            if ($is_single_file_plugin) {
                // Single-file plugin: wp-content/plugins/hello-dolly.php
                $file_path = $plugins_base . '/' . $relative_path;
                // \debug3a("Construindo path [SINGLE]: {$plugins_base}/{$relative_path}");
            } else {
                // Plugin com pasta: wp-content/plugins/plugin-name/relative/path.php
                $file_path = $plugins_base . '/' . $plugin_slug . '/' . $relative_path;
                // \debug3a("Construindo path [PASTA]: {$plugins_base}/{$plugin_slug}/{$relative_path}");
            }

            $file_path = preg_replace('#/+#', '/', $file_path);
            // \debug3a("Comparando: {$relative_path}");
            // \debug3a("File path final: {$file_path}");

            // 7.3. Verificar existência
            $local_exists = isset($local_hashes[$relative_path]);
            $repo_exists = isset($normalized_repo_hashes[$relative_path]);

            // DEBUG DETALHADO
            // \debug3a("Status: Local=" . ($local_exists ? 'EXISTE' : 'NÃO EXISTE') .
            //    " | Repo=" . ($repo_exists ? 'EXISTE' : 'NÃO EXISTE'));

            // 7.4. DEBUG dos hashes (se ambos existem)
            if ($local_exists && $repo_exists) {
                $local_hash_short = substr($local_hashes[$relative_path], 0, 8);
                $repo_hash_short = substr($normalized_repo_hashes[$relative_path], 0, 8);
                $hashes_match = ($local_hashes[$relative_path] === $normalized_repo_hashes[$relative_path]);
                // \debug3a("Hashes: Local[{$local_hash_short}...] Repo[{$repo_hash_short}...] " .
                //    ($hashes_match ? '✅ IGUAIS' : '❌ DIFERENTES'));
            }

            // 7.5. Comparar e registrar issues
            if ($local_exists && $repo_exists) {
                if ($local_hashes[$relative_path] !== $normalized_repo_hashes[$relative_path]) {
                    // Arquivo modificado
                    // \debug3a("❌ MODIFICADO: {$relative_path}");
                    $this->store_plugin_issue(
                        $file_path,
                        'PLUGIN_MODIFIED',
                        sprintf(__('Plugin file modified: %s (version %s)', 'site-checkup'), $plugin_name, $version),
                        $plugin_slug
                    );
                    $issues[] = $file_path;
                } else {
                    // \debug3a("✅ OK: {$relative_path}");
                }
            } elseif ($local_exists && !$repo_exists) {
                // Arquivo extra local
                // \debug3a("⚠️ EXTRA LOCAL: {$relative_path}");
                $this->store_plugin_issue(
                    $file_path,
                    'PLUGIN_EXTRA_FILE',
                    sprintf(__('Extra file in plugin: %s', 'site-checkup'), $plugin_name),
                    $plugin_slug
                );
                $issues[] = $file_path;
            } elseif (!$local_exists && $repo_exists) {
                // Arquivo faltando localmente
                // \debug3a("⚠️ FALTANDO: {$relative_path}");
                $this->store_plugin_issue(
                    $file_path,
                    'PLUGIN_MISSING_FILE',
                    sprintf(__('Missing plugin file: %s (version %s)', 'site-checkup'), $plugin_name, $version),
                    $plugin_slug
                );
                $issues[] = $file_path;
            } else {
                // Caso impossível (path existe em nenhum dos arrays, mas está em $all_paths)
                // \debug3a("❌ ERRO LÓGICO: {$relative_path} está em all_paths mas não em local nem repo");
            }
        }

        // ================================================
        // 8. RESUMO FINAL
        // ================================================
        // \debug3a("=== RESUMO FINAL DA COMPARAÇÃO ===");
        // \debug3a("Plugin: {$plugin_name} ({$plugin_slug})");
        // \debug3a("Tipo: " . ($is_single_file_plugin ? 'Single-file' : 'Com pasta'));
        // \debug3a("Versão comparada: {$version}");
        // \debug3a("Arquivos locais: " . count($local_hashes));
        // \debug3a("Arquivos repositório: " . count($normalized_repo_hashes));
        // \debug3a("Arquivos ignorados: " . count($combined_ignore_files));
        // \debug3a("Issues encontradas: " . count($issues));

        // DEBUG EXTRA: Listar issues encontradas
        if (count($issues) > 0) {
            // \debug3a("=== LISTA DE ISSUES ===");
            foreach ($issues as $issue_path) {
                // \debug3a("  - {$issue_path}");
            }
        }

        return $issues;
    }




    /**
     * Store plugin issue in database
     */
    private function store_plugin_issue($file_path, $issue_type, $description, $plugin_slug)
    {
        global $wpdb;




        // Check if issue already exists and is active or ignored
        $existing = $wpdb->get_row($wpdb->prepare(
            "SELECT id, status FROM {$this->table_name} 
             WHERE file_path = %s AND issue_type = %s AND (is_active = 1 OR status = 'ignored')",
            $file_path,
            $issue_type
        ));

        if ($existing) {
            if ($existing->status === 'ignored') return;

            $wpdb->update(
                $this->table_name,
                [
                    'last_detected' => current_time('mysql'),
                    'updated_at' => current_time('mysql'),
                    'occurrence_count' => $wpdb->get_var($wpdb->prepare(
                        "SELECT occurrence_count + 1 FROM {$this->table_name} WHERE id = %d",
                        $existing->id
                    ))
                ],
                ['id' => $existing->id]
            );
        } else {
            $file_info = $this->get_plugin_file_info($file_path);

            $wpdb->insert(
                $this->table_name,
                [
                    'file_path' => $file_path,
                    'issue_type' => $issue_type,
                    'severity' => $this->determine_plugin_severity($file_path, $issue_type),
                    'description' => $description,
                    'status' => 'detected',
                    'reviewed' => false,
                    'first_detected' => current_time('mysql'),
                    'last_detected' => current_time('mysql'),
                    'file_hash' => $file_info['hash'] ?? null,
                    'file_size' => $file_info['size'] ?? null,
                    'permissions' => $file_info['permissions'] ?? null,
                    'scanner_version' => 'plugin_scanner_1.0',
                    'scan_id' => 'plugin_scan_' . time(),
                    'detection_method' => 'hash_check',
                    'occurrence_count' => 1,
                    'is_active' => true,
                    'created_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql'),
                    'user_notes' => 'Plugin: ' . $plugin_slug
                ]
            );
        }
    }

    /**
     * Get relative file paths with hashes for local plugin
     */


    private function get_local_files_relative($directory, $plugin_slug)
    {
        $files = [];
        if (!is_dir($directory)) return $files;

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $full_path = $file->getPathname();

                // CAMINHO RELATIVO APENAS do diretório do plugin
                // Ex: /wp-content/plugins/antihacker/includes/file.php
                //     → diretório: /wp-content/plugins/antihacker
                //     → relative_path: includes/file.php (SEM plugin_slug!)
                $relative_path = substr($full_path, strlen($directory) + 1);

                $files[$relative_path] = hash_file('sha256', $full_path);

                // \debug3a("Local file: {$relative_path}"); // includes/file.php
            }
        }

        return $files;
    }

    /**
     * Get additional file info for plugin files
     */
    private function get_plugin_file_info($file_path)
    {
        $full_path = ABSPATH . $file_path;
        $info = [];

        if (file_exists($full_path)) {
            $info['hash'] = @hash_file('sha256', $full_path) ?: null;
            $info['size'] = @filesize($full_path) ?: null;
            $perms = @fileperms($full_path);
            $info['permissions'] = $perms ? substr(sprintf('%o', $perms), -4) : null;
        }

        return $info;
    }

    /**
     * Determine severity for plugin issues
     */
    private function determine_plugin_severity($file_path, $issue_type)
    {
        $critical_files = [
            'main plugin file' => '/^wp-content\/plugins\/[^\/]+\/[^\/]+\.php$/',
            'security files' => '/\.(php|inc|phtml)$/i'
        ];

        foreach ($critical_files as $pattern) {
            if (preg_match($pattern, $file_path)) {
                return 'high';
            }
        }

        return 'medium';
    }


    public function get_next_plugin_to_scan()
    {
        $plugins = get_plugins();
        $plugin_files = array_keys($plugins);

        if (empty($plugin_files)) {
            // //  // //debug4("Nenhum plugin encontrado");
            return false;
        }

        // //  // //debug4("Total plugins: " . count($plugin_files));

        // Pega o último índice escaneado COM SUCESSO
        $last_scanned_index = (int) get_option('bill_last_plugin_scanned_index', -1);
        // //  // //debug4("Último índice escaneado: " . $last_scanned_index);

        // Tenta encontrar um plugin elegível
        $max_attempts = count($plugin_files);
        $attempts = 0;

        while ($attempts < $max_attempts) {
            // Calcula próximo índice (circular)
            $next_index = ($last_scanned_index + 1) % count($plugin_files);
            $plugin_file = $plugin_files[$next_index];

            $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_file);

            /*
            // Verifica se já foi escaneado nos últimos 7 dias
            $last_scan_time = get_option($this->option_prefix . md5($plugin_file), 0);
            if ($last_scan_time > 0) {
                $days_since_scan = floor((time() - $last_scan_time) / DAY_IN_SECONDS);
            }
            */

            // Plugin elegível encontrado!
            // //  // //debug4("✅ Plugin elegível: " . $plugin_data['Name']);
            return $plugin_file;
        }

        // //  // //debug4("❌ Nenhum plugin elegível encontrado após verificar todos");
        return false;
    }
} // Plugin_Integrity_Scanner

function super_simple_debug_1()
{
    echo '<div style="background:#e0e0e0; padding:15px; margin:15px;">';
    echo '<h3>🔍 DEBUG SUPER SIMPLES</h3>';

    // 1. Informações básicas
    echo '<h4>Informações Básicas:</h4>';
    echo '<ul>';
    echo '<li>Último índice: ' . get_option('bill_last_plugin_scanned_index', -1) . '</li>';

    // 2. Tenta instanciar o scanner COM NAMESPACE CORRETO
    $full_class_name = 'BillSecurity\\IntegrityScanner\\Plugin_Integrity_Scanner';

    if (class_exists($full_class_name)) {
        try {
            $scanner = new $full_class_name();
            $next = $scanner->get_next_plugin_to_scan();
            echo '<li>Próximo plugin: ' . ($next ? esc_html($next) : 'Nenhum') . '</li>';
        } catch (Exception $e) {
            echo '<li>Erro ao instanciar: ' . esc_html($e->getMessage()) . '</li>';
        }
    } else {
        echo '<li>Classe não encontrada</li>';
        echo '<li>Procurando por: <code>' . esc_html($full_class_name) . '</code></li>';

        // Debug: mostra todas as classes para verificar
        echo '<li>Classes declaradas contendo "Plugin_Integrity_Scanner":</li>';
        $all_classes = get_declared_classes();
        $found = false;

        foreach ($all_classes as $class) {
            if (strpos($class, 'Plugin_Integrity_Scanner') !== false) {
                echo '<li><code>' . esc_html($class) . '</code></li>';
                $found = true;
            }
        }

        if (!$found) {
            echo '<li>Nenhuma classe encontrada com esse nome</li>';
        }
    }
    echo '</ul>';

    // 3. Resto do código da função...
    // ...
}




/*
function super_simple_debug()
{
    global $wpdb;

    // Processamento do Reset
    if (isset($_POST['reset_transient_action'])) {
        delete_transient(sanitize_text_field($_POST['reset_transient_action']));
        echo '<div style="background:#d4edda;padding:15px;margin:15px;border-left:5px solid #28a745;">✅ Transient removido!</div>';
    }

    echo '<div style="background:#f4f4f4; padding:20px; margin:20px; font-family: sans-serif; border: 1px solid #ccc; border-radius: 8px;">';
    echo '<h2 style="margin-top:0;">🔍 PAINEL DE CONTROLE DE SCANS</h2>';

    // Linha extra: última atualização da tabela
    $table_name = $wpdb->prefix . 'bill_site_integrity_issues';
    $last_table_update = $wpdb->get_var("SELECT MAX(updated_at) FROM $table_name");

    echo '<div style="background:#e3f2fd; padding:10px; margin-bottom:20px; border-radius:5px; font-size:13px;">';
    echo '📊 <strong>Última atualização da tabela:</strong> ';
    echo $last_table_update ? date('d/m/Y H:i:s', strtotime($last_table_update)) : 'Nunca atualizada';
    echo '</div>';

    // 1. SCAN DE PLUGINS (12H)
    $scan12 = [
        'titulo' => 'SCAN DE PLUGINS (12H)',
        'transient' => 'plugin_scanner_12h_interval',
        'intervalo' => 12
    ];

    // 2. SCAN DE MALWARE (24H)
    $scan24 = [
        'titulo' => 'SCAN DE MALWARE (24H)',
        'transient' => 'malware_scanner_24h_interval',
        'intervalo' => 24
    ];

    echo '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">';

    // --- COLUNA 1: PLUGINS (APENAS TRANSIENT) ---
    display_scan_box($scan12, function () {
        // Usa APENAS o transient para calcular
        $transient_timeout = get_option('_transient_timeout_plugin_scanner_12h_interval');

        if ($transient_timeout) {
            // Transient foi criado quando scan rodou
            $time_created = $transient_timeout - (12 * HOUR_IN_SECONDS);
            return $time_created;
        }

        return null; // Nunca scaneou
    });

    // --- COLUNA 2: MALWARE (APENAS TRANSIENT) ---
    display_scan_box($scan24, function () {
        // Usa APENAS o transient para calcular
        $transient_timeout = get_option('_transient_timeout_malware_scanner_24h_interval');

        if ($transient_timeout) {
            // Transient foi criado quando scan rodou
            $time_created = $transient_timeout - (24 * HOUR_IN_SECONDS);
            return $time_created;
        }

        return null; // Nunca scaneou
    });

    echo '</div>'; // Fim do grid

    echo '</div>';
}

function display_scan_box($scan, $get_last_scan_callback)
{
    $name = $scan['transient'];
    $val = get_transient($name);
    $last_scan_ts = $get_last_scan_callback();

    echo '<div style="background:#fff; border:1px solid #ddd; padding:15px; border-radius: 5px;">';
    echo "<h3 style='margin:0 0 10px 0; color:#0073aa;'>{$scan['titulo']}</h3>";
    echo '<ul style="list-style:none; padding:0; font-size:13px; line-height:1.8;">';

    // Transient Info
    if ($val !== false) {
        echo "<li>✅ <strong>Transient:</strong> <code>{$name}</code></li>";
        $expiration = get_option('_transient_timeout_' . $name);

        if ($expiration) {
            $diff = $expiration - time();

            // Tempo restante
            $hours_remaining = floor($diff / 3600);
            $minutes_remaining = floor(($diff % 3600) / 60);
            $seconds_remaining = $diff % 60;

            echo "<li>Expira em: " . date('H:i:s', $expiration) .
                " ({$hours_remaining}h {$minutes_remaining}m {$seconds_remaining}s restantes)</li>";

            // Quando foi criado (quando scan rodou)
            $created_time = $expiration - ($scan['intervalo'] * HOUR_IN_SECONDS);
            echo "<li>Criado em: " . date('d/m/Y H:i:s', $created_time) . "</li>";

            // Status baseado no transient
            $hours_since_created = round((time() - $created_time) / 3600, 1);
            $color = ($hours_since_created > $scan['intervalo']) ? 'red' : 'green';

            echo "<li>📅 <strong>Último scan (transient):</strong></li>";
            echo "<li>Executado há: {$hours_since_created}h</li>";
            echo "<li style='color:{$color}; font-weight:bold;'>Status: Há {$hours_since_created}h (Meta: {$scan['intervalo']}h)</li>";
        }
    } else {
        echo "<li style='color:orange;'>❌ <strong>Transient ausente:</strong> Rodará na próxima visita.</li>";
        echo "<li>📅 <strong>Último scan:</strong> Nenhum registro</li>";
    }

    // Botão Reset
    echo '<li style="margin-top:10px;"><form method="post"><input type="hidden" name="reset_transient_action" value="' . $name . '">';
    echo '<button type="submit" style="cursor:pointer; font-size:10px; padding:5px 10px; background:#f0f0f0; border:1px solid #ccc; border-radius:3px;">RESET TIMER</button></form></li>';

    echo '</ul></div>';
}


$scanner = new Bill_Catch_Malware();


// Debug
////if (isset($_GET['simple_debug']) && is_admin()) {
// add_action('admin_notices', 'super_simple_debug');
//}
super_simple_debug();
die('xxxxxxxxxxxxxxxxxx');
*/



$scanner = new Bill_Catch_Malware();
$scanner_plugin = new Plugin_Integrity_Scanner();
