<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\EmployeePaidLeaveDaysInput;
use App\Entity\Employee;
use App\Entity\EmployeeLeaveBalance;
use App\Enum\ContractType;
use App\Enum\LeaveRuleCode;
use App\Repository\EmployeeLeaveBalanceRepository;
use App\Repository\EmployeeRepository;
use App\Service\AuditLogger;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

final readonly class EmployeePaidLeaveDaysProcessor implements ProcessorInterface
{
    public function __construct(
        private EmployeeRepository $employeeRepository,
        private EmployeeLeaveBalanceRepository $leaveBalanceRepository,
        private EntityManagerInterface $entityManager,
        private AuditLogger $auditLogger,
    ) {}

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

        $employeeId = (int) ($uriVariables['id'] ?? 0);
        if ($employeeId <= 0) {
            throw new UnprocessableEntityHttpException('id must be a positive integer.');
        }

        $employee = $this->employeeRepository->find($employeeId);
        if (!$employee instanceof Employee) {
            throw new NotFoundHttpException('Employee not found.');
        }

        $year     = $data->year ?? $this->resolveCurrentYear($employee);
        $ruleCode = $this->resolveRuleCode($employee);

        $balance = $this->leaveBalanceRepository->findOneByEmployeeRuleAndYear($employee, $ruleCode, $year);

        if (null === $balance) {
            $balance = new EmployeeLeaveBalance();
            $balance->setEmployee($employee);
            $balance->setRuleCode($ruleCode);
            $balance->setYear($year);
            $this->entityManager->persist($balance);
        }

        $balance->setPaidLeaveDays($data->paidLeaveDays);
        $balance->touch();

        $empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
        $this->auditLogger->log(
            $employee,
            'update',
            'paid_leave_days',
            $balance->getId(),
            sprintf('Congés N-1 payés modifiés pour %s (année %d) : %s', $empName, $year, (string) $data->paidLeaveDays),
            ['new' => ['paidLeaveDays' => $data->paidLeaveDays, 'year' => $year]],
        );

        $this->entityManager->flush();

        $data->year = $year;

        return $data;
    }

    private function resolveRuleCode(Employee $employee): LeaveRuleCode
    {
        if (ContractType::FORFAIT === $employee->getContract()?->getType()) {
            return LeaveRuleCode::FORFAIT_218;
        }

        return LeaveRuleCode::CDI_CDD_NON_FORFAIT;
    }

    private function resolveCurrentYear(Employee $employee): int
    {
        $today = new DateTimeImmutable('today');

        if (ContractType::FORFAIT === $employee->getContract()?->getType()) {
            return (int) $today->format('Y');
        }

        $month = (int) $today->format('n');

        return $month >= 6 ? (int) $today->format('Y') + 1 : (int) $today->format('Y');
    }
}
