<?php
namespace IntentDeep\VirtualFiles\Core;

use IntentDeep\VirtualFiles\Traits\SettingHelper;

class Cache {
    use SettingHelper;

    /**
     * Cache transient prefix
     */
    private const CACHE_PREFIX = 'intentdeep_vf_';

    /**
     * Cache expiration time in seconds (default: 1 hour)
     */
    private const CACHE_EXPIRATION = HOUR_IN_SECONDS;

    /**
     * Get cached file content
     *
     * @param int $file_id The virtual file ID
     * @return array|null Cached file data or null if not found
     */
    public function get_file_cache(int $file_id): ?array {
        if (!$this->is_cache_enabled()) {
            return null;
        }

        $cache_key = $this->get_file_cache_key($file_id);
        $cached_data = get_transient($cache_key);

        if ($cached_data === false) {
            return null;
        }

        return $cached_data;
    }

    /**
     * Set file content cache
     *
     * @param int $file_id The virtual file ID
     * @param array $file_data File data to cache
     * @return bool True if cache was set successfully
     */
    public function set_file_cache(int $file_id, array $file_data): bool {
        if (!$this->is_cache_enabled()) {
            return false;
        }

        $cache_key = $this->get_file_cache_key($file_id);
        $expiration = $this->get_cache_expiration();

        return set_transient($cache_key, $file_data, $expiration);
    }

    /**
     * Delete file cache
     *
     * @param int $file_id The virtual file ID
     * @return bool True if cache was deleted successfully
     */
    public function delete_file_cache(int $file_id): bool {
        $cache_key = $this->get_file_cache_key($file_id);
        return delete_transient($cache_key);
    }

    /**
     * Clear all virtual file caches
     *
     * @return int Number of caches cleared
     */
    public function clear_all_caches(): int {
        global $wpdb;

        $prefix = self::CACHE_PREFIX . 'file_';
        $cleared = 0;

        // Get all transients with our prefix
        $transients = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
                $wpdb->esc_like('_transient_' . $prefix) . '%'
            )
        );

        foreach ($transients as $transient) {
            $transient_key = str_replace('_transient_', '', $transient);
            if (delete_transient($transient_key)) {
                $cleared++;
            }
        }

        return $cleared;
    }

    /**
     * Warm up cache for active virtual files
     *
     * @return int Number of files cached
     */
    public function warm_up_cache(): int {
        if (!$this->is_cache_enabled()) {
            return 0;
        }

        $cached_count = 0;
        $active_files = $this->get_active_virtual_files();

        foreach ($active_files as $file) {
            $file_data = $this->prepare_file_data($file->ID);
            if ($file_data && $this->set_file_cache($file->ID, $file_data)) {
                $cached_count++;
            }
        }

        return $cached_count;
    }

    /**
     * Get cache statistics
     *
     * @return array Cache statistics
     */
    public function get_cache_stats(): array {
        global $wpdb;

        $prefix = self::CACHE_PREFIX . 'file_';

        // Count cached files
        $cached_count = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s",
                $wpdb->esc_like('_transient_' . $prefix) . '%'
            )
        );

        // Get total cache size
        $cache_size = 0;
        $transients = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE %s",
                $wpdb->esc_like('_transient_' . $prefix) . '%'
            )
        );

        foreach ($transients as $value) {
            $cache_size += strlen(serialize($value));
        }

        return [
            'cached_files' => intval($cached_count),
            'cache_size' => $cache_size,
            'cache_size_formatted' => size_format($cache_size),
            'cache_enabled' => $this->is_cache_enabled(),
            'cache_expiration' => $this->get_cache_expiration(),
        ];
    }

    /**
     * Check if caching is enabled
     *
     * @return bool True if caching is enabled (requires Pro)
     */
    private function is_cache_enabled(): bool {
        // Check if user has Pro access
        $has_pro = function_exists('intentdeep_vf_fs') && intentdeep_vf_fs()->can_use_premium_code__premium_only();

        if (!$has_pro) {
            return false;
        }

        // Check if cache is enabled in settings
        $settings = $this->getSettings();
        return !empty($settings['cache_enabled']);
    }

    /**
     * Get cache expiration time
     *
     * @return int Cache expiration in seconds
     */
    private function get_cache_expiration(): int {
        $settings = $this->getSettings();
        $expiration = intval($settings['cache_expiration'] ?? self::CACHE_EXPIRATION);
        return $expiration > 0 ? $expiration : self::CACHE_EXPIRATION;
    }

    /**
     * Generate cache key for a file
     *
     * @param int $file_id The virtual file ID
     * @return string Cache key
     */
    private function get_file_cache_key(int $file_id): string {
        return self::CACHE_PREFIX . 'file_' . $file_id;
    }

    /**
     * Get active virtual files for cache warming
     *
     * @return array Array of active virtual file posts
     */
    private function get_active_virtual_files(): array {
        return get_posts([
            'post_type' => 'idep_virtual_file',
            'posts_per_page' => -1,
            'post_status' => 'publish',
            'meta_query' => [
                [
                    'key' => '_vf_status',
                    'value' => 'active',
                    'compare' => '='
                ],
                [
                    'key' => '_vf_filename',
                    'value' => '',
                    'compare' => '!='
                ]
            ]
        ]);
    }

    /**
     * Prepare file data for caching
     *
     * @param int $file_id The virtual file ID
     * @return array|null Prepared file data or null on failure
     */
    private function prepare_file_data(int $file_id): ?array {
        $post = get_post($file_id);

        if (!$post || $post->post_type !== 'idep_virtual_file') {
            return null;
        }

        // Check if file is active
        $status = get_post_meta($post->ID, '_vf_status', true);
        if ($status !== 'active') {
            return null;
        }

        // Get file data
        $filename = get_post_meta($post->ID, '_vf_filename', true);
        $content = get_post_meta($post->ID, '_vf_content', true);
        $mime_type = get_post_meta($post->ID, '_vf_mime_type', true);

        if (empty($filename) || empty($content)) {
            return null;
        }

        // Validate content for security
        if (!$this->is_safe_content($content)) {
            return null;
        }

        return [
            'filename' => $filename,
            'content' => $content,
            'mime_type' => $mime_type,
            'content_length' => strlen($content),
            'cached_at' => current_time('timestamp'),
            'post_modified' => strtotime($post->post_modified_gmt),
        ];
    }

    /**
     * Check if content is safe to cache
     *
     * @param string $content The content to check
     * @return bool True if content is safe
     */
    private function is_safe_content(string $content): bool {
        // Check for PHP code
        if (preg_match('/<\?php/i', $content)) {
            return false;
        }

        // Check for JavaScript (unless it's a JSON file)
        if (preg_match('/<script/i', $content)) {
            return false;
        }

        // Check for potentially dangerous patterns
        $dangerous_patterns = [
            '/eval\s*\(/i',
            '/exec\s*\(/i',
            '/system\s*\(/i',
            '/shell_exec\s*\(/i',
            '/passthru\s*\(/i',
            '/file_get_contents\s*\(/i',
            '/fopen\s*\(/i',
            '/curl_exec\s*\(/i'
        ];

        foreach ($dangerous_patterns as $pattern) {
            if (preg_match($pattern, $content)) {
                return false;
            }
        }

        return true;
    }
}