<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Async;

use Ekesto\CacheInvalidator\Integrations\Caching\AdapterManager;
use Ekesto\CacheInvalidator\Integrations\Caching\CacheAdapterInterface;
use Ekesto\CacheInvalidator\Core\EmissionBuilder;
use Ekesto\CacheInvalidator\Core\EmissionHelper;
use Ekesto\CacheInvalidator\Core\GlobalInvalidationExecutor;
use Ekesto\CacheInvalidator\Core\InvalidationEngine;
use Ekesto\CacheInvalidator\InvalidatorRuntime;
use Ekesto\CacheInvalidator\Core\PurgeCoordinator;
use Ekesto\CacheInvalidator\Core\TrustDecider;
use Ekesto\CacheInvalidator\Integrations\Multilingual\FanoutPolicy;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverFactory;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverInterface;
use Ekesto\CacheInvalidator\Resolvers\RelationshipGraphResolver;
use Ekesto\CacheInvalidator\Resolvers\RelationshipResolver;
use Ekesto\CacheInvalidator\Support\Debug;

/**
 * Processes the async invalidation queue.
 */
final class AsyncInvalidationProcessor
{
    private const LOCK_KEY = 'ekesto_eci_queue_lock';
    private const LOCK_TTL = 120; // seconds

    /**
     * Register the cron hook handler.
     *
     * @return void
     */
    public static function registerHooks(): void
    {
        add_action(AsyncInvalidationQueue::CRON_HOOK, [self::class, 'process']);
    }

    /**
     * Process all queued invalidation jobs.
     *
     * @return void
     */
    public static function process(): void
    {
        if (get_transient(self::LOCK_KEY)) {
            Debug::log('Async processor skipped: lock held by another process');
            return;
        }
        set_transient(self::LOCK_KEY, time(), self::LOCK_TTL);

        try {
            self::processInternal();
        } finally {
            delete_transient(self::LOCK_KEY);
        }
    }

