<?php

namespace App\Tests\Appointment;

use App\Appointment\Entity\Appointment;
use App\Doctor\Entity\Doctor;
use App\Insurance\Entity\Insurance;
use App\Insurance\Entity\TenantInsurance;
use App\Insurance\Enum\InsuranceType;
use App\Insurance\Service\TenantServiceCategoryService;
use App\Tests\ApiTestCase;

/**
 * An appointment carries the insurance it is billed with: the service kind and the
 * basic insurance. Both are validated against that tenant's own settings/contracts.
 */
class AppointmentInsuranceSelectionTest extends ApiTestCase
{
    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
    {
        // اسلات یکتا به‌ازای هر نوبت: db_test بین اجراها پاک نمی‌شود.
        $start       = strtotime('+30 days') + random_int(0, 500_000) * 7;
        $appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 900);
        $appointment->setVisitPriceRials(5_952_000);
        $this->em->persist($appointment);
        $this->em->flush();

        return $appointment;
    }

    private function makeContract(Doctor $doctor, InsuranceType $type = InsuranceType::Basic): Insurance
    {
        $insurance = new Insurance('بیمه نوبت ' . random_int(1000, 9999), $type);
        $this->em->persist($insurance);
        $this->em->flush();

        $contract = new TenantInsurance(TenantInsurance::TYPE_DOCTOR, $doctor->getId(), $insurance->getId());
        $contract->setCoveragePercent(30)->setKind($type->value);
        $this->em->persist($contract);
        $this->em->flush();

        return $insurance;
    }

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

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

        // پاسخ PATCH تودرتوست: success(['data' => …]) → data.data
        self::assertSame(200, $this->responseCode());
        $row = $body['data']['data'];
        self::assertSame('inpatient', $row['insurance_service_category']);
        self::assertSame('خدمات بستری', $row['insurance_service_category_label']);
        self::assertSame($insurance->getId(), $row['insurance_base_id']);
    }

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

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

        $body = $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $doctor->getUser(), [
            'insurance_service_category' => null,
            'insurance_base_id'          => null,
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertNull($body['data']['data']['insurance_service_category']);
        self::assertNull($body['data']['data']['insurance_base_id']);
    }

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

        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $doctor->getUser(), [
            'insurance_service_category' => 'dental',
        ]);

        self::assertSame(422, $this->responseCode());
    }

    public function testDisabledCategoryIsRejected(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        static::getContainer()->get(TenantServiceCategoryService::class)
            ->save('doctor', $doctor->getId(), [['key' => 'inpatient', 'enabled' => false]]);

        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $doctor->getUser(), [
            'insurance_service_category' => 'inpatient',
        ]);

        self::assertSame(422, $this->responseCode());
    }

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

        $insurance = new Insurance('بیمه بی‌قرارداد ' . random_int(1000, 9999), InsuranceType::Basic);
        $this->em->persist($insurance);
        $this->em->flush();

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

        self::assertSame(422, $this->responseCode());
    }

    public function testSupplementaryInsuranceIsRejectedOnAnAppointment(): void
    {
        $doctor      = $this->makeDoctor();
        $appointment = $this->makeAppointment($doctor);
        $insurance   = $this->makeContract($doctor, InsuranceType::Supplementary);

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

        self::assertSame(422, $this->responseCode());
    }

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

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

        self::assertSame(200, $this->responseCode());
        self::assertSame('inpatient', $body['data']['appointment']['insurance_service_category']);
        self::assertSame($insurance->getId(), $body['data']['appointment']['insurance_base_id']);
    }
}
