<?php

namespace App\Tests\Doctor;

use App\Appointment\Entity\WeeklySchedule;
use App\Doctor\Entity\Doctor;
use App\Specialty\Entity\Specialty;
use App\Tests\ApiTestCase;

/**
 * Public doctors list: bookable doctors (appointment flag on + weekly schedule
 * with online booking enabled) must rank above non-bookable ones, and the
 * active=1 filter must return only truly bookable doctors — matching the
 * `active` field of the list payload.
 */
class DoctorListBookableSortFilterTest extends ApiTestCase
{
    private Specialty $specialty;
    private Doctor $bookable;
    private Doctor $noSchedule;
    private Doctor $flagOff;
    private Doctor $bookingDisabled;

    protected function setUp(): void
    {
        parent::setUp();
        $this->client->catchExceptions(false);

        // db_test is shared across runs — scope every request to a fresh
        // specialty so only this test's doctors are visible.
        $this->specialty = new Specialty('تخصص تست نوبت', 'sp-' . bin2hex(random_bytes(6)));
        $this->em->persist($this->specialty);

        $this->bookable        = $this->makeDoctor('دکتر نوبت‌دار');
        $this->noSchedule      = $this->makeDoctor('دکتر بدون برنامه');
        $this->flagOff         = $this->makeDoctor('دکتر غیرفعال');
        $this->bookingDisabled = $this->makeDoctor('دکتر رزرو خاموش');

        $this->flagOff->setActiveDoctorAppointment(false);

        $days = [['sessions' => [['active' => true, 'start_time' => '09:00', 'end_time' => '13:00']]]];
        $this->em->persist($this->newWeeklySchedule($this->bookable, $days));
        $this->em->persist($this->newWeeklySchedule($this->flagOff, $days));

        $disabled = $this->newWeeklySchedule($this->bookingDisabled, $days);
        $disabled->setMeta(['online_booking_enabled' => false]);
        $this->em->persist($disabled);

        $this->em->flush();
    }

    private function makeDoctor(string $name): Doctor
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), $name);
        $doctor->getSpecialties()->add($this->specialty);
        $this->em->persist($doctor);

        return $doctor;
    }

    /** @return array<int, array<string, mixed>> */
    private function listDoctors(string $extraQuery = ''): array
    {
        $this->client->request(
            'GET',
            '/api/v1/doctors?specialty_id=' . $this->specialty->getId() . '&limit=50' . $extraQuery
        );
        $this->assertSame(200, $this->responseCode(), substr($this->client->getResponse()->getContent(), 0, 1500));
        $body = json_decode($this->client->getResponse()->getContent(), true);

        return $body['data'];
    }

    public function testDefaultListHidesDeactivatedDoctorsBookableFirst(): void
    {
        $items = $this->listDoctors();

        // Deactivated doctor (active flag off) must be absent from the public
        // list even without an explicit `active` filter.
        $uuids = array_column($items, 'uuid');
        $this->assertNotContains($this->flagOff->getUuid(), $uuids, 'deactivated doctor must not appear');

        $this->assertCount(3, $items);
        $this->assertSame($this->bookable->getUuid(), $items[0]['uuid']);
        $this->assertTrue($items[0]['active']);

        $rest = array_column(array_slice($items, 1), 'uuid');
        sort($rest);
        $expected = [$this->noSchedule->getUuid(), $this->bookingDisabled->getUuid()];
        sort($expected);
        $this->assertSame($expected, $rest);
        foreach (array_slice($items, 1) as $item) {
            $this->assertFalse($item['active'], $item['name'] . ' must not be bookable');
        }
    }

    public function testActiveFilterReturnsOnlyBookableDoctors(): void
    {
        $items = $this->listDoctors('&active=1');

        $this->assertCount(1, $items);
        $this->assertSame($this->bookable->getUuid(), $items[0]['uuid']);
        $this->assertTrue($items[0]['active']);
    }

    public function testActiveZeroReturnsOnlyFlagOffDoctors(): void
    {
        $items = $this->listDoctors('&active=0');

        $this->assertCount(1, $items);
        $this->assertSame($this->flagOff->getUuid(), $items[0]['uuid']);
    }
}