    /**
     * Internal processing logic (called within lock).
     *
     * @return void
     */
    private static function processInternal(): void
    {
        $settingsRepository = InvalidatorRuntime::settingsRepository();
        $asyncDecision = $settingsRepository->getAsyncSchedulingDecision();
        // Async OFF prevents scheduling, not execution of already queued work.
        // The processor intentionally drains queued jobs regardless of async_enabled.

        if (!$settingsRepository->isInvalidationEnabled()) {
            $drained = AsyncInvalidationQueue::drain();
            AsyncInvalidationQueue::removeProcessed($drained['keys'], $drained['created_map'] ?? []);
            Debug::log('Async processor skipped: invalidation disabled', ['dropped_jobs' => count($drained['items'])]);
            return;
        }

        $adapter = AdapterManager::getActiveAdapter();
        if ($adapter === null) {
            return;
        }

        $drained = AsyncInvalidationQueue::drain();
        $jobs = $drained['items'];
        $processedKeys = $drained['keys'];
        $createdMap = $drained['created_map'] ?? [];

        Debug::info('Async processor started', [
            'jobs' => count($jobs),
            'async_mode' => (string) ($asyncDecision['async_mode'] ?? 'off'),
            'execution_mode' => 'async',
            'reason' => (string) ($asyncDecision['reason'] ?? 'async_disabled'),
        ]);

        if (empty($jobs)) {
            AsyncInvalidationQueue::removeProcessed($processedKeys, $createdMap);
            return;
        }

        $resolver = LanguageResolverFactory::create();
        $allPurgedUrls = [];
        $allTrustedUrls = [];
        $processedPostCount = 0;
        $jobTriggerKinds = [];
        $jobTriggerReasons = [];
        $triggerPostTypes = [];
        $fanoutModes = [];
        $fanoutLanguages = [];
        $jobCorrelationIds = [];
        $globalInvalidationEscalated = false;

        foreach ($jobs as $job) {
            $triggerLanguage = is_string($job['trigger_language'] ?? null) ? $job['trigger_language'] : null;
            $triggerClass = sanitize_key((string) ($job['trigger_class'] ?? 'async'));
            $triggerKind = sanitize_key((string) ($job['trigger_kind'] ?? 'post'));
            $jobReason = sanitize_key((string) ($job['reason'] ?? ''));
            $triggerPostType = sanitize_key((string) ($job['trigger_post_type'] ?? ''));
            $jobCorrelationId = trim((string) ($job['correlation_id'] ?? ''));
            if ($jobCorrelationId !== '') {
                $jobCorrelationIds[] = $jobCorrelationId;
            }

            if ($triggerKind === 'queue_overflow') {
                self::executeGlobalInvalidationForQueueOverflow($job, $adapter, $resolver);
                $globalInvalidationEscalated = true;
                break;
            }

            if (self::shouldEscalateTaxonomyJobToGlobalInvalidation($job, $triggerKind)) {
                self::executeGlobalInvalidationForTaxonomyJob(
                    $job,
                    $adapter,
                    $resolver,
                    $triggerLanguage,
                    $triggerKind,
                    $jobReason,
                    $triggerPostType
                );
                $globalInvalidationEscalated = true;
                break;
            }

            $fanoutContext = [
                'trigger_class' => $triggerClass !== '' ? $triggerClass : 'async',
                'trigger_kind' => $triggerKind,
                'trigger_event' => $jobReason !== '' ? $jobReason : 'async_deep_invalidation',
                'reason' => $jobReason,
                'trigger_post_id' => (int) ($job['trigger_post_id'] ?? 0),
                'trigger_post_type' => $triggerPostType,
            ];

            if ($triggerKind !== '') {
                $jobTriggerKinds[] = $triggerKind;
            }
            if ($jobReason !== '') {
                $jobTriggerReasons[] = $jobReason;
            }
            if ($triggerPostType !== '') {
                $triggerPostTypes[] = $triggerPostType;
            }

            // Phase 2.1: execution-only async tail (no purge, no re-resolution)
            if (($job['split_mode'] ?? '') === 'async_tail') {

                $asyncUrls = is_array($job['immediate_target_urls'] ?? null)
                    ? $job['immediate_target_urls']
                    : [];

                if (!empty($asyncUrls)) {
                    // Preserve the sync partition's identity so one editor save remains one targeted update group.
                    EmissionHelper::emit($asyncUrls, [
                        'trigger_class' => 'async',
                        'trigger_kind' => $triggerKind,
                        'trigger_event' => $jobReason !== '' ? $jobReason : 'async_tail',
                        'reason' => $jobReason,
                        'trigger_post_id' => (int) ($job['trigger_post_id'] ?? 0),
                        'trigger_post_type' => $triggerPostType,
                        'correlation_id' => $jobCorrelationId,
                        'split_mode' => 'async_tail',
                    ]);
                }

                Debug::log('async_tail_execution', [
                    'url_count' => count($asyncUrls),
                    'correlation_id' => $job['correlation_id'] ?? '',
                ]);

                continue;
            }

            // 1. Purge immediate targets (pages + URL targets).
            $immediateIds = is_array($job['immediate_target_page_ids'] ?? null) ? $job['immediate_target_page_ids'] : [];
            $immediateUrls = is_array($job['immediate_target_urls'] ?? null) ? $job['immediate_target_urls'] : [];
            $immediateResult = PurgeCoordinator::purgeTargets(
                $immediateIds,
                $immediateUrls,
                $adapter,
                $resolver,
                $triggerLanguage,
                $fanoutContext
            );
            $emittedUrls = is_array($immediateResult['emitted_urls'] ?? null) ? $immediateResult['emitted_urls'] : [];

            $allPurgedUrls = array_merge($allPurgedUrls, $emittedUrls);

            $jobFanoutMode = (string) ($immediateResult['fanout_mode'] ?? FanoutPolicy::MODE_SAME_LANGUAGE);
            $jobFanoutLanguages = is_array($immediateResult['fanout_languages'] ?? null)
                ? $immediateResult['fanout_languages']
                : [];
            $fanoutModes[] = $jobFanoutMode;
            $fanoutLanguages = array_merge($fanoutLanguages, $jobFanoutLanguages);

            $immediateTrustedUrls = is_array($job['immediate_trusted_urls'] ?? null)
                ? $job['immediate_trusted_urls']
                : [];
            if (!empty($immediateTrustedUrls)) {
                $allTrustedUrls = array_merge(
                    $allTrustedUrls,
                    FanoutPolicy::expandUrls($immediateTrustedUrls, $resolver, $jobFanoutLanguages)
                );
            }

            // 2. Deep/supplemental processing by trigger kind.
            if ($triggerKind === 'post') {
                $deepUrls = self::processPostTrigger(
                    $job,
                    $adapter,
                    $resolver,
                    $processedPostCount,
                    $jobFanoutLanguages
                );
                $allPurgedUrls = array_merge($allPurgedUrls, $deepUrls);
            } elseif ($triggerKind === 'taxonomy_assign') {
                $taxUrls = self::processTaxonomyAssignTrigger(
                    $job,
                    $adapter,
                    $resolver,
                    $processedPostCount,
                    $jobFanoutLanguages
                );
                $allPurgedUrls = array_merge($allPurgedUrls, $taxUrls);
            } elseif ($triggerKind === 'taxonomy_term') {
                $termUrls = self::processTaxonomyTermTrigger(
                    $job,
                    $adapter,
                    $resolver,
                    $processedPostCount,
                    $jobFanoutLanguages
                );
                $allPurgedUrls = array_merge($allPurgedUrls, $termUrls);
            }
        }

        AsyncInvalidationQueue::removeProcessed($processedKeys, $createdMap);

        if ($globalInvalidationEscalated) {
            Debug::info('Async processor completed via global escalation', [
                'jobs' => count($jobs),
            ]);
            return;
        }

        $allPurgedUrls = array_values(array_unique(array_filter($allPurgedUrls)));
        $triggerPostTypes = array_values(array_unique(array_filter(array_map('strval', $triggerPostTypes))));
        $fanoutLanguages = array_values(array_unique(array_filter(array_map('strval', $fanoutLanguages))));
        $fanoutMode = in_array(FanoutPolicy::MODE_ALL_LANGUAGES, $fanoutModes, true)
            ? FanoutPolicy::MODE_ALL_LANGUAGES
            : FanoutPolicy::MODE_SAME_LANGUAGE;

        $trustedUrls = TrustDecider::intersectWithUrls(
            TrustDecider::normalizeUrls($allTrustedUrls),
            $allPurgedUrls
        );
        $uniqueCorrelationIds = array_values(array_unique($jobCorrelationIds));
        $emissionCorrelationId = $uniqueCorrelationIds[0] ?? '';
        $context = [
            'source' => $adapter->getKey(),
            'source_label' => $adapter->getLabel(),
            'reason' => 'async_deep_invalidation',
            'trigger_class' => 'async',
            'trigger_event' => 'async_deep_invalidation',
            'trigger_post_id' => 0,
            'trigger_post_type' => '',
            'groups' => $triggerPostTypes,
            'async' => true,
            'jobs_processed' => count($jobs),
            'posts_purged' => $processedPostCount,
            'trigger_kinds' => array_values(array_unique($jobTriggerKinds)),
            'trigger_reasons' => array_values(array_unique($jobTriggerReasons)),
            'fanout_mode' => $fanoutMode,
            'fanout_languages' => $fanoutLanguages,
            'trusted_urls' => $trustedUrls,
        ];
        if ($emissionCorrelationId !== '') {
            $context['correlation_id'] = $emissionCorrelationId;
        }
        if (count($uniqueCorrelationIds) > 1) {
            $context['merged_correlation_ids'] = $uniqueCorrelationIds;
        }
        $emissionPayload = EmissionBuilder::build($trustedUrls, $allPurgedUrls);
        $context['trusted_urls'] = $emissionPayload['trusted_urls'];
        Debug::verbose('untrusted origin', [
            'origin' => 'async',
            'count' => count($allPurgedUrls),
        ]);

        Debug::info('Async processor completed', [
            'jobs' => count($jobs),
            'posts_purged' => $processedPostCount,
            'urls_emitted' => count($allPurgedUrls),
        ]);

        EmissionHelper::emit($emissionPayload['urls'], $context);
    }

