<?php

namespace App\Tests\Appointment;

use App\Appointment\Entity\Appointment;
use App\Appointment\Entity\WeeklySchedule;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;

/**
 * قرارداد `GET /api/v1/appointments/user` — همان endpointی که **پنل کاربرِ سایت عمومی**
 * (`nobat724_front/services/response.js` → `getMyAppointments`) مصرف می‌کند.
 *
 * ⚠️ با `GET /api/v1/my/appointments` اشتباه گرفته نشود: آن یکی سریالایزر جدا دارد و
 * پنل ادمین از آن استفاده می‌کند. این یکی `Appointment::toArray()` را برمی‌گرداند.
 *
 * سایت روی این فیلدها حساب می‌کند و شکستنشان در build هیچ‌کدام از دو ریپو خطا نمی‌دهد؛
 * پس تنها تور ایمنی همین تست است.
 */
class PublicSiteAppointmentContractTest extends ApiTestCase
{
    /** @return array{0:\App\Auth\Entity\User,1:Doctor,2:ServiceSection} */
    private function serviceDoctor(int $buffer = 10): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر قرارداد سایت');
        $this->em->persist($doctor);
        $this->em->flush();

        $schedule = $this->newWeeklySchedule($doctor, [
            '0' => ['sessions' => [[
                'active' => true, 'start_time' => '09:00', 'end_time' => '18:00',
                'duration_per_patient' => 20, 'location_id' => 1,
            ]]],
        ]);
        $schedule->setMeta(['booking_mode' => WeeklySchedule::MODE_SERVICE, 'buffer_minutes' => $buffer]);
        $this->em->persist($schedule);

        $section = new ServiceSection('doctor', $doctor->getId(), 'زیبایی');
        $this->em->persist($section);
        $this->em->flush();

        return [$owner, $doctor, $section];
    }

    private function service(ServiceSection $section, string $name, int $minutes): ServiceItem
    {
        $item = new ServiceItem($section, $name, 1200000);
        $item->setDurationMinutes($minutes)->setBookable(true);
        $this->em->persist($item);
        $this->em->flush();

        return $item;
    }

    /** @return array{0:\App\Auth\Entity\User,1:Appointment} */
    private function bookedFor(Doctor $doctor, array $items, bool $reserve = false): array
    {
        $patient = $this->createUser(['ROLE_USER']);
        $start   = (int) strtotime('+3 days 10:00');
        $appt    = $this->newAppointment($doctor, $patient, $start, $start + 35 * 60);
        $appt->transitionTo(Appointment::STATUS_CONFIRMED);
        if ($reserve) {
            $appt->rescheduleTo($start, $start, true);
        }
        if ($items !== []) {
            $appt->replaceServiceItems($items);
            $appt->setServiceDuration(35, 10);
        }
        $this->em->persist($appt);
        $this->em->flush();

        return [$patient, $appt];
    }

    /** پاسخ این endpoint دو لایه است: `data.data[]`. */
    private function rows(\App\Auth\Entity\User $patient): array
    {
        $res = $this->authJson('GET', '/api/v1/appointments/user', $patient);
        self::assertSame(200, $this->responseCode());

        return $res['data']['data'];
    }

    // ── ✅ موفق ──────────────────────────────────────────────────────────────

    public function testServiceFieldsThePublicPanelNeedsArePresent(): void
    {
        [, $doctor, $section] = $this->serviceDoctor();
        $face   = $this->service($section, 'لیزر صورت', 20);
        $bikini = $this->service($section, 'لیزر بیکینی', 15);
        [$patient] = $this->bookedFor($doctor, [$face, $bikini]);

        $row = $this->rows($patient)[0];

        self::assertArrayHasKey('service_items', $row);
        self::assertCount(2, $row['service_items'], 'فهرست کامل، نه فقط سرویس اول');
        self::assertSame(['لیزر صورت', 'لیزر بیکینی'], array_column($row['service_items'], 'name'));

        self::assertSame(35, $row['service_total_minutes']);
        self::assertSame(10, $row['service_buffer_minutes']);
        self::assertArrayHasKey('clinic_uuid', $row);
        self::assertNull($row['clinic_uuid'], 'مطب شخصی → null');
    }

    public function testReserveEntryKeepsItsServicesAndDuration(): void
    {
        [, $doctor, $section] = $this->serviceDoctor();
        $item = $this->service($section, 'لیزر', 45);
        [$patient] = $this->bookedFor($doctor, [$item], reserve: true);

        $row = $this->rows($patient)[0];

        self::assertTrue($row['is_reserve']);
        self::assertCount(1, $row['service_items']);
        self::assertSame(35, $row['service_total_minutes'], 'مدت برای تبدیل بعدی می‌ماند');
    }

    // ── ⚠️ مرزی ──────────────────────────────────────────────────────────────

    public function testSlotModeAppointmentLeavesTheServiceFieldsNull(): void
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر اسلاتی');
        $this->em->persist($doctor);
        $this->em->flush();
        [$patient] = $this->bookedFor($doctor, []);

        $row = $this->rows($patient)[0];

        // پنل سایت روی این با شرط && می‌خواند؛ null باید null بماند نه صفر.
        self::assertNull($row['service_total_minutes']);
        self::assertNull($row['service_buffer_minutes']);
        self::assertSame([], $row['service_items']);
    }

    public function testAppointmentWithoutServicesReturnsAnEmptyArrayNotNull(): void
    {
        [, $doctor] = $this->serviceDoctor();
        [$patient]  = $this->bookedFor($doctor, []);

        $row = $this->rows($patient)[0];

        self::assertIsArray($row['service_items'], 'سایت روی length می‌خواند؛ null کرش می‌دهد');
        self::assertSame([], $row['service_items']);
    }

    public function testEachServiceItemCarriesTheKeysTheSiteReads(): void
    {
        [, $doctor, $section] = $this->serviceDoctor();
        $item = $this->service($section, 'لیزر', 30);
        [$patient] = $this->bookedFor($doctor, [$item]);

        $first = $this->rows($patient)[0]['service_items'][0];

        foreach (['uuid', 'name', 'price_rials'] as $key) {
            self::assertArrayHasKey($key, $first);
        }
    }
}
