<?php

namespace App\Doctrine;

use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Entity\OrganizationalUnit;
use App\Entity\User;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bundle\SecurityBundle\Security;

readonly class UserAllowedOrganizationalUnitExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
    public function __construct(
        private Security $security,
    )
    {
    }
    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
    {
        $this->addWhere($queryBuilder, $resourceClass);
    }

    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
    {
        $this->addWhere($queryBuilder, $resourceClass);
    }

    private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
    {
        /** @var User $user */
        $user = $this->security->getUser();

        if (OrganizationalUnit::class !== $resourceClass || null === $user || in_array('ROLE_SUPER_ADMIN', $user->getRoles())) {
            return;
        }

        $organizationalUnitIds = [];
        foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) {
            $this->addOrganizationalUnitAndChildrenIds($allowedOrganizationalUnit, $organizationalUnitIds);
        }

        $rootAlias = $queryBuilder->getRootAliases()[0];
        $queryBuilder->andWhere(sprintf('%s.id in (:ou)', $rootAlias));
        $queryBuilder->setParameter('ou', $organizationalUnitIds);
    }

    private function addOrganizationalUnitAndChildrenIds(OrganizationalUnit $organizationalUnit, array &$organizationalUnitIds): void
    {
        if (!in_array($organizationalUnit->getId(), $organizationalUnitIds)) {
            $organizationalUnitIds[] = $organizationalUnit->getId();
        }

        foreach ($organizationalUnit->getOrganizationalUnits() as $child) {
            $this->addOrganizationalUnitAndChildrenIds($child, $organizationalUnitIds);
        }
    }

}