<?php

namespace FleekDash\Integrations\Core;

defined('ABSPATH') || exit;

/**
 * High-performance cache system for integrations
 */
class Cache
{
    private const PREFIX = 'fleekdash_integrations_';
    private const TTL_SHORT = 300;    // 5 minutes
    private const TTL_MEDIUM = 3600;  // 1 hour
    private const TTL_LONG = 86400;   // 24 hours

    private array $memoryCache = [];
    private bool $useObjectCache = false;
    private bool $useTransients = true;

    public function __construct()
    {
        if (function_exists('wp_using_ext_object_cache')) {
            $this->useObjectCache = (bool) wp_using_ext_object_cache();
        } else {
            $this->useObjectCache = false;
        }
        $this->useTransients = !$this->useObjectCache;
    }

    /**
     * Get cached value with fallback strategy
     */
    public function get(string $key, $default = null)
    {
        $fullKey = self::PREFIX . $key;

        // Try memory cache first
        if (isset($this->memoryCache[$fullKey])) {
            return $this->memoryCache[$fullKey];
        }

        // Try object cache
        if ($this->useObjectCache) {
            $value = wp_cache_get($fullKey, 'fleekdash_integrations');
            if ($value !== false) {
                $this->memoryCache[$fullKey] = $value;
                return $value;
            }
        }

        // Try transients as fallback
        if ($this->useTransients) {
            $value = get_transient($fullKey);
            if ($value !== false) {
                $this->memoryCache[$fullKey] = $value;
                return $value;
            }
        }

        return $default;
    }

    /**
     * Set cached value with multiple strategies
     */
    public function set(string $key, $value, int $ttl = self::TTL_MEDIUM): bool
    {
        $fullKey = self::PREFIX . $key;

        // Set in memory cache
        $this->memoryCache[$fullKey] = $value;

        $success = true;

        // Set in object cache
        if ($this->useObjectCache) {
            $success = wp_cache_set($fullKey, $value, 'fleekdash_integrations', $ttl);
        }

        // Set in transients as fallback
        if ($this->useTransients) {
            $success = set_transient($fullKey, $value, $ttl);
        }

        return $success;
    }

    /**
     * Delete cached value
     */
    public function delete(string $key): bool
    {
        $fullKey = self::PREFIX . $key;

        // Remove from memory
        unset($this->memoryCache[$fullKey]);

        $success = true;

        // Remove from object cache
        if ($this->useObjectCache) {
            $success = wp_cache_delete($fullKey, 'fleekdash_integrations');
        }

        // Remove from transients
        if ($this->useTransients) {
            $success = delete_transient($fullKey);
        }

        return $success;
    }

    /**
     * Flush all caches
     */
    public function flush(): bool
    {
        $this->memoryCache = [];

        // Try to flush object cache if available
        if ($this->useObjectCache && function_exists('wp_cache_flush_group')) {
            wp_cache_flush_group('fleekdash_integrations');
        }

        return true;
    }

    /**
     * Remember pattern - get or compute and cache
     */
    public function remember(string $key, callable $callback, int $ttl = self::TTL_MEDIUM)
    {
        $value = $this->get($key);

        if ($value === null) {
            $value = $callback();
            $this->set($key, $value, $ttl);
        }

        return $value;
    }

    /**
     * Cache discovery results
     */
    public function cacheDiscovery(array $integrations): void
    {
        $this->set('discovered_integrations', $integrations, self::TTL_MEDIUM);
        $this->set('discovery_timestamp', time(), self::TTL_MEDIUM);
    }

    /**
     * Get discovery data
     */
    public function getDiscovery(): ?array
    {
        return $this->get('discovered_integrations');
    }

    /**
     * Check if discovery cache is valid
     */
    public function isDiscoveryValid(): bool
    {
        $timestamp = $this->get('discovery_timestamp');
        if (!$timestamp) {
            return false;
        }

        return (time() - $timestamp) < self::TTL_MEDIUM;
    }

    /**
     * Cache integration metadata
     */
    public function cacheIntegrationMeta(string $integrationId, array $meta): void
    {
        $this->set("integration_meta_{$integrationId}", $meta, self::TTL_LONG);
    }

    /**
     * Get cached integration metadata
     */
    public function getIntegrationMeta(string $integrationId): ?array
    {
        return $this->get("integration_meta_{$integrationId}");
    }

    /**
     * Invalidate all integration caches
     */
    public function invalidateAll(): void
    {
        // Clear memory cache
        $this->memoryCache = [];

        // Get all cached keys and remove them
        $keys = [
            'discovered_integrations',
            'discovery_timestamp'
        ];

        // Add dynamic keys
        $integrationIds = $this->get('integration_ids', []);
        foreach ($integrationIds as $id) {
            $keys[] = "integration_meta_{$id}";
            $keys[] = "integration_config_{$id}";
            $keys[] = "integration_status_{$id}";
        }

        foreach ($keys as $key) {
            $this->delete($key);
        }
    }

    /**
     * Warm up critical caches
     */
    public function warmUp(): void
    {
        // Pre-load frequently accessed data
        add_action('wp_loaded', function () {
            // Load integration discovery in background
            if (!$this->isDiscoveryValid()) {
                wp_schedule_single_event(time() + 30, 'fleekdash_warm_cache');
            }
        });
    }

    /**
     * Get cache statistics
     */
    public function getStats(): array
    {
        return [
            'memory_cache_size' => count($this->memoryCache),
            'using_object_cache' => $this->useObjectCache,
            'using_transients' => $this->useTransients,
            'discovery_valid' => $this->isDiscoveryValid(),
        ];
    }

    /**
     * Generate cache key
     */
    private function generateKey(string $key): string
    {
        return self::PREFIX . md5($key);
    }
}
