<?php

namespace App\Tests\Appointment;

use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use App\Tests\ApiTestCase;

/**
 * نوبت‌دهی سرویسی، سرویس‌های همان محیط را می‌خواهد. باگ اصلی این بود که شمارش
 * همیشه با entity_type='doctor' انجام می‌شد، پس کلینیکی که سرویس bookable داشت هم
 * خطای «حداقل یک سرویس لازم است» می‌گرفت.
 */
class ServiceModeContextTest extends ApiTestCase
{
    private function makeDoctorInClinic(): array
    {
        $doctorUser = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $doctor     = new Doctor($doctorUser, 'دکتر تست');
        $doctor->setMobileNumber($doctorUser->getMobileNumber());
        $this->em->persist($doctor);

        $ownerUser = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']);
        $clinic    = new Clinic($ownerUser);
        $clinic->setName('کلینیک تست');
        $clinic->getDoctors()->add($doctor);
        $this->em->persist($clinic);
        $this->em->flush();

        return [$doctor, $clinic, $ownerUser];
    }

    private function bookableServiceFor(string $entityType, int $entityId): void
    {
        $section = new ServiceSection($entityType, $entityId, 'بخش تست');
        $this->em->persist($section);

        $item = new ServiceItem($section, 'ویزیت', 500_000);
        $item->setBookable(true);
        $item->setDurationMinutes(20);
        $this->em->persist($item);
        $this->em->flush();
    }

    private function servicePayload(Doctor $doctor, int $locationId, ?Clinic $clinic): array
    {
        return array_filter([
            'doctor_uuid' => $doctor->getUuid(),
            'clinic_uuid' => $clinic?->getUuid(),
            'schedule'    => [
                ['day' => 'saturday', 'sessions' => [
                    ['active' => true, 'location_id' => $locationId, 'start' => '09:00', 'end' => '12:00'],
                ]],
            ],
            'meta' => ['booking_mode' => 'service'],
        ], fn($v) => $v !== null);
    }

    public function testClinicServiceModeAcceptsClinicOwnedService(): void
    {
        [$doctor, $clinic, $owner] = $this->makeDoctorInClinic();
        $this->bookableServiceFor('clinic', $clinic->getId());

        $address = DoctorAddress::forClinic($clinic->getId());
        $this->em->persist($address);
        $this->em->flush();

        $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->servicePayload($doctor, $address->getId(), $clinic));

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

    public function testPersonalServiceModeIgnoresClinicServices(): void
    {
        [$doctor, $clinic] = $this->makeDoctorInClinic();
        $this->bookableServiceFor('clinic', $clinic->getId());

        $address = DoctorAddress::forDoctor($doctor);
        $this->em->persist($address);
        $this->em->flush();

        $body = $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->servicePayload($doctor, $address->getId(), null));

        self::assertSame(422, $this->responseCode(), 'سرویس کلینیک نباید مطب شخصی را راضی کند');
        self::assertSame('booking_mode', $body['errors'][0]['field'] ?? null);
    }

    public function testPersonalServiceModeAcceptsOwnService(): void
    {
        [$doctor] = $this->makeDoctorInClinic();
        $this->bookableServiceFor('doctor', $doctor->getId());

        $address = DoctorAddress::forDoctor($doctor);
        $this->em->persist($address);
        $this->em->flush();

        $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->servicePayload($doctor, $address->getId(), null));

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

    public function testBookingModeLocksPerContextNotGlobally(): void
    {
        [$doctor, $clinic, $owner] = $this->makeDoctorInClinic();
        $this->bookableServiceFor('clinic', $clinic->getId());

        $personal = DoctorAddress::forDoctor($doctor);
        $inClinic = DoctorAddress::forClinic($clinic->getId());
        $this->em->persist($personal);
        $this->em->persist($inClinic);
        $this->em->flush();

        // مطب شخصی: اسلاتی
        $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), [
            'doctor_uuid' => $doctor->getUuid(),
            'schedule'    => [['day' => 'saturday', 'sessions' => [
                ['active' => true, 'location_id' => $personal->getId(), 'start' => '09:00', 'end' => '12:00'],
            ]]],
            'meta' => ['booking_mode' => 'slot'],
        ]);
        self::assertSame(201, $this->responseCode());

        // همان پزشک در کلینیک: سرویسی — قفلِ محیط دیگر نباید مانع شود
        $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->servicePayload($doctor, $inClinic->getId(), $clinic));
        self::assertSame(201, $this->responseCode());
    }
}
