<?php

namespace App\Tests\ClinicService;

use App\Auth\Entity\User;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Inventory\Entity\InventoryItem;
use App\Inventory\Entity\InventoryPackage;
use App\Tests\ApiTestCase;

/**
 * دو قابلیت صفحه‌ی جزئیات سرویس:
 *  ۱. اتصال اختیاری خدمت به یک پکیج کالای مصرفی (کالاهای مرتبط)
 *  ۲. تاریخچه‌ی تغییرات خدمت (service_item_audit_logs)
 */
class ServiceItemPackageAndAuditTest extends ApiTestCase
{
    /** @return array{0: User, 1: Doctor, 2: ServiceSection} */
    private function makeDoctorWithSection(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر تست پکیج');
        $this->em->persist($doctor);
        $this->em->flush();

        $section = new ServiceSection('doctor', $doctor->getId(), 'تزریقات');
        $this->em->persist($section);
        $this->em->flush();

        return [$owner, $doctor, $section];
    }

    private function makePackage(Doctor $doctor, string $title = 'پکیج سرم'): InventoryPackage
    {
        $package = new InventoryPackage('doctor', $doctor->getId(), $title);
        $this->em->persist($package);
        $this->em->flush();

        return $package;
    }

    private function makeItem(ServiceSection $section, string $name = 'سرم ۵۰۰cc'): ServiceItem
    {
        $item = new ServiceItem($section, $name, 850_000);
        $this->em->persist($item);
        $this->em->flush();

        return $item;
    }

    // ── کالاهای مرتبط ────────────────────────────────────────────────────────

