<?php

declare(strict_types=1);

namespace App\Module\Absence\Infrastructure\ApiPlatform\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Module\Absence\Domain\Entity\AbsenceBalance;
use App\Module\Absence\Domain\Repository\AbsenceBalanceRepositoryInterface;
use App\Shared\Domain\Contract\UserInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;

/**
 * @implements ProviderInterface<AbsenceBalance>
 */
final readonly class AbsenceBalanceProvider implements ProviderInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private AbsenceBalanceRepositoryInterface $balanceRepository,
        private Security $security,
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): AbsenceBalance|array|null
    {
        $user = $this->security->getUser();
        assert($user instanceof UserInterface);

        $isAdmin = $this->security->isGranted('ROLE_ADMIN');

        if (isset($uriVariables['id'])) {
            $balance = $this->balanceRepository->findById((int) $uriVariables['id']);
            if (null === $balance) {
                return null;
            }
            if (!$isAdmin && $balance->getUser() !== $user) {
                return null;
            }

            return $balance;
        }

        $qb = $this->entityManager->getRepository(AbsenceBalance::class)
            ->createQueryBuilder('b')
            ->orderBy('b.type', 'ASC')
        ;

        if (!$isAdmin) {
            $qb->andWhere('b.user = :user')->setParameter('user', $user);
        }

        $filters = $context['filters'] ?? [];

        if (isset($filters['type'])) {
            $qb->andWhere('b.type = :type')->setParameter('type', $filters['type']);
        }
        if (isset($filters['period'])) {
            $qb->andWhere('b.period = :period')->setParameter('period', $filters['period']);
        }
        if ($isAdmin && isset($filters['user'])) {
            $qb->andWhere('b.user = :filterUser')
                ->setParameter('filterUser', self::extractId($filters['user']))
            ;
        }

        return $qb->getQuery()->getResult();
    }

    private static function extractId(string $value): int
    {
        return is_numeric($value) ? (int) $value : (int) basename($value);
    }
}
