<?php

namespace App\Tests\Appointment;

use App\Appointment\Entity\WeeklySchedule;
use App\Appointment\Service\SlotCalculatorService;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;

/**
 * بازهٔ مجاز رزرو آنلاین (booking_window) با سه واحد روز/هفته/ماه و پیش‌فرض «۳ ماه».
 */
class BookingWindowUnitTest extends ApiTestCase
{
    /** پزشکی با شیفت فعال در هر هفت روز هفته، تا هر تاریخِ آزمون اسلات داشته باشد. */
    private function makeAlwaysOpenDoctor(array $meta = []): Doctor
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر بازهٔ رزرو');
        $this->em->persist($doctor);

        $days = [];
        foreach (range(0, 6) as $dayKey) {
            $days[(string) $dayKey] = ['sessions' => [[
                'active'               => true,
                'start_time'           => '10:00',
                'end_time'             => '12:00',
                'duration_per_patient' => 30,
                'location_id'          => 1,
            ]]];
        }

        $schedule = $this->newWeeklySchedule($doctor, $days);
        if ($meta !== []) {
            $schedule->setMeta($meta);
        }
        $this->em->persist($schedule);
        $this->em->flush();

        return $doctor;
    }

    private function assertBookable(Doctor $doctor, string $relative): void
    {
        $calc = static::getContainer()->get(SlotCalculatorService::class);
        $date = date('Y-m-d', strtotime($relative));

        $this->assertNotSame([], $calc->getAllSlotsWithAvailability($doctor, $date), "باید داخل بازه باشد: {$relative}");
    }

    private function assertOutsideWindow(Doctor $doctor, string $relative): void
    {
        $calc = static::getContainer()->get(SlotCalculatorService::class);
        $date = date('Y-m-d', strtotime($relative));

        $this->assertSame([], $calc->getAllSlotsWithAvailability($doctor, $date), "باید بیرون بازه باشد: {$relative}");
        $this->assertSame(SlotCalculatorService::EMPTY_OUTSIDE_WINDOW, $calc->explainEmptyDay($doctor, $date));
    }

    public function testDayUnitLimitsWindowToExactDays(): void
    {
        $doctor = $this->makeAlwaysOpenDoctor([
            'booking_window_value' => 2,
            'booking_window_unit'  => 'day',
        ]);

        $this->assertBookable($doctor, '+2 days');
        $this->assertOutsideWindow($doctor, '+3 days');
    }

    public function testWeekUnitStillSupported(): void
    {
        $doctor = $this->makeAlwaysOpenDoctor([
            'booking_window_value' => 1,
            'booking_window_unit'  => 'week',
        ]);

        $this->assertBookable($doctor, '+7 days');
        $this->assertOutsideWindow($doctor, '+8 days');
    }

    public function testDefaultWindowIsThreeMonths(): void
    {
        $this->assertSame(3, WeeklySchedule::DEFAULT_META['booking_window_value']);
        $this->assertSame('month', WeeklySchedule::DEFAULT_META['booking_window_unit']);

        $doctor = $this->makeAlwaysOpenDoctor();

        $this->assertBookable($doctor, '+2 months');
        $this->assertOutsideWindow($doctor, '+3 months +2 days');
    }

    public function testInvalidUnitKeepsCurrentValue(): void
    {
        $owner    = $this->createUser(['ROLE_DOCTOR']);
        $doctor   = new Doctor($owner, 'دکتر واحد نامعتبر');
        $schedule = $this->newWeeklySchedule($doctor, []);

        $schedule->setMeta(['booking_window_unit' => 'day', 'booking_window_value' => 10]);
        $schedule->setMeta(['booking_window_unit' => 'year']);

        $this->assertSame('day', $schedule->getMeta()['booking_window_unit']);
        $this->assertSame(10, $schedule->getMeta()['booking_window_value']);
    }
}
