<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Notification;
use App\Entity\User;
use App\Entity\WorkHour;
use App\Repository\UserRepository;
use App\Repository\WorkHourRepository;
use App\Security\EmployeeScopeService;
use App\Service\AuditLogger;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

final readonly class WorkHourSiteValidationProcessor implements ProcessorInterface
{
    public function __construct(
        private Security $security,
        private EmployeeScopeService $employeeScopeService,
        private WorkHourRepository $workHourRepository,
        private UserRepository $userRepository,
        private EntityManagerInterface $entityManager,
        private AuditLogger $auditLogger,
    ) {}

    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): WorkHour
    {
        if (!$data instanceof WorkHour) {
            throw new AccessDeniedHttpException('Invalid payload.');
        }

        $user = $this->security->getUser();
        if (!$user instanceof User) {
            throw new AccessDeniedHttpException('Authentication required.');
        }

        // Réservé aux profils "Sites" (ni admin, ni self).
        if (in_array('ROLE_ADMIN', $user->getRoles(), true) || in_array('ROLE_SELF', $user->getRoles(), true)) {
            throw new AccessDeniedHttpException('Only site managers can update site validation.');
        }

        $siteId = $data->getEmployee()?->getSite()?->getId();
        if (!$siteId) {
            throw new AccessDeniedHttpException('Employee site is required.');
        }

        $allowedSiteIds = $this->employeeScopeService->getAllowedSiteIds($user);
        if (!in_array($siteId, $allowedSiteIds, true)) {
            throw new AccessDeniedHttpException('Employee is outside your site scope.');
        }

        $uow = $this->entityManager->getUnitOfWork();
        $uow->computeChangeSets();
        $changeSet                     = $uow->getEntityChangeSet($data);
        $isSiteValidationChangedToTrue = isset($changeSet['isSiteValid'])
            && false === $changeSet['isSiteValid'][0]
            && true === $changeSet['isSiteValid'][1];

        if (isset($changeSet['isSiteValid'])) {
            $employee = $data->getEmployee();
            $empName  = $employee ? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? '')) : '';
            $workDate = $data->getWorkDate();
            $newVal   = $changeSet['isSiteValid'][1] ? 'validé' : 'dévalidé';

            $this->auditLogger->log(
                $employee,
                'site_validate',
                'work_hour',
                $data->getId(),
                sprintf('Validation site %s pour %s le %s', $newVal, $empName, $workDate->format('d/m/Y')),
                ['old' => ['isSiteValid' => $changeSet['isSiteValid'][0]], 'new' => ['isSiteValid' => $changeSet['isSiteValid'][1]]],
                $workDate instanceof DateTimeImmutable ? $workDate : DateTimeImmutable::createFromInterface($workDate),
            );
        }

        $this->entityManager->flush();

        // Notification uniquement quand la dernière ligne du site est validée pour la date.
        if ($isSiteValidationChangedToTrue) {
            $workDate   = $data->getWorkDate();
            $hasPending = $this->workHourRepository->hasPendingSiteValidationForSiteAndDate($siteId, $workDate);
            if (!$hasPending) {
                $message = 'a validé les heures';

                foreach ($this->userRepository->findAllAdmins() as $admin) {
                    $notification = new Notification();
                    $notification->setRecipient($admin)
                        ->setActor($user)
                        ->setMessage($message)
                        ->setCategory('Heures')
                        ->setTarget('/hours')
                    ;
                    $this->entityManager->persist($notification);
                }

                $this->entityManager->flush();
            }
        }

        return $data;
    }
}
