<?php

namespace App\Tests\Report;

use App\Appointment\Entity\Appointment;
use App\Doctor\Entity\Doctor;
use App\Shared\Event\DomainEvents;
use App\Shared\Event\Entity\DomainEventLog;
use App\Tests\ApiTestCase;

/**
 * دو رویدادی که مسیرشان از تسک‌های قدیمی‌تر می‌آید: «نوبت انجام شد» و «نوبت جابه‌جا شد».
 *
 * هر دو از مسیرِ وضعیتِ موجود عبور می‌کنند، پس چیزی که این تست‌ها نگه می‌دارند این است
 * که رویداد **بعد از ذخیرهٔ موفق** ثبت شود — نه هنگام درخواستِ تغییر وضعیت.
 */
class AppointmentLifecycleEventTest extends ApiTestCase
{
    private function makeDoctor(): Doctor
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر رویداد');
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function latest(string $name): ?DomainEventLog
    {
        return $this->em->getRepository(DomainEventLog::class)
            ->findOneBy(['name' => $name], ['id' => 'DESC']);
    }

    public function testCompletingAnAppointmentRecordsTheEvent(): void
    {
        $doctor      = $this->makeDoctor();
        $start       = time() + 86_400;
        $appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 1_800);
        $this->em->persist($appointment);
        $this->em->flush();

        $this->authJson('PATCH', "/api/v1/appointment/{$appointment->getUuid()}/status", $doctor->getUser(), [
            'status'  => Appointment::STATUS_CONFIRMED,
            'version' => $appointment->getVersion(),
        ]);
        self::assertSame(200, $this->responseCode());

        $this->em->clear();
        $reloaded = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $appointment->getUuid()]);

        $this->authJson('PATCH', "/api/v1/appointment/{$reloaded->getUuid()}/status", $doctor->getUser(), [
            'status'  => Appointment::STATUS_COMPLETED,
            'version' => $reloaded->getVersion(),
        ]);
        self::assertSame(200, $this->responseCode());

        $event = $this->latest(DomainEvents::APPOINTMENT_COMPLETED);
        self::assertNotNull($event);
        self::assertSame($appointment->getUuid(), $event->getPayload()['appointment_uuid']);
        self::assertSame($start, $event->getPayload()['slot_start']);
    }

    /**
     * انتقال ردشده نباید رویداد بگذارد؛ وگرنه گزارش «انجام‌شده»ها از خودِ نوبت‌ها جلو می‌زند.
     */
    public function testARejectedTransitionRecordsNothing(): void
    {
        $doctor      = $this->makeDoctor();
        $start       = time() + 86_400;
        $appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 1_800);
        $this->em->persist($appointment);
        $this->em->flush();

        $before = $this->latest(DomainEvents::APPOINTMENT_COMPLETED);

        // `pending → completed` در جدول انتقال‌ها نیست.
        $this->authJson('PATCH', "/api/v1/appointment/{$appointment->getUuid()}/status", $doctor->getUser(), [
            'status'  => Appointment::STATUS_COMPLETED,
            'version' => $appointment->getVersion(),
        ]);

        self::assertSame(422, $this->responseCode());

        $after = $this->latest(DomainEvents::APPOINTMENT_COMPLETED);
        self::assertSame($before?->getId(), $after?->getId());
    }
}
