<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\ApplicationSystem;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\AdminBundle\ApplicationSystem\ParticipantListFacade;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Offer\Base;
use Ferienpass\CoreBundle\Entity\Participant\BaseParticipant;
use Ferienpass\CoreBundle\Enum\AttendanceTransition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Workflow\WorkflowInterface;

final class ParticipantListFacadeTest extends TestCase
{
    public function testConfirmAppliesTransitionToEligibleAttendances(): void
    {
        $em = $this->createMock(EntityManagerInterface::class);
        $em->expects(self::once())->method('flush');

        $workflow = $this->createMock(WorkflowInterface::class);
        $workflow->method('can')->willReturn(true);
        $workflow->expects(self::once())
            ->method('apply')
            ->with(
                self::anything(),
                AttendanceTransition::CONFIRM,
                self::callback(fn (array $ctx) => $ctx['notify'] === true)
            );

        $facade = new ParticipantListFacade($em, $workflow);
        $attendance = new Attendance(new Base(), new BaseParticipant());
        $facade->confirm([$attendance]);
    }

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

        $workflow = $this->createMock(WorkflowInterface::class);
        $workflow->expects(self::never())->method('apply');

        $facade = new ParticipantListFacade($em, $workflow);

        $attendance = $this->createMock(Attendance::class);
        $attendance->method('getParticipant')->willReturn(null);

        $facade->confirm([$attendance]);
    }

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

        $workflow = $this->createMock(WorkflowInterface::class);
        $workflow->method('can')->willReturn(false);
        $workflow->expects(self::never())->method('apply');

        $facade = new ParticipantListFacade($em, $workflow);
        $attendance = new Attendance(new Base(), new BaseParticipant());
        $facade->confirm([$attendance]);
    }

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

        $workflow = $this->createMock(WorkflowInterface::class);
        $workflow->method('can')->willReturn(true);
        $workflow->expects(self::once())
            ->method('apply')
            ->with(
                self::anything(),
                AttendanceTransition::REJECT,
            );

        $facade = new ParticipantListFacade($em, $workflow);
        $attendance = new Attendance(new Base(), new BaseParticipant());
        $facade->reject([$attendance]);
    }

    public function testApplyTransitionThrowsWhenNotifyWithoutCommit(): void
    {
        $em = $this->createStub(EntityManagerInterface::class);
        $workflow = $this->createStub(WorkflowInterface::class);

        $facade = new ParticipantListFacade($em, $workflow);

        $this->expectException(\RuntimeException::class);
        $facade->confirm([], notify: true, commit: false);
    }

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

        $group = Uuid::v7();

        $workflow = $this->createMock(WorkflowInterface::class);
        $workflow->method('can')->willReturn(true);
        $workflow->expects(self::once())
            ->method('apply')
            ->with(
                self::anything(),
                AttendanceTransition::CONFIRM,
                self::callback(fn (array $ctx) => isset($ctx['group']) && $ctx['group'] === (string) $group)
            );

        $facade = new ParticipantListFacade($em, $workflow);
        $attendance = new Attendance(new Base(), new BaseParticipant());
        $facade->confirm([$attendance], group: $group);
    }
}
