<?php

namespace App\Patient\Service;

use App\Appointment\Entity\Appointment;
use App\Auth\Repository\UserRepository;
use App\Billing\Service\BillingCalculator;
use App\Billing\ValueObject\Money;
use App\ClinicService\Repository\ServiceItemRepository;
use App\Clinic\Repository\ClinicRepository;
use App\Insurance\Service\TenantInsuranceService;
use App\Doctor\Repository\DoctorAddressRepository;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Patient\Entity\SessionService;
use App\Patient\Repository\PatientRecordRepository;
use App\Patient\Repository\PatientSessionRepository;
use App\Patient\Repository\SessionServiceRepository;
use App\Staff\Repository\ClinicStaffRepository;
use App\Subscription\Service\SubscriptionService;

class PatientService
{
    public function __construct(
        private readonly PatientRecordRepository  $recordRepo,
        private readonly PatientSessionRepository $sessionRepo,
        private readonly SessionServiceRepository $sessionServiceRepo,
        private readonly ServiceItemRepository    $serviceItemRepo,
        private readonly ClinicStaffRepository    $staffRepo,
        private readonly UserRepository           $userRepo,
        private readonly SubscriptionService      $subscriptionService,
        private readonly DoctorAddressRepository  $addressRepo,
        private readonly ClinicRepository         $clinicRepo,
        private readonly TenantInsuranceService   $tenantInsuranceService,
        private readonly BillingCalculator        $billingCalculator,
    ) {}

    /**
     * محاسبه‌ی سهم بیمار.
     * ویزیت با درصد تخفیف انتخاب‌شده در فرم؛ هر خدمت با قاعده‌ی پوشش همان بیمه‌گر برای همان خدمت
     * (TenantServiceCoverage از طریق BillingCalculator). خدمتی که آن بیمه را پوشش نمی‌دهد، کامل بر عهده‌ی بیمار است.
     *
     * @param array<array{item_id: int, price_rials: int}> $serviceItems قیمت کل هر ردیف (با احتساب تعداد)
     */
    public function calculateFinalPrice(
        int $visitPrice,
        float $baseDiscount,
        float $suppDiscount,
        array $serviceItems,
        string $entityType = 'doctor',
        int $entityId = 0,
        ?int $baseInsuranceId = null,
        ?int $suppInsuranceId = null,
    ): array {
        $afterBase  = $visitPrice * (1 - $baseDiscount / 100);
        $afterSupp  = $afterBase  * (1 - $suppDiscount / 100);
        $visitShare = (int) round($afterSupp);

        $servicesTotal   = 0;
        $servicesPatient = 0;
        foreach ($serviceItems as $svc) {
            $servicesTotal += $svc['price_rials'];

            $baseRule = $this->tenantInsuranceService->coverageRuleForService($entityType, $entityId, $baseInsuranceId, $svc['item_id']);
            $suppRule = $this->tenantInsuranceService->coverageRuleForService($entityType, $entityId, $suppInsuranceId, $svc['item_id']);
            $breakdown = $this->billingCalculator->calculateItem(new Money($svc['price_rials']), $baseRule, $suppRule);

            $servicesPatient += $breakdown->patientRials;
        }

        return [
            'services_total_rials' => $servicesTotal,
            'final_price_rials'    => $visitShare + $servicesPatient,
        ];
    }

    public function autoCreateOnAppointmentConfirm(Appointment $appointment): void
    {
        $doctor = $appointment->getDoctor();

        // پرونده‌ی پزشک
        $this->autoCreateForEntity('doctor', $doctor->getId(), $appointment, $doctor->getId());

        // کلینیک نوبت را تعیین کن: اول از آدرس انتخاب‌شده، وگرنه اگر دکتر فقط عضو یک کلینیک باشد.
        $clinicId = null;
        $addressId = $appointment->getAddressId();
        if ($addressId !== null) {
            $clinicId = $this->addressRepo->find($addressId)?->getClinicId();
        }
        if ($clinicId === null) {
            $clinics = $this->clinicRepo->findByDoctor($doctor);
            if (count($clinics) === 1) {
                $clinicId = $clinics[0]->getId();
            }
        }

        if ($clinicId !== null) {
            $this->autoCreateForEntity('clinic', $clinicId, $appointment, $clinicId);
        }
    }

    private function autoCreateForEntity(string $entityType, int $entityId, Appointment $appointment, int $createdById): void
    {
        if (!$this->subscriptionService->hasFeature($entityType, $entityId, 'patient_records')) {
            return;
        }

        $patient = $appointment->getUser();

        $record = $this->recordRepo->findByEntityAndUser($entityType, $entityId, $patient);
        if ($record === null) {
            $record = new PatientRecord($entityType, $entityId, $patient, 'system', $createdById);
            $this->recordRepo->save($record);
        }

        $session = new PatientSession($record, $appointment);
        $this->sessionRepo->save($session);
    }

    public function createSession(
        PatientRecord $record,
        array $data,
        string $entityType,
        int $entityId
    ): PatientSession {
        $session = new PatientSession($record);

        if (!empty($data['appointment_uuid'])) {
            // appointment را از بیرون resolve می‌کنند و session را ست می‌کنند
        }

        $session->setInsuranceBaseId(isset($data['insurance_base_id']) ? (int) $data['insurance_base_id'] : null);
        $session->setInsuranceSupplementaryId(isset($data['insurance_supplementary_id']) ? (int) $data['insurance_supplementary_id'] : null);
        $session->setVisitPriceRials((int) ($data['visit_price_rials'] ?? 0));
        $session->setBaseInsuranceDiscountPercent((float) ($data['base_insurance_discount_percent'] ?? 0));
        $session->setSupplementaryDiscountPercent((float) ($data['supplementary_discount_percent'] ?? 0));
        $session->setPaymentMethod($data['payment_method'] ?? 'pending');
        $session->setNotes($data['notes'] ?? null);

        // جمع‌آوری service items (با احتساب تعداد)
        $serviceItemsData = [];
        foreach (($data['services'] ?? []) as $svc) {
            $item = $this->serviceItemRepo->findByUuid($svc['service_item_uuid'] ?? '');
            if ($item !== null) {
                $qty = max(1, (int) ($svc['quantity'] ?? 1));
                $serviceItemsData[] = ['item_id' => $item->getId(), 'price_rials' => $item->getPriceRials() * $qty];
            }
        }

        $priceCalc = $this->calculateFinalPrice(
            $session->getVisitPriceRials(),
            $session->getBaseInsuranceDiscountPercent(),
            $session->getSupplementaryDiscountPercent(),
            $serviceItemsData,
            $entityType,
            $entityId,
            $session->getInsuranceBaseId(),
            $session->getInsuranceSupplementaryId(),
        );

        $session->setServicesTotalRials($priceCalc['services_total_rials']);
        $session->setFinalPriceRials($priceCalc['final_price_rials']);

        $this->sessionRepo->save($session);

        // ثبت session services
        foreach (($data['services'] ?? []) as $svc) {
            $item = $this->serviceItemRepo->findByUuid($svc['service_item_uuid'] ?? '');
            if ($item === null) {
                continue;
            }
            $staff = !empty($svc['staff_uuid']) ? $this->staffRepo->findByUuid($svc['staff_uuid']) : null;
            $qty   = max(1, (int) ($svc['quantity'] ?? 1));
            $ss    = new SessionService($session, $item, $staff, $qty);
            $this->sessionServiceRepo->save($ss);
            $session->addService($ss);
        }

        return $session;
    }
}
