<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\WorkHourBulkSiteValidation;
use App\ApiResource\WorkHourBulkValidationResult;
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 App\Service\WorkHours\WorkHourBulkValidationExecutor;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

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

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

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

        if (in_array('ROLE_ADMIN', $user->getRoles(), true) || in_array('ROLE_SELF', $user->getRoles(), true)) {
            throw new AccessDeniedHttpException('Only site managers can bulk update site validation.');
        }

        $result = $this->executor->execute(
            user: $user,
            workDateValue: $data->workDate,
            employeeIds: $data->employeeIds,
            shouldSkip: static fn (WorkHour $workHour): bool => $workHour->isValid() || $workHour->isSiteValid() === $data->isSiteValid,
            applyUpdate: static function (WorkHour $workHour) use ($data): void {
                $workHour->setIsSiteValid($data->isSiteValid);
            }
        );

        if ($result->updated > 0) {
            $workDate = DateTimeImmutable::createFromFormat('Y-m-d', $data->workDate);
            $action   = $data->isSiteValid ? 'validé' : 'dévalidé';

            $this->auditLogger->log(
                null,
                'site_validate',
                'work_hour',
                null,
                sprintf('Validation site %s pour %d employé(s) le %s', $action, $result->updated, $data->workDate),
                ['employeeIds' => $data->employeeIds, 'isSiteValid' => $data->isSiteValid],
                $workDate ?: null,
            );
        }

        if ($data->isSiteValid && $result->updated > 0) {
            $this->createNotificationsIfSiteFullyValidated($user, $data->workDate);
        }

        return $result;
    }

    private function createNotificationsIfSiteFullyValidated(User $user, string $workDateValue): void
    {
        $workDate = DateTimeImmutable::createFromFormat('Y-m-d', $workDateValue);
        if (!$workDate) {
            return;
        }

        $siteIds = $this->employeeScopeService->getAllowedSiteIds($user);

        foreach ($siteIds as $siteId) {
            if ($this->workHourRepository->hasPendingSiteValidationForSiteAndDate($siteId, $workDate)) {
                continue;
            }

            $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();
    }
}
