<?php

namespace App\Tests\Billing;

use App\Auth\Entity\User;
use App\Billing\Entity\Invoice;
use App\Doctor\Entity\Doctor;
use App\Inventory\Entity\InventoryItem;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Patient\Entity\SessionConsumable;
use App\Patient\Entity\SessionPayment;
use App\Tests\ApiTestCase;

/**
 * GET /api/v1/billing/invoices/{uuid} — invoice detail enriched with its
 * source session (`session` key: payments, consumables, discount, paid totals).
 */
class InvoiceShowSessionTest extends ApiTestCase
{
    /** @return array{0: User, 1: Doctor} owner user + their doctor profile */
    private function doctor(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر تست');
        $this->em->persist($doctor);
        $this->em->flush();

        return [$owner, $doctor];
    }

    private function patientRecord(Doctor $doctor): PatientRecord
    {
        $patient = $this->createUser(['ROLE_USER']);
        $record  = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
        $this->em->persist($record);
        $this->em->flush();

        return $record;
    }

    private function setField(object $obj, string $prop, mixed $value): void
    {
        $ref = new \ReflectionProperty($obj, $prop);
        $ref->setAccessible(true);
        $ref->setValue($obj, $value);
    }

    public function testShowIncludesSessionPaymentsConsumablesAndDiscount(): void
    {
        [$owner, $doctor] = $this->doctor();
        $record  = $this->patientRecord($doctor);

        $session = new PatientSession($record);
        $session->setServicesTotalRials(2_400_000)
            ->setFinalPriceRials(2_200_000)
            ->setDiscount('amount', 200_000, 200_000)
            ->setSessionAt(1_700_000_000)
            ->setPaidAt(1_700_100_000);
        $this->em->persist($session);
        $this->em->flush();

        $payment = new SessionPayment($session, 'wallet', 1_500_000, 1_700_100_000);
        $payment->setCreatedByName('منشی تست');
        $this->em->persist($payment);
        $session->addPayment($payment);

        $item = new InventoryItem('doctor', $doctor->getId(), 'عینک');
        $item->setPrice(20_000);
        $this->em->persist($item);
        $this->em->flush();

        $consumable = new SessionConsumable($session, $item, 2);
        $this->em->persist($consumable);
        $session->addConsumable($consumable);
        $this->em->flush();

        $invoice = new Invoice('doctor', $doctor->getId());
        $invoice->setPatientSessionId($session->getId())
            ->setPatientRecordId($record->getId());
        $this->em->persist($invoice);
        $this->em->flush();

        $res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
        self::assertSame(200, $this->responseCode());

        $data = $res['data']['data'];
        self::assertSame($invoice->getUuid(), $data['uuid']);
        self::assertIsArray($data['session']);

        $s = $data['session'];
        self::assertSame($session->getUuid(), $s['uuid']);
        self::assertSame(1_700_000_000, $s['session_at']);
        self::assertSame(1_700_100_000, $s['paid_at']);
        self::assertSame(2_400_000, $s['services_total_rials']);
        self::assertSame(2_200_000, $s['final_price_rials']);
        self::assertSame(200_000, $s['discount_rials']);

        // payments — real rows, not heuristics
        self::assertCount(1, $s['payments']);
        self::assertSame('wallet', $s['payments'][0]['method']);
        self::assertSame(1_500_000, $s['payments'][0]['amount_rials']);
        self::assertSame('منشی تست', $s['payments'][0]['created_by_name']);
        self::assertSame(1_500_000, $s['paid_total_rials']);

        // consumables — snapshot price × quantity
        self::assertCount(1, $s['consumables']);
        self::assertSame('عینک', $s['consumables'][0]['item_name']);
        self::assertSame(2, $s['consumables'][0]['quantity']);
        self::assertSame(40_000, $s['consumables'][0]['line_total_rials']);
        self::assertSame(40_000, $s['consumables_total_rials']);
    }

    public function testShowSessionNullWhenInvoiceHasNoSession(): void
    {
        [$owner, $doctor] = $this->doctor();
        $record = $this->patientRecord($doctor);

        $invoice = new Invoice('doctor', $doctor->getId());
        $invoice->setPatientRecordId($record->getId());
        $this->em->persist($invoice);
        $this->em->flush();

        $res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
        self::assertSame(200, $this->responseCode());
        self::assertNull($res['data']['data']['session']);
    }

    public function testShowNotFoundForUnknownUuid(): void
    {
        [$owner] = $this->doctor();
        $this->authJson('GET', '/api/v1/billing/invoices/00000000-0000-0000-0000-000000000000', $owner);
        self::assertSame(404, $this->responseCode());
    }

    public function testShowNotFoundForOtherTenant(): void
    {
        [, $doctor] = $this->doctor();
        $invoice = new Invoice('doctor', $doctor->getId());
        $this->em->persist($invoice);
        $this->em->flush();

        [$other] = $this->doctor();
        $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $other);
        self::assertSame(404, $this->responseCode());
    }

    public function testShowSessionEmptyPaymentsAndConsumables(): void
    {
        [$owner, $doctor] = $this->doctor();
        $record  = $this->patientRecord($doctor);

        $session = new PatientSession($record);
        $this->em->persist($session);
        $this->em->flush();

        $invoice = new Invoice('doctor', $doctor->getId());
        $invoice->setPatientSessionId($session->getId());
        $this->em->persist($invoice);
        $this->em->flush();

        $res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
        self::assertSame(200, $this->responseCode());

        $s = $res['data']['data']['session'];
        self::assertSame([], $s['payments']);
        self::assertSame([], $s['consumables']);
        self::assertSame(0, $s['paid_total_rials']);
        self::assertSame(0, $s['discount_rials']);
    }
}
