<?php

namespace App\Tests\Resource;

use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\Skill;
use App\Resource\Repository\ClinicResourceRepository;

/**
 * `findEligible()` — پرس‌وجوی داغِ تسک ۰۶. جدا از HTTP تست می‌شود چون قرارداد
 * «همهٔ مهارت‌ها، نه یکی» یک تصمیم SQL است و باید مستقل از کنترلر قفل بماند.
 */
class ResourceEligibilityTest extends ResourceTestCase
{
    private function repo(): ClinicResourceRepository
    {
        return static::getContainer()->get(ClinicResourceRepository::class);
    }

    private function skillIdOf(string $uuid): int
    {
        return (int) $this->em->getRepository(Skill::class)->findOneBy(['uuid' => $uuid])->getId();
    }

    private function assign(\App\Auth\Entity\User $user, string $resourceUuid, array $skillUuids): void
    {
        $this->authJson('PUT', "/api/v1/resource/$resourceUuid/skills", $user, [
            'skills' => array_map(static fn (string $u): array => ['skill_uuid' => $u], $skillUuids),
        ]);
    }

    /**
     * نیازمندی «مهارت الف و ب» یعنی هر دو. با یک `IN` ساده، منبعی که فقط یکی را
     * دارد هم برمی‌گشت — به همین دلیل `HAVING COUNT(DISTINCT …)` هست.
     */
    public function testAllSkillsAreRequiredNotAny(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type = $this->resourceType($address, 'operator', 'اپراتور');

        $laser = $this->createSkill($user, 'لیزر آلکساندرایت');
        $botox = $this->createSkill($user, 'بوتاکس');

        $both = $this->createResource($user, $address, $type, ['name' => 'اپراتور کامل']);
        $one  = $this->createResource($user, $address, $type, ['name' => 'اپراتور نصفه']);

        $this->assign($user, $both['data']['uuid'], [$laser['data']['uuid'], $botox['data']['uuid']]);
        $this->assign($user, $one['data']['uuid'], [$laser['data']['uuid']]);

        $this->em->clear();

        $eligible = $this->repo()->findEligible(
            $this->em->getRepository(\App\Doctor\Entity\DoctorAddress::class)->find($address->getId()),
            $this->em->getRepository(\App\Resource\Entity\ResourceType::class)->find($type->getId()),
            [$this->skillIdOf($laser['data']['uuid']), $this->skillIdOf($botox['data']['uuid'])],
        );

        self::assertCount(1, $eligible);
        self::assertSame('اپراتور کامل', $eligible[0]->getName());
    }

    public function testSingleSkillMatchesEveryResourceThatHasIt(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type  = $this->resourceType($address, 'operator', 'اپراتور');
        $laser = $this->createSkill($user, 'لیزر');

        foreach (['اپراتور ۱', 'اپراتور ۲'] as $name) {
            $r = $this->createResource($user, $address, $type, ['name' => $name]);
            $this->assign($user, $r['data']['uuid'], [$laser['data']['uuid']]);
        }
        $this->createResource($user, $address, $type, ['name' => 'اپراتور بی‌مهارت']);

        $this->em->clear();

        $eligible = $this->repo()->findEligible(
            $this->em->getRepository(\App\Doctor\Entity\DoctorAddress::class)->find($address->getId()),
            $this->em->getRepository(\App\Resource\Entity\ResourceType::class)->find($type->getId()),
            [$this->skillIdOf($laser['data']['uuid'])],
        );

        self::assertCount(2, $eligible);
    }

    public function testInactiveResourceIsNeverEligible(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type  = $this->resourceType($address, 'operator', 'اپراتور');
        $laser = $this->createSkill($user, 'لیزر');

        $resource = $this->createResource($user, $address, $type, ['name' => 'اپراتور خاموش']);
        $this->assign($user, $resource['data']['uuid'], [$laser['data']['uuid']]);
        $this->authJson('PATCH', "/api/v1/resource/{$resource['data']['uuid']}", $user, ['active' => false]);

        $this->em->clear();

        $eligible = $this->repo()->findEligible(
            $this->em->getRepository(\App\Doctor\Entity\DoctorAddress::class)->find($address->getId()),
            $this->em->getRepository(\App\Resource\Entity\ResourceType::class)->find($type->getId()),
            [$this->skillIdOf($laser['data']['uuid'])],
        );

        self::assertSame([], $eligible);
    }

    /** بدون شرط مهارت، همهٔ منابع فعالِ آن شعبه و نوع برمی‌گردند. */
    public function testNoSkillFilterReturnsEveryActiveResourceOfThatType(): void
    {
        [$user, $clinic, $address] = $this->clinicWithAddress();
        $second = $this->extraAddress($clinic);
        $type   = $this->resourceType($address, 'operator', 'اپراتور');

        $this->createResource($user, $address, $type, ['name' => 'اینجا ۱']);
        $this->createResource($user, $address, $type, ['name' => 'اینجا ۲']);
        $this->createResource($user, $second, $type, ['name' => 'شعبهٔ دیگر']);

        $this->em->clear();

        $eligible = $this->repo()->findEligible(
            $this->em->getRepository(\App\Doctor\Entity\DoctorAddress::class)->find($address->getId()),
            $this->em->getRepository(\App\Resource\Entity\ResourceType::class)->find($type->getId()),
        );

        self::assertCount(2, $eligible);
    }

    /** فیلتر مهارت روی endpoint فهرست هم همان معنا را می‌دهد. */
    public function testListEndpointFiltersBySkill(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type  = $this->resourceType($address, 'operator', 'اپراتور');
        $laser = $this->createSkill($user, 'لیزر');

        $withSkill = $this->createResource($user, $address, $type, ['name' => 'با مهارت']);
        $this->assign($user, $withSkill['data']['uuid'], [$laser['data']['uuid']]);
        $this->createResource($user, $address, $type, ['name' => 'بدون مهارت']);

        $body = $this->authJson('GET', "/api/v1/resources?skill_uuid={$laser['data']['uuid']}", $user);

        self::assertSame(200, $this->responseCode());
        self::assertCount(1, $body['data']);
        self::assertSame('با مهارت', $body['data'][0]['name']);
    }

    /** مهارت محیط دیگر نباید بی‌صدا «هیچ نتیجه» بدهد. */
    public function testListEndpointRejectsAForeignSkillFilter(): void
    {
        [$user] = $this->clinicWithAddress();
        [$otherUser] = $this->clinicWithAddress('شعبهٔ دیگر');
        $foreign = $this->createSkill($otherUser, 'مهارت بیگانه');

        $this->authJson('GET', "/api/v1/resources?skill_uuid={$foreign['data']['uuid']}", $user);

        self::assertSame(404, $this->responseCode());
    }
}
