<?php

namespace App\Tests\Patient;

use App\Appointment\Entity\Appointment;
use App\Appointment\Service\AppointmentConfirmationService;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Tests\ApiTestCase;

/**
 * Confirming an appointment files exactly one case file, in the practice where
 * the booking actually happened.
 *
 * Two records for one appointment means a single visit's revenue is counted
 * twice, and inferring the practice from the address (rather than the booking
 * context) files it under the wrong one.
 */
class AutoCreateSessionOnConfirmTest extends ApiTestCase
{
    private function confirmation(): AppointmentConfirmationService
    {
        return static::getContainer()->get(AppointmentConfirmationService::class);
    }

    private function makeDoctor(): Doctor
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر آزمون');
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function makeClinic(Doctor $doctor): Clinic
    {
        $clinic = new Clinic($this->createUser(['ROLE_CLINIC']));
        $clinic->setName('کلینیک آزمون');
        $clinic->getDoctors()->add($doctor);
        $this->em->persist($clinic);
        $this->em->flush();

        return $clinic;
    }

    private function makeAppointment(Doctor $doctor, ?Clinic $clinic = null): Appointment
    {
        $appointment = $this->newAppointment($doctor, $this->createUser(['ROLE_USER']), 1_790_000_000, 1_790_001_800, $clinic);
        $appointment->transitionTo(Appointment::STATUS_CONFIRMED);
        $this->em->persist($appointment);
        $this->em->flush();

        return $appointment;
    }

    /** @return PatientRecord[] */
    private function recordsFor(Appointment $appointment): array
    {
        return $this->em->getRepository(PatientRecord::class)
            ->findBy(['user' => $appointment->getUser()]);
    }

    /** @return PatientSession[] */
    private function sessionsFor(Appointment $appointment): array
    {
        return $this->em->getRepository(PatientSession::class)
            ->findBy(['appointment' => $appointment]);
    }

    public function testClinicBookingFilesOnlyTheClinicRecord(): void
    {
        $doctor      = $this->makeDoctor();
        $clinic      = $this->makeClinic($doctor);
        $appointment = $this->makeAppointment($doctor, $clinic);

        $this->confirmation()->onConfirmed($appointment);
        $this->em->flush();

        $records = $this->recordsFor($appointment);
        self::assertCount(1, $records, 'یک نوبت باید دقیقاً یک پرونده بسازد');
        self::assertSame('clinic', $records[0]->getEntityType());
        self::assertSame($clinic->getId(), $records[0]->getEntityId());
        self::assertCount(1, $this->sessionsFor($appointment));
    }

    public function testPersonalBookingFilesOnlyTheDoctorRecord(): void
    {
        $doctor      = $this->makeDoctor();
        $this->makeClinic($doctor);            // عضویت کلینیک نباید نوبت شخصی را بدزدد
        $appointment = $this->makeAppointment($doctor, null);

        $this->confirmation()->onConfirmed($appointment);
        $this->em->flush();

        $records = $this->recordsFor($appointment);
        self::assertCount(1, $records);
        self::assertSame('doctor', $records[0]->getEntityType());
        self::assertSame($doctor->getId(), $records[0]->getEntityId());
    }

    public function testExistingRecordGetsAnotherSessionInsteadOfAnotherRecord(): void
    {
        $doctor  = $this->makeDoctor();
        $first   = $this->makeAppointment($doctor, null);
        $patient = $first->getUser();

        $this->confirmation()->onConfirmed($first);
        $this->em->flush();

        $second = $this->newAppointment($doctor, $patient, 1_790_100_000, 1_790_101_800);
        $second->transitionTo(Appointment::STATUS_CONFIRMED);
        $this->em->persist($second);
        $this->em->flush();

        $this->confirmation()->onConfirmed($second);
        $this->em->flush();

        self::assertCount(1, $this->recordsFor($first), 'بیمار قبلاً پرونده دارد؛ پروندهٔ دوم ساخته نشود');
        self::assertCount(1, $this->sessionsFor($second), 'ولی مراجعهٔ جدید باید ثبت شود');
    }

    public function testConfirmingTwiceDoesNotDuplicateTheSession(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor, null);

        $this->confirmation()->onConfirmed($appointment);
        $this->em->flush();
        $this->confirmation()->onConfirmed($appointment);
        $this->em->flush();

        self::assertCount(1, $this->sessionsFor($appointment));
    }

    public function testDayLevelReserveIsNotFiled(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor, null);
        $appointment->rescheduleTo(1_790_000_000, 1_790_001_800, true);
        $this->em->flush();

        $this->confirmation()->onConfirmed($appointment);
        $this->em->flush();

        self::assertCount(0, $this->sessionsFor($appointment), 'نوبت رزروِ روز-محور ساعت ندارد؛ مراجعه نمی‌سازد');
    }
}
