<?php
declare(strict_types=1);

namespace Ekesto\CacheWarmup\Settings;

/**
 * Cache store and invalidation hooks for the priority-pages REST payload.
 */
final class PriorityPagesCache
{
    private const OBJECT_CACHE_GROUP = 'ekesto-cache-warmup';
    private const OBJECT_CACHE_KEY = 'priority-pages-v3';
    private const TRANSIENT_KEY = 'ekesto_cache_warmup_priority_pages_v3';
    private const LEGACY_OBJECT_CACHE_KEYS = ['priority-pages-v1', 'priority-pages-v2'];
    private const LEGACY_TRANSIENT_KEYS = [
        'ekesto_cache_warmup_priority_pages_v1',
        'ekesto_cache_warmup_priority_pages_v2',
    ];
    private const TRANSIENT_TTL = 300;

    /**
     * @var array<int,string>
     */
    private const PERMALINK_AFFECTING_OPTIONS = [
        'permalink_structure',
        'show_on_front',
        'page_on_front',
        'page_for_posts',
        'home',
        'siteurl',
    ];

    /**
     * @var bool
     */
    private static bool $hooksRegistered = false;

    /**
     * Register cache invalidation hooks once per request.
     *
     * @return void
     */
    public static function register(): void
    {
        if (self::$hooksRegistered) {
            return;
        }

        add_action('save_post_page', [self::class, 'invalidate'], 10, 0);
        add_action('deleted_post', [self::class, 'onDeletedPost'], 10, 2);
        add_action('trashed_post', [self::class, 'onTrashedPost'], 10, 1);
        add_action('untrashed_post', [self::class, 'onUntrashedPost'], 10, 1);
        add_action('transition_post_status', [self::class, 'onTransitionPostStatus'], 10, 3);
        add_action('updated_option', [self::class, 'onUpdatedOption'], 10, 3);

        self::$hooksRegistered = true;
    }

    /**
     * Return cached priority page rows if available.
     *
     * @return array<int,array<string,mixed>>|null
     */
    public static function get(): ?array
    {
        $cached = wp_cache_get(self::OBJECT_CACHE_KEY, self::OBJECT_CACHE_GROUP);
        if (is_array($cached)) {
            return $cached;
        }

        $cached = get_transient(self::TRANSIENT_KEY);
        if (!is_array($cached)) {
            return null;
        }

        wp_cache_set(
            self::OBJECT_CACHE_KEY,
            $cached,
            self::OBJECT_CACHE_GROUP,
            self::TRANSIENT_TTL
        );

        return $cached;
    }

    /**
     * Persist priority page rows in object cache and transient storage.
     *
     * @param array<int,array<string,mixed>> $rows
     * @return void
     */
    public static function set(array $rows): void
    {
        wp_cache_set(
            self::OBJECT_CACHE_KEY,
            $rows,
            self::OBJECT_CACHE_GROUP,
            self::TRANSIENT_TTL
        );
        set_transient(self::TRANSIENT_KEY, $rows, self::TRANSIENT_TTL);
    }

    /**
     * Invalidate cached page rows.
     *
     * @return void
     */
    public static function invalidate(): void
    {
        wp_cache_delete(self::OBJECT_CACHE_KEY, self::OBJECT_CACHE_GROUP);
        delete_transient(self::TRANSIENT_KEY);

        foreach (self::LEGACY_OBJECT_CACHE_KEYS as $cacheKey) {
            wp_cache_delete($cacheKey, self::OBJECT_CACHE_GROUP);
        }

        foreach (self::LEGACY_TRANSIENT_KEYS as $transientKey) {
            delete_transient($transientKey);
        }
    }

    /**
     * Invalidate cache when a page is deleted.
     *
     * @param int $postId
     * @param \WP_Post|false|null $post
     * @return void
     */
    public static function onDeletedPost(int $postId, $post): void
    {
        if ($post instanceof \WP_Post && $post->post_type === 'page') {
            self::invalidate();
            return;
        }

        if (get_post_type($postId) === 'page') {
            self::invalidate();
        }
    }

    /**
     * Invalidate cache when a page is moved to trash.
     *
     * @param int $postId
     * @return void
     */
    public static function onTrashedPost(int $postId): void
    {
        if (get_post_type($postId) === 'page') {
            self::invalidate();
        }
    }

    /**
     * Invalidate cache when a page is restored from trash.
     *
     * @param int $postId
     * @return void
     */
    public static function onUntrashedPost(int $postId): void
    {
        if (get_post_type($postId) === 'page') {
            self::invalidate();
        }
    }

    /**
     * Invalidate cache on page status transitions.
     *
     * @param string $newStatus
     * @param string $oldStatus
     * @param \WP_Post $post
     * @return void
     */
    public static function onTransitionPostStatus(string $newStatus, string $oldStatus, \WP_Post $post): void
    {
        if ($newStatus !== $oldStatus && $post->post_type === 'page') {
            self::invalidate();
        }
    }

    /**
     * Invalidate cache when permalink-related options change.
     *
     * @param string $option
     * @param mixed $oldValue
     * @param mixed $newValue
     * @return void
     */
    public static function onUpdatedOption(string $option, $oldValue, $newValue): void
    {
        if (!in_array($option, self::PERMALINK_AFFECTING_OPTIONS, true)) {
            return;
        }

        if ($oldValue !== $newValue) {
            self::invalidate();
        }
    }
}
