<?php

namespace App\Tests\Billing;

use App\Auth\Entity\User;
use App\Billing\Entity\Invoice;
use App\Billing\Entity\InvoiceItem;
use App\Billing\Repository\ClaimRepository;
use App\Billing\Service\InvoiceService;
use App\Billing\ValueObject\ShareBreakdown;
use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Tests\ApiTestCase;

/**
 * مطالبهٔ بیمه اثر جانبیِ نهایی‌شدن صورتحساب است، نه کارِ یک endpoint خاص.
 * پیش از این فقط `POST /api/v1/patient/{uuid}/session` مطالبه می‌ساخت، و هر
 * صورتحسابی که از مسیر دیگری نهایی می‌شد بی‌صدا بدون مطالبه می‌ماند.
 */
class ClaimAutoCreationTest extends ApiTestCase
{
    private Doctor $doctor;
    private PatientRecord $record;

    protected function setUp(): void
    {
        parent::setUp();

        $owner        = $this->createUser(['ROLE_DOCTOR']);
        $this->doctor = new Doctor($owner, 'دکتر مطالبه');
        $this->em->persist($this->doctor);
        $this->em->flush();

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

    /** صورتحساب draft با سهم بیمهٔ پایه/تکمیلی روی تنها آیتمش. */
    private function draftInvoice(?int $baseId, ?int $suppId, int $baseShare, int $suppShare): Invoice
    {
        $total   = 10_000_000;
        $invoice = new Invoice('doctor', $this->doctor->getId());
        $invoice->setPatientRecordId((int) $this->record->getId())
            ->setBaseInsuranceId($baseId)
            ->setSupplementaryInsuranceId($suppId);

        $item = new InvoiceItem(
            $invoice,
            'ویزیت',
            $total,
            1,
            new ShareBreakdown($total, $baseShare, $suppShare, $total - $baseShare - $suppShare),
        );
        $invoice->addItem($item);
        $invoice->recalculateTotals();

        $this->em->persist($invoice);
        $this->em->persist($item);
        $this->em->flush();

        return $invoice;
    }

    private function claimRepo(): ClaimRepository
    {
        return static::getContainer()->get(ClaimRepository::class);
    }

    private function invoiceService(): InvoiceService
    {
        return static::getContainer()->get(InvoiceService::class);
    }

    public function testFinalizingAnInsuredInvoiceCreatesItsClaims(): void
    {
        $invoice = $this->draftInvoice(1, 2, 6_000_000, 2_000_000);

        $this->invoiceService()->finalize($invoice);

        $kinds = $this->claimRepo()->kindsForInvoice((int) $invoice->getId());
        sort($kinds);
        self::assertSame(['base', 'supplementary'], $kinds);
    }

    public function testASupplementaryOnlyInvoiceStillGetsItsClaim(): void
    {
        // همان حالتی که در پنل دیده شد: بیمار بیمهٔ پایه ندارد، فقط تکمیلی.
        $invoice = $this->draftInvoice(null, 2, 0, 4_760_000);

        $this->invoiceService()->finalize($invoice);

        self::assertSame(['supplementary'], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
    }

    public function testFinalizingTwiceDoesNotDuplicateClaims(): void
    {
        $invoice = $this->draftInvoice(1, null, 3_000_000, 0);

        $this->invoiceService()->finalize($invoice);
        $this->invoiceService()->finalize($invoice);

        self::assertCount(1, $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
    }

    public function testAnInvoiceWithoutAnyInsuranceShareGetsNoClaim(): void
    {
        $invoice = $this->draftInvoice(null, null, 0, 0);

        $this->invoiceService()->finalize($invoice);

        self::assertSame([], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
    }

    /** بیمه‌ای که فقط روی صورتحساب ثبت شده ولی سهمی نبرده، مطالبه نمی‌سازد. */
    public function testAZeroShareInsuranceIsNotClaimed(): void
    {
        $invoice = $this->draftInvoice(1, 2, 6_000_000, 0);

        $this->invoiceService()->finalize($invoice);

        self::assertSame(['base'], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
    }
}
