<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Entity\Employee;
use App\Entity\User;
use App\Enum\ContractType;
use App\Repository\EmployeeRepository;
use App\Service\Contracts\EmployeeContractResolver;
use App\Service\WorkHours\OvertimeContingentExportBuilder;
use DateTimeImmutable;
use Dompdf\Dompdf;
use Dompdf\Options;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Twig\Environment;

final class OvertimeContingentPrintProvider implements ProviderInterface
{
    public function __construct(
        private Environment $twig,
        private readonly RequestStack $requestStack,
        private EmployeeRepository $employeeRepository,
        private OvertimeContingentExportBuilder $exportBuilder,
        private EmployeeContractResolver $contractResolver,
        private readonly Security $security,
    ) {}

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

        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return new Response('Missing request.', Response::HTTP_BAD_REQUEST);
        }

        $year = (int) $request->query->get('year', (string) (int) new DateTimeImmutable('now')->format('Y'));
        if ($year < 2000 || $year > 2100) {
            throw new UnprocessableEntityHttpException('year must be between 2000 and 2100.');
        }

        $from = new DateTimeImmutable(sprintf('%d-01-01', $year));
        $to   = new DateTimeImmutable(sprintf('%d-12-31', $year));

        // Filtre sites optionnel (vide = tout le perimetre).
        $rawSiteIds = (string) $request->query->get('siteIds', '');
        $siteIds    = array_values(array_filter(array_map('intval', array_filter(explode(',', $rawSiteIds), 'strlen'))));

        // Perimetre selon le profil : admin -> tous, chef de site -> ses sites.
        $employees = $this->employeeRepository->findScoped($user);

        $today    = new DateTimeImmutable('today');
        $bySite   = [];
        $siteMeta = [];
        foreach ($employees as $employee) {
            if (!$this->hasContractInRange($employee, $from, $to)) {
                continue;
            }
            // Exclure les forfait (contrat courant).
            $currentContract = $this->contractResolver->resolveForEmployeeAndDate($employee, $today);
            if (null !== $currentContract && ContractType::FORFAIT === $currentContract->getType()) {
                continue;
            }
            $site = $employee->getSite();
            if (null === $site) {
                continue;
            }
            $siteId = $site->getId();
            if ([] !== $siteIds && !in_array($siteId, $siteIds, true)) {
                continue;
            }
            $bySite[$siteId][] = $employee;
            $siteMeta[$siteId] ??= [
                'name'  => $site->getName(),
                'order' => $site->getDisplayOrder(),
                'color' => $site->getColor(),
            ];
        }

        uasort($siteMeta, static function (array $a, array $b): int {
            return [$a['order'], $a['name']] <=> [$b['order'], $b['name']];
        });

        $groups = [];
        foreach ($siteMeta as $siteId => $meta) {
            $siteEmployees = $bySite[$siteId];
            // Meme tri que le calendrier : displayOrder, puis nom, puis prenom.
            usort($siteEmployees, static function (Employee $a, Employee $b): int {
                return [$a->getDisplayOrder(), $a->getLastName(), $a->getFirstName()]
                    <=> [$b->getDisplayOrder(), $b->getLastName(), $b->getFirstName()];
            });

            $rows = $this->exportBuilder->buildRows($siteEmployees, $year);

            $renderRows = [];
            foreach ($rows as $row) {
                $cells = [];
                for ($m = 1; $m <= 12; ++$m) {
                    $cells[] = $row->months[$m] > 0 ? $this->formatMinutes($row->months[$m]) : '—';
                }
                $renderRows[] = [
                    'employeeName' => $row->employeeName,
                    'cells'        => $cells,
                    'totalHours'   => $this->formatMinutes($row->totalMinutes),
                    'capHours'     => $row->capHours,
                    'exceeded'     => $row->totalMinutes > $row->capHours * 60,
                ];
            }

            $groups[] = ['siteName' => $meta['name'], 'siteColor' => $meta['color'], 'rows' => $renderRows];
        }

        $options = new Options();
        $options->set('isRemoteEnabled', true);
        $dompdf = new Dompdf($options);

        $html = $this->twig->render('overtime-contingent/print.html.twig', [
            'groups'     => $groups,
            'year'       => $year,
            'exportedAt' => new DateTimeImmutable('now')->format('d/m/Y H:i'),
        ]);

        $dompdf->loadHtml($html);
        $dompdf->setPaper('A4', 'landscape');
        $dompdf->render();

        $filename = sprintf('contingent_heures_supp_%d.pdf', $year);

        return new Response($dompdf->output(), Response::HTTP_OK, [
            'Content-Type'        => 'application/pdf',
            'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
        ]);
    }

    private function hasContractInRange(Employee $employee, DateTimeImmutable $from, DateTimeImmutable $to): bool
    {
        $fromDay = $from->format('Y-m-d');
        $toDay   = $to->format('Y-m-d');

        foreach ($employee->getContractPeriods() as $period) {
            $start = $period->getStartDate()->format('Y-m-d');
            $end   = $period->getEndDate()?->format('Y-m-d');
            if ($start <= $toDay && (null === $end || $end >= $fromDay)) {
                return true;
            }
        }

        return false;
    }

    private function formatMinutes(int $minutes): string
    {
        $h = intdiv($minutes, 60);
        $m = $minutes % 60;

        return sprintf('%dh%02d', $h, $m);
    }
}
