<?php

declare(strict_types=1);

namespace Inpsyde\MultilingualPress\Module\Redirect\Redirector;

use Inpsyde\Assets\Asset;
use Inpsyde\Assets\AssetManager;
use Inpsyde\Assets\Script;
use Inpsyde\MultilingualPress\Framework\Api\TranslationSearchArgs;
use Inpsyde\MultilingualPress\Module\Redirect\NoredirectPermalinkFilter;
use Inpsyde\MultilingualPress\Module\Redirect\NoRedirectStorage\NoRedirectStorage;
use Inpsyde\MultilingualPress\Module\Redirect\RedirectTarget\LanguageNegotiator;
use Inpsyde\MultilingualPress\Module\Redirect\Settings\Repository\Repository;

use function Inpsyde\MultilingualPress\currentSiteLocale;
use function Inpsyde\MultilingualPress\siteLanguageTag;

/**
 * @psalm-type language = string
 * @psalm-type url = string
 */
final class JsRedirector implements Redirector
{
    public const FILTER_UPDATE_INTERVAL = 'multilingualpress.noredirect_update_interval';
    public const SCRIPT_HANDLE = 'multilingualpress-redirect';
    public const FILTER_REDIRECT_TARGET = 'multilingualpress-redirect-js-redirect_target';

    /**
     * @var Repository
     */
    protected $redirectSettingsRepository;

    /**
     * @var string
     */
    protected $urlToModuleAssetsFolder;

    /**
     * @var TranslationSearchArgs
     */
    protected $translationSearchArgs;

    /**
     * @var LanguageNegotiator
     */
    protected $languageNegotiator;

    public function __construct(
        Repository $redirectSettingsRepository,
        string $urlToModuleAssetsFolder,
        TranslationSearchArgs $translationSearchArgs,
        LanguageNegotiator $languageNegotiator
    ) {

        $this->redirectSettingsRepository = $redirectSettingsRepository;
        $this->urlToModuleAssetsFolder = $urlToModuleAssetsFolder;
        $this->translationSearchArgs = $translationSearchArgs;
        $this->languageNegotiator = $languageNegotiator;
    }

    /**
     * @inheritdoc
     */
    public function redirect()
    {
        $redirectTarget = (array)apply_filters(self::FILTER_REDIRECT_TARGET, $this->redirectTarget());

        if (empty($redirectTarget)) {
            return;
        }

        /**
         * Filters the lifetime, in seconds, for data in the noredirect storage.
         *
         * @param int $lifetime
         */
        $lifetime = (int)apply_filters(NoRedirectStorage::FILTER_LIFETIME, NoRedirectStorage::LIFETIME_IN_SECONDS);

        /**
         * Filters the update interval, in seconds, for the timestamp of noredirect storage data.
         *
         * @param int $updateInterval
         */
        $updateInterval = (int)apply_filters(self::FILTER_UPDATE_INTERVAL, MINUTE_IN_SECONDS);

        $urlToModuleAssetsFolder = $this->urlToModuleAssetsFolder;

        add_action(
            AssetManager::ACTION_SETUP,
            static function (AssetManager $assetManager) use ($urlToModuleAssetsFolder, $lifetime, $updateInterval, $redirectTarget) {

                $frontendScript = new Script(JsRedirector::SCRIPT_HANDLE, "{$urlToModuleAssetsFolder}js/frontend.min.js");
                $frontendScript
                    ->forLocation(Asset::FRONTEND)
                    ->withLocalize('MultilingualPressRedirectorSettings', [
                        'currentLanguage' => currentSiteLocale(),
                        'noredirectKey' => NoredirectPermalinkFilter::QUERY_ARGUMENT,
                        'storageLifetime' => absint($lifetime * 1000),
                        'updateTimestampInterval' => absint($updateInterval * 1000),
                        'redirectTarget' => $redirectTarget,
                    ])
                    ->isInHeader();

                $assetManager->register($frontendScript);
            }
        );
    }

    /**
     * Retrieves the redirect target.
     *
     * @return array<string, string> A map of redirect target language to redirect target URL.
     * @psalm-return array<language, url>
     */
    protected function redirectTarget(): array
    {
        $args = $this->translationSearchArgs->makeNotStrictSearch();
        $redirectTarget = $this->languageNegotiator->redirectTarget($args);
        $redirectTargetLanguage = $redirectTarget->language();
        $redirectTargetUrl = $redirectTarget->url();

        if (!empty($redirectTargetLanguage) && !empty($redirectTargetUrl)) {
            return [$redirectTargetLanguage => $redirectTargetUrl];
        }

        $fallbackSiteId = (int)$this->redirectSettingsRepository->settingValue(Repository::MODULE_SETTING_FALLBACK_REDIRECT_SITE_ID);

        if (!$fallbackSiteId || $fallbackSiteId < 1) {
            return [];
        }

        return [siteLanguageTag($fallbackSiteId) => get_site_url($fallbackSiteId)];
    }
}
