<?php

namespace App\Tests\Patient;

use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Staff\Entity\ClinicStaff;
use App\Tests\ApiTestCase;

/**
 * سرویس‌های یک مراجعه با uuid از بدنهٔ درخواست می‌آیند. service_items فرزند
 * aggregate است (→ service_sections) و ستون محیط ندارد، پس TenantFilter پوششش
 * نمی‌دهد؛ کالای مصرفی این قاعده را از قبل رعایت می‌کرد ولی سرویس نه — قیمتِ
 * سرویسِ محیط دیگر وارد محاسبهٔ فاکتور می‌شد و روی SessionService ذخیره می‌ماند.
 */
class SessionServiceTenantTest extends ApiTestCase
{
    /** @return array{0: \App\Auth\Entity\User, 1: Doctor, 2: PatientRecord} */
    private function recordFor(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر مراجعه');
        $this->em->persist($doctor);
        $this->em->flush();

        $patient = $this->createUser(['ROLE_USER']);
        $record  = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
        $this->em->persist($record);
        $this->em->flush();

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

    private function serviceFor(Doctor $doctor, string $name, int $price): ServiceItem
    {
        $section = new ServiceSection('doctor', $doctor->getId(), 'بخش ' . $name);
        $this->em->persist($section);

        $item = new ServiceItem($section, $name, $price);
        $this->em->persist($item);
        $this->em->flush();

        return $item;
    }

    private function createSession(\App\Auth\Entity\User $owner, PatientRecord $record, array $body = []): array
    {
        $res = $this->authJson(
            'POST',
            '/api/v1/patient/' . $record->getUuid() . '/session',
            $owner,
            $body + ['visit_price_rials' => 500_000],
        );
        self::assertSame(201, $this->responseCode(), 'ساخت مراجعه باید موفق باشد');

        return $res['data'];
    }

    /** ✅ سرویس خودی وارد فاکتور می‌شود. */
    public function testOwnServiceIsPriced(): void
    {
        [$owner, $doctor, $record] = $this->recordFor();
        $mine = $this->serviceFor($doctor, 'سرویس خودی', 900_000);

        $session = $this->createSession($owner, $record, [
            'services' => [['service_item_uuid' => $mine->getUuid(), 'quantity' => 1]],
        ]);

        self::assertSame(900_000, $session['services_total_rials'], 'سرویس خودی باید قیمت بخورد');
    }

    /** ❌ سرویسِ محیط دیگر نه در قیمت می‌آید نه ذخیره می‌شود. */
    public function testAnotherEnvironmentsServiceIsIgnoredOnCreate(): void
    {
        [$owner, , $record] = $this->recordFor();
        [, $stranger]       = $this->recordFor();
        $foreign            = $this->serviceFor($stranger, 'سرویس بیگانه', 900_000);

        $session = $this->createSession($owner, $record, [
            'services' => [['service_item_uuid' => $foreign->getUuid(), 'quantity' => 1]],
        ]);

        self::assertSame(0, $session['services_total_rials'], 'قیمت سرویس بیگانه نباید اضافه شود');

        // قیمت‌گذاری و ذخیره‌سازی دو حلقهٔ جدا در PatientService هستند؛ این یکی
        // ذخیره را می‌سنجد و مستقیم از دیتابیس می‌خواند تا به شکل پاسخ وابسته نباشد.
        self::assertSame(0, $this->storedServiceCount($session['uuid']), 'نباید روی مراجعه ذخیره شود');
    }

    private function storedServiceCount(string $sessionUuid): int
    {
        $this->em->clear();
        $session = $this->em->getRepository(PatientSession::class)->findOneBy(['uuid' => $sessionUuid]);

        return $session === null ? -1 : $session->getServices()->count();
    }

    /** ❌ همان قاعده در ویرایش مراجعه. */
    public function testAnotherEnvironmentsServiceIsIgnoredOnUpdate(): void
    {
        [$owner, , $record] = $this->recordFor();
        [, $stranger]       = $this->recordFor();
        $foreign            = $this->serviceFor($stranger, 'سرویس بیگانه', 900_000);

        $session = $this->createSession($owner, $record);

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session['uuid'], $owner, [
            'services' => [['service_item_uuid' => $foreign->getUuid(), 'quantity' => 1]],
        ]);
        self::assertSame(200, $this->responseCode());

        self::assertSame(0, $res['data']['services_total_rials'], 'سرویس بیگانه نباید در فاکتور بنشیند');
        self::assertSame([], $res['data']['services'] ?? [], 'و نباید روی مراجعه ذخیره شود');
    }

    /** ⚠️ مرزی: سرویس خودی می‌ماند حتی وقتی یک بیگانه هم در فهرست است. */
    public function testOwnServiceSurvivesAlongsideAForeignOne(): void
    {
        [$owner, $doctor, $record] = $this->recordFor();
        [, $stranger]              = $this->recordFor();
        $mine                      = $this->serviceFor($doctor, 'سرویس خودی', 300_000);
        $foreign                   = $this->serviceFor($stranger, 'سرویس بیگانه', 900_000);

        $session = $this->createSession($owner, $record);

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session['uuid'], $owner, [
            'services' => [
                ['service_item_uuid' => $mine->getUuid(), 'quantity' => 1],
                ['service_item_uuid' => $foreign->getUuid(), 'quantity' => 1],
            ],
        ]);
        self::assertSame(200, $this->responseCode());

        self::assertSame(300_000, $res['data']['services_total_rials'], 'فقط سرویس خودی باید بماند');
        self::assertCount(1, $res['data']['services'] ?? []);
    }

    /** ❌ پرسنلِ محیط دیگر هم روی سرویس نمی‌نشیند. */
    public function testAnotherEnvironmentsStaffIsNotAttached(): void
    {
        [$owner, $doctor, $record] = $this->recordFor();
        [, $stranger]              = $this->recordFor();
        $mine                      = $this->serviceFor($doctor, 'سرویس خودی', 300_000);

        $foreignStaff = new ClinicStaff('doctor', $stranger->getId(), 'پرسنل بیگانه');
        $this->em->persist($foreignStaff);
        $this->em->flush();

        $session = $this->createSession($owner, $record);

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session['uuid'], $owner, [
            'services' => [[
                'service_item_uuid' => $mine->getUuid(),
                'staff_uuid'        => $foreignStaff->getUuid(),
                'quantity'          => 1,
            ]],
        ]);
        self::assertSame(200, $this->responseCode());

        $service = ($res['data']['services'] ?? [])[0] ?? null;
        self::assertNotNull($service, 'سرویس خودی باید ثبت شود');
        self::assertNull($service['staff_uuid'] ?? null, 'پرسنل بیگانه نباید چسبیده باشد');
    }
}