    /**
     * Process a post-type trigger in async mode.
     *
     * @param array<string,mixed>         $job
     * @param CacheAdapterInterface        $adapter
     * @param LanguageResolverInterface|null $resolver
     * @param int                          $processedPostCount
     * @param string[]                     $fanoutLanguages
     * @return string[]
     */
    private static function processPostTrigger(
        array $job,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver,
        int &$processedPostCount,
        array $fanoutLanguages
    ): array {
        $triggerPostId = (int) ($job['trigger_post_id'] ?? 0);
        $triggerPostType = sanitize_key((string) ($job['trigger_post_type'] ?? ''));

        if ($triggerPostId <= 0 || $triggerPostType === '') {
            return [];
        }

        $edges = RelationshipGraphResolver::resolveRelationshipEdges($triggerPostType);
        Debug::log('async_post_trigger_edges', [
            'trigger_post_id' => $triggerPostId,
            'trigger_post_type' => $triggerPostType,
            'edges' => $edges,
        ]);
        if (empty($edges)) {
            return [];
        }

        $settingsRepository = InvalidatorRuntime::settingsRepository();
        $postIdsByPostType = [
            $triggerPostType => [$triggerPostId],
        ];
        $discoveredTargetKeys = [];
        $urls = [];

        foreach ($edges as [$fromPostType, $toPostType]) {
            $fromIds = $postIdsByPostType[$fromPostType] ?? [];
            $fromIds = array_values(array_unique(array_filter(array_map('absint', $fromIds), static fn(int $id): bool => $id > 0)));
            if (empty($fromIds)) {
                continue;
            }

            $toIds = [];
            foreach ($fromIds as $fromId) {
                $foundIds = RelationshipResolver::findHostPostIds($toPostType, $fromPostType, $fromId);

                Debug::log('async_post_trigger_found_hosts', [
                    'from_post_type' => $fromPostType,
                    'to_post_type' => $toPostType,
                    'from_id' => $fromId,
                    'found_ids' => $foundIds,
                ]);

                foreach ($foundIds as $foundId) {
                    $foundId = absint($foundId);
                    if ($foundId <= 0) {
                        continue;
                    }

                    $targetKey = $toPostType . ':' . $foundId;
                    if (isset($discoveredTargetKeys[$targetKey])) {
                        continue;
                    }

                    $discoveredTargetKeys[$targetKey] = true;
                    $toIds[] = $foundId;
                }
            }

            if (empty($toIds)) {
                continue;
            }

            $toConfig = $settingsRepository->getPostTypeConfig($toPostType);
            $canTraverse = true;
            if ($toConfig !== null) {
                $hostEnabled = (bool) ($toConfig['enabled'] ?? true);
                $relationshipsEnabled = (bool) (($toConfig['propagation']['relationships_enabled'] ?? true));
                Debug::log('async_post_trigger_host_filter', [
                    'to_post_type' => $toPostType,
                    'to_ids' => $toIds,
                    'host_enabled' => $hostEnabled ?? null,
                    'relationships_enabled' => $relationshipsEnabled ?? null,
                    'publicly_queryable' => $ptObj->publicly_queryable ?? null,
                    'can_traverse' => $canTraverse,
                ]);
                $canTraverse = $hostEnabled && $relationshipsEnabled;

                if ($canTraverse) {
                    $ptObj = get_post_type_object($toPostType);
                    if ($ptObj && $ptObj->publicly_queryable) {
                        $processedPostCount += count($toIds);
                        $urls = array_merge($urls, InvalidationEngine::purgePostIds(
                            $toIds,
                            $adapter,
                            $resolver,
                            $fanoutLanguages
                        ));
                    }
                }
            }

            if ($canTraverse) {
                $postIdsByPostType[$toPostType] = array_values(array_unique(array_merge(
                    $postIdsByPostType[$toPostType] ?? [],
                    $toIds
                )));
            }
        }

        return $urls;
    }

