<?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/my/appointments` سریالایزر خودش را دارد (array hydration)، نه
 * `Appointment::toArray()`. پس فیلدهای سرویسی باید صریحاً همان‌جا اضافه شوند —
 * وگرنه پنل ادمین و پنل کاربر سایت داده‌ای برای نمایش ندارند.
 */
class MyAppointmentsServiceFieldsTest extends ApiTestCase
{
    /** @return array{0:\App\Auth\Entity\User,1:Doctor,2:ServiceSection} */
    private function serviceDoctor(): 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' => 10]);
        $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, 120000);
        $item->setDurationMinutes($minutes)->setBookable(true);
        $this->em->persist($item);
        $this->em->flush();

        return $item;
    }

    private function appointment(Doctor $doctor, array $items, bool $reserve = false): Appointment
    {
        $patient = $this->createUser(['ROLE_USER']);
        $start   = (int) strtotime('+4 days 10:00');
        $appt    = $this->newAppointment($doctor, $patient, $reserve ? $start : $start, $reserve ? $start : $start + 1800);
        if ($reserve) {
            $appt->rescheduleTo($start, $start, true);
        }
        if ($items !== []) {
            $appt->replaceServiceItems($items);
            $appt->setServiceDuration(35, 10);
        }
        $this->em->persist($appt);
        $this->em->flush();

        return $appt;
    }

    private function rowFor(\App\Auth\Entity\User $actor, string $uuid, bool $reserve): ?array
    {
        $res = $this->authJson('GET', '/api/v1/my/appointments?limit=50' . ($reserve ? '&reserve=1' : ''), $actor);
        foreach ($res['data'] ?? [] as $row) {
            if ($row['uuid'] === $uuid) {
                return $row;
            }
        }

        return null;
    }

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

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

        $row = $this->rowFor($owner, $appt->getUuid(), false);

        self::assertNotNull($row);
        self::assertCount(2, $row['service_items'], 'فهرست کامل سرویس‌ها باید بیاید');
        self::assertSame(
            ['لیزر صورت', 'لیزر بیکینی'],
            array_column($row['service_items'], 'name'),
        );
        self::assertSame('لیزر صورت', $row['service_item']['name'], 'سرویس تکی همان اولی می‌ماند');
    }

    public function testDurationAndBufferAreExposed(): void
    {
        [$owner, $doctor, $section] = $this->serviceDoctor();
        $item = $this->service($section, 'لیزر', 35);
        $appt = $this->appointment($doctor, [$item]);

        $row = $this->rowFor($owner, $appt->getUuid(), false);

        self::assertSame(35, $row['service_total_minutes']);
        self::assertSame(10, $row['service_buffer_minutes']);
    }

    public function testClinicUuidIsExposedForBookingModeDetection(): void
    {
        [$owner, $doctor, $section] = $this->serviceDoctor();
        $item = $this->service($section, 'لیزر', 30);
        $appt = $this->appointment($doctor, [$item]);

        $row = $this->rowFor($owner, $appt->getUuid(), false);

        self::assertArrayHasKey('clinic_uuid', $row);
        self::assertNull($row['clinic_uuid'], 'مطب شخصی → null، و کلاینت باید بتواند تفکیک کند');
    }

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

        $row = $this->rowFor($owner, $appt->getUuid(), true);

        self::assertNotNull($row, 'نوبت رزرو باید در فهرست reserve=1 باشد');
        self::assertTrue($row['is_reserve']);
        self::assertCount(1, $row['service_items']);
        self::assertSame(35, $row['service_total_minutes'], 'مدت برای تبدیل بعدی لازم است');
    }

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

    public function testAppointmentWithoutServicesReturnsAnEmptyListNotNull(): void
    {
        [$owner, $doctor] = $this->serviceDoctor();
        $appt = $this->appointment($doctor, []);

        $row = $this->rowFor($owner, $appt->getUuid(), false);

        self::assertSame([], $row['service_items'], 'آرایهٔ خالی، نه null — کلاینت روی length می‌خواند');
        self::assertNull($row['service_total_minutes']);
        self::assertNull($row['service_buffer_minutes']);
    }

    public function testPaginationIsNotBrokenByTheServiceItemsJoin(): void
    {
        [$owner, $doctor, $section] = $this->serviceDoctor();
        $a = $this->service($section, 'س۱', 10);
        $b = $this->service($section, 'س۲', 10);
        $c = $this->service($section, 'س۳', 10);
        // سه سرویس روی یک نوبت: اگر collection را JOIN می‌کردیم، این یک نوبت سه ردیف
        // می‌شد و صفحهٔ اول یک آیتم کمتر می‌داشت.
        $appt = $this->appointment($doctor, [$a, $b, $c]);

        $res = $this->authJson('GET', '/api/v1/my/appointments?limit=50', $owner);
        $matching = array_values(array_filter($res['data'], fn($r) => $r['uuid'] === $appt->getUuid()));

        self::assertCount(1, $matching, 'نوبت باید دقیقاً یک ردیف باشد');
        self::assertCount(3, $matching[0]['service_items']);
    }
}