    public function testAttachingAPackageIsReflectedInTheDetailResponse(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $package = $this->makePackage($doctor);
        $item    = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => $package->getUuid(),
        ]);
        $this->assertSame(200, $this->responseCode());

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid(), $owner);
        $this->assertSame($package->getUuid(), $body['data']['inventory_package_uuid']);
        $this->assertSame('پکیج سرم', $body['data']['inventory_package_title']);
    }

    public function testDetachingWithNullClearsThePackage(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $package = $this->makePackage($doctor);
        $item    = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => $package->getUuid(),
        ]);
        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => null,
        ]);

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid(), $owner);
        $this->assertNull($body['data']['inventory_package_uuid']);
    }

    public function testPackageOfAnotherTenantIsRejected(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        $stranger = $this->createUser(['ROLE_DOCTOR']);
        $other    = new Doctor($stranger, 'دکتر غریبه');
        $this->em->persist($other);
        $this->em->flush();
        $foreignPackage = $this->makePackage($other, 'پکیج غریبه');

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => $foreignPackage->getUuid(),
        ]);

        $this->assertSame(422, $this->responseCode());
    }

    public function testUnknownPackageUuidIsRejected(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => 'no-such-package',
        ]);

        $this->assertSame(422, $this->responseCode());
    }

    // ── کالاهای تکی (مکمل پکیج) ──────────────────────────────────────────────

    private function makeInventoryItem(Doctor $doctor, string $name, int $price = 50_000): InventoryItem
    {
        $inventoryItem = new InventoryItem('doctor', $doctor->getId(), $name);
        $inventoryItem->setPrice($price)->setStock(100);
        $this->em->persist($inventoryItem);
        $this->em->flush();

        return $inventoryItem;
    }

    public function testAServiceCanHaveBothAPackageAndIndividualItems(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $package = $this->makePackage($doctor);
        $gauze   = $this->makeInventoryItem($doctor, 'گاز استریل');
        $syringe = $this->makeInventoryItem($doctor, 'سرنگ ۵cc');
        $item    = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => $package->getUuid(),
            'consumables' => [
                ['item_uuid' => $gauze->getUuid(),   'amount' => 2],
                ['item_uuid' => $syringe->getUuid(), 'amount' => 1],
            ],
        ]);
        $this->assertSame(200, $this->responseCode());

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid(), $owner);
        $row  = $body['data'];

        $this->assertSame($package->getUuid(), $row['inventory_package_uuid'], 'پکیج باید کنار اقلام تکی بماند');
        $this->assertCount(2, $row['consumables']);

        $byName = array_column($row['consumables'], null, 'name');
        $this->assertSame(2, $byName['گاز استریل']['amount']);
        $this->assertSame(1, $byName['سرنگ ۵cc']['amount']);
        $this->assertSame(50_000, $byName['گاز استریل']['price']);
    }

    public function testResendingConsumablesReplacesTheWholeList(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $gauze   = $this->makeInventoryItem($doctor, 'گاز استریل');
        $syringe = $this->makeInventoryItem($doctor, 'سرنگ ۵cc');
        $item    = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'consumables' => [
                ['item_uuid' => $gauze->getUuid(),   'amount' => 2],
                ['item_uuid' => $syringe->getUuid(), 'amount' => 1],
            ],
        ]);

        // فقط یک قلم با تعداد جدید ارسال می‌شود
        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'consumables' => [['item_uuid' => $gauze->getUuid(), 'amount' => 5]],
        ]);

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid(), $owner);
        $this->assertCount(1, $body['data']['consumables']);
        $this->assertSame('گاز استریل', $body['data']['consumables'][0]['name']);
        $this->assertSame(5, $body['data']['consumables'][0]['amount']);
    }

    public function testEmptyConsumablesArrayClearsThem(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $gauze = $this->makeInventoryItem($doctor, 'گاز استریل');
        $item  = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'consumables' => [['item_uuid' => $gauze->getUuid(), 'amount' => 2]],
        ]);
        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, ['consumables' => []]);

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid(), $owner);
        $this->assertSame([], $body['data']['consumables']);
    }

    public function testConsumableOfAnotherTenantIsRejected(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        $stranger = $this->createUser(['ROLE_DOCTOR']);
        $other    = new Doctor($stranger, 'دکتر غریبه');
        $this->em->persist($other);
        $this->em->flush();
        $foreignItem = $this->makeInventoryItem($other, 'کالای غریبه');

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'consumables' => [['item_uuid' => $foreignItem->getUuid(), 'amount' => 1]],
        ]);

        $this->assertSame(422, $this->responseCode());
    }

    public function testConsumablesCanBeSetAtCreationTime(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $gauze = $this->makeInventoryItem($doctor, 'گاز استریل');

        $created = $this->authJson('POST', '/api/v1/service-item', $owner, [
            'section_uuid' => $section->getUuid(),
            'name'         => 'پانسمان',
            'price_rials'  => 200_000,
            'consumables'  => [['item_uuid' => $gauze->getUuid(), 'amount' => 3]],
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertCount(1, $created['data']['consumables']);
        $this->assertSame(3, $created['data']['consumables'][0]['amount']);
    }

    public function testChangingConsumablesIsLogged(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $gauze = $this->makeInventoryItem($doctor, 'گاز استریل');
        $item  = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'consumables' => [['item_uuid' => $gauze->getUuid(), 'amount' => 2]],
        ]);

        $body   = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid() . '/audit-logs', $owner);
        $byField = array_column($body['data'], null, 'field');

        $this->assertArrayHasKey('consumables', $byField);
        $this->assertNull($byField['consumables']['old_value']);
        $this->assertSame('گاز استریل×2', $byField['consumables']['new_value']);
    }

    // ── لاگ تغییرات ──────────────────────────────────────────────────────────

    public function testCreatingAServiceWritesACreateLog(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();

        $created = $this->authJson('POST', '/api/v1/service-item', $owner, [
            'section_uuid' => $section->getUuid(),
            'name'         => 'خدمت تازه',
            'price_rials'  => 100_000,
        ]);
        $this->assertSame(201, $this->responseCode());

        $body = $this->authJson('GET', '/api/v1/service-item/' . $created['data']['uuid'] . '/audit-logs', $owner);
        $this->assertSame(200, $this->responseCode());
        $logs = $body['data'];
        $this->assertCount(1, $logs);
        $this->assertSame('create', $logs[0]['operation']);
        $this->assertSame('خدمت تازه', $logs[0]['new_value']);
    }

    public function testEachChangedFieldGetsItsOwnLogRow(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'name'        => 'نام جدید',
            'price_rials' => 990_000,
        ]);

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid() . '/audit-logs', $owner);
        $fields = array_column($body['data'], 'field');
        sort($fields);

        $this->assertSame(['name', 'price_rials'], $fields);

        $byField = array_column($body['data'], null, 'field');
        $this->assertSame('سرم ۵۰۰cc', $byField['name']['old_value']);
        $this->assertSame('نام جدید', $byField['name']['new_value']);
        $this->assertSame('850000', $byField['price_rials']['old_value']);
        $this->assertSame('990000', $byField['price_rials']['new_value']);
    }

    public function testUnchangedFieldsAreNotLogged(): void
    {
        [$owner, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        // همان مقادیر فعلی دوباره ارسال می‌شوند
        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'name'        => 'سرم ۵۰۰cc',
            'price_rials' => 850_000,
        ]);

        $body = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid() . '/audit-logs', $owner);
        $this->assertSame([], $body['data']);
    }

    public function testAttachingAPackageIsLogged(): void
    {
        [$owner, $doctor, $section] = $this->makeDoctorWithSection();
        $package = $this->makePackage($doctor);
        $item    = $this->makeItem($section);

        $this->authJson('PATCH', '/api/v1/service-item/' . $item->getUuid(), $owner, [
            'inventory_package_uuid' => $package->getUuid(),
        ]);

        $body   = $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid() . '/audit-logs', $owner);
        $fields = array_column($body['data'], 'field');

        $this->assertContains('inventory_package', $fields);
    }

    public function testAuditLogsOfAnotherTenantAreNotReadable(): void
    {
        [, , $section] = $this->makeDoctorWithSection();
        $item = $this->makeItem($section);

        $stranger = $this->createUser(['ROLE_DOCTOR']);
        $this->em->persist(new Doctor($stranger, 'دکتر غریبه'));
        $this->em->flush();

        $this->authJson('GET', '/api/v1/service-item/' . $item->getUuid() . '/audit-logs', $stranger);

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