<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Core;

use Ekesto\CacheInvalidator\Integrations\Caching\CacheAdapterInterface;
use Ekesto\CacheInvalidator\Integrations\Caching\UrlPurgeCapableAdapterInterface;
use Ekesto\CacheInvalidator\InvalidatorRuntime;
use Ekesto\CacheInvalidator\Integrations\Multilingual\FanoutPolicy;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverInterface;
use Ekesto\CacheInvalidator\Support\Debug;

/**
 * Coordinates purging ID and URL targets with adapter capability awareness.
 */
final class PurgeCoordinator
{
    /**
     * Purges post ID targets and URL targets, then returns a normalized result.
     *
     * @param int[]                           $postIds Post IDs to purge.
     * @param string[]                        $urls Absolute URLs to purge (if supported).
     * @param CacheAdapterInterface           $adapter Active adapter.
     * @param LanguageResolverInterface|null  $resolver Language resolver.
     * @param string|null                     $triggerLanguage Trigger language code.
     * @param array<string,mixed>             $triggerContext Trigger context.
     * @return array<string,mixed> Result payload.
     */
    public static function purgeTargets(
        array $postIds,
        array $urls,
        CacheAdapterInterface $adapter,
        ?LanguageResolverInterface $resolver,
        ?string $triggerLanguage,
        array $triggerContext
    ): array {
        $postIds = array_values(array_unique(array_filter(array_map('absint', $postIds), static fn(int $id): bool => $id > 0)));
        $urls = array_values(array_unique(array_filter(array_map(static fn($url): string => trim((string) $url), $urls))));

        $fanoutContext = self::buildFanoutContext($triggerContext);
        $normalizedTriggerLanguage = is_string($triggerLanguage) ? trim($triggerLanguage) : '';
        if ($normalizedTriggerLanguage !== '') {
            $fanoutContext['trigger_language'] = $normalizedTriggerLanguage;
        }
        $fanoutContext['resolver_available'] = $resolver !== null;
        $fanoutContext['trigger_language_available'] = $normalizedTriggerLanguage !== '';

        $effectiveTriggerLanguage = $normalizedTriggerLanguage !== '' ? $normalizedTriggerLanguage : null;
        $fanoutMode = FanoutPolicy::resolveMode($fanoutContext);
        $fanoutLanguages = FanoutPolicy::resolveTargetLanguages($resolver, $effectiveTriggerLanguage, $fanoutContext);
        $expandedUrls = FanoutPolicy::expandUrls($urls, $resolver, $fanoutLanguages);

        if (
            ($fanoutContext['trigger_kind'] ?? '') === 'template_archive'
            && $fanoutMode === FanoutPolicy::MODE_ALL_LANGUAGES
            && !empty($fanoutLanguages)
            && !empty($urls)
            && count($expandedUrls) <= count($urls)
        ) {
            Debug::log('multilingual_url_fanout_unexpanded', [
                'trigger_kind' => 'template_archive',
                'trigger_class' => sanitize_key((string) ($fanoutContext['trigger_class'] ?? '')),
                'canonical_url_count' => count($urls),
                'expanded_url_count' => count($expandedUrls),
                'fanout_mode' => $fanoutMode,
                'fanout_languages' => $fanoutLanguages,
                'trigger_post_id' => (int) ($fanoutContext['trigger_post_id'] ?? 0),
                'trigger_post_type' => sanitize_key((string) ($fanoutContext['trigger_post_type'] ?? '')),
            ]);
        }

        $postUrls = [];
        if (!empty($postIds)) {
            $postUrls = InvalidationEngine::purgePostIds(
                $postIds,
                $adapter,
                $resolver,
                $fanoutLanguages
            );
        }

        $purgedUrlsDirectly = [];
        $purgeSkippedUrls = [];

        if (!empty($expandedUrls)) {
            if ($adapter instanceof UrlPurgeCapableAdapterInterface) {
                foreach ($expandedUrls as $url) {
                    if ($adapter->purgeUrl($url)) {
                        $purgedUrlsDirectly[] = $url;
                    } else {
                        $purgeSkippedUrls[] = $url;
                    }
                }
            } else {
                $purgeSkippedUrls = $expandedUrls;
            }
        }

        $emittedUrls = array_values(array_unique(array_filter(array_merge($postUrls, $expandedUrls))));

        return [
            'post_ids' => $postIds,
            'post_urls' => array_values(array_unique(array_filter($postUrls))),
            'url_targets' => $expandedUrls,
            'purged_urls_directly' => array_values(array_unique(array_filter($purgedUrlsDirectly))),
            'purge_skipped_urls' => array_values(array_unique(array_filter($purgeSkippedUrls))),
            'emitted_urls' => $emittedUrls,
            'fanout_mode' => $fanoutMode,
            'fanout_languages' => $fanoutLanguages,
        ];
    }

    /**
     * Build normalized fanout context with settings defaults.
     *
     * @param array<string,mixed> $triggerContext Trigger context.
     * @return array<string,mixed>
     */
    private static function buildFanoutContext(array $triggerContext): array
    {
        $context = $triggerContext;

        $context['trigger_class'] = sanitize_key((string) ($context['trigger_class'] ?? ''));
        $context['trigger_kind'] = sanitize_key((string) ($context['trigger_kind'] ?? ''));
        $context['trigger_post_type'] = sanitize_key((string) ($context['trigger_post_type'] ?? ''));
        $context['trigger_post_id'] = absint($context['trigger_post_id'] ?? 0);

        $global = InvalidatorRuntime::settingsRepository()->getGlobalInvalidation();
        $context['multilingual_fanout_post_triggers_enabled'] = (bool) (
            $context['multilingual_fanout_post_triggers_enabled']
            ?? $global['multilingual_fanout_post_triggers_enabled']
            ?? false
        );
        $context['multilingual_fanout_non_post_triggers_enabled'] = (bool) (
            $context['multilingual_fanout_non_post_triggers_enabled']
            ?? $global['multilingual_fanout_non_post_triggers_enabled']
            ?? true
        );

        return $context;
    }
}
