<?php

namespace App\Tests\Resource;

use App\Resource\Entity\ClinicResource;

class ResourceCrudTest extends ResourceTestCase
{
    public function testResourceIsCreatedWithTenantPairDerivedFromTheAddress(): void
    {
        [$user, $clinic, $address] = $this->clinicWithAddress();
        $type = $this->resourceType($address);

        $body = $this->createResource($user, $address, $type, ['capacity' => 2, 'setup_minutes' => 5]);

        self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE));
        self::assertSame(2, $body['data']['capacity']);
        self::assertSame(5, $body['data']['setup_minutes']);
        self::assertSame($address->getUuid(), $body['data']['address_uuid']);
        self::assertNull($body['data']['subject_kind'], 'منبع بدون پل یعنی دستگاه');

        $resource = $this->em->getRepository(ClinicResource::class)->findOneBy(['uuid' => $body['data']['uuid']]);
        self::assertSame('clinic', $resource->getEntityType());
        self::assertSame($clinic->getId(), $resource->getEntityId());
    }

    public function testDefaultsAreOneCapacityAndZeroBuffers(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $body = $this->createResource($user, $address, $this->resourceType($address));

        self::assertSame(1, $body['data']['capacity']);
        self::assertSame(0, $body['data']['setup_minutes']);
        self::assertSame(0, $body['data']['cleanup_minutes']);
        self::assertTrue($body['data']['active']);
    }

    public function testZeroCapacityIsRejected(): void
    {
        [$user, , $address] = $this->clinicWithAddress();

        $body = $this->createResource($user, $address, $this->resourceType($address), ['capacity' => 0]);

        self::assertSame(422, $this->responseCode());
        self::assertSame('capacity', $body['errors'][0]['field']);
    }

    /** پزشک هم‌زمان دو بیمار ندارد — قید در خودِ entity است، نه فقط در سرویس. */
    public function testCapacityAboveOneOnAPersonTypeIsRejected(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $doctorType = $this->resourceType($address, 'doctor', 'پزشک');

        $body = $this->createResource($user, $address, $doctorType, ['capacity' => 2]);

        self::assertSame(422, $this->responseCode());
        self::assertSame('capacity', $body['errors'][0]['field']);
        self::assertStringContainsString('شخص', $body['errors'][0]['message']);
    }

    /** ۰ و ۱۰ هر دو معتبرند: آماده‌سازی می‌تواند صفر باشد و تمیزکاری نباشد یا برعکس. */
    public function testZeroSetupWithNonZeroCleanupIsValid(): void
    {
        [$user, , $address] = $this->clinicWithAddress();

        $body = $this->createResource($user, $address, $this->resourceType($address), [
            'setup_minutes'   => 0,
            'cleanup_minutes' => 10,
        ]);

        self::assertSame(201, $this->responseCode());
        self::assertSame(10, $body['data']['cleanup_minutes']);
    }

    public function testUnknownAttributeKeyIsAcceptedButNonScalarValueIsNot(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type = $this->resourceType($address);

        $ok = $this->createResource($user, $address, $type, [
            'attributes' => ['gender' => 'female', 'anything_else' => 42],
        ]);
        self::assertSame(201, $this->responseCode(), 'کلید ناشناخته عمداً پذیرفته می‌شود');
        self::assertSame('female', $ok['data']['attributes']['gender']);
        self::assertSame(42, $ok['data']['attributes']['anything_else']);

        $bad = $this->createResource($user, $address, $type, [
            'name'       => 'دستگاه دوم',
            'attributes' => ['nested' => ['a' => 1]],
        ]);
        self::assertSame(422, $this->responseCode());
        self::assertSame('attributes', $bad['errors'][0]['field']);
    }

    public function testAttributeKeyMustBeSnakeCase(): void
    {
        [$user, , $address] = $this->clinicWithAddress();

        $body = $this->createResource($user, $address, $this->resourceType($address), [
            'attributes' => ['Device Model' => 'x'],
        ]);

        self::assertSame(422, $this->responseCode());
        self::assertSame('attributes', $body['errors'][0]['field']);
    }

    public function testForeignAddressIsNotFound(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $type = $this->resourceType($address);

        [, , $foreignAddress] = $this->clinicWithAddress('شعبهٔ بیگانه');

        $this->authJson('POST', '/api/v1/resource', $user, [
            'address_uuid' => $foreignAddress->getUuid(),
            'type_uuid'    => $type->getUuid(),
            'name'         => 'دستگاه',
        ]);

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

    public function testForeignResourceIsNotFound(): void
    {
        [$ownerUser, , $ownerAddress] = $this->clinicWithAddress();
        $created = $this->createResource($ownerUser, $ownerAddress, $this->resourceType($ownerAddress));

        [$otherUser] = $this->clinicWithAddress('شعبهٔ دیگر');

        $this->authJson('GET', "/api/v1/resource/{$created['data']['uuid']}", $otherUser);
        self::assertSame(404, $this->responseCode());

        $this->authJson('PATCH', "/api/v1/resource/{$created['data']['uuid']}", $otherUser, ['name' => 'دزدیده‌شده']);
        self::assertSame(404, $this->responseCode());

        $this->authJson('DELETE', "/api/v1/resource/{$created['data']['uuid']}", $otherUser);
        self::assertSame(404, $this->responseCode());
    }

    public function testResourceIsUpdatedAndDeleted(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $created = $this->createResource($user, $address, $this->resourceType($address));

        $body = $this->authJson('PATCH', "/api/v1/resource/{$created['data']['uuid']}", $user, [
            'name'            => 'لیزر دو',
            'cleanup_minutes' => 15,
            'active'          => false,
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertSame('لیزر دو', $body['data']['name']);
        self::assertSame(15, $body['data']['cleanup_minutes']);
        self::assertFalse($body['data']['active']);

        $this->authJson('DELETE', "/api/v1/resource/{$created['data']['uuid']}", $user);
        self::assertSame(200, $this->responseCode());

        $this->authJson('GET', "/api/v1/resource/{$created['data']['uuid']}", $user);
        self::assertSame(404, $this->responseCode());
    }

    public function testListIsFilteredByAddressTypeAndActive(): void
    {
        [$user, $clinic, $address] = $this->clinicWithAddress();
        $second  = $this->extraAddress($clinic);
        $device  = $this->resourceType($address, 'device', 'دستگاه');
        $chair   = $this->resourceType($address, 'chair', 'صندلی');

        $this->createResource($user, $address, $device, ['name' => 'دستگاه شعبهٔ یک']);
        $this->createResource($user, $second, $device, ['name' => 'دستگاه شعبهٔ دو']);
        $off = $this->createResource($user, $address, $chair, ['name' => 'صندلی خاموش']);
        $this->authJson('PATCH', "/api/v1/resource/{$off['data']['uuid']}", $user, ['active' => false]);

        $all = $this->authJson('GET', '/api/v1/resources', $user);
        self::assertCount(3, $all['data']);

        $byAddress = $this->authJson('GET', "/api/v1/resources?address_uuid={$address->getUuid()}", $user);
        self::assertCount(2, $byAddress['data']);

        $byType = $this->authJson('GET', "/api/v1/resources?type_uuid={$device->getUuid()}", $user);
        self::assertCount(2, $byType['data']);

        $activeOnly = $this->authJson('GET', '/api/v1/resources?active=1', $user);
        self::assertCount(2, $activeOnly['data']);
    }

    public function testListShowsOnlyTheCurrentEnvironment(): void
    {
        [$user, , $address] = $this->clinicWithAddress();
        $this->createResource($user, $address, $this->resourceType($address));

        [$otherUser, , $otherAddress] = $this->clinicWithAddress('شعبهٔ دیگر');
        $this->createResource($otherUser, $otherAddress, $this->resourceType($otherAddress));

        $body = $this->authJson('GET', '/api/v1/resources', $user);

        self::assertCount(1, $body['data']);
    }

    public function testBlankNameIsRejected(): void
    {
        [$user, , $address] = $this->clinicWithAddress();

        $body = $this->createResource($user, $address, $this->resourceType($address), ['name' => '   ']);

        self::assertSame(422, $this->responseCode());
        self::assertSame('name', $body['errors'][0]['field']);
    }
}
