<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\ContractSuspension;
use App\Entity\EmployeeContractPeriod;
use App\Service\AuditLogger;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

final readonly class ContractSuspensionWriteProcessor implements ProcessorInterface
{
    public function __construct(
        #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
        private ProcessorInterface $persistProcessor,
        private EntityManagerInterface $entityManager,
        private AuditLogger $auditLogger,
    ) {}

    public function process(
        mixed $data,
        Operation $operation,
        array $uriVariables = [],
        array $context = []
    ): mixed {
        if (!$data instanceof ContractSuspension) {
            return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
        }

        $period = $data->getContractPeriod();

        if (!$period instanceof EmployeeContractPeriod && null !== $data->getContractPeriodId()) {
            $period = $this->entityManager->find(EmployeeContractPeriod::class, $data->getContractPeriodId());
            if ($period instanceof EmployeeContractPeriod) {
                $data->setContractPeriod($period);
            }
        }

        if (!$period instanceof EmployeeContractPeriod) {
            throw new UnprocessableEntityHttpException('contractPeriodId is required.');
        }

        $this->validate($data, $period);

        $isNew    = null === $data->getId();
        $employee = $period->getEmployee();
        $empName  = $employee ? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? '')) : '';
        $start    = $data->getStartDate()->format('d/m/Y');
        $end      = $data->getEndDate()?->format('d/m/Y') ?? 'indéfinie';

        $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);

        $this->auditLogger->log(
            $employee,
            $isNew ? 'create' : 'update',
            'contract_suspension',
            $data->getId(),
            sprintf('Suspension %s pour %s du %s au %s', $isNew ? 'créée' : 'modifiée', $empName, $start, $end),
            ['new' => ['start' => $start, 'end' => $end]],
            DateTimeImmutable::createFromInterface($data->getStartDate()),
        );
        $this->entityManager->flush();

        return $result;
    }

    private function validate(ContractSuspension $suspension, EmployeeContractPeriod $period): void
    {
        // Compare as Y-m-d strings to avoid timezone issues between Doctrine and API Platform DateTimeImmutable
        $startDate   = $suspension->getStartDate()->format('Y-m-d');
        $endDate     = $suspension->getEndDate()?->format('Y-m-d');
        $periodStart = $period->getStartDate()->format('Y-m-d');
        $periodEnd   = $period->getEndDate()?->format('Y-m-d');

        if (null !== $periodEnd && $periodEnd < new DateTimeImmutable('today')->format('Y-m-d')) {
            throw new UnprocessableEntityHttpException('Impossible de suspendre une période de contrat clôturée.');
        }

        if (null !== $endDate && $endDate < $startDate) {
            throw new UnprocessableEntityHttpException('La date de fin doit être postérieure à la date de début.');
        }

        if ($startDate < $periodStart) {
            throw new UnprocessableEntityHttpException('La suspension ne peut pas commencer avant le début du contrat.');
        }

        if (null !== $periodEnd) {
            if ($startDate > $periodEnd) {
                throw new UnprocessableEntityHttpException('La suspension ne peut pas commencer après la fin du contrat.');
            }
            if (null !== $endDate && $endDate > $periodEnd) {
                throw new UnprocessableEntityHttpException('La suspension ne peut pas se terminer après la fin du contrat.');
            }
        }

        $this->validateNoOverlap($suspension, $period);
    }

    private function validateNoOverlap(ContractSuspension $suspension, EmployeeContractPeriod $period): void
    {
        $start = $suspension->getStartDate()->format('Y-m-d');
        $end   = $suspension->getEndDate()?->format('Y-m-d');

        foreach ($period->getSuspensions() as $existing) {
            if ($existing->getId() === $suspension->getId() && null !== $suspension->getId()) {
                continue;
            }

            $existingStart = $existing->getStartDate()->format('Y-m-d');
            $existingEnd   = $existing->getEndDate()?->format('Y-m-d');

            if (null === $end && null === $existingEnd) {
                throw new UnprocessableEntityHttpException('Les suspensions ne peuvent pas se chevaucher.');
            }

            if (null === $end) {
                if ($start <= $existingEnd) {
                    throw new UnprocessableEntityHttpException('Les suspensions ne peuvent pas se chevaucher.');
                }

                continue;
            }

            if (null === $existingEnd) {
                if ($existingStart <= $end) {
                    throw new UnprocessableEntityHttpException('Les suspensions ne peuvent pas se chevaucher.');
                }

                continue;
            }

            if ($start <= $existingEnd && $end >= $existingStart) {
                throw new UnprocessableEntityHttpException('Les suspensions ne peuvent pas se chevaucher.');
            }
        }
    }
}