    /**
     * Process taxonomy assignment trigger: purge the affected post single (if publicly_queryable).
     *
     * @param array<string,mixed>          $job
     * @param CacheAdapterInterface        $adapter
     * @param LanguageResolverInterface|null $resolver
     * @param int                          $processedPostCount
     * @param string[]                     $fanoutLanguages
     * @return string[]
     */
    private static function processTaxonomyAssignTrigger(
        array $job,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver,
        int &$processedPostCount,
        array $fanoutLanguages
    ): array {
        $objectId = (int) ($job['object_id'] ?? 0);
        if ($objectId <= 0) {
            return [];
        }

        $post = get_post($objectId);
        if (!$post instanceof \WP_Post || $post->post_status !== 'publish') {
            return [];
        }

        $ptObj = get_post_type_object((string) $post->post_type);
        if (!$ptObj || !$ptObj->publicly_queryable) {
            return [];
        }

        $processedPostCount += 1;

        return InvalidationEngine::purgePostIds(
            [(int) $post->ID],
            $adapter,
            $resolver,
            $fanoutLanguages
        );
    }

    /**
     * Process taxonomy term lifecycle trigger.
     *
     * @param array<string,mixed>           $job
     * @param CacheAdapterInterface         $adapter
     * @param LanguageResolverInterface|null $resolver
     * @param int                           $processedPostCount
     * @param string[]                      $fanoutLanguages
     * @return string[]
     */
    private static function processTaxonomyTermTrigger(
        array $job,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver,
        int &$processedPostCount,
        array $fanoutLanguages
    ): array {
        /**
         * Note on async taxonomy jobs:
         *
         * Tag gating (tags_enabled) is enforced at trigger time (listener layer).
         * Already queued async taxonomy_term jobs may still execute after the setting
         * is toggled off. This is expected and self-resolving as the queue drains.
         *
         * We intentionally do NOT re-check tags_enabled here to avoid duplicating
         * policy logic in the async execution layer and to keep the system simple.
         */
        $taxonomy = sanitize_key((string) ($job['taxonomy'] ?? ''));
        $termId = (int) ($job['term_id'] ?? 0);

        if ($taxonomy === '' || $termId <= 0) {
            return [];
        }

        $watchedPostTypes = RelationshipGraphResolver::getWatchedPostTypes();
        if (empty($watchedPostTypes)) {
            return [];
        }

        // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Async taxonomy invalidation discovery is cron-driven, scoped to one term and watched post types, and returns IDs only.
        $postIds = get_posts([
            'post_type' => $watchedPostTypes,
            'tax_query' => [
                [
                    'taxonomy' => $taxonomy,
                    'terms' => $termId,
                    'field' => 'term_id',
                ],
            ],
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ]);
        // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_tax_query

        if (!is_array($postIds) || empty($postIds)) {
            return [];
        }

        $purgeIds = [];
        foreach ($postIds as $postId) {
            $postId = (int) $postId;
            if ($postId <= 0) {
                continue;
            }

            $foundPostType = get_post_type($postId);
            if (!is_string($foundPostType) || $foundPostType === '') {
                continue;
            }

            $ptObj = get_post_type_object($foundPostType);
            if (!$ptObj || !$ptObj->publicly_queryable) {
                continue;
            }

            $purgeIds[] = $postId;
        }

        $purgeIds = array_values(array_unique($purgeIds));
        if (empty($purgeIds)) {
            return [];
        }

        $processedPostCount += count($purgeIds);

        return InvalidationEngine::purgePostIds(
            $purgeIds,
            $adapter,
            $resolver,
            $fanoutLanguages
        );
    }

