# master-addons/3.1.2/inc/classes/template-library-cache.php

Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder &amp; Template Kits, version 3.1.2. 1,225 lines.

- Page: https://pluginprobe.com/plugins/master-addons/3.1.2/code/inc/classes/template-library-cache.php
- Raw: https://pluginprobe.com/plugins/master-addons/3.1.2/raw/inc/classes/template-library-cache.php
- Modified: 2026-06-01T13:31:18+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/master-addons/3.1.2/code/inc/classes/template-library-cache.php#L10-L20`.

```php
<?php

namespace MasterAddons\Inc\Classes;

use MasterAddons\Inc\Admin\Templates;

if (!defined('ABSPATH')) {
    exit;
}

// phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_rmdir, WordPress.WP.AlternativeFunctions.file_system_operations_is_writable, WordPress.WP.AlternativeFunctions.rename_rename -- Local template-library cache directory housekeeping (under wp-content/uploads); native PHP filesystem calls are appropriate here and WP_Filesystem credential prompts are not desirable for background cache operations.

/**
 * Template Library Cache
 * Provides file-based caching with scheduled updates for template import functionality
 */
class Template_Library_Cache
{

    private static $instance = null;
    private $cache_dir;
    private $cache_expiry;
    private $config;

    public function __construct()
    {
        $upload_dir = wp_upload_dir();
        $basedir = !empty($upload_dir['basedir']) ? $upload_dir['basedir'] : '';
        $this->cache_dir = $basedir . '/master_addons/templates-library/';
        $this->cache_expiry = apply_filters('jltma_cache_expiry', 6 * HOUR_IN_SECONDS); // 6 hours default, filterable

        // Initialize config safely
        add_action('init', [$this, 'init_config'], 20);
        add_action('init', [$this, 'init'], 25);
    }

    public function init_config()
    {
        // Initialize config after templates system is ready
        if (function_exists('MasterAddons\\Inc\\Admin\\Templates\\master_addons_templates')) {
            $templates_instance = Templates\master_addons_templates();
            if ($templates_instance && isset($templates_instance->config)) {
                $this->config = $templates_instance->config->get('api');
            }
        }

    }

    public function init()
    {
        // Clean up old incorrect template-kits folder if it exists
        $this->cleanup_incorrect_folders();
        
        // Ensure cache directory exists
        $this->ensure_cache_directory();

        // Schedule cache updates
        add_action('wp', [$this, 'schedule_cache_updates']);
        add_action('jltma_templates_cache_update', [$this, 'update_templates_cache']);

        // Admin hooks
        add_action('admin_init', [$this, 'maybe_clear_cache']);

        // Performance optimizations
        add_action('wp_ajax_jltma_preload_cache', [$this, 'preload_cache_ajax']);
        add_action('jltma_templates_preload_cache', [$this, 'preload_popular_templates']);
        add_action('jltma_background_preload', [$this, 'do_background_preload']);

        // Extend existing cache methods
        add_filter('jltma_templates_cache_enabled', '__return_true');
    }

    /**
     * Ensure cache directory exists with proper structure
     */
    private function ensure_cache_directory()
    {
        // Check if uploads directory is writable
        if (!$this->is_uploads_writable()) {
            return false;
        }

        if (!file_exists($this->cache_dir)) {
            if (!wp_mkdir_p($this->cache_dir)) {
                return false;
            }

            // Create subdirectories for different template types
            // Removed 'template-kits' as it should be in its own separate folder
            $subdirs = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
            foreach ($subdirs as $subdir) {
                wp_mkdir_p($this->cache_dir . $subdir . '/');
                wp_mkdir_p($this->cache_dir . $subdir . '/categories/');
                wp_mkdir_p($this->cache_dir . $subdir . '/keywords/');
                wp_mkdir_p($this->cache_dir . $subdir . '/templates/');
                wp_mkdir_p($this->cache_dir . $subdir . '/images/');
            }

            // Create .htaccess for security
            $htaccess_content = "Options -Indexes\n<Files \"*.json\">\nOrder allow,deny\nAllow from all\n</Files>";
            $this->fs_put_contents($this->cache_dir . '.htaccess', $htaccess_content);

            // Create index.php files
            $index_content = "<?php\n// Silence is golden.\n";
            $this->fs_put_contents($this->cache_dir . 'index.php', $index_content);

            foreach ($subdirs as $subdir) {
                $this->fs_put_contents($this->cache_dir . $subdir . '/index.php', $index_content);
            }
        }

        return true;
    }

    /**
     * Clean up incorrect folders created in wrong location
     */
    private function cleanup_incorrect_folders()
    {
        // Remove template-kits folder from templates-library if it exists
        $incorrect_folder = $this->cache_dir . 'template-kits/';
        if (file_exists($incorrect_folder)) {
            $this->delete_directory_recursively($incorrect_folder);
        }
        
        // Also check for any variations that might have been created
        $variations = ['template_kits', 'templatekits', 'template-kit'];
        foreach ($variations as $variant) {
            $incorrect_variant = $this->cache_dir . $variant . '/';
            if (file_exists($incorrect_variant)) {
                $this->delete_directory_recursively($incorrect_variant);
            }
        }
    }
    
    /**
     * Delete a directory and all its contents recursively
     */
    private function delete_directory_recursively($dir)
    {
        if (!file_exists($dir)) {
            return;
        }

        if (!is_dir($dir)) {
            return;
        }

        // Normalize the directory path to avoid double slashes
        $dir = rtrim($dir, '/\\');

        // Try to scan the directory, but handle failures gracefully
        $scan_result = @scandir($dir);
        if ($scan_result === false) {
            // If we can't scan it, try to remove it directly
            @rmdir($dir);
            return;
        }

        $files = array_diff($scan_result, array('.', '..'));
        foreach ($files as $file) {
            $path = $dir . DIRECTORY_SEPARATOR . $file;
            if (is_dir($path)) {
                $this->delete_directory_recursively($path);
            } else {
                wp_delete_file($path);
            }
        }
        @rmdir($dir);
    }

    /**
     * Check if uploads directory is writable
     */
    private function is_uploads_writable()
    {
        $upload_dir = wp_upload_dir();

        // Check if uploads dir exists and is writable
        if (!file_exists($upload_dir['basedir'])) {
            return false;
        }

        return is_writable($upload_dir['basedir']);
    }

    /**
     * Schedule cache update events via Background_Task_Manager
     */
    public function schedule_cache_updates()
    {
        Background_Task_Manager::get_instance()->schedule_recurring(
            'jltma_templates_cache_update',
            12 * HOUR_IN_SECONDS
        );
    }

    /**
     * Get cached templates for specific tab
     */
    public function get_cached_templates($tab, $force_refresh = false)
    {
        // Try transient cache first if file cache is not available
        if (!$this->is_file_cache_available()) {
            return $this->get_transient_cached_templates($tab, $force_refresh);
        }

        $cache_file = $this->cache_dir . "{$tab}/templates/templates.json";
        $cache_meta_file = $this->cache_dir . "{$tab}/templates/meta.json";

        // Check if cache exists and is valid
        if (!$force_refresh && $this->is_cache_valid($cache_meta_file)) {
            $cached_data = $this->read_cache_file($cache_file);
            if ($cached_data !== false) {
                // Update thumbnail URLs to use cache folder first
                foreach ($cached_data as &$template) {
                    $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                    if ($cached_thumbnail) {
                        $template['thumbnail'] = $cached_thumbnail;
                    }
                }
                return $cached_data;
            }
        }

        // Fetch fresh data from remote API
        $fresh_data = $this->fetch_remote_templates($tab);

        if ($fresh_data !== false) {
            // Update thumbnail URLs to use cache folder first
            foreach ($fresh_data as &$template) {
                $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                if ($cached_thumbnail) {
                    $template['thumbnail'] = $cached_thumbnail;
                }
            }

            // Cache the data
            $this->write_cache_file($cache_file, $fresh_data);
            $this->write_cache_meta($cache_meta_file);

            // Cache individual template thumbnails
            $this->cache_template_images($fresh_data, $tab);

            return $fresh_data;
        }

        // Fallback to expired cache if available
        $fallback_data = $this->read_cache_file($cache_file);
        if ($fallback_data !== false) {
            // Update thumbnail URLs to use cache folder first for fallback data
            foreach ($fallback_data as &$template) {
                $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                if ($cached_thumbnail) {
                    $template['thumbnail'] = $cached_thumbnail;
                }
            }
        }
        return $fallback_data;
    }

    /**
     * Get cached categories for specific tab
     */
    public function get_cached_categories($tab, $force_refresh = false)
    {
        // Try transient cache first if file cache is not available
        if (!$this->is_file_cache_available()) {
            return $this->get_transient_cached_categories($tab, $force_refresh);
        }

        $cache_file = $this->cache_dir . "{$tab}/categories/categories.json";
        $cache_meta_file = $this->cache_dir . "{$tab}/categories/meta.json";

        if (!$force_refresh && $this->is_cache_valid($cache_meta_file)) {
            $cached_data = $this->read_cache_file($cache_file);
            if ($cached_data !== false) {
                return $cached_data;
            }
        }

        $fresh_data = $this->fetch_remote_categories($tab);

        if ($fresh_data !== false) {
            $this->write_cache_file($cache_file, $fresh_data);
            $this->write_cache_meta($cache_meta_file);
            return $fresh_data;
        }

        return $this->read_cache_file($cache_file);
    }

    /**
     * Get cached keywords for specific tab
     */
    public function get_cached_keywords($tab, $force_refresh = false)
    {
        // Try transient cache first if file cache is not available
        if (!$this->is_file_cache_available()) {
            return $this->get_transient_cached_keywords($tab, $force_refresh);
        }

        $cache_file = $this->cache_dir . "{$tab}/keywords/keywords.json";
        $cache_meta_file = $this->cache_dir . "{$tab}/keywords/meta.json";

        if (!$force_refresh && $this->is_cache_valid($cache_meta_file)) {
            $cached_data = $this->read_cache_file($cache_file);
            if ($cached_data !== false) {
                return $cached_data;
            }
        }

        $fresh_data = $this->fetch_remote_keywords($tab);

        if ($fresh_data !== false) {
            $this->write_cache_file($cache_file, $fresh_data);
            $this->write_cache_meta($cache_meta_file);
            return $fresh_data;
        }

        return $this->read_cache_file($cache_file);
    }

    /**
     * Get cached individual template
     */
    public function get_cached_template($template_id, $tab, $force_refresh = false)
    {
        $cache_file = $this->cache_dir . "{$tab}/templates/template-{$template_id}.json";
        $cache_meta_file = $this->cache_dir . "{$tab}/templates/template-{$template_id}-meta.json";

        if (!$force_refresh && $this->is_cache_valid($cache_meta_file)) {
            $cached_data = $this->read_cache_file($cache_file);
            if ($cached_data !== false) {
                return $cached_data;
            }
        }

        // For individual templates, we don't cache them unless they're part of a larger fetch
        // This prevents excessive API calls for single template requests
        return false;
    }

    /**
     * Cache individual template data (called after successful API fetch)
     */
    public function cache_template_data($template_id, $tab, $data)
    {
        $cache_file = $this->cache_dir . "{$tab}/templates/template-{$template_id}.json";
        $cache_meta_file = $this->cache_dir . "{$tab}/templates/template-{$template_id}-meta.json";

        $this->write_cache_file($cache_file, $data);
        $this->write_cache_meta($cache_meta_file);
    }

    /**
     * Fetch templates from remote API
     */
    private function fetch_remote_templates($tab)
    {
        if (empty($this->config)) {
            return false;
        }

        $api_url = $this->config['base'] . $this->config['path'] . $this->config['endpoints']['templates'] . $tab;

        $response = wp_remote_get($api_url, [
            'timeout' => 60,
            'sslverify' => false,
            'headers' => [
                'User-Agent' => 'Master Addons Templates Cache/' . JLTMA_VER
            ]
        ]);
        if (is_wp_error($response)) {
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (json_last_error() !== JSON_ERROR_NONE || !isset($data['success']) || !$data['success']) {
            return false;
        }

        return isset($data['templates']) ? $data['templates'] : [];
    }

    /**
     * Fetch categories from remote API
     */
    private function fetch_remote_categories($tab)
    {
        if (empty($this->config)) {
            return false;
        }

        $api_url = $this->config['base'] . $this->config['path'] . $this->config['endpoints']['categories'] . $tab;

        $response = wp_remote_get($api_url, [
            'timeout' => 60,
            'sslverify' => false
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (json_last_error() !== JSON_ERROR_NONE || !isset($data['success']) || !$data['success']) {
            return false;
        }

        return isset($data['terms']) ? $data['terms'] : [];
    }

    /**
     * Fetch keywords from remote API
     */
    private function fetch_remote_keywords($tab)
    {
        if (empty($this->config)) {
            return false;
        }

        $api_url = $this->config['base'] . $this->config['path'] . $this->config['endpoints']['keywords'] . $tab;

        $response = wp_remote_get($api_url, [
            'timeout' => 60,
            'sslverify' => false
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (json_last_error() !== JSON_ERROR_NONE || !isset($data['success']) || !$data['success']) {
            return false;
        }

        return isset($data['terms']) ? $data['terms'] : [];
    }

    /**
     * Cache template images locally
     */
    private function cache_template_images($templates, $tab)
    {
        if (!is_array($templates)) {
            return;
        }

        foreach ($templates as $template) {
            if (isset($template['thumbnail']) && !empty($template['thumbnail'])) {
                $template_id = $template['template_id'] ?? uniqid();
                $this->cache_image($template['thumbnail'], $tab, "template-{$template_id}-thumb");
            }

            if (isset($template['preview']) && !empty($template['preview'])) {
                $template_id = $template['template_id'] ?? uniqid();
                $this->cache_image($template['preview'], $tab, "template-{$template_id}-preview");
            }
        }
    }

    /**
     * Cache individual image
     */
    private function cache_image($image_url, $tab, $filename)
    {
        if (empty($image_url)) {
            return false;
        }

        $extension = pathinfo($image_url, PATHINFO_EXTENSION);
        if (empty($extension)) {
            $extension = 'jpg';
        }

        $local_file = $this->cache_dir . "{$tab}/images/{$filename}.{$extension}";

        // Skip if already cached and recent
        if (file_exists($local_file) && (time() - filemtime($local_file)) < DAY_IN_SECONDS) {
            return $local_file;
        }

        // Ensure the directory exists before trying to write
        $image_dir = dirname($local_file);
        if (!file_exists($image_dir)) {
            wp_mkdir_p($image_dir);
        }

        $response = wp_remote_get($image_url, [
            'timeout' => 30
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $image_data = wp_remote_retrieve_body($response);

        if ($this->fs_put_contents($local_file, $image_data)) {
            return $local_file;
        }

        return false;
    }

    /**
     * Update templates cache (scheduled event)
     */
    public function update_templates_cache()
    {
        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
        $btm = Background_Task_Manager::get_instance();

        foreach ($template_types as $tab) {
            try {
                $btm->execute_with_retry(function () use ($tab) {
                    // Update templates
                    $this->get_cached_templates($tab, true);
                    // Update categories
                    $this->get_cached_categories($tab, true);
                    // Update keywords
                    $this->get_cached_keywords($tab, true);
                }, 3, "templates_cache_sync_{$tab}");
            } catch (\Exception $e) {
                // Individual tab failure logged by execute_with_retry; continue with others
            }
        }

        // Clean up old cache files
        $this->cleanup_old_cache();

        // Update last cache time
        set_transient('jltma_templates_last_cache_update', time(), DAY_IN_SECONDS);
    }

    /**
     * Check if cache is valid
     */
    private function is_cache_valid($meta_file)
    {
        if (!file_exists($meta_file)) {
            return false;
        }

        $meta = json_decode(file_get_contents($meta_file), true);
        if (!$meta || !isset($meta['timestamp'])) {
            return false;
        }

        return (time() - $meta['timestamp']) < $this->cache_expiry;
    }

    /**
     * Read cache file with priority tracking
     */
    private function read_cache_file($file_path)
    {
        if (!file_exists($file_path)) {
            return false;
        }

        // Track access for priority system
        $this->track_cache_access($file_path);

        $content = file_get_contents($file_path);
        if ($content === false) {
            return false;
        }

        $data = json_decode($content, true);
        return json_last_error() === JSON_ERROR_NONE ? $data : false;
    }

    /**
     * Write a file through the WP_Filesystem API.
     *
     * @param string $file
     * @param string $contents
     * @return bool
     */
    private function fs_put_contents($file, $contents)
    {
        global $wp_filesystem;
        if (empty($wp_filesystem)) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
            WP_Filesystem();
        }
        if (empty($wp_filesystem)) {
            return false;
        }
        return $wp_filesystem->put_contents($file, $contents, FS_CHMOD_FILE);
    }

    /**
     * Write cache file
     */
    private function write_cache_file($file_path, $data)
    {
        $dir = dirname($file_path);
        if (!file_exists($dir)) {
            wp_mkdir_p($dir);
        }

        $json = wp_json_encode($data, JSON_PRETTY_PRINT);
        return $this->fs_put_contents($file_path, $json) !== false;
    }

    /**
     * Write cache metadata
     */
    private function write_cache_meta($meta_file)
    {
        $meta = [
            'timestamp' => time(),
            'version' => JLTMA_VER,
            'expiry' => $this->cache_expiry
        ];

        return $this->fs_put_contents($meta_file, wp_json_encode($meta)) !== false;
    }

    /**
     * Clear all template cache
     */
    public function clear_cache()
    {
        $cleared = false;

        // Clear file cache if available
        if (file_exists($this->cache_dir)) {
            $this->delete_directory_contents($this->cache_dir);
            $this->ensure_cache_directory();
            $cleared = true;
        }

        // Always clear transient cache
        $this->clear_transient_cache();

        // Clear related transients (legacy support)
        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
        foreach ($template_types as $tab) {
            delete_transient("master_addons_templates_master-api_{$tab}");
            delete_transient("master_addons_categories_master-api_{$tab}");
            delete_transient("master_addons_keywords_master-api_{$tab}");
        }

        delete_transient('jltma_templates_last_cache_update');

        return true;
    }

    /**
     * Refresh cache by clearing and fetching fresh data from API
     */
    public function refresh_cache()
    {
        // Clear all existing cache
        $this->clear_cache();

        // Force fetch fresh data from API for all template types
        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
        $refreshed_data = [];

        foreach ($template_types as $tab) {
            // Force refresh templates
            $templates = $this->get_cached_templates($tab, true);

            // Force refresh categories
            $categories = $this->get_cached_categories($tab, true);

            // Force refresh keywords
            $keywords = $this->get_cached_keywords($tab, true);

            $refreshed_data[$tab] = [
                'templates' => is_array($templates) ? count($templates) : 0,
                'categories' => is_array($categories) ? count($categories) : 0,
                'keywords' => is_array($keywords) ? count($keywords) : 0
            ];
        }

        // Update last cache refresh time
        set_transient('jltma_templates_last_cache_update', time(), DAY_IN_SECONDS);

        // Log successful refresh

        return $refreshed_data;
    }

    /**
     * Clean up old cache files with priority system
     */
    private function cleanup_old_cache()
    {
        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];

        // Cache priority ages (high priority files are kept longer)
        $priority_ages = [
            'high' => 14 * DAY_IN_SECONDS,   // 14 days for high priority
            'medium' => 7 * DAY_IN_SECONDS,  // 7 days for medium priority
            'low' => 3 * DAY_IN_SECONDS      // 3 days for low priority
        ];

        foreach ($template_types as $tab) {
            $tab_dir = $this->cache_dir . $tab . '/';
            if (!file_exists($tab_dir)) {
                continue;
            }

            $subdirs = ['categories', 'keywords', 'templates', 'images'];
            foreach ($subdirs as $subdir) {
                $full_dir = $tab_dir . $subdir . '/';
                if (!file_exists($full_dir)) {
                    continue;
                }

                $files = glob($full_dir . '*');
                foreach ($files as $file) {
                    if (is_file($file)) {
                        $file_age = time() - filemtime($file);
                        $priority = $this->get_file_cache_priority($file, $tab);
                        $max_age = $priority_ages[$priority] ?? $priority_ages['low'];

                        if ($file_age > $max_age) {
                            wp_delete_file($file);
                        }
                    }
                }
            }
        }
    }

    /**
     * Determine cache file priority based on usage patterns
     */
    private function get_file_cache_priority($file_path, $tab)
    {
        $filename = basename($file_path);
        $access_count = $this->get_file_access_count($file_path);
        $recent_access = $this->get_recent_access_time($file_path);

        // High priority: Frequently accessed files (>10 times) or recently accessed (within 2 days)
        if ($access_count > 10 || (time() - $recent_access) < (2 * DAY_IN_SECONDS)) {
            return 'high';
        }

        // Medium priority: Moderately accessed files (3-10 times) or accessed within a week
        if ($access_count >= 3 || (time() - $recent_access) < (7 * DAY_IN_SECONDS)) {
            return 'medium';
        }

        // Low priority: Everything else
        return 'low';
    }

    /**
     * Get file access count from usage tracking
     */
    private function get_file_access_count($file_path)
    {
        $access_data = get_transient('jltma_cache_access_' . md5($file_path));
        return $access_data ? (int) $access_data['count'] : 0;
    }

    /**
     * Get recent access time for file
     */
    private function get_recent_access_time($file_path)
    {
        $access_data = get_transient('jltma_cache_access_' . md5($file_path));
        return $access_data ? (int) $access_data['last_access'] : filemtime($file_path);
    }

    /**
     * Track cache file access for priority system
     */
    private function track_cache_access($file_path)
    {
        $access_key = 'jltma_cache_access_' . md5($file_path);
        $access_data = get_transient($access_key) ?: ['count' => 0, 'last_access' => 0];

        $access_data['count']++;
        $access_data['last_access'] = time();

        set_transient($access_key, $access_data, 30 * DAY_IN_SECONDS);
    }

    /**
     * Delete directory contents recursively
     */
    private function delete_directory_contents($dir)
    {
        if (!file_exists($dir)) {
            return;
        }

        $files = glob($dir . '*', GLOB_MARK);
        foreach ($files as $file) {
            if (is_dir($file)) {
                $this->delete_directory_contents($file);
                rmdir($file);
            } else {
                wp_delete_file($file);
            }
        }
    }

    /**
     * Handle cache clearing from admin
     */
    public function maybe_clear_cache()
    {
        if (isset($_GET['jltma_clear_templates_cache']) &&
            isset($_GET['_wpnonce']) &&
            wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'jltma_clear_templates_cache') &&
            current_user_can('manage_options')) {

            $this->clear_cache();

            wp_safe_redirect(add_query_arg([
                'jltma_templates_cache_cleared' => '1'
            ], remove_query_arg(['jltma_clear_templates_cache', '_wpnonce'])));
            exit;
        }

        // Handle cache refresh from admin
        if (isset($_GET['jltma_refresh_templates_cache']) &&
            isset($_GET['_wpnonce']) &&
            wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'jltma_refresh_templates_cache') &&
            current_user_can('manage_options')) {

            $this->refresh_cache();

            wp_safe_redirect(add_query_arg([
                'jltma_templates_cache_refreshed' => '1'
            ], remove_query_arg(['jltma_refresh_templates_cache', '_wpnonce'])));
            exit;
        }
    }

    /**
     * Get cached image URL
     */
    public function get_cached_image_url($original_url, $tab, $filename)
    {
        $extension = pathinfo($original_url, PATHINFO_EXTENSION) ?: 'jpg';
        $local_file = $this->cache_dir . "{$tab}/images/{$filename}.{$extension}";

        if (file_exists($local_file)) {
            $upload_dir = wp_upload_dir();
            $relative_path = str_replace($upload_dir['basedir'], '', $local_file);
            return $upload_dir['baseurl'] . $relative_path;
        }

        return $original_url;
    }
    
    /**
     * Get template kit thumbnail from cache or generate fallback URL
     */
    public function get_kit_thumbnail_url($kit_name, $template_name = 'home', $original_url = null)
    {
        // Normalize kit name for filename
        $kit_slug = sanitize_title($kit_name);
        $template_slug = sanitize_title($template_name);

        // Check cache directory first (templates-library images)
        $cache_image_dir = $this->cache_dir . 'master_section/images/';
        $cached_file_patterns = [
            "{$kit_slug}-{$template_slug}.jpg",
            "{$kit_slug}-{$template_slug}.png",
            "{$kit_slug}.jpg",
            "{$kit_slug}.png"
        ];

        foreach ($cached_file_patterns as $pattern) {
            $cached_file = $cache_image_dir . $pattern;
            if (file_exists($cached_file)) {
                $upload_dir = wp_upload_dir();
                $relative_path = str_replace($upload_dir['basedir'], '', $cached_file);
                return $upload_dir['baseurl'] . $relative_path;
            }
        }

        // If original URL provided, return it
        if ($original_url) {
            return $original_url;
        }

        // Generate expected thumbnail URL from master-addons.com
        $kit_version = $this->get_kit_version($kit_name);
        return "https://master-addons.com/templates-kit/{$kit_slug}{$kit_version}/{$template_slug}.jpg";
    }

    /**
     * Get kit version suffix for URL generation
     */
    private function get_kit_version($kit_name)
    {
        // Common version patterns for kits
        $version_patterns = [
            'business-agency' => '-v1',
            'restaurant' => '-v2',
            'portfolio' => '-v1',
            'ecommerce' => '-v3'
        ];

        $kit_slug = sanitize_title($kit_name);
        return $version_patterns[$kit_slug] ?? '-v1';
    }

    /**
     * Get cache statistics
     */
    public function get_cache_stats()
    {
        $stats = [
            'cache_dir_exists' => file_exists($this->cache_dir),
            'cache_size' => $this->get_directory_size($this->cache_dir),
            'last_update' => get_transient('jltma_templates_last_cache_update'),
            'template_types' => [],
            'next_scheduled_update' => wp_next_scheduled('jltma_templates_cache_update'),
            'total_kits' => 0,
            'total_templates' => 0
        ];

        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
        $total_templates = 0;

        foreach ($template_types as $type) {
            $template_count = 0;
            $image_count = 0;

            // Count file cache if available
            if ($this->is_file_cache_available()) {
                $template_count = count(glob($this->cache_dir . "{$type}/templates/template-*.json"));
                $image_count = count(glob($this->cache_dir . "{$type}/images/*"));
            } else {
                // Count transient cache
                $cached_templates = get_transient("jltma_templates_{$type}");
                if ($cached_templates && is_array($cached_templates)) {
                    $template_count = count($cached_templates);
                }
            }

            $stats['template_types'][$type] = [
                'templates' => $template_count,
                'images' => $image_count
            ];

            $total_templates += $template_count;
        }

        // For template kits, we'll count unique kits from cached data
        $stats['total_kits'] = $this->count_cached_kits();
        $stats['total_templates'] = $total_templates;

        return $stats;
    }

    /**
     * Count cached template kits
     */
    private function count_cached_kits()
    {
        $kit_count = 0;

        // If using file cache, look for kit manifest files
        if ($this->is_file_cache_available()) {
            $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
            foreach ($template_types as $type) {
                $templates_file = $this->cache_dir . "{$type}/templates/templates.json";
                if (file_exists($templates_file)) {
                    $templates_data = $this->read_cache_file($templates_file);
                    if ($templates_data && is_array($templates_data)) {
                        $kit_count += count($templates_data);
                    }
                }
            }
        } else {
            // Count from transients
            $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];
            foreach ($template_types as $type) {
                $cached_templates = get_transient("jltma_templates_{$type}");
                if ($cached_templates && is_array($cached_templates)) {
                    $kit_count += count($cached_templates);
                }
            }
        }

        return $kit_count;
    }

    /**
     * Get directory size in bytes
     */
    private function get_directory_size($dir)
    {
        $size = 0;
        if (file_exists($dir)) {
            foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)) as $file) {
                if ($file->isFile()) {
                    $size += $file->getSize();
                }
            }
        }
        return $size;
    }

    /**
     * Check if file cache is available
     */
    private function is_file_cache_available()
    {
        return file_exists($this->cache_dir) && is_writable($this->cache_dir);
    }

    /**
     * Get cached templates using transients (fallback method)
     */
    private function get_transient_cached_templates($tab, $force_refresh = false)
    {
        $transient_key = "jltma_templates_{$tab}";
        $meta_transient_key = "jltma_templates_{$tab}_meta";

        // Check if cache exists and is valid
        if (!$force_refresh) {
            $cached_meta = get_transient($meta_transient_key);
            if ($cached_meta && (time() - $cached_meta['timestamp']) < $this->cache_expiry) {
                $cached_data = get_transient($transient_key);
                if ($cached_data !== false) {
                    // Update thumbnail URLs to use cache folder first for transient cached templates
                    foreach ($cached_data as &$template) {
                        $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                        if ($cached_thumbnail) {
                            $template['thumbnail'] = $cached_thumbnail;
                        }
                    }
                    return $cached_data;
                }
            }
        }

        // Fetch fresh data from remote API
        $fresh_data = $this->fetch_remote_templates($tab);

        if ($fresh_data !== false) {
            // Update thumbnail URLs to use cache folder first for fresh transient templates
            foreach ($fresh_data as &$template) {
                $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                if ($cached_thumbnail) {
                    $template['thumbnail'] = $cached_thumbnail;
                }
            }

            // Cache the data using transients
            set_transient($transient_key, $fresh_data, $this->cache_expiry);
            set_transient($meta_transient_key, ['timestamp' => time()], $this->cache_expiry);

            return $fresh_data;
        }

        // Return cached data even if expired
        $fallback_transient_data = get_transient($transient_key);
        if ($fallback_transient_data !== false) {
            // Update thumbnail URLs to use cache folder first for expired transient templates
            foreach ($fallback_transient_data as &$template) {
                $cached_thumbnail = $this->get_kit_thumbnail_url('', $template['title'], $template['thumbnail']);
                if ($cached_thumbnail) {
                    $template['thumbnail'] = $cached_thumbnail;
                }
            }
        }
        return $fallback_transient_data;
    }

    /**
     * Get cached categories using transients (fallback method)
     */
    private function get_transient_cached_categories($tab, $force_refresh = false)
    {
        $transient_key = "jltma_categories_{$tab}";
        $meta_transient_key = "jltma_categories_{$tab}_meta";

        if (!$force_refresh) {
            $cached_meta = get_transient($meta_transient_key);
            if ($cached_meta && (time() - $cached_meta['timestamp']) < $this->cache_expiry) {
                $cached_data = get_transient($transient_key);
                if ($cached_data !== false) {
                    return $cached_data;
                }
            }
        }

        $fresh_data = $this->fetch_remote_categories($tab);

        if ($fresh_data !== false) {
            set_transient($transient_key, $fresh_data, $this->cache_expiry);
            set_transient($meta_transient_key, ['timestamp' => time()], $this->cache_expiry);
            return $fresh_data;
        }

        return get_transient($transient_key);
    }

    /**
     * Get cached keywords using transients (fallback method)
     */
    private function get_transient_cached_keywords($tab, $force_refresh = false)
    {
        $transient_key = "jltma_keywords_{$tab}";
        $meta_transient_key = "jltma_keywords_{$tab}_meta";

        if (!$force_refresh) {
            $cached_meta = get_transient($meta_transient_key);
            if ($cached_meta && (time() - $cached_meta['timestamp']) < $this->cache_expiry) {
                $cached_data = get_transient($transient_key);
                if ($cached_data !== false) {
                    return $cached_data;
                }
            }
        }

        $fresh_data = $this->fetch_remote_keywords($tab);

        if ($fresh_data !== false) {
            set_transient($transient_key, $fresh_data, $this->cache_expiry);
            set_transient($meta_transient_key, ['timestamp' => time()], $this->cache_expiry);
            return $fresh_data;
        }

        return get_transient($transient_key);
    }

    /**
     * Clear transient cache (fallback method)
     */
    private function clear_transient_cache()
    {
        $template_types = ['master_section', 'master_pages', 'master_popups', 'master_headers', 'master_footers'];

        foreach ($template_types as $tab) {
            delete_transient("jltma_templates_{$tab}");
            delete_transient("jltma_templates_{$tab}_meta");
            delete_transient("jltma_categories_{$tab}");
            delete_transient("jltma_categories_{$tab}_meta");
            delete_transient("jltma_keywords_{$tab}");
            delete_transient("jltma_keywords_{$tab}_meta");
        }
    }

    /**
     * Preload popular templates in background
     */
    public function preload_popular_templates()
    {
        $popular_tabs = ['master_section', 'master_headers'];

        foreach ($popular_tabs as $tab) {
            if (!get_transient("jltma_preload_{$tab}")) {
                wp_schedule_single_event(time() + 60, 'jltma_background_preload', [$tab]);
                set_transient("jltma_preload_{$tab}", true, HOUR_IN_SECONDS);
            }
        }
    }

    /**
     * Background preload handler (callback for jltma_background_preload action)
     *
     * @param string $tab Template tab to preload
     */
    public function do_background_preload($tab)
    {
        if (!empty($tab)) {
            $this->get_cached_templates($tab, true);
        }
    }

    /**
     * AJAX handler for cache preloading
     */
    public function preload_cache_ajax()
    {
        if (!current_user_can('manage_options')) {
            wp_die(-1);
        }

        if (!check_ajax_referer('jltma_preload_cache_nonce', 'security', false)) {
            wp_die(-1);
        }

        $tab = sanitize_text_field( wp_unslash( $_POST['tab'] ?? '' ) );

        if (empty($tab)) {
            wp_send_json_error('Invalid tab');
        }

        // Preload in background
        $this->get_cached_templates($tab, true);

        wp_send_json_success('Cache preloaded for ' . $tab);
    }

    /**
     * Get singleton instance
     */
    public static function get_instance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// Initialize templates cache manager
Template_Library_Cache::get_instance();

```
