<?php

declare(strict_types=1);

namespace App\Tests\State;

use ApiPlatform\Metadata\Post;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\ContractSuspension;
use App\Entity\EmployeeContractPeriod;
use App\Enum\ContractNature;
use App\Service\AuditLogger;
use App\State\ContractSuspensionWriteProcessor;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

/**
 * @internal
 */
final class ContractSuspensionWriteProcessorTest extends TestCase
{
    public function testPersistsValidSuspension(): void
    {
        $period     = $this->buildPeriodWithId(1, '2026-01-01', null);
        $suspension = new ContractSuspension();
        $suspension->setContractPeriod($period);
        $suspension->setStartDate(new DateTimeImmutable('2026-03-01'));
        $suspension->setEndDate(new DateTimeImmutable('2026-04-30'));
        $suspension->setComment('Congé sans solde');

        $persistProcessor = $this->createMock(ProcessorInterface::class);
        $persistProcessor->expects(self::once())->method('process')->willReturn($suspension);

        $entityManager = $this->createStub(EntityManagerInterface::class);

        $processor = new ContractSuspensionWriteProcessor($persistProcessor, $entityManager, $this->createStub(AuditLogger::class));
        $result    = $processor->process($suspension, new Post());

        self::assertSame($suspension, $result);
    }

    public function testRejectsEndDateBeforeStartDate(): void
    {
        $period     = $this->buildPeriodWithId(1, '2026-01-01', null);
        $suspension = new ContractSuspension();
        $suspension->setContractPeriod($period);
        $suspension->setStartDate(new DateTimeImmutable('2026-05-01'));
        $suspension->setEndDate(new DateTimeImmutable('2026-03-01'));

        $persistProcessor = $this->createStub(ProcessorInterface::class);
        $entityManager    = $this->createStub(EntityManagerInterface::class);

        $processor = new ContractSuspensionWriteProcessor($persistProcessor, $entityManager, $this->createStub(AuditLogger::class));

        $this->expectException(UnprocessableEntityHttpException::class);
        $processor->process($suspension, new Post());
    }

    public function testRejectsStartDateBeforePeriodStart(): void
    {
        $period     = $this->buildPeriodWithId(1, '2026-06-01', null);
        $suspension = new ContractSuspension();
        $suspension->setContractPeriod($period);
        $suspension->setStartDate(new DateTimeImmutable('2026-01-01'));

        $persistProcessor = $this->createStub(ProcessorInterface::class);
        $entityManager    = $this->createStub(EntityManagerInterface::class);

        $processor = new ContractSuspensionWriteProcessor($persistProcessor, $entityManager, $this->createStub(AuditLogger::class));

        $this->expectException(UnprocessableEntityHttpException::class);
        $processor->process($suspension, new Post());
    }

    public function testRejectsOverlappingSuspension(): void
    {
        $period = $this->buildPeriodWithId(1, '2026-01-01', null);

        $existing = new ContractSuspension();
        $existing->setContractPeriod($period);
        $existing->setStartDate(new DateTimeImmutable('2026-03-01'));
        $existing->setEndDate(new DateTimeImmutable('2026-04-30'));
        $period->getSuspensions()->add($existing);

        $suspension = new ContractSuspension();
        $suspension->setContractPeriod($period);
        $suspension->setStartDate(new DateTimeImmutable('2026-04-01'));
        $suspension->setEndDate(new DateTimeImmutable('2026-05-31'));

        $persistProcessor = $this->createStub(ProcessorInterface::class);
        $entityManager    = $this->createStub(EntityManagerInterface::class);

        $processor = new ContractSuspensionWriteProcessor($persistProcessor, $entityManager, $this->createStub(AuditLogger::class));

        $this->expectException(UnprocessableEntityHttpException::class);
        $processor->process($suspension, new Post());
    }

    public function testRejectsClosedPeriod(): void
    {
        $period     = $this->buildPeriodWithId(1, '2025-01-01', '2025-12-31');
        $suspension = new ContractSuspension();
        $suspension->setContractPeriod($period);
        $suspension->setStartDate(new DateTimeImmutable('2025-06-01'));
        $suspension->setEndDate(new DateTimeImmutable('2025-07-31'));

        $persistProcessor = $this->createStub(ProcessorInterface::class);
        $entityManager    = $this->createStub(EntityManagerInterface::class);

        $processor = new ContractSuspensionWriteProcessor($persistProcessor, $entityManager, $this->createStub(AuditLogger::class));

        $this->expectException(UnprocessableEntityHttpException::class);
        $processor->process($suspension, new Post());
    }

    private function buildPeriodWithId(int $id, string $start, ?string $end): EmployeeContractPeriod
    {
        $period = new EmployeeContractPeriod();
        $period->setStartDate(new DateTimeImmutable($start));
        if (null !== $end) {
            $period->setEndDate(new DateTimeImmutable($end));
        }
        $period->setContractNature(ContractNature::CDI);

        $ref = new ReflectionProperty($period, 'id');
        $ref->setValue($period, $id);

        return $period;
    }
}