    /**
     * Execute deterministic global invalidation for queue overflow safety fallback.
     *
     * @param array<string,mixed>           $job
     * @param CacheAdapterInterface         $adapter
     * @param LanguageResolverInterface|null $resolver
     * @return void
     */
    private static function executeGlobalInvalidationForQueueOverflow(
        array $job,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver
    ): void {
        $pendingItems = (int) ($job['pending_items'] ?? 0);
        $asyncDecision = InvalidatorRuntime::settingsRepository()->getAsyncSchedulingDecision();
        $asyncMode = (string) ($asyncDecision['async_mode'] ?? 'off');
        if (Debug::verboseEnabled() || $asyncMode !== 'off') {
            Debug::log('invalidation execution decision', [
                'entry_point' => 'queue_overflow_fallback',
                'async_mode' => $asyncMode,
                'execution_mode' => 'async',
                'reason' => (string) ($asyncDecision['reason'] ?? 'async_disabled'),
            ]);
        }
        Debug::log('async queue overflow global escalation', [
            'pending_items' => $pendingItems,
            'trigger_kind' => 'queue_overflow',
            'reason' => 'queue_overflow',
        ]);

        GlobalInvalidationExecutor::executeGlobalInvalidation(
            $adapter,
            $resolver,
            null,
            'queue_overflow',
            [
                'trigger_class' => 'async',
                'trigger_kind' => 'queue_overflow',
                'trigger_event' => 'queue_overflow',
                'reason' => 'queue_overflow',
                'trigger_post_id' => 0,
                'trigger_post_type' => '',
                'groups' => [],
                'async' => true,
                'intent' => 'GLOBAL_INVALIDATION',
                'resolution' => 'GLOBAL_INVALIDATION',
                'fallback' => true,
                'fallback_reason' => 'queue_overflow',
            ]
        );
    }

