<?php
/**
 * Trusted Hashes Database Manager
 *
 * Manages a custom table for storing integrity scan trusted file hashes.
 * This replaces the transient-based storage to avoid MySQL max_allowed_packet issues
 * with large datasets (24k+ files).
 *
 * @package VMPFence
 * @since 1.1.9
 */

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

class VMPFence_Trusted_Hashes_DB {
    
    /**
     * Database table name (without prefix)
     * @var string
     */
    private static $table_name = 'vmpfence_trusted_hashes';
    
    /**
     * Get the full table name with WordPress prefix
     *
     * @return string
     */
    private static function get_table_name() {
        global $wpdb;
        return $wpdb->prefix . self::$table_name;
    }
    
    /**
     * Create the trusted hashes table
     *
     * @return bool True on success, false on failure
     */
    public static function create_table() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        $charset_collate = $wpdb->get_charset_collate();
        
        $sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            file_path varchar(500) NOT NULL,
            hash_value varchar(64) NOT NULL,
            hash_source varchar(50) NOT NULL DEFAULT 'unknown',
            plugin_slug varchar(100) DEFAULT NULL,
            theme_slug varchar(100) DEFAULT NULL,
            package_version varchar(20) DEFAULT NULL,
            created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY file_path (file_path(191)),
            KEY hash_source (hash_source),
            KEY plugin_slug (plugin_slug),
            KEY theme_slug (theme_slug)
        ) {$charset_collate};";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
        
        // Verify table was created
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name;
        
        if ($table_exists) {
            VMPFence_Secure_Logger::log('info', 'Trusted hashes table created successfully', [
                'table' => $table_name
            ]);
        } else {
            VMPFence_Secure_Logger::log('error', 'Failed to create trusted hashes table', [
                'table' => $table_name,
                'sql' => $sql
            ]);
        }
        
        return $table_exists;
    }
    
    /**
     * Check if the trusted hashes table exists
     *
     * @return bool True if table exists, false otherwise
     */
    public static function table_exists() {
        global $wpdb;
        $table_name = self::get_table_name();
        return $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name;
    }
    
    /**
     * Drop the trusted hashes table (cleanup after scan)
     *
     * @return bool True on success, false on failure
     */
    public static function drop_table() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        $result = $wpdb->query("DROP TABLE IF EXISTS {$table_name}");
        
        VMPFence_Secure_Logger::log('debug', 'Dropped trusted hashes table', [
            'table' => $table_name,
            'result' => $result !== false
        ]);
        
        return $result !== false;
    }
    
    /**
     * Clear all hashes from the table (faster than drop/create for re-scan)
     *
     * @return bool True on success, false on failure
     */
    public static function clear_all_hashes() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        $result = $wpdb->query("TRUNCATE TABLE {$table_name}");
        
        VMPFence_Secure_Logger::log('debug', 'Cleared all trusted hashes', [
            'table' => $table_name,
            'result' => $result !== false
        ]);
        
        return $result !== false;
    }
    
    /**
     * Insert hashes in bulk (efficient batch insert)
     *
     * @param array $hashes Array of hash data arrays
     * @return bool True on success, false on failure
     */
    public static function insert_hashes_bulk($hashes) {
        global $wpdb;
        
        if (empty($hashes)) {
            return true;
        }
        
        $table_name = self::get_table_name();
        $values = array();
        $placeholders = array();
        
        foreach ($hashes as $file_path => $hash_data) {
            $placeholders[] = "(%s, %s, %s, %s, %s, %s)";
            $values[] = $file_path;
            $values[] = $hash_data['hash'];
            $values[] = $hash_data['source'];
            $values[] = isset($hash_data['plugin']) ? $hash_data['plugin'] : null;
            $values[] = isset($hash_data['theme']) ? $hash_data['theme'] : null;
            $values[] = isset($hash_data['version']) ? $hash_data['version'] : null;
        }
        
        $sql = "INSERT INTO {$table_name} 
                (file_path, hash_value, hash_source, plugin_slug, theme_slug, package_version) 
                VALUES " . implode(', ', $placeholders) . "
                ON DUPLICATE KEY UPDATE 
                hash_value = VALUES(hash_value),
                hash_source = VALUES(hash_source),
                plugin_slug = VALUES(plugin_slug),
                theme_slug = VALUES(theme_slug),
                package_version = VALUES(package_version)";
        
        $prepared = $wpdb->prepare($sql, $values);
        $result = $wpdb->query($prepared);
        
        if ($result === false) {
            VMPFence_Secure_Logger::log('error', 'Failed to insert trusted hashes', [
                'count' => count($hashes),
                'error' => $wpdb->last_error
            ]);
            return false;
        }
        
        VMPFence_Secure_Logger::log('debug', 'Inserted trusted hashes in bulk', [
            'count' => count($hashes),
            'affected_rows' => $result
        ]);
        
        return true;
    }
    
    /**
     * Insert hashes in batches to avoid memory issues
     *
     * @param array $hashes Array of all hashes
     * @param int $batch_size Number of hashes per batch
     * @return bool True on success, false on failure
     */
    public static function insert_hashes_batched($hashes, $batch_size = 500) {
        $chunks = array_chunk($hashes, $batch_size, true);
        $total_chunks = count($chunks);
        
        VMPFence_Secure_Logger::log('info', 'Starting batched hash insertion', [
            'total_hashes' => count($hashes),
            'batch_size' => $batch_size,
            'total_batches' => $total_chunks
        ]);
        
        foreach ($chunks as $index => $chunk) {
            $success = self::insert_hashes_bulk($chunk);
            
            if (!$success) {
                VMPFence_Secure_Logger::log('error', 'Batch insertion failed', [
                    'batch' => $index + 1,
                    'total_batches' => $total_chunks
                ]);
                return false;
            }
            
            // Log progress every 10 batches
            if (($index + 1) % 10 === 0 || ($index + 1) === $total_chunks) {
                VMPFence_Secure_Logger::log('debug', 'Hash insertion progress', [
                    'batch' => $index + 1,
                    'total_batches' => $total_chunks,
                    'progress_percent' => round((($index + 1) / $total_chunks) * 100, 2)
                ]);
            }
        }
        
        return true;
    }
    
    /**
     * Get hash data for a specific file path
     *
     * @param string $file_path Relative file path
     * @return array|null Hash data array or null if not found
     */
    public static function get_hash($file_path) {
        global $wpdb;
        
        $table_name = self::get_table_name();
        
        $result = $wpdb->get_row($wpdb->prepare(
            "SELECT hash_value, hash_source, plugin_slug, theme_slug, package_version 
             FROM {$table_name} 
             WHERE file_path = %s",
            $file_path
        ), ARRAY_A);
        
        if (!$result) {
            return null;
        }
        
        return array(
            'hash' => $result['hash_value'],
            'source' => $result['hash_source'],
            'plugin' => $result['plugin_slug'],
            'theme' => $result['theme_slug'],
            'version' => $result['package_version']
        );
    }
    
    /**
     * Get all hashes as an associative array (file_path => hash_data)
     * Use with caution on large datasets - prefer get_hash() for individual lookups
     *
     * @return array Associative array of file paths to hash data
     */
    public static function get_all_hashes() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        
        $results = $wpdb->get_results(
            "SELECT file_path, hash_value, hash_source, plugin_slug, theme_slug, package_version 
             FROM {$table_name}",
            ARRAY_A
        );
        
        if (!$results) {
            return array();
        }
        
        $hashes = array();
        foreach ($results as $row) {
            $hashes[$row['file_path']] = array(
                'hash' => $row['hash_value'],
                'source' => $row['hash_source'],
                'plugin' => $row['plugin_slug'],
                'theme' => $row['theme_slug'],
                'version' => $row['package_version']
            );
        }
        
        return $hashes;
    }
    
    /**
     * Get total count of stored hashes
     *
     * @return int Total count
     */
    public static function get_hash_count() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        $count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");
        
        return (int) $count;
    }
    
    /**
     * Check if the table exists and has data
     *
     * @return bool True if table exists and has data
     */
    public static function has_data() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        
        // Check if table exists
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name;
        if (!$table_exists) {
            return false;
        }
        
        // Check if table has data
        $count = self::get_hash_count();
        return $count > 0;
    }
    
    /**
     * Get statistics about the stored hashes
     *
     * @return array Statistics array
     */
    public static function get_statistics() {
        global $wpdb;
        
        $table_name = self::get_table_name();
        
        $stats = array(
            'total_hashes' => 0,
            'by_source' => array(),
            'by_plugin' => array(),
            'by_theme' => array()
        );
        
        // Total count
        $stats['total_hashes'] = self::get_hash_count();
        
        // Count by source
        $by_source = $wpdb->get_results(
            "SELECT hash_source, COUNT(*) as count 
             FROM {$table_name} 
             GROUP BY hash_source",
            ARRAY_A
        );
        
        if ($by_source) {
            foreach ($by_source as $row) {
                $stats['by_source'][$row['hash_source']] = (int) $row['count'];
            }
        }
        
        // Count by plugin
        $by_plugin = $wpdb->get_results(
            "SELECT plugin_slug, COUNT(*) as count 
             FROM {$table_name} 
             WHERE plugin_slug IS NOT NULL 
             GROUP BY plugin_slug",
            ARRAY_A
        );
        
        if ($by_plugin) {
            foreach ($by_plugin as $row) {
                $stats['by_plugin'][$row['plugin_slug']] = (int) $row['count'];
            }
        }
        
        // Count by theme
        $by_theme = $wpdb->get_results(
            "SELECT theme_slug, COUNT(*) as count 
             FROM {$table_name} 
             WHERE theme_slug IS NOT NULL 
             GROUP BY theme_slug",
            ARRAY_A
        );
        
        if ($by_theme) {
            foreach ($by_theme as $row) {
                $stats['by_theme'][$row['theme_slug']] = (int) $row['count'];
            }
        }
        
        return $stats;
    }
}
