<?php

declare(strict_types=1);

namespace Inpsyde\MultilingualPress\Module\Redirect;

use Inpsyde\MultilingualPress\Core\Frontend\AlternateLanguages;
use Inpsyde\MultilingualPress\Framework\Http\ServerRequest;
use Inpsyde\MultilingualPress\Framework\Module\Exception\ModuleAlreadyRegistered;
use Inpsyde\MultilingualPress\Framework\Module\Module;
use Inpsyde\MultilingualPress\Framework\Module\ModuleManager;
use Inpsyde\MultilingualPress\Framework\Module\ModuleServiceProvider;
use Inpsyde\MultilingualPress\Framework\PluginProperties;
use Inpsyde\MultilingualPress\Framework\Service\Container;
use Inpsyde\MultilingualPress\Framework\Service\Exception\NameOverwriteNotAllowed;
use Inpsyde\MultilingualPress\Framework\Service\Exception\WriteAccessOnLockedContainer;
use Inpsyde\MultilingualPress\Module\Redirect\GeoLocation\GeolocationFinder;
use Inpsyde\MultilingualPress\Module\Redirect\GeoLocation\GeolocationFinderInterface;
use Inpsyde\MultilingualPress\Module\Redirect\NoRedirectStorage\NoRedirectObjectCacheStorage;
use Inpsyde\MultilingualPress\Module\Redirect\NoRedirectStorage\NoRedirectSessionStorage;
use Inpsyde\MultilingualPress\Module\Redirect\NoRedirectStorage\NoRedirectStorage;
use Inpsyde\MultilingualPress\Module\Redirect\Redirector\NotFoundSiteRedirect;
use Inpsyde\MultilingualPress\Module\Redirect\Redirector\Redirector;
use Inpsyde\MultilingualPress\Module\Redirect\Settings\Repository\Repository;

/**
 * @psalm-type languageCode = string
 */
final class ServiceProvider implements ModuleServiceProvider
{
    public const MODULE_ID = 'redirect';
    public const PARAMETER_CONFIG_MODULE_DIR_PATH = 'multilingualpress.redirect.moduleDirPath';
    public const MODULE_SCRIPTS_HANDLER_NAME = 'multilingualpress-redirect';
    public const CONFIGURATION_NAME_FOR_URL_TO_MODULE_ASSETS = 'multilingualpress.redirect.urlToModuleAssets';

    public const SETTING_NONCE_ACTION = 'multilingualpress_save_redirect_setting_nonce_';
    public const CONFIGURATION_NAME_FOR_REDIRECT_TYPES = 'multilingualpress.redirect.types';

    public const CONFIGURATION_NAME_FOR_REDIRECT_USER_LANGUAGES = 'multilingualpress.redirect.userLanguages';

    /**
     * @inheritdoc
     * @throws ModuleAlreadyRegistered
     */
    public function registerModule(ModuleManager $moduleManager): bool
    {
        return $moduleManager->register(
            new Module(
                self::MODULE_ID,
                [
                    'description' => __(
                        'Enable the Redirect checkbox on each site: this allows you to enable/disable the automatic redirection feature according to the user browser language settings.',
                        'multilingualpress'
                    ),
                    'name' => __('Redirect', 'multilingualpress'),
                    'active' => true,
                ]
            )
        );
    }

    /**
     * @inheritdoc
     *
     * @throws NameOverwriteNotAllowed
     * @throws WriteAccessOnLockedContainer
     *
     * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
     */
    public function register(Container $container)
    {
        // phpcs:enable

        $container->share(
            self::PARAMETER_CONFIG_MODULE_DIR_PATH,
            static function (): string {
                return __DIR__;
            }
        );

        $container->addService(
            AcceptLanguageParser::class,
            static function (): AcceptLanguageParser {
                return new AcceptLanguageParser();
            }
        );

        $container->addService(
            NoredirectPermalinkFilter::class,
            static function (): NoredirectPermalinkFilter {
                return new NoredirectPermalinkFilter();
            }
        );

        $container->addService(
            NoRedirectStorage::class,
            static function (): NoRedirectStorage {
                return is_user_logged_in() && wp_using_ext_object_cache()
                    ? new NoRedirectObjectCacheStorage()
                    : new NoRedirectSessionStorage();
            }
        );

        $container->addService(
            RedirectRequestChecker::class,
            static function (Container $container): RedirectRequestChecker {
                return new RedirectRequestChecker(
                    $container[Repository::class],
                    $container[NoRedirectStorage::class]
                );
            }
        );

        /**
         * The configuration of the URL to module assets folder.
         */
        $container->share(
            self::CONFIGURATION_NAME_FOR_URL_TO_MODULE_ASSETS,
            static function (Container $container): string {
                $pluginProperties = $container->get(PluginProperties::class);

                return $pluginProperties->dirUrl() . 'src/modules/Redirect/public/';
            }
        );

        $container->share(
            GeolocationFinder::class,
            static function (Container $container): GeolocationFinderInterface {
                return new GeolocationFinder(
                    $container->get(ServerRequest::class),
                    'http://www.geoplugin.net/json.gp'
                );
            }
        );

        $moduleDirPath = $container->get(self::PARAMETER_CONFIG_MODULE_DIR_PATH);

        require $moduleDirPath . '/Settings/services.php';
        require $moduleDirPath . '/Redirector/services.php';
        require $moduleDirPath . '/RedirectTarget/services.php';
    }

    /**
     * @inheritdoc
     */
    public function activateModule(Container $container)
    {
        $moduleDirPath = $container->get(self::PARAMETER_CONFIG_MODULE_DIR_PATH);

        if (is_admin()) {
            require $moduleDirPath . '/Settings/hooks.php';
            return;
        }

        $filter = $container->get(NoredirectPermalinkFilter::class);
        $filter->enable();

        add_filter(AlternateLanguages::FILTER_HREFLANG_URL, [$filter, 'removeNoRedirectQueryArgument']);

        if ($container->get(RedirectRequestChecker::class)->isRedirectRequest()) {
            add_action('template_redirect', [$container->get(Redirector::class), 'redirect'], 1);
            add_action(Redirector::ACTION_TARGET_NOT_FOUND, [$container->get(NotFoundSiteRedirect::class), 'redirect']);
        }
    }
}