    /**
     * Determine whether a queued taxonomy job requests a global invalidation escalation.
     *
     * @param array<string,mixed> $job
     * @param string              $triggerKind
     * @return bool
     */
    private static function shouldEscalateTaxonomyJobToGlobalInvalidation(array $job, string $triggerKind): bool
    {
        if (!in_array($triggerKind, ['taxonomy_assign', 'taxonomy_term'], true)) {
            return false;
        }

        $intent = strtoupper(sanitize_key((string) ($job['intent'] ?? '')));
        $resolution = strtoupper(sanitize_key((string) ($job['resolution'] ?? '')));

        return $intent === 'GLOBAL_INVALIDATION' || $resolution === 'GLOBAL_INVALIDATION';
    }

    /**
     * Execute global invalidation for a queued taxonomy job marked as global intent.
     *
     * @param array<string,mixed>          $job
     * @param CacheAdapterInterface        $adapter
     * @param LanguageResolverInterface|null $resolver
     * @param string|null                  $triggerLanguage
     * @param string                       $triggerKind
     * @param string                       $jobReason
     * @param string                       $triggerPostType
     * @return void
     */
    private static function executeGlobalInvalidationForTaxonomyJob(
        array $job,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver,
        ?string $triggerLanguage,
        string $triggerKind,
        string $jobReason,
        string $triggerPostType
    ): void {
        $reason = $jobReason !== '' ? $jobReason : 'taxonomy_term_change';
        $taxonomy = sanitize_key((string) ($job['taxonomy'] ?? ''));
        $triggerPostId = (int) ($job['trigger_post_id'] ?? 0);
        if ($triggerPostId <= 0) {
            $triggerPostId = (int) ($job['object_id'] ?? 0);
        }

        $groups = is_array($job['groups'] ?? null) ? $job['groups'] : [];
        $groups = array_values(array_unique(array_filter(array_map(static fn($group): string => sanitize_key((string) $group), $groups))));
        $groups = array_values(array_filter($groups, static fn(string $group): bool => $group !== ''));
        sort($groups, SORT_STRING);

        Debug::log('async taxonomy full_flush escalation', [
            'full_flush_escalation' => true,
            'source' => 'taxonomy',
            'trigger_kind' => $triggerKind,
            'taxonomy' => $taxonomy,
            'reason' => $reason,
            'trigger_post_id' => max(0, $triggerPostId),
            'trigger_post_type' => $triggerPostType,
            'post_types' => $groups,
        ]);

        GlobalInvalidationExecutor::executeGlobalInvalidation(
            $adapter,
            $resolver,
            $triggerLanguage,
            $reason,
            [
                'trigger_class' => 'taxonomy',
                'trigger_kind' => $triggerKind,
                'trigger_event' => $reason,
                'reason' => $reason,
                'trigger_post_id' => max(0, $triggerPostId),
                'trigger_post_type' => $triggerPostType,
                'taxonomy' => $taxonomy,
                'groups' => $groups,
                'async' => true,
                'full_flush' => true,
                'target_mode' => 'full_flush',
                'intent' => 'GLOBAL_INVALIDATION',
                'resolution' => 'GLOBAL_INVALIDATION',
                'fallback' => false,
                'fallback_reason' => '',
                'full_flush_escalation' => true,
                'full_flush_escalation_source' => 'taxonomy',
            ]
        );
    }
}
