<?php

namespace App\Tests\Insurance;

use App\Appointment\Entity\Appointment;
use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Insurance\Entity\EntityInsurancePricing;
use App\Patient\Entity\PatientRecord;
use App\Shared\Constant\ErrorCodes;
use App\Tests\ApiTestCase;

/**
 * فلگ «الزامی کردن هزینه ویزیت» (require_visit_price روی ردیف free-visit):
 * - PUT /insurance-pricing: فلگ فعال بدون قیمت > 0 → 422؛ فلگ-فقط بدون ردیف → ردیف ساخته نشود.
 * - POST /patient/{uuid}/session: با فلگ فعال، visit_price_rials <= 0 → 422.
 * - POST /my/appointment و /admin/appointment: فلگ برای پزشکِ نوبت resolve می‌شود
 *   (ردیف پزشک، وگرنه کلینیکِ واحد او)؛ بدون هزینه ویزیت → 422.
 */
class RequireVisitPriceTest extends ApiTestCase
{
    /** @return array{0: User, 1: Doctor} */
    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 pricingRow(string $type, int $entityId, int $priceRials, bool $require): EntityInsurancePricing
    {
        $row = new EntityInsurancePricing($type, $entityId, null, $priceRials);
        $row->setRequireVisitPrice($require);
        $this->em->persist($row);
        $this->em->flush();

        return $row;
    }

    private function recordFor(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 appointmentBody(string $doctorUuid): array
    {
        $start = time() + 86_400 + random_int(0, 3_600) * 100;

        return [
            'doctor_uuid'           => $doctorUuid,
            'slot_start'            => $start,
            'slot_end'              => $start + 1_800,
            'patient_mobile'        => '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT),
            'patient_name'          => 'بیمار تست',
            'patient_national_code' => '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT),
        ];
    }

    // ── PUT /api/v1/insurance-pricing ────────────────────────────────────────

    public function testSaveFlagOnWithZeroPriceIs422(): void
    {
        [$owner] = $this->doctor();

        $res = $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, [
            'free_visit_price_rials' => 0,
            'require_visit_price'    => true,
        ]);

        self::assertSame(422, $this->responseCode());
        self::assertSame(ErrorCodes::ERR_VALIDATION_001, $res['errors'][0]['code']);
    }

    public function testSaveFlagOnWithValidPricePersistsBoth(): void
    {
        [$owner] = $this->doctor();

        $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, [
            'free_visit_price_rials' => 500_000,
            'require_visit_price'    => true,
        ]);
        self::assertSame(200, $this->responseCode());

        $res = $this->authJson('GET', '/api/v1/insurance-pricing', $owner);
        self::assertSame(500_000, $res['data']['free_visit_price_rials']);
        self::assertTrue($res['data']['require_visit_price']);
    }

    public function testSaveFlagAloneKeepsStoredPrice(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 300_000, false);

        $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => true]);
        self::assertSame(200, $this->responseCode());

        $res = $this->authJson('GET', '/api/v1/insurance-pricing', $owner);
        self::assertSame(300_000, $res['data']['free_visit_price_rials']);
        self::assertTrue($res['data']['require_visit_price']);
    }

    public function testSaveFlagAloneOnEnabledRowWithZeroStoredPriceIs422(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 0, false);

        $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => true]);
        self::assertSame(422, $this->responseCode());
    }

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

        $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => false]);
        self::assertSame(200, $this->responseCode());

        $row = $this->em->getRepository(EntityInsurancePricing::class)->findOneBy([
            'entityType'  => EntityInsurancePricing::TYPE_DOCTOR,
            'entityId'    => $doctor->getId(),
            'insuranceId' => null,
        ]);
        self::assertNull($row);
    }

    // ── POST /api/v1/patient/{uuid}/session ─────────────────────────────────

    public function testSessionWithoutVisitPriceIs422WhenFlagOn(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
        $record = $this->recordFor($doctor);

        $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
            'visit_price_rials' => 0,
        ]);

        self::assertSame(422, $this->responseCode());
        self::assertSame(ErrorCodes::ERR_VALIDATION_001, $res['errors'][0]['code']);
    }

    public function testSessionWithVisitPriceIs201WhenFlagOn(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
        $record = $this->recordFor($doctor);

        $res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
            'visit_price_rials' => 400_000,
        ]);

        self::assertSame(201, $this->responseCode());
        self::assertSame(400_000, $res['data']['visit_price_rials']);
    }

    public function testSessionWithZeroVisitPriceStaysAllowedWhenFlagOff(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, false);
        $record = $this->recordFor($doctor);

        $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
            'visit_price_rials' => 0,
        ]);

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

    // ── POST /api/v1/my/appointment ─────────────────────────────────────────

    public function testMyAppointmentWithoutVisitPriceIs422WhenFlagOn(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);

        $res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));

        self::assertSame(422, $this->responseCode());
        self::assertSame('visit_price_rials', $res['errors'][0]['field']);
    }

    public function testMyAppointmentWithVisitPriceIs201AndStored(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);

        $body = $this->appointmentBody($doctor->getUuid());
        $body['visit_price_rials'] = 500_000;
        $res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $body);

        self::assertSame(201, $this->responseCode());
        $appointment = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $res['data']['uuid']]);
        self::assertSame(500_000, $appointment->getVisitPriceRials());
    }

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

        $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));

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

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

        $clinicOwner = $this->createUser(['ROLE_CLINIC']);
        $clinic      = new Clinic($clinicOwner);
        $clinic->getDoctors()->add($doctor);
        $this->em->persist($clinic);
        $this->em->flush();

        $this->pricingRow(EntityInsurancePricing::TYPE_CLINIC, $clinic->getId(), 500_000, true);

        $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));

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

    // ── POST /api/v1/admin/appointment ──────────────────────────────────────

    public function testAdminAppointmentWithoutVisitPriceIs422WhenFlagOn(): void
    {
        [, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
        $admin = $this->createUser(['ROLE_ADMIN']);

        $res = $this->authJson('POST', '/api/v1/admin/appointment', $admin, $this->appointmentBody($doctor->getUuid()));

        self::assertSame(422, $this->responseCode());
        self::assertSame('visit_price_rials', $res['errors'][0]['field']);
    }

    public function testAdminAppointmentWithVisitPriceIs201(): void
    {
        [, $doctor] = $this->doctor();
        $this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
        $admin = $this->createUser(['ROLE_ADMIN']);

        $body = $this->appointmentBody($doctor->getUuid());
        $body['visit_price_rials'] = 500_000;
        $res = $this->authJson('POST', '/api/v1/admin/appointment', $admin, $body);

        self::assertSame(201, $this->responseCode());
        $appointment = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $res['data']['uuid']]);
        self::assertSame(500_000, $appointment->getVisitPriceRials());
    }
}
