<?php

namespace App\Tests\Appointment;

use App\Appointment\Availability\Entity\ResourceOccupancy;
use App\Clinic\Entity\Clinic;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\DoctorAddress;
use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\ResourceType;
use App\Tests\ApiTestCase;

/**
 * هدف کارایی مستند: جستجوی یک ماهه زیر نیم ثانیه.
 *
 * این تست بخشی از تسک است نه اختیاری — موتوری که یک کوئری داخل حلقهٔ روز یا حلقهٔ
 * کاندید داشته باشد در تست‌های کوچک سبز می‌ماند و فقط در تولید معلوم می‌شود.
 */
class AvailabilityPerformanceTest extends ApiTestCase
{
    private const TEHRAN = 'Asia/Tehran';

    /** سقف نرم‌افزاری؛ روی ماشین کندِ CI هم باید بگذرد. */
    private const BUDGET_MS = 500;

    public function testThirtyDaySearchWithTwentyResourcesStaysUnderBudget(): void
    {
        $this->client->disableReboot();

        $user   = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']);
        $clinic = new Clinic($user);
        $clinic->setName('کلینیک بار');
        $this->em->persist($clinic);
        $this->em->flush();

        $section = new ServiceSection('clinic', $clinic->getId(), 'لیزر');
        $this->em->persist($section);

        $address = DoctorAddress::forClinic($clinic->getId());
        $address->setName('شعبهٔ شلوغ');
        $this->em->persist($address);
        $this->em->flush();

        $service = new ServiceItem($section, 'لیزر');
        $service->setSoloDurationMinutes(20);
        $this->em->persist($service);

        $roomType     = new ResourceType('clinic', $clinic->getId(), 'room', 'اتاق');
        $operatorType = new ResourceType('clinic', $clinic->getId(), 'operator', 'اپراتور');
        $this->em->persist($roomType);
        $this->em->persist($operatorType);
        $this->em->flush();

        // ۲۰ منبع، هرکدام با شیفت هفتگی کامل.
        $resources = [];
        foreach (range(1, 10) as $n) {
            $resources[] = $this->makeResource($address, $roomType, "اتاق $n");
            $resources[] = $this->makeResource($address, $operatorType, "اپراتور $n");
        }
        $this->em->flush();

        // ۵۰۰ نوبت ثبت‌شدهٔ پراکنده در ۳۰ روز.
        $start = (new \DateTimeImmutable('next saturday', new \DateTimeZone(self::TEHRAN)))
            ->setTime(0, 0)
            ->getTimestamp();

        for ($i = 0; $i < 500; $i++) {
            $resource = $resources[$i % count($resources)];
            $day      = $start + intdiv($i, 17) * 86400;
            $from     = $day + (9 + ($i % 7)) * 3600;

            $this->em->persist(new ResourceOccupancy($resource, $from, $from + 1800));
        }
        $this->em->flush();

        $this->authJson('PUT', "/api/v1/service-item/{$service->getUuid()}/segments", $user, [
            'segments' => [[
                'sequence' => 1, 'name' => 'لیزر', 'duration_minutes' => 20,
                'requirements' => [
                    ['type_uuid' => $roomType->getUuid()],
                    ['type_uuid' => $operatorType->getUuid()],
                ],
            ]],
        ]);
        self::assertSame(200, $this->responseCode());

        $began = microtime(true);

        $body = $this->authJson('POST', '/api/v1/appointment-availability', $user, [
            'service_uuid' => $service->getUuid(),
            'branch_uuid'  => $address->getUuid(),
            'from'         => $start,
            'to'           => $start + 29 * 86400,
        ]);

        $elapsedMs = (microtime(true) - $began) * 1000;

        self::assertSame(200, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE));
        self::assertNotEmpty($body['data']['slots'], 'با ۲۰ منبع باید وقت پیدا شود');
        self::assertLessThan(
            self::BUDGET_MS,
            $elapsedMs,
            sprintf('جستجوی ۳۰ روزه %.0f میلی‌ثانیه طول کشید (سقف %d)', $elapsedMs, self::BUDGET_MS),
        );
    }

    private function makeResource(DoctorAddress $address, ResourceType $type, string $name): ClinicResource
    {
        $resource = new ClinicResource($address, $type, $name);
        $this->em->persist($resource);

        foreach (range(0, 6) as $day) {
            $this->em->persist(new \App\Resource\Entity\ResourceCalendar($resource, $day, 540, 1020));
        }

        return $resource;
    }
}
