<?php

namespace App\Tests\Insurance;

use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Insurance\Entity\Insurance;
use App\Insurance\Entity\TenantInsurance;
use App\Insurance\Enum\InsuranceType;
use App\Insurance\Enum\ServiceCategory;
use App\Insurance\Service\InsuranceCoverageDefaultService;
use App\Insurance\Service\TenantInsuranceService;
use App\Tests\ApiTestCase;

/**
 * The effective coverage percentage is resolved through a four-level chain:
 * service override → contract category override → central admin default →
 * legacy contract percentage. A contract that overrode nothing must follow the
 * admin default live, which is what makes central management work at all.
 */
class CoveragePercentResolutionTest extends ApiTestCase
{
    private TenantInsuranceService $service;
    private InsuranceCoverageDefaultService $defaults;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service  = static::getContainer()->get(TenantInsuranceService::class);
        $this->defaults = static::getContainer()->get(InsuranceCoverageDefaultService::class);
    }

    /** @return array{0: TenantInsurance, 1: Insurance, 2: ServiceItem} */
    private function makeContract(float $legacyPercent = 55.0): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر تست درصد پوشش');
        $this->em->persist($doctor);

        $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->setCoveragePercent($legacyPercent);
        $section = new ServiceSection(TenantInsurance::TYPE_DOCTOR, $doctor->getId(), 'بخش بستری');
        $this->em->persist($contract);
        $this->em->persist($section);
        $this->em->flush();

        $item = (new ServiceItem($section, 'جراحی', 5_952_000))
            ->setServiceCategory(ServiceCategory::Inpatient)
            ->setInsuranceCovered(true);
        $this->em->persist($item);
        $this->em->flush();

        return [$contract, $insurance, $item];
    }

    public function testFallsBackToLegacyContractPercentWhenNothingIsConfigured(): void
    {
        [$contract] = $this->makeContract(55.0);

        $this->assertSame(55.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
    }

    public function testAdminDefaultWinsOverLegacyContractPercent(): void
    {
        [$contract, $insurance] = $this->makeContract(55.0);

        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient', 'coverage_percent' => 70],
        ]);

        $this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
        $this->assertSame(70.0, $this->service->resolvePercent($contract, ServiceCategory::Outpatient));
    }

    /** تغییر تنظیمات مرکزی باید فوراً روی قرارداد بدون override اثر بگذارد (fallback زنده). */
    public function testChangingTheAdminDefaultImmediatelyChangesTheContract(): void
    {
        [$contract, $insurance] = $this->makeContract(55.0);

        $this->defaults->save($insurance->getId(), [['key' => 'inpatient', 'coverage_percent' => 30]]);
        $this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));

        $this->defaults->save($insurance->getId(), [['key' => 'inpatient', 'coverage_percent' => 45]]);
        $this->assertSame(45.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
    }

    public function testContractCategoryOverrideWinsOverAdminDefault(): void
    {
        [$contract, $insurance] = $this->makeContract(55.0);
        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient', 'coverage_percent' => 70],
        ]);

        $this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);

        $this->assertSame(90.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
        $this->assertSame(70.0, $this->service->resolvePercent($contract, ServiceCategory::Outpatient));
    }

    public function testServiceOverrideWinsOverEveryOtherLevel(): void
    {
        [$contract, $insurance, $item] = $this->makeContract(55.0);
        // سرپایی هم پیش‌فرض دارد، وگرنه ذخیرهٔ درصدها با «درصد پوشش الزامی است» رد می‌شود.
        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient', 'coverage_percent' => 70],
        ]);
        $this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);

        $this->service->setServiceCoverage($contract, $item->getId(), true, 25.0, null, null);

        $rule = $this->service->coverageRuleForService(
            TenantInsurance::TYPE_DOCTOR,
            $contract->getEntityId(),
            $contract->getInsuranceId(),
            $item->getId(),
        );

        $this->assertSame(25.0, $rule->coveragePercent);
    }

    /** درصد نوعِ خودِ خدمت باید انتخاب شود، نه درصد سرپایی. */
    public function testServiceRuleUsesTheCategoryOfTheServiceItem(): void
    {
        [$contract, $insurance, $item] = $this->makeContract(55.0);
        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient', 'coverage_percent' => 70],
        ]);

        $rule = $this->service->coverageRuleForService(
            TenantInsurance::TYPE_DOCTOR,
            $contract->getEntityId(),
            $contract->getInsuranceId(),
            $item->getId(),
        );

        $this->assertSame(30.0, $rule->coveragePercent);
        $this->assertSame(0.0, $rule->franchisePercent, 'فرانشیز در بیمهٔ پایه صفر می‌ماند');
    }

    /** ویزیت خدمتِ سرپایی است. */
    public function testVisitRuleAlwaysUsesTheOutpatientPercent(): void
    {
        [$contract, $insurance] = $this->makeContract(55.0);
        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient' , 'coverage_percent' => 70],
        ]);

        $rule = $this->service->coverageRule(
            TenantInsurance::TYPE_DOCTOR,
            $contract->getEntityId(),
            $contract->getInsuranceId(),
        );

        $this->assertSame(70.0, $rule->coveragePercent);
    }

    public function testRemovingTheOverrideReturnsToTheAdminDefault(): void
    {
        [$contract, $insurance] = $this->makeContract(55.0);
        $this->defaults->save($insurance->getId(), [
            ['key' => 'inpatient',  'coverage_percent' => 30],
            ['key' => 'outpatient', 'coverage_percent' => 70],
        ]);
        $this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);

        $this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => null]]);

        $this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
    }
}
