<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Multilingual\Internal;

use Ekesto\CacheInvalidator\ContentVisibility;
use Ekesto\CacheInvalidator\Integrations\Multilingual\FanoutPolicy;
use Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverFactory;
use Ekesto\CacheInvalidator\Integrations\Multilingual\ResolverContext;
use Ekesto\CacheInvalidator\Integrations\Multilingual\ResolverMetadataInterface;

/**
 * Resolves multilingual sibling object IDs for object-model integrations.
 */
final class ObjectSiblingResolver
{
    /**
     * Expand an object ID into its multilingual sibling IDs.
     *
     * Always includes the original object ID. URL-model integrations and
     * unavailable resolvers collapse to the original ID only.
     *
     * @param int $objectId Source object ID.
     * @param string $postType Source object post type.
     * @return int[]
     */
    public static function expandObjectIds(int $objectId, string $postType): array
    {
        $objectId = absint($objectId);
        if ($objectId <= 0) {
            return [];
        }

        $postType = sanitize_key($postType);
        $resolvedIds = [$objectId];
        if ($postType === '') {
            return ContentVisibility::normalizePostIds($resolvedIds);
        }

        $resolver = LanguageResolverFactory::create();
        if ($resolver === null || !$resolver->isActive()) {
            return ContentVisibility::normalizePostIds($resolvedIds);
        }

        if (ResolverContext::getResolverModel($resolver) !== ResolverMetadataInterface::MODEL_OBJECT) {
            return ContentVisibility::normalizePostIds($resolvedIds);
        }

        foreach (self::resolveCandidateLanguages($resolver, $objectId, $postType) as $language) {
            $mappedId = $resolver->mapObjectId($objectId, $postType, $language);
            if ($mappedId === null) {
                continue;
            }

            if ($mappedId > 0) {
                $resolvedIds[] = $mappedId;
            }
        }

        return ContentVisibility::normalizePostIds($resolvedIds);
    }

    /**
     * Resolve candidate languages for sibling object expansion.
     *
     * @param \Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverInterface $resolver Active resolver.
     * @param int $objectId Source object ID.
     * @param string $postType Source object post type.
     * @return string[]
     */
    private static function resolveCandidateLanguages(
        \Ekesto\CacheInvalidator\Integrations\Multilingual\LanguageResolverInterface $resolver,
        int $objectId,
        string $postType
    ): array {
        $triggerLanguage = $resolver->getTriggerLanguage($objectId);
        $languages = $resolver->resolveTargetLanguages([
            'fanout_mode' => FanoutPolicy::MODE_ALL_LANGUAGES,
            'trigger_language' => is_string($triggerLanguage) ? trim($triggerLanguage) : '',
            'trigger_post_id' => $objectId,
            'trigger_post_type' => $postType,
            'source_post_id' => $objectId,
            'source_post_type' => $postType,
        ]);

        $normalized = self::normalizeLanguages(is_array($languages) ? $languages : []);
        if (!empty($normalized)) {
            return $normalized;
        }

        if (is_string($triggerLanguage) && trim($triggerLanguage) !== '') {
            return [trim($triggerLanguage)];
        }

        $defaultLanguage = trim((string) $resolver->getDefaultLanguage());
        if ($defaultLanguage !== '') {
            return [$defaultLanguage];
        }

        return [];
    }

    /**
     * Normalize language codes.
     *
     * @param array<int,mixed> $languages Candidate language codes.
     * @return string[]
     */
    private static function normalizeLanguages(array $languages): array
    {
        $normalized = [];

        foreach ($languages as $language) {
            if (!is_string($language)) {
                continue;
            }

            $language = trim($language);
            if ($language === '') {
                continue;
            }

            $normalized[] = $language;
        }

        return array_values(array_unique($normalized));
    }
}
