<?php
/**
 * @author      Alex Bilbie <hello@alexbilbie.com>
 * @copyright   Copyright (c) Alex Bilbie
 * @license     http://mit-license.org/
 *
 * @link        https://github.com/thephpleague/oauth2-server
 */

namespace Docuform\Api\Authentication\Repositories;

use DI\DependencyException;
use DI\NotFoundException;
use Docuform\Database\DbException;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use Docuform\Api\Authentication\Entities\ApiClientEntity;
use Docuform\Api\Authentication\Entities\ScopeEntity;
use DI\Container;
use Docuform\Api\RestApi;
use Docuform\Api\UserType;
use Docuform\Api\ApiScopes;

class ScopeRepository implements ScopeRepositoryInterface
{
  private Container $m_container;

  public function __construct($container)
  {
    $this->m_container = $container;
  }

  /**
   * {@inheritdoc}
   */
  public function getScopeEntityByIdentifier(string $identifier): ScopeEntity|ScopeEntityInterface|null
  {
    $scopes = ApiScopes::getScopes();

    if (in_array($identifier, $scopes) === false)
    {
      return null;
    }

    $scope = new ScopeEntity();
    $scope->setIdentifier($identifier);

    return $scope;
  }

  /**
   * @param array                 $scopes
   * @param string                $grantType
   * @param ClientEntityInterface $clientEntity
   * @param string|null           $userIdentifier
   * @param string|null           $authCodeId
   *
   * @return array|ScopeEntityInterface[]
   *
   * @throws OAuthServerException
   * @throws DependencyException
   * @throws NotFoundException
   * @throws DbException
   */
  public function finalizeScopes(
      array $scopes,
      string $grantType,
      ClientEntityInterface $clientEntity,
      string|null $userIdentifier = null,
      ?string $authCodeId=null): array
  {
    $scopesResult = array();

    /** @var ApiClientEntity $client */
    $client = $clientEntity;

    /** @var RestApi $restApi */
    $restApi = $this->m_container->get("RestApi");

    if ($restApi->getLicense()->isActive())
    {
      $allowedScopes = explode (' ', $client->getScopes());

      $scopesWanted = array();

      /** @var ScopeEntity $scopeObject */
      foreach ($scopes as $scopeObject)
      {
        $scopesWanted[] = $scopeObject->getIdentifier();
      }

      $scopesIntersect = array_values(array_intersect($allowedScopes, $scopesWanted));

      // Allowed scopes by user
      if (!empty($userIdentifier))
      {
        $allowedScopesUser = $allowedScopes;

        $userRepo = new UserRepository($this->m_container);

        $user = $userRepo->getUserEntityByUuid($userIdentifier);

        if ($user !== false)
        {
          if (!$user->isAdmin)
          {
            if (($key = array_search(ApiScopes::admin, $allowedScopesUser)) !== false)
            {
              unset($allowedScopesUser[$key]);
            }

            if ($user->isReadOnly)
            {
              if (($key = array_search(ApiScopes::server_write, $allowedScopesUser)) !== false)
              {
                unset($allowedScopesUser[$key]);
              }

              if (($key = array_search(ApiScopes::client_write, $allowedScopesUser)) !== false)
              {
                unset($allowedScopesUser[$key]);
              }
            }

            if ($user->userType == UserType::USERTYPE_USER || $user->isReadOnly)
            {
              if (($key = array_search(ApiScopes::bulk, $allowedScopesUser)) !== false)
              {
                unset($allowedScopesUser[$key]);
              }
            }

            if ($user->userType != UserType::USERTYPE_SERVICEACCOUNT &&
              $user->userType != UserType::USERTYPE_CUSTOMER && $user->userType != UserType::USERTYPE_DEALER)
            {
              if (($key = array_search(ApiScopes::accounting, $allowedScopesUser)) !== false)
              {
                unset($allowedScopesUser[$key]);
              }
            }
          }

          $scopesIntersect = array_values(array_intersect($allowedScopesUser, $scopesIntersect));
        }
        else
        {
          throw OAuthServerException::invalidGrant();
        }
      }

      if (!$restApi->getLicense()->hasEShop())
      {
        if (($key = array_search(ApiScopes::eshop, $scopesIntersect)) !== false)
        {
          unset($scopesIntersect[$key]);
        }
      }

      sort ($scopesIntersect);

      foreach ($scopesIntersect as $scope)
      {
        $scopeEntity = new ScopeEntity();

        $scopeEntity->setIdentifier($scope);
        $scopesResult[] = $scopeEntity;
      }
    }

    return $scopesResult;
  }
}