<?php

declare(strict_types=1);

namespace App\Security;

use App\Entity\Employee;
use App\Entity\User;
use Doctrine\ORM\QueryBuilder;

class EmployeeScopeService
{
    public const string SITE_ACCESS_ROLE = 'SITE_ACCESS';

    /**
     * Règle métier centrale d'accès à un employé.
     * - Admin : accès global
     * - Self : uniquement son employé lié
     * - Site : uniquement les employés des sites autorisés.
     */
    public function canAccessEmployee(User $user, Employee $employee): bool
    {
        if (in_array('ROLE_ADMIN', $user->getRoles(), true)) {
            return true;
        }

        if (in_array('ROLE_SELF', $user->getRoles(), true)) {
            return $user->getEmployee()?->getId() === $employee->getId();
        }

        $employeeSiteId = $employee->getSite()?->getId();
        if (!$employeeSiteId) {
            return false;
        }

        return in_array($employeeSiteId, $this->getAllowedSiteIds($user), true);
    }

    /**
     * Retourne la liste des sites accessibles via user_site_roles.
     *
     * @return list<int>
     */
    public function getAllowedSiteIds(User $user): array
    {
        $siteIds = [];

        foreach ($user->getSiteRoles() as $siteRole) {
            if (self::SITE_ACCESS_ROLE !== $siteRole->getRole()) {
                continue;
            }

            $siteId = $siteRole->getSite()?->getId();
            if ($siteId) {
                $siteIds[] = $siteId;
            }
        }

        return array_values(array_unique($siteIds));
    }

    /**
     * Applique le scope directement sur un QueryBuilder Doctrine.
     * Cette méthode est utilisée pour filtrer les collections SQL
     * avant sérialisation (plus sûr et plus performant).
     */
    public function applyEmployeeScope(QueryBuilder $qb, string $employeeAlias, string $paramPrefix, User $user): void
    {
        if (in_array('ROLE_ADMIN', $user->getRoles(), true)) {
            return;
        }

        if (in_array('ROLE_SELF', $user->getRoles(), true)) {
            $employeeId = $user->getEmployee()?->getId();
            if (!$employeeId) {
                $qb->andWhere('1 = 0');

                return;
            }

            $qb->andWhere(sprintf('%s.id = :%s_employee_id', $employeeAlias, $paramPrefix))
                ->setParameter(sprintf('%s_employee_id', $paramPrefix), $employeeId)
            ;

            return;
        }

        $siteIds = $this->getAllowedSiteIds($user);
        if ([] === $siteIds) {
            $qb->andWhere('1 = 0');

            return;
        }

        $qb->andWhere(sprintf('%s.site IN (:%s_site_ids)', $employeeAlias, $paramPrefix))
            ->setParameter(sprintf('%s_site_ids', $paramPrefix), $siteIds)
        ;
    }
}
