<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\EmployeeLeaveRecap;
use App\Entity\Contract;
use App\Entity\Employee;
use App\Entity\User;
use App\Enum\ContractType;
use App\Repository\EmployeeRepository;
use App\Security\EmployeeScopeService;
use App\Service\Leave\LeaveRecapRowBuilder;
use App\Util\LeaveRecapCutoff;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

final readonly class EmployeeLeaveRecapProvider implements ProviderInterface
{
    public function __construct(
        private Security $security,
        private EmployeeRepository $employeeRepository,
        private EmployeeScopeService $employeeScopeService,
        private LeaveRecapRowBuilder $rowBuilder,
        private EntityManagerInterface $entityManager,
    ) {}

    /**
     * @return list<EmployeeLeaveRecap>
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
    {
        $user = $this->security->getUser();
        if (!$user instanceof User) {
            throw new AccessDeniedHttpException('Authentication required.');
        }

        if (!$user->hasLeaveRecapAccess()) {
            throw new AccessDeniedHttpException('Leave recap access not granted.');
        }

        $cutoff    = LeaveRecapCutoff::resolveCutoff(new DateTimeImmutable('today'));
        $cutoffYmd = $cutoff->format('Y-m-d');
        $employees = $this->resolveScopedEmployees($user);
        $rows      = [];

        foreach ($employees as $employee) {
            if (!$employee->getHasActiveContract()) {
                continue;
            }

            $row = $this->rowBuilder->build($employee, $cutoff);

            $resource                    = new EmployeeLeaveRecap();
            $resource->employeeId        = (int) $employee->getId();
            $resource->lastName          = $row['lastName'] ?? '';
            $resource->firstName         = $row['firstName'] ?? '';
            $site                        = $employee->getSite();
            $resource->siteId            = $site?->getId();
            $resource->siteName          = $site?->getName();
            $resource->siteColor         = $site?->getColor();
            $resource->contractName      = $row['contractName'] ?? null;
            $resource->contractSortKey   = $this->resolveContractSortKey($employee->getContract());
            $resource->cpN1Remaining     = is_numeric($row['cpN1Remaining']) ? (float) $row['cpN1Remaining'] : 0.0;
            $resource->cpN               = (string) $row['cpN'];
            $resource->acquiredSaturdays = (string) $row['acquiredSaturdays'];
            $resource->rtt               = (string) $row['rtt'];
            $resource->cutoffDate        = $cutoffYmd;

            $rows[] = $resource;
            $this->entityManager->clear();
        }

        usort($rows, static function (EmployeeLeaveRecap $a, EmployeeLeaveRecap $b): int {
            $siteCmp = strcmp((string) ($a->siteName ?? 'zzz'), (string) ($b->siteName ?? 'zzz'));
            if (0 !== $siteCmp) {
                return $siteCmp;
            }
            $contractCmp = $a->contractSortKey <=> $b->contractSortKey;
            if (0 !== $contractCmp) {
                return $contractCmp;
            }
            $lastCmp = strcmp($a->lastName, $b->lastName);
            if (0 !== $lastCmp) {
                return $lastCmp;
            }

            return strcmp($a->firstName, $b->firstName);
        });

        return $rows;
    }

    /**
     * Sort order: FORFAIT → 39h → 35h → 25h → 4h → autres.
     */
    private function resolveContractSortKey(?Contract $contract): int
    {
        if (null === $contract) {
            return 99;
        }

        if (ContractType::FORFAIT === $contract->getType()) {
            return 0;
        }

        $weeklyHours = $contract->getWeeklyHours();

        return match ($weeklyHours) {
            39      => 1,
            35      => 2,
            25      => 3,
            4       => 4,
            default => 99,
        };
    }

    /**
     * @return list<Employee>
     */
    private function resolveScopedEmployees(User $user): array
    {
        if (in_array('ROLE_ADMIN', $user->getRoles(), true)) {
            return $this->employeeRepository->findForPrintBySiteIds([]);
        }

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

            return $employee instanceof Employee ? [$employee] : [];
        }

        $siteIds = $this->employeeScopeService->getAllowedSiteIds($user);
        if ([] === $siteIds) {
            return [];
        }

        return $this->employeeRepository->findForPrintBySiteIds($siteIds);
    }
}
