<?php

namespace FleekDash\Services;

use FleekDash\Core\DeadlockSafeQuery;
use FleekDash\Core\CacheInvalidationManager;

// Load debug helpers
if (!\function_exists('fleekdash_log_info')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

/**
 * Analytics Cache Service
 *
 * Specialized caching service for analytics data with intelligent TTL
 * and cache invalidation strategies.
 *
 * @since 2.2.0 - Refactored to use DeadlockSafeQuery for deadlock resilience
 */
class AnalyticsCacheService
{

    /**
     * Cache key prefix
     */
    const CACHE_PREFIX = 'fleekdash_analytics_';

    /**
     * Default cache TTL configurations (in seconds)
     */
    const CACHE_TTLS = [
        'real_time' => 10,        // 10 seconds for real-time data
        'kpi_summary' => 60,      // 1 minute for KPI summaries (faster updates)
        'visitors' => 60,         // 1 minute for visitor data (faster updates)
        'pageviews' => 60,        // 1 minute for pageview data (faster updates)
        'popular_posts' => 300,   // 5 minutes for popular posts
        'user_registrations' => 300, // 5 minutes for user registration data
        'referrers' => 1800,      // 30 minutes for referrer data
        'devices' => 1800,        // 30 minutes for device data
        'geographic' => 1800,     // 30 minutes for geographic data
        'default' => 60           // 1 minute default for development
    ];

    /**
     * Get cache key for analytics data
     */
    public function get_cache_key($endpoint, $params = [])
    {
        $key_parts = [self::CACHE_PREFIX, $endpoint];

        if (!empty($params)) {
            // Sort params to ensure consistent cache keys
            ksort($params);
            $key_parts[] = md5(serialize($params));
        }

        return implode('_', $key_parts);
    }

    /**
     * Get cached data with fallback to callback
     */
    public function get_cached_data($endpoint, $params, $callback, $custom_ttl = null)
    {
        $cache_key = $this->get_cache_key($endpoint, $params);
        $cached = \get_transient($cache_key);

        if ($cached !== false) {
            // Add cache hit metadata
            if (is_array($cached)) {
                $cached['_cache_hit'] = true;
                $cached['_cached_at'] = \get_option($cache_key . '_timestamp', time());
            }
            return $cached;
        }

        // Execute callback to get fresh data
        $data = call_user_func($callback);

        // Add cache metadata
        if (is_array($data)) {
            $data['_cache_hit'] = false;
            $data['_cached_at'] = time();
        }

        // Determine TTL
        $ttl = $custom_ttl ?? $this->get_ttl_for_endpoint($endpoint);

        // Store in cache
        \set_transient($cache_key, $data, $ttl);
        \update_option($cache_key . '_timestamp', time(), false);

        return $data;
    }

    /**
     * Get TTL for specific endpoint
     */
    private function get_ttl_for_endpoint($endpoint)
    {
        return self::CACHE_TTLS[$endpoint] ?? self::CACHE_TTLS['default'];
    }

    /**
     * Invalidate cache for specific endpoint
     */
    public function invalidate_cache($endpoint, $params = [])
    {
        $cache_key = $this->get_cache_key($endpoint, $params);
        \delete_transient($cache_key);
        \delete_option($cache_key . '_timestamp');

        return true;
    }

    /**
     * Invalidate all analytics cache
     *
     * Uses deadlock-safe queries to prevent database deadlocks
     * during concurrent cache invalidation operations.
     *
     * @return bool Success
     */
    public function invalidate_all_cache()
    {
        global $wpdb;

        $queries = [
            // Delete all transients with our prefix
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s
                 OR option_name LIKE %s",
                '_transient_' . self::CACHE_PREFIX . '%',
                '_transient_timeout_' . self::CACHE_PREFIX . '%'
            ),
            // Delete timestamp options
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s",
                self::CACHE_PREFIX . '%_timestamp'
            ),
        ];

        $result = DeadlockSafeQuery::batchDelete($queries, 'Invalidate all analytics cache');

        return $result['success'];
    }

    /**
     * Get cache statistics
     */
    public function get_cache_stats()
    {
        global $wpdb;

        // Count cached items
        $cache_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->options}
             WHERE option_name LIKE %s",
            '_transient_' . self::CACHE_PREFIX . '%'
        ));

        // Get cache size (approximate)
        $cache_size_result = $wpdb->get_var($wpdb->prepare(
            "SELECT SUM(LENGTH(option_value)) FROM {$wpdb->options}
             WHERE option_name LIKE %s",
            '_transient_' . self::CACHE_PREFIX . '%'
        ));

        $cache_size = $cache_size_result ? round($cache_size_result / 1024, 2) : 0; // KB

        // Get oldest and newest cache entries
        $oldest_cache = $wpdb->get_var($wpdb->prepare(
            "SELECT MIN(option_value) FROM {$wpdb->options}
             WHERE option_name LIKE %s",
            self::CACHE_PREFIX . '%_timestamp'
        ));

        $newest_cache = $wpdb->get_var($wpdb->prepare(
            "SELECT MAX(option_value) FROM {$wpdb->options}
             WHERE option_name LIKE %s",
            self::CACHE_PREFIX . '%_timestamp'
        ));

        return [
            'total_cached_items' => (int) $cache_count,
            'cache_size_kb' => $cache_size,
            'oldest_cache_timestamp' => $oldest_cache ? (int) $oldest_cache : null,
            'newest_cache_timestamp' => $newest_cache ? (int) $newest_cache : null,
            'cache_ttls' => self::CACHE_TTLS,
            'cache_prefix' => self::CACHE_PREFIX
        ];
    }

    /**
     * Warm up cache for common analytics queries
     */
    public function warm_cache()
    {
        // Common time ranges to pre-cache
        $time_ranges = ['24h', '7d', '30d'];
        $endpoints = ['visitors', 'pageviews', 'kpi_summary'];

        $warmed = 0;

        foreach ($endpoints as $endpoint) {
            foreach ($time_ranges as $range) {
                $params = ['range' => $range];
                $cache_key = $this->get_cache_key($endpoint, $params);

                // Only warm if not already cached
                if (\get_transient($cache_key) === false) {
                    // Trigger cache population via a lightweight background request
                    $this->schedule_cache_warming($endpoint, $params);
                    $warmed++;
                }
            }
        }

        return [
            'warmed_entries' => $warmed,
            'total_attempted' => count($endpoints) * count($time_ranges)
        ];
    }

    /**
     * Schedule cache warming (could be enhanced with WP Cron)
     */
    private function schedule_cache_warming($endpoint, $params)
    {
        // For now, just mark for warming
        // In a full implementation, this could use WP Cron or background processing
        $warming_key = 'warming_' . $this->get_cache_key($endpoint, $params);
        \set_transient($warming_key, time(), 300); // 5 minutes

        return true;
    }

    /**
     * Smart cache invalidation based on WordPress events
     *
     * @since 2.2.0 - Delegates to CacheInvalidationManager for centralized,
     *                debounced cache invalidation that prevents deadlocks.
     */
    public function setup_smart_invalidation()
    {
        // Use the centralized CacheInvalidationManager for all cache invalidation
        // This prevents duplicate invalidations and uses deadlock-safe queries
        CacheInvalidationManager::setupHooks();

        // Setup cache warm-up scheduler (still handled by this service)
        $this->setup_cache_warmup();
    }

    /**
     * Setup cache warm-up scheduler with WP-Cron
     */
    public function setup_cache_warmup()
    {
        // Register the warm-up cron job
        \add_action('fleekdash_analytics_cache_warmup', [$this, 'warm_up_cache']);

        // Schedule the warm-up if not already scheduled
        if (!wp_next_scheduled('fleekdash_analytics_cache_warmup')) {
            \wp_schedule_event(time(), 'hourly', 'fleekdash_analytics_cache_warmup');
        }

        // Add custom cron schedule for every 30 minutes
        \add_filter('cron_schedules', [$this, 'add_custom_cron_schedules']);
    }

    /**
     * Add custom cron schedules
     */
    public function add_custom_cron_schedules($schedules)
    {
        $schedules['thirty_minutes'] = [
            'interval' => 30 * MINUTE_IN_SECONDS,
            'display' => __('Every 30 Minutes', 'fleekdash')
        ];

        return $schedules;
    }

    /**
     * Warm up analytics cache proactively
     */
    public function warm_up_cache()
    {
        fleekdash_log_debug('FleekDash: Starting cache warm-up');

        // Define the most important endpoints to warm up
        $endpoints_to_warm = [
            'kpi_summary' => [['range' => '30d'], ['range' => '7d']],
            'visitors' => [['range' => '30d'], ['range' => '7d']],
            'pageviews' => [['range' => '30d'], ['range' => '7d']],
            'popular_posts' => [['range' => '30d'], ['range' => '7d']],
            'real_time' => [[]],
        ];

        $warmed_count = 0;
        $start_time = microtime(true);

        foreach ($endpoints_to_warm as $endpoint => $param_sets) {
            foreach ($param_sets as $params) {
                try {
                    // Check if cache already exists and is fresh
                    $cache_key = $this->get_cache_key($endpoint, $params);
                    $existing = \get_transient($cache_key);

                    if ($existing === false) {
                        // Cache is expired, warm it up with placeholder
                        $ttl = $this->get_ttl_for_endpoint($endpoint);
                        $data = ['warmed' => true, 'timestamp' => time(), 'params' => $params];
                        \set_transient($cache_key, $data, $ttl);
                        $warmed_count++;

                        // Small delay to prevent server overload
                        usleep(50000); // 50ms
                    }
                } catch (\Exception $e) {
                    fleekdash_log_error("FleekDash: Cache warm-up failed for {$endpoint}: " . $e->getMessage());
                }
            }
        }

        $execution_time = microtime(true) - $start_time;

        fleekdash_log_info("FleekDash: Cache warm-up completed. Warmed {$warmed_count} endpoints in " . round($execution_time, 2) . "s");

        // Store warm-up stats
        \update_option('fleekdash_last_cache_warmup', [
            'timestamp' => \current_time('mysql'),
            'warmed_count' => $warmed_count,
            'execution_time' => $execution_time
        ], false);
    }

    /**
     * Get cache warm-up statistics
     */
    public function get_warmup_stats()
    {
        $last_warmup = \get_option('fleekdash_last_cache_warmup', []);
        $next_scheduled = wp_next_scheduled('fleekdash_analytics_cache_warmup');

        return [
            'last_warmup' => $last_warmup,
            'next_scheduled' => $next_scheduled ? date('Y-m-d H:i:s', $next_scheduled) : null,
            'is_scheduled' => $next_scheduled !== false,
            'schedule_interval' => 'hourly'
        ];
    }

    /**
     * Invalidate content-related caches
     *
     * @since 2.2.0 - Delegates to CacheInvalidationManager for debounced,
     *                deadlock-safe invalidation.
     */
    public function invalidate_content_caches()
    {
        // Delegate to centralized manager (handles debouncing and deadlock safety)
        CacheInvalidationManager::getInstance()->invalidateContentCache();
    }

    /**
     * Invalidate user-related caches
     *
     * @since 2.2.0 - Delegates to CacheInvalidationManager for debounced,
     *                deadlock-safe invalidation.
     */
    public function invalidate_user_caches()
    {
        // Delegate to centralized manager (handles debouncing and deadlock safety)
        CacheInvalidationManager::getInstance()->onUserChange();
    }

    /**
     * Invalidate all variations of an endpoint (different time ranges, params)
     *
     * @since 2.2.0 - Uses DeadlockSafeQuery to prevent database deadlocks
     *
     * @param string $endpoint Endpoint name to invalidate
     * @return int|false Number of deleted entries or false on failure
     */
    public function invalidate_endpoint_variations($endpoint)
    {
        global $wpdb;

        // Delete all transients that start with our endpoint
        // Note: The cache key format includes double underscore: fleekdash_analytics__endpoint_hash
        $pattern = '_transient_' . self::CACHE_PREFIX . '_' . $endpoint . '%';
        $timeout_pattern = '_transient_timeout_' . self::CACHE_PREFIX . '_' . $endpoint . '%';
        $timestamp_pattern = self::CACHE_PREFIX . '_' . $endpoint . '%_timestamp';

        $queries = [
            // Delete transients and their timeouts
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s
                 OR option_name LIKE %s",
                $pattern,
                $timeout_pattern
            ),
            // Delete timestamp options
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s",
                $timestamp_pattern
            ),
        ];

        $result = DeadlockSafeQuery::batchDelete($queries, "Invalidate endpoint: {$endpoint}");

        // Log for debugging
        \fleekdash_log_debug("🔄 FleekDash: Invalidated {$result['total_deleted']} cache entries for endpoint: {$endpoint}");

        return $result['success'] ? $result['total_deleted'] : false;
    }

    /**
     * Get cache health score (0-100)
     */
    public function get_cache_health_score()
    {
        $stats = $this->get_cache_stats();
        $score = 100;

        // Deduct points for issues
        if ($stats['total_cached_items'] === 0) {
            $score -= 50; // No cache items
        }

        if ($stats['cache_size_kb'] > 10240) { // > 10MB
            $score -= 20; // Cache too large
        }

        // Check for stale cache (older than 24 hours)
        if (
            $stats['oldest_cache_timestamp'] &&
            (time() - $stats['oldest_cache_timestamp']) > 86400
        ) {
            $score -= 15; // Stale cache entries
        }

        return max(0, $score);
    }

    /**
     * Cleanup expired cache entries
     *
     * @since 2.2.0 - Uses DeadlockSafeQuery to prevent database deadlocks
     *
     * @return int|false Number of deleted entries or false on failure
     */
    public function cleanup_expired_cache()
    {
        global $wpdb;

        // WordPress automatically handles transient cleanup, but we can force it
        $current_time = time();

        $queries = [
            // Delete expired transients
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s
                 AND CAST(option_value AS UNSIGNED) < %d",
                '_transient_timeout_' . self::CACHE_PREFIX . '%',
                $current_time
            ),
            // Delete orphaned transient values using derived table
            // to avoid MySQL "can't specify target table for update in FROM clause" error
            $wpdb->prepare(
                "DELETE o1 FROM {$wpdb->options} o1
                 LEFT JOIN (
                     SELECT CONCAT('_transient_', SUBSTRING(option_name, 19)) as valid_name
                     FROM {$wpdb->options} o2
                     WHERE o2.option_name LIKE %s
                 ) as valid ON o1.option_name = valid.valid_name
                 WHERE o1.option_name LIKE %s
                 AND valid.valid_name IS NULL",
                '_transient_timeout_' . self::CACHE_PREFIX . '%',
                '_transient_' . self::CACHE_PREFIX . '%'
            ),
        ];

        $result = DeadlockSafeQuery::batchDelete($queries, 'Cleanup expired analytics cache');

        return $result['success'] ? $result['total_deleted'] : false;
    }

    /**
     * Set cache data with TTL
     *
     * @param string $key Cache key
     * @param mixed $data Data to cache
     * @param int $ttl Time to live in seconds
     * @return bool Success
     */
    public function set($key, $data, $ttl = 300)
    {
        return \set_transient($key, $data, $ttl);
    }

    /**
     * Get cache data
     *
     * @param string $key Cache key
     * @return mixed|false Cached data or false if not found
     */
    public function get($key)
    {
        return \get_transient($key);
    }

    /**
     * Delete cache data
     *
     * @param string $key Cache key
     * @return bool Success
     */
    public function delete($key)
    {
        return \delete_transient($key);
    }

    /**
     * Get cache metadata
     *
     * @param string $key Cache key
     * @return array|false Metadata or false if not found
     */
    public function get_metadata($key)
    {
        $timestamp = \get_option($key . '_timestamp', false);

        if ($timestamp === false) {
            return false;
        }

        return [
            'timestamp' => $timestamp,
            'key' => $key,
            'exists' => \get_transient($key) !== false
        ];
    }

    /**
     * Clear all analytics cache
     *
     * @since 2.2.0 - Uses DeadlockSafeQuery to prevent database deadlocks
     *
     * @return int Number of items deleted
     */
    public function clear_all()
    {
        global $wpdb;

        $queries = [
            // Delete all FleekDash analytics transients
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s
                 OR option_name LIKE %s",
                '_transient_' . self::CACHE_PREFIX . '%',
                '_transient_timeout_' . self::CACHE_PREFIX . '%'
            ),
            // Also delete bridge cache
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options}
                 WHERE option_name LIKE %s
                 OR option_name LIKE %s",
                '_transient_bridge_%',
                '_transient_timeout_bridge_%'
            ),
        ];

        $result = DeadlockSafeQuery::batchDelete($queries, 'Clear all analytics cache');

        return $result['total_deleted'];
    }
}
