<?php

namespace App\Services\Care\Dentistry;

use App\Models\DentalPlanItem;
use App\Models\DentalProcedure;
use App\Models\DentalTreatmentPlan;
use App\Models\Organization;
use App\Models\Visit;
use App\Services\Care\AuditLogger;
use App\Services\Care\BillService;
use App\Services\Care\SpecialtyShellService;

class DentalProcedureService
{
    public function __construct(
        protected SpecialtyShellService $shell,
        protected BillService $bills,
        protected DentalTreatmentPlanService $plans,
    ) {}

    /**
     * @param  array{
     *     procedure_code: string,
     *     tooth_code?: ?string,
     *     surfaces?: list<string>,
     *     anesthesia?: ?string,
     *     notes?: ?string,
     *     plan_item_id?: ?int,
     *     bill?: bool
     * }  $data
     */
    public function complete(
        Organization $organization,
        Visit $visit,
        array $data,
        string $ownerRef,
        ?string $actorRef = null,
    ): DentalProcedure {
        $visit->loadMissing('patient');
        $code = (string) ($data['procedure_code'] ?? '');
        $service = collect($this->shell->provisionedServices($organization, 'dentistry'))
            ->first(fn ($s) => ($s['code'] ?? '') === $code)
            ?? DentalCatalog::defaultServices()[$code] ?? null;

        if (! $service) {
            throw new \InvalidArgumentException("Unknown dental procedure [{$code}].");
        }

        $tooth = $data['tooth_code'] ?? null;
        if ($tooth && ! DentalCatalog::isValidToothCode($tooth)) {
            throw new \InvalidArgumentException("Invalid FDI tooth code [{$tooth}].");
        }

        $surfaces = array_values(array_intersect(
            array_map('strtoupper', $data['surfaces'] ?? []),
            DentalCatalog::surfaces(),
        ));

        $planItem = null;
        if (! empty($data['plan_item_id'])) {
            $planItem = DentalPlanItem::query()->find((int) $data['plan_item_id']);
            if ($planItem && $planItem->plan?->patient_id !== $visit->patient_id) {
                throw new \InvalidArgumentException('Plan item does not belong to this patient.');
            }
        }

        $label = $service['label'];
        if ($tooth) {
            $label .= ' · tooth '.$tooth;
            if ($surfaces !== []) {
                $label .= ' ('.implode('', $surfaces).')';
            }
        }

        $amount = (int) ($service['amount_minor'] ?? 0);

        $procedure = DentalProcedure::create([
            'owner_ref' => $ownerRef,
            'organization_id' => $organization->id,
            'patient_id' => $visit->patient_id,
            'visit_id' => $visit->id,
            'plan_item_id' => $planItem?->id,
            'tooth_code' => $tooth,
            'surfaces' => $surfaces,
            'procedure_code' => $code,
            'label' => $label,
            'anesthesia' => $data['anesthesia'] ?? null,
            'status' => DentalProcedure::STATUS_COMPLETED,
            'amount_minor' => $amount,
            'bill_line_item_id' => null,
            'provider_ref' => $actorRef ?? $ownerRef,
            'notes' => $data['notes'] ?? null,
            'performed_at' => now(),
        ]);

        if ($data['bill'] ?? true) {
            $bill = $this->bills->generateFromVisit($visit, $ownerRef, $actorRef);
            $bill = $this->bills->addManualLineItem($bill, $ownerRef, [
                'type' => $service['type'] ?? 'procedure',
                'description' => $label,
                'quantity' => 1,
                'unit_price_minor' => $amount,
                'source_type' => 'dental_procedure',
                'source_id' => $procedure->id,
            ], $actorRef);
            $billLineId = $bill->lineItems()->where('source_type', 'dental_procedure')->where('source_id', $procedure->id)->value('id');
            $procedure->update(['bill_line_item_id' => $billLineId]);
            if ($planItem && $billLineId) {
                $planItem->update(['bill_line_item_id' => $billLineId]);
            }
        }

        if ($planItem) {
            $this->plans->markItemDone($planItem);
            $plan = $planItem->plan;
            if ($plan && $plan->status === DentalTreatmentPlan::STATUS_ACCEPTED) {
                $plan->update(['status' => DentalTreatmentPlan::STATUS_IN_PROGRESS]);
            }
        }

        AuditLogger::record(
            $ownerRef,
            'dental.procedure.completed',
            $organization->id,
            $actorRef,
            DentalProcedure::class,
            $procedure->id,
        );

        return $procedure->fresh();
    }
}
