<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Caching\Adapters;

use Ekesto\CacheInvalidator\Integrations\Caching\AdapterSignalGuard;
use Ekesto\CacheInvalidator\Integrations\Caching\CacheAdapterInterface;
use Ekesto\CacheInvalidator\Integrations\Caching\CacheAdapterUrlNormalizer;
use Ekesto\CacheInvalidator\Integrations\Caching\EmitsFullInvalidationFromCacheClear;
use Ekesto\CacheInvalidator\Integrations\Caching\EmitsSingleTargetCacheAdapterInvalidation;
use Ekesto\CacheInvalidator\Integrations\Caching\UrlPurgeCapableAdapterInterface;

/**
 * WP Fastest Cache adapter.
 *
 * Uses WP Fastest Cache public functions for exact URL and full cache purge.
 */
final class WpFastestCacheAdapter implements CacheAdapterInterface, UrlPurgeCapableAdapterInterface
{
    use EmitsFullInvalidationFromCacheClear;
    use EmitsSingleTargetCacheAdapterInvalidation;

    /**
     * Returns a human-readable adapter label.
     *
     * @return string
     */
    public function getLabel(): string
    {
        return 'WP Fastest Cache';
    }

    /**
     * Returns a unique adapter key.
     *
     * @return string
     */
    public function getKey(): string
    {
        return 'wp-fastest-cache';
    }

    /**
     * Returns whether the precise URL and full purge APIs are available.
     *
     * @return bool
     */
    public function isAvailable(): bool
    {
        return function_exists('wpfc_clear_cache_by_url')
            && function_exists('wpfc_clear_all_cache');
    }

    /**
     * Returns whether WP Fastest Cache supports runtime cache-status detection.
     *
     * @return bool
     */
    public function supportsCacheStatusDetection(): bool
    {
        return true;
    }

    /**
     * Returns whether WP Fastest Cache page caching is enabled.
     *
     * @return bool
     */
    public function isCacheEnabled(): bool
    {
        $stored = get_option('WpFastestCache', '');
        if (is_string($stored)) {
            $stored = json_decode($stored, true);
        } elseif (is_object($stored)) {
            $stored = get_object_vars($stored);
        }

        return is_array($stored) && !empty($stored['wpFastestCacheStatus']);
    }

    /**
     * Registers WP Fastest Cache purge-completion signals.
     *
     * @return void
     */
    public function registerHooks(): void
    {
        add_action('wpfc_delete_cache', [$this, 'handleFullClear'], 10, 0);

        // WP Fastest Cache removes the entry at priority 10.
        add_action('wpfc_clear_cache_by_url', [$this, 'handleUrlPurge'], 20, 1);
    }

    /**
     * Handles a completed full purge initiated outside Cache Autopilot.
     *
     * WP Fastest Cache may clear itself inside WordPress upgrader callbacks.
     * Cache Autopilot owns deferred lifecycle handling for those requests, so
     * adapter-level operational work must not run there.
     *
     * @return void
     */
    public function handleFullClear(): void
    {
        if (function_exists('doing_action') && doing_action('upgrader_process_complete')) {
            return;
        }

        $this->emitFullInvalidationFromCacheClear('wp_fastest_cache');
    }

    /**
     * Handles a completed URL purge initiated outside Cache Autopilot.
     *
     * @param mixed $url Purged URL payload.
     * @return void
     */
    public function handleUrlPurge($url): void
    {
        $hook = (string) current_filter();
        $normalizedUrl = $this->normalizePurgeUrl($url);
        if ($normalizedUrl === '') {
            $this->logIgnoredSingleTarget($hook, 'invalid_or_cross_origin_url', [
                'request_type' => 'public_url_api',
                'classification_path' => 'url',
            ]);
            return;
        }

        $validated = [
            'url' => $normalizedUrl,
            'post_id' => 0,
            'post_type' => '',
            'mode' => 'url',
        ];

        $this->emitSingleTargetInvalidation(
            $hook,
            [$normalizedUrl],
            $validated,
            [
                'request_type' => 'public_url_api',
                'classification_path' => 'url',
            ],
            [
                'trigger_class' => 'cache_adapter',
                'trigger_event' => 'wp_fastest_cache_url_purge',
                'trigger_post_id' => 0,
                'trigger_post_type' => '',
                'reason' => 'supported_cache_plugin_single_target_purge',
                'source' => 'wp_fastest_cache',
                'trusted_source' => 'cache_adapter_single_target',
                'resolution' => 'TARGETED',
                'intent' => 'TARGETED',
            ]
        );
    }

    /**
     * Returns the adapter source key used in single-target signal logs.
     *
     * @return string
     */
    protected function getSingleTargetSignalSource(): string
    {
        return 'wp_fastest_cache';
    }

    /**
     * Purges cache for a specific post ID through its exact permalink URL.
     *
     * @param int $postId Post/page ID to purge.
     * @return bool True when the purge call was executed.
     */
    public function purgePostId(int $postId): bool
    {
        $postId = absint($postId);
        if ($postId <= 0) {
            return false;
        }

        $url = get_permalink($postId);
        if (!is_string($url) || $url === '') {
            return false;
        }

        return $this->purgeUrl($url);
    }

    /**
     * Purges cache for a specific absolute URL.
     *
     * @param string $url Absolute URL.
     * @return bool True when the purge call was executed.
     */
    public function purgeUrl(string $url): bool
    {
        if (!$this->isAvailable()) {
            return false;
        }

        $url = $this->normalizePurgeUrl($url);
        if ($url === '') {
            return false;
        }

        AdapterSignalGuard::runWithSuppressedSignals(static function () use ($url): void {
            wpfc_clear_cache_by_url($url);
        });

        return true;
    }

    /**
     * Purges all cache entries for the active site.
     *
     * @return bool True when the purge call was executed successfully.
     */
    public function purgeAll(): bool
    {
        if (!$this->isAvailable()) {
            return false;
        }

        $result = AdapterSignalGuard::runWithSuppressedSignals(static function () {
            return wpfc_clear_all_cache();
        });

        return !is_array($result) || !isset($result['success']) || $result['success'] !== false;
    }

    /**
     * Normalizes one exact, same-origin URL supported by WP Fastest Cache.
     *
     * WP Fastest Cache currently discards query strings while resolving its
     * cache path, so query targets must fail closed instead of purging another
     * entry and reporting success.
     *
     * @param mixed $candidate URL candidate.
     * @return string
     */
    private function normalizePurgeUrl($candidate): string
    {
        $url = CacheAdapterUrlNormalizer::normalize($candidate);
        if ($url === '') {
            return '';
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts) || !empty($parts['query'])) {
            return '';
        }

        return $url;
    }
}
