<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Site;

use CpCreation\VitiCore\AgeVerification\AgeVerificationProofManager;
use CpCreation\VitiCore\AgeVerification\Repository\AgeVerificationConfigRepository;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\LocalizedRouting\LocalizedPageRouteResolver;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Provider\LocaleProvider;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Behavior\HasTemplateOverride;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Intl\Countries;
use Symfony\Component\Intl\Languages;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('site:AgeGate', template: '@components/site/AgeGate.html.twig')]
final class AgeGate
{
    use DefaultActionTrait;
    use HasTemplateOverride;

    private const array PREFERRED_COUNTRY_CODES = ['FR', 'BE', 'GB', 'DE', 'IT', 'NL', 'ES'];

    #[LiveProp(writable: true)]
    public string $countryCode = '';

    #[LiveProp(writable: true)]
    public string $birthYear = '';

    #[LiveProp]
    public ?string $pageRoute = null;

    /** @var array<string, mixed> */
    #[LiveProp]
    public array $pageRouteParameters = [];

    #[LiveProp]
    public string $pageQueryString = '';

    public ?string $errorMessage = null;
    public bool $underage = false;
    public bool $validated = false;

    public function __construct(
        private readonly AgeVerificationConfigRepository $configRepository,
        private readonly LocaleProvider $localeProvider,
        private readonly LocalizedPageRouteResolver $localizedPageRouteResolver,
        private readonly RequestStack $requestStack,
        private readonly RouterInterface $router,
        private readonly TranslatorInterface $translator,
    ) {
    }

    public function mount(): void
    {
        $request = $this->requestStack->getCurrentRequest();
        if ('' === $this->countryCode) {
            $this->countryCode = $this->configRepository->findOne()?->getDefaultCountryCode() ?? 'FR';
        }

        if (null === $this->pageRoute) {
            $route = $request?->attributes->get('_canonical_route');
            if (!\is_string($route) || '' === $route) {
                $route = $request?->attributes->get('_route');
                if (\is_string($route)) {
                    $route = preg_replace('/\.[a-z]{2}$/', '', $route);
                }
            }

            $this->pageRoute = \is_string($route) && '' !== $route ? $route : null;
            $routeParameters = $request?->attributes->get('_route_params', []);
            $this->pageRouteParameters = \is_array($routeParameters) ? $routeParameters : [];
            unset($this->pageRouteParameters['_locale']);
        }

        if ('' === $this->pageQueryString) {
            $queryString = $request?->server->get('QUERY_STRING');
            $this->pageQueryString = \is_string($queryString) ? $queryString : '';
        }
    }

    #[LiveAction]
    public function validateAge(): void
    {
        $this->errorMessage = null;
        $this->underage = false;

        $request = $this->requestStack->getCurrentRequest();
        $config = $this->configRepository->findOne();
        if (null === $request || null === $config || !$config->isEnabled()) {
            $this->validated = true;

            return;
        }

        $birthYear = mb_trim($this->birthYear);
        if (!Countries::exists($this->countryCode) || '' === $birthYear) {
            $this->errorMessage = $this->translator->trans('age_verification.error.required');

            return;
        }

        if (1 !== preg_match('/^[0-9]{4}$/', $birthYear)) {
            $this->errorMessage = $this->translator->trans('age_verification.error.invalid_year');

            return;
        }

        $birthYearValue = (int) $birthYear;
        $currentYear = (int) new \DateTimeImmutable()->format('Y');
        if ($birthYearValue < $currentYear - 120 || $birthYearValue > $currentYear - 1) {
            $this->errorMessage = $this->translator->trans('age_verification.error.invalid_year');

            return;
        }

        $minimumAge = $config->resolveMinimumAge($this->countryCode);
        if ($currentYear - $birthYearValue < $minimumAge) {
            $this->underage = true;
            $this->errorMessage = $this->translator->trans('age_verification.error.underage', [
                '%age%' => $minimumAge,
            ]);

            return;
        }

        $request->attributes->set(
            AgeVerificationProofManager::REQUEST_REMEMBER_DAYS,
            $config->getRememberForDays(),
        );
        $request->attributes->set(
            AgeVerificationProofManager::REQUEST_CONFIG_FINGERPRINT,
            $config->getProofFingerprint(),
        );
        $this->birthYear = '';
        $this->countryCode = '';
        $this->validated = true;
    }

    /** @return array<string, string> */
    public function getPreferredCountries(): array
    {
        $countries = Countries::getNames($this->getCurrentLocale());
        $preferredCountries = [];

        foreach (self::PREFERRED_COUNTRY_CODES as $countryCode) {
            if (isset($countries[$countryCode])) {
                $preferredCountries[$countryCode] = $countries[$countryCode];
            }
        }

        return $preferredCountries;
    }

    /** @return array<string, string> */
    public function getOtherCountries(): array
    {
        return array_diff_key(
            Countries::getNames($this->getCurrentLocale()),
            array_flip(self::PREFERRED_COUNTRY_CODES),
        );
    }

    /** @return array<string, array{name: string, url: string}> */
    public function getLanguageOptions(): array
    {
        if (null !== $this->pageRoute) {
            $languageOptions = [];
            $localizedRoutes = $this->localizedPageRouteResolver->resolve(
                $this->pageRoute,
                $this->pageRouteParameters,
                $this->getCurrentLocale(),
            );

            foreach ($localizedRoutes->routes as $localeCode => $localizedRoute) {
                $languageOptions[$localeCode] = [
                    'name' => $localizedRoute->languageName,
                    'url' => $this->appendCurrentQueryString($localizedRoute->path),
                ];
            }

            return $languageOptions;
        }

        $languageOptions = [];
        foreach ($this->localeProvider->getLocales() as $localeCode) {
            if (!\is_string($localeCode) || '' === $localeCode) {
                continue;
            }

            try {
                $languageOptions[$localeCode] = [
                    'name' => Languages::getName($localeCode, $localeCode) ?: mb_strtoupper($localeCode),
                    'url' => $this->appendCurrentQueryString(
                        $this->router->generate('setLocale', ['locale' => $localeCode]),
                    ),
                ];
            } catch (RouteNotFoundException) {
                continue;
            }
        }

        return $languageOptions;
    }

    public function getCurrentLocale(): string
    {
        return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr';
    }

    public function getRequiredAge(): int
    {
        return $this->configRepository->findOne()?->resolveMinimumAge($this->countryCode) ?? 21;
    }

    private function appendCurrentQueryString(string $path): string
    {
        if ('' === $this->pageQueryString) {
            return $path;
        }

        return $path.(str_contains($path, '?') ? '&' : '?').$this->pageQueryString;
    }
}
