<?php

declare(strict_types=1);

namespace App\Tests\State;

use ApiPlatform\Metadata\Post;
use App\ApiResource\WorkHourBulkUpsert;
use App\Entity\Contract;
use App\Entity\Employee;
use App\Entity\User;
use App\Entity\WorkHour;
use App\Enum\TrackingMode;
use App\Repository\Contract\AbsenceReadRepositoryInterface;
use App\Repository\Contract\EmployeeScopedRepositoryInterface;
use App\Repository\Contract\WorkHourReadRepositoryInterface;
use App\Service\AuditLogger;
use App\Service\Contracts\EmployeeContractResolver;
use App\State\WorkHourBulkUpsertProcessor;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Security;

/**
 * Garde anti-perte de données : une entrée vide ne supprime une ligne existante
 * QUE si la suppression est explicitement demandée (flag `delete: true`).
 * Sans ce flag, une grille périmée (vieil onglet) ne peut plus rien détruire.
 *
 * @internal
 */
final class WorkHourBulkUpsertProcessorTest extends TestCase
{
    public function testEmptyEntryWithoutDeleteFlagPreservesExistingRow(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::never())->method('remove');
        $em->expects(self::once())->method('flush');

        $result = $this->buildProcessor($em)->process(
            $this->payload(['employeeId' => 7]),
            new Post(),
        );

        self::assertSame(0, $result->deleted);
        self::assertSame(1, $result->processed);
    }

    public function testEmptyEntryWithDeleteFlagRemovesExistingRow(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::once())->method('remove');
        $em->expects(self::once())->method('flush');

        $result = $this->buildProcessor($em)->process(
            $this->payload(['employeeId' => 7, 'delete' => true]),
            new Post(),
        );

        self::assertSame(1, $result->deleted);
    }

    public function testNonEmptyEntryStillUpdatesRegardlessOfFlag(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::never())->method('remove');
        $em->expects(self::once())->method('flush');

        $result = $this->buildProcessor($em)->process(
            $this->payload([
                'employeeId'    => 7,
                'morningFrom'   => '08:00',
                'morningTo'     => '12:30',
                'afternoonFrom' => '14:00',
                'afternoonTo'   => '18:00',
            ]),
            new Post(),
        );

        self::assertSame(1, $result->updated);
    }

    /**
     * @param array<string, mixed> $entry
     */
    private function payload(array $entry): WorkHourBulkUpsert
    {
        $payload           = new WorkHourBulkUpsert();
        $payload->workDate = '2026-06-24';
        $payload->entries  = [$entry];

        return $payload;
    }

    private function buildProcessor(EntityManagerInterface $em): WorkHourBulkUpsertProcessor
    {
        $user = new User()->setUsername('Elodie')->setRoles(['ROLE_ADMIN']);

        $employee = new Employee()->setFirstName('Delphine')->setLastName('BACHELIER');

        // Ligne existante NON vide (journée complète saisie entre-temps par un autre utilisateur).
        $existing = new WorkHour()
            ->setEmployee($employee)
            ->setWorkDate(new DateTimeImmutable('2026-06-24'))
            ->setMorningFrom('08:00')
            ->setMorningTo('12:00')
            ->setAfternoonFrom('14:00')
            ->setAfternoonTo('18:00')
        ;

        $contract = new Contract()->setTrackingMode(TrackingMode::TIME)->setWeeklyHours(35);

        $security = $this->createStub(Security::class);
        $security->method('getUser')->willReturn($user);

        $employeeRepository = $this->createStub(EmployeeScopedRepositoryInterface::class);
        $employeeRepository->method('findAccessibleByIds')->willReturn([7 => $employee]);

        $workHourRepository = $this->createStub(WorkHourReadRepositoryInterface::class);
        $workHourRepository->method('findByDateAndEmployeesIndexedByEmployeeId')->willReturn([7 => $existing]);

        $absenceRepository = $this->createStub(AbsenceReadRepositoryInterface::class);
        $absenceRepository->method('findByDateAndEmployees')->willReturn([]);

        $contractResolver = $this->createStub(EmployeeContractResolver::class);
        $contractResolver->method('resolveForEmployeeAndDate')->willReturn($contract);
        $contractResolver->method('resolveIsDriverForEmployeeAndDate')->willReturn(false);

        return new WorkHourBulkUpsertProcessor(
            $em,
            $security,
            $employeeRepository,
            $workHourRepository,
            $absenceRepository,
            $contractResolver,
            $this->createStub(AuditLogger::class),
        );
    }
}
