<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\Security;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

/**
 * PS does not authenticate the logged in employee in the Security component if the request is handled by the legacy dispatcher,
 * so we do it ourselves if we need to check user permissions.
 *
 * @see \PrestaShop\PrestaShop\Adapter\Security\Admin::onKernelRequest
 *
 * This decorator is used only in the legacy context. To be removed when we drop support for PS 1.7.6.
 *
 * @internal
 */
final class AuthorizationChecker implements AuthorizationCheckerInterface
{
    private $context;
    private $authorizationChecker;
    private $tokenStorage;
    private $userProvider;

    public function __construct(\Context $context, AuthorizationCheckerInterface $authorizationChecker, TokenStorageInterface $tokenStorage, UserProviderInterface $userProvider)
    {
        $this->context = $context;
        $this->authorizationChecker = $authorizationChecker;
        $this->tokenStorage = $tokenStorage;
        $this->userProvider = $userProvider;
    }

    public function isGranted($attributes, $subject = null): bool
    {
        $this->authenticate();

        try {
            return $this->authorizationChecker->isGranted($attributes, $subject);
        } catch (AuthenticationCredentialsNotFoundException $e) {
            return false;
        }
    }

    private function authenticate(): void
    {
        if (null !== $this->tokenStorage->getToken()) {
            return;
        }

        $employee = $this->context->employee;

        if (null === $employee || !$employee->isLoggedBack()) {
            return;
        }

        try {
            $user = $this->userProvider->loadUserByUsername((string) $employee->email);
        } catch (UsernameNotFoundException $e) {
            return;
        }

        $token = new UsernamePasswordToken($user, null, 'admin', $user->getRoles());

        $this->tokenStorage->setToken($token);
    }
}
