<?php
declare(strict_types=1);

namespace Ekesto\CacheWarmup\Integrations;

use Ekesto\CacheInvalidator\InvalidatorRuntime;
use Ekesto\CacheInvalidator\Integrations\Multilingual\FanoutPolicy;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverFactory;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverInterface;
use Ekesto\CacheWarmup\Debug;

/**
 * Read-only bridge from warmup planning to Invalidator multilingual authority.
 */
final class InvalidatorMultilingualBridge
{
    /**
     * Resolve priority page IDs and Home URLs for an immutable full-run snapshot.
     *
     * @param array<int,int> $priorityPageIds Configured priority page IDs.
     * @param string $homeUrl Current site Home URL.
     * @return array{page_ids:array<int,int>,home_urls:array<int,string>,fanout_enabled:bool}
     */
    public static function resolvePriorityTargets(array $priorityPageIds, string $homeUrl): array
    {
        $priorityPageIds = self::normalizeIds($priorityPageIds);
        $homeUrl = trim($homeUrl);
        $fallback = [
            'page_ids' => $priorityPageIds,
            'home_urls' => $homeUrl !== '' ? [$homeUrl] : [],
            'fanout_enabled' => false,
        ];
        $fanoutEnabled = false;

        try {
            $fanoutEnabled = self::isPostTriggerFanoutEnabled();
            $fallback['fanout_enabled'] = $fanoutEnabled;
            $resolver = self::createResolver();
            if ($resolver === null) {
                return $fallback;
            }

            $defaultLanguage = trim($resolver->getDefaultLanguage());
            $mainHomeUrl = self::mapHomeUrl($resolver, $homeUrl, $defaultLanguage);
            $homeUrls = $mainHomeUrl !== '' ? [$mainHomeUrl] : [];
            if (!$fanoutEnabled) {
                return [
                    'page_ids' => $priorityPageIds,
                    'home_urls' => $homeUrls,
                    'fanout_enabled' => false,
                ];
            }

            $languages = $resolver->resolveTargetLanguages([
                'fanout_mode' => FanoutPolicy::MODE_ALL_LANGUAGES,
                'trigger_language' => $defaultLanguage,
            ]);
            $languages = self::normalizeLanguages($languages);

            $expandedIds = $priorityPageIds;
            foreach ($priorityPageIds as $pageId) {
                foreach ($languages as $language) {
                    $mappedId = $resolver->mapObjectId($pageId, 'page', $language);
                    if (is_int($mappedId) && $mappedId > 0) {
                        $expandedIds[] = $mappedId;
                    }
                }
            }

            if ($mainHomeUrl !== '') {
                foreach ($languages as $language) {
                    $mappedHomeUrl = trim($resolver->mapPermalink($mainHomeUrl, $language));
                    if ($mappedHomeUrl !== '') {
                        $homeUrls[] = $mappedHomeUrl;
                    }
                }
            }

            return [
                'page_ids' => self::normalizeIds($expandedIds),
                'home_urls' => array_values(array_unique($homeUrls)),
                'fanout_enabled' => true,
            ];
        } catch (\Throwable $throwable) {
            Debug::verbose('priority multilingual bridge fallback', [
                'error' => $throwable->getMessage(),
            ]);
            return $fallback;
        }
    }

    /**
     * Read the Invalidator-owned post-trigger fanout setting.
     *
     * @return bool
     */
    private static function isPostTriggerFanoutEnabled(): bool
    {
        if (!class_exists(InvalidatorRuntime::class) || !method_exists(InvalidatorRuntime::class, 'settingsRepository')) {
            return false;
        }

        $settings = InvalidatorRuntime::settingsRepository()->getGlobalInvalidation();
        return !empty($settings['multilingual_fanout_post_triggers_enabled']);
    }

    /**
     * Create the active Invalidator language resolver when available.
     *
     * @return LanguageResolverInterface|null
     */
    private static function createResolver(): ?LanguageResolverInterface
    {
        if (!class_exists(LanguageResolverFactory::class)) {
            return null;
        }

        $resolver = LanguageResolverFactory::create();
        return $resolver instanceof LanguageResolverInterface && $resolver->isActive()
            ? $resolver
            : null;
    }

    /**
     * Map Home into the default language when possible.
     *
     * @param LanguageResolverInterface $resolver Active language resolver.
     * @param string $homeUrl Current Home URL.
     * @param string $defaultLanguage Default site language.
     * @return string
     */
    private static function mapHomeUrl(
        LanguageResolverInterface $resolver,
        string $homeUrl,
        string $defaultLanguage
    ): string {
        if ($homeUrl === '' || $defaultLanguage === '') {
            return $homeUrl;
        }

        $mapped = trim($resolver->mapPermalink($homeUrl, $defaultLanguage));
        return $mapped !== '' ? $mapped : $homeUrl;
    }

    /**
     * Normalize positive IDs while preserving order.
     *
     * @param array<int,mixed> $ids Raw IDs.
     * @return array<int,int>
     */
    private static function normalizeIds(array $ids): array
    {
        $normalized = [];
        foreach ($ids as $id) {
            $id = (int) $id;
            if ($id > 0) {
                $normalized[$id] = $id;
            }
        }

        return array_values($normalized);
    }

    /**
     * Normalize language codes while preserving order.
     *
     * @param array<int,mixed> $languages Raw language codes.
     * @return array<int,string>
     */
    private static function normalizeLanguages(array $languages): array
    {
        $normalized = [];
        foreach ($languages as $language) {
            $language = strtolower(trim((string) $language));
            if ($language !== '') {
                $normalized[$language] = $language;
            }
        }

        return array_values($normalized);
    }
}
