<?php

namespace App\Tests\Appointment;

use App\Appointment\Entity\Appointment;
use App\Billing\Service\InvoiceService;
use App\Doctor\Entity\Doctor;
use App\Insurance\Entity\Insurance;
use App\Insurance\Entity\TenantInsurance;
use App\Insurance\Enum\InsuranceType;
use App\Insurance\Service\InsuranceCoverageDefaultService;
use App\Patient\Entity\PatientSession;
use App\Tests\ApiTestCase;

/**
 * Confirming an appointment used to hand the whole amount to the patient. With an
 * insurance picked on the appointment the visit is now split through BillingCalculator,
 * using the coverage percent of that appointment's service kind.
 */
class AppointmentConfirmInsuranceSharesTest extends ApiTestCase
{
    private const VISIT_RIALS = 5_952_000;

    private function makeDoctor(): Doctor
    {
        $user   = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $doctor = new Doctor($user, 'دکتر تست سهم بیمه');
        $doctor->setMobileNumber($user->getMobileNumber());
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function makeAppointment(Doctor $doctor): Appointment
    {
        $start       = strtotime('+30 days') + random_int(0, 500_000) * 7;
        $appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 900);
        $appointment->setVisitPriceRials(self::VISIT_RIALS);
        $this->em->persist($appointment);
        $this->em->flush();

        return $appointment;
    }

    /** قرارداد پایه با درصد مرکزی: سرپایی ۷۰٪، بستری ۳۰٪. */
    private function makeInsuranceWithDefaults(Doctor $doctor): Insurance
    {
        $insurance = new Insurance('بیمه سهم ' . random_int(1000, 9999), InsuranceType::Basic);
        $this->em->persist($insurance);
        $this->em->flush();

        $contract = new TenantInsurance(TenantInsurance::TYPE_DOCTOR, $doctor->getId(), $insurance->getId());
        $contract->setKind('basic');
        $this->em->persist($contract);
        $this->em->flush();

        static::getContainer()->get(InsuranceCoverageDefaultService::class)->save($insurance->getId(), [
            ['key' => 'outpatient', 'coverage_percent' => 70],
            ['key' => 'inpatient',  'coverage_percent' => 30],
        ]);

        return $insurance;
    }

    private function reloadSession(string $uuid): PatientSession
    {
        $this->em->clear();

        return $this->em->getRepository(PatientSession::class)->findOneBy(['uuid' => $uuid]);
    }

    public function testInpatientReferenceScenario(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        $insurance   = $this->makeInsuranceWithDefaults($doctor);

        $body = $this->authJson('POST', '/api/v1/appointment/' . $appointment->getUuid() . '/confirm', $doctor->getUser(), [
            'insurance_service_category' => 'inpatient',
            'insurance_base_id'          => $insurance->getId(),
        ]);

        self::assertSame(200, $this->responseCode());
        $session = $body['data']['session'];
        self::assertSame(1_785_600, $session['base_insurance_rials']);
        self::assertSame(4_166_400, $session['patient_share_rials']);
        self::assertSame(4_166_400, $session['final_price_rials']);
    }

    public function testOutpatientUsesItsOwnPercent(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        $insurance   = $this->makeInsuranceWithDefaults($doctor);

        $body = $this->authJson('POST', '/api/v1/appointment/' . $appointment->getUuid() . '/confirm', $doctor->getUser(), [
            'insurance_service_category' => 'outpatient',
            'insurance_base_id'          => $insurance->getId(),
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertSame(4_166_400, $body['data']['session']['base_insurance_rials']); // 70٪
        self::assertSame(1_785_600, $body['data']['session']['patient_share_rials']);
    }

    /** نوبتِ بدون بیمه باید مثل قبل کاملاً سهم بیمار بماند. */
    public function testAppointmentWithoutInsuranceStaysFullyOnThePatient(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);

        $body = $this->authJson('POST', '/api/v1/appointment/' . $appointment->getUuid() . '/confirm', $doctor->getUser(), []);

        self::assertSame(200, $this->responseCode());
        self::assertSame(0, $body['data']['session']['base_insurance_rials']);
        self::assertSame(self::VISIT_RIALS, $body['data']['session']['patient_share_rials']);
    }

    /** بدون انتخاب کاربر، تنها نوعِ فعالِ tenant مبنا می‌شود. */
    public function testSingleEnabledCategoryIsUsedWithoutAnExplicitChoice(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        $insurance   = $this->makeInsuranceWithDefaults($doctor);

        $this->authJson('PUT', '/api/v1/insurance-pricing', $doctor->getUser(), [
            'service_categories' => [['key' => 'outpatient', 'enabled' => false]],
        ]);
        self::assertSame(200, $this->responseCode());

        $body = $this->authJson('POST', '/api/v1/appointment/' . $appointment->getUuid() . '/confirm', $doctor->getUser(), [
            'insurance_base_id' => $insurance->getId(),
        ]);

        self::assertSame(200, $this->responseCode());
        // فقط «بستری» فعال است → ۳۰٪
        self::assertSame(1_785_600, $body['data']['session']['base_insurance_rials']);

        $session = $this->reloadSession($body['data']['session']['uuid']);
        self::assertSame('inpatient', $session->getInsuranceServiceCategory()->value);
        self::assertSame($insurance->getId(), $session->getInsuranceBaseId());
    }

    public function testInvoiceSnapshotsTheServiceCategory(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        $insurance   = $this->makeInsuranceWithDefaults($doctor);

        $body = $this->authJson('POST', '/api/v1/appointment/' . $appointment->getUuid() . '/confirm', $doctor->getUser(), [
            'insurance_service_category' => 'inpatient',
            'insurance_base_id'          => $insurance->getId(),
        ]);
        $session = $this->reloadSession($body['data']['session']['uuid']);

        $invoice = static::getContainer()->get(InvoiceService::class)
            ->createFromSession($session, 'doctor', $doctor->getId());

        self::assertSame('inpatient', $invoice->toArray()['service_category']);
        self::assertSame('خدمات بستری', $invoice->toArray()['service_category_label']);
        self::assertSame(1_785_600, $invoice->toArray()['base_insurance_rials']);
        self::assertSame(4_166_400, $invoice->toArray()['patient_rials']);
    }
}
