<?php

namespace App\Services\Care;

use App\Models\Appointment;
use App\Models\Consultation;
use App\Models\ConsultationDocument;
use App\Models\Diagnosis;
use App\Models\VitalSign;
use Illuminate\Http\UploadedFile;

class ConsultationService
{
    public function __construct(
        protected AppointmentService $appointments,
    ) {}

    public function startFromAppointment(Appointment $appointment, string $ownerRef, ?string $actorRef = null): Consultation
    {
        if ($appointment->status !== Appointment::STATUS_IN_CONSULTATION) {
            $this->appointments->startConsultation(
                $appointment,
                $ownerRef,
                $appointment->practitioner_id,
                $actorRef,
            );
            $appointment->refresh();
        }

        $existing = Consultation::where('appointment_id', $appointment->id)
            ->where('status', '!=', Consultation::STATUS_COMPLETED)
            ->latest('id')
            ->first();
        if ($existing) {
            return $existing->load(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner']);
        }

        $consultation = Consultation::create([
            'owner_ref' => $ownerRef,
            'visit_id' => $appointment->visit_id,
            'appointment_id' => $appointment->id,
            'practitioner_id' => $appointment->practitioner_id,
            'patient_id' => $appointment->patient_id,
            'status' => Consultation::STATUS_DRAFT,
            'started_at' => now(),
        ]);

        AuditLogger::record($ownerRef, 'consultation.started', $appointment->organization_id, $actorRef, Consultation::class, $consultation->id);

        return $consultation->load(['patient', 'practitioner', 'visit']);
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public function save(Consultation $consultation, string $ownerRef, array $data, ?string $actorRef = null): Consultation
    {
        $consultation->update([
            'symptoms' => $data['symptoms'] ?? $consultation->symptoms,
            'clinical_notes' => $data['clinical_notes'] ?? $consultation->clinical_notes,
            'practitioner_id' => $data['practitioner_id'] ?? $consultation->practitioner_id,
        ]);

        if (array_key_exists('vitals', $data) && is_array($data['vitals'])) {
            $this->saveVitals($consultation, $ownerRef, $data['vitals'], $actorRef);
        }

        if (array_key_exists('diagnoses', $data)) {
            $this->syncDiagnoses($consultation, $ownerRef, $data['diagnoses']);
        }

        if (! empty($data['documents'])) {
            $this->storeDocuments($consultation, $ownerRef, $data['documents'], $actorRef);
        }

        AuditLogger::record($ownerRef, 'consultation.updated', $consultation->visit->organization_id, $actorRef, Consultation::class, $consultation->id);

        return $consultation->fresh(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner', 'visit', 'appointment']);
    }

    public function complete(Consultation $consultation, string $ownerRef, ?string $actorRef = null): Consultation
    {
        $consultation->update([
            'status' => Consultation::STATUS_COMPLETED,
            'completed_at' => now(),
            'completed_by' => $actorRef,
        ]);

        if ($consultation->appointment) {
            $this->appointments->complete($consultation->appointment, $ownerRef, $actorRef);
        } elseif ($consultation->visit) {
            app(VisitService::class)->complete($consultation->visit, $ownerRef, $actorRef);
        }

        AuditLogger::record($ownerRef, 'consultation.completed', $consultation->visit->organization_id, $actorRef, Consultation::class, $consultation->id);

        return $consultation->fresh(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner']);
    }

    /**
     * @param  array<string, mixed>  $vitals
     * @param  array{source?: string, device_id?: int|null, raw_payload?: array<string, mixed>|null}  $provenance
     */
    public function saveVitals(
        Consultation $consultation,
        string $ownerRef,
        array $vitals,
        ?string $actorRef = null,
        array $provenance = [],
    ): ?VitalSign {
        $numeric = collect($vitals)->only([
            'bp_systolic', 'bp_diastolic', 'pulse', 'temperature',
            'weight_kg', 'height_cm', 'spo2', 'respiratory_rate',
        ])->filter(fn ($v) => $v !== null && $v !== '')->all();

        if ($numeric === []) {
            return null;
        }

        return VitalSign::create([
            'owner_ref' => $ownerRef,
            'consultation_id' => $consultation->id,
            'bp_systolic' => $vitals['bp_systolic'] ?? null,
            'bp_diastolic' => $vitals['bp_diastolic'] ?? null,
            'pulse' => $vitals['pulse'] ?? null,
            'temperature' => $vitals['temperature'] ?? null,
            'weight_kg' => $vitals['weight_kg'] ?? null,
            'height_cm' => $vitals['height_cm'] ?? null,
            'spo2' => $vitals['spo2'] ?? null,
            'respiratory_rate' => $vitals['respiratory_rate'] ?? null,
            'recorded_by' => $actorRef,
            'recorded_at' => now(),
            'source' => $provenance['source'] ?? VitalSign::SOURCE_MANUAL,
            'device_id' => $provenance['device_id'] ?? null,
            'raw_payload' => $provenance['raw_payload'] ?? null,
        ]);
    }

    /**
     * @param  array<int, array<string, mixed>>  $rows
     */
    protected function syncDiagnoses(Consultation $consultation, string $ownerRef, array $rows): void
    {
        $consultation->diagnoses()->delete();

        foreach ($rows as $row) {
            if (empty($row['description'])) {
                continue;
            }
            Diagnosis::create([
                'owner_ref' => $ownerRef,
                'consultation_id' => $consultation->id,
                'code' => $row['code'] ?? null,
                'description' => $row['description'],
                'is_primary' => (bool) ($row['is_primary'] ?? false),
                'notes' => $row['notes'] ?? null,
            ]);
        }
    }

    /**
     * @param  array<int, UploadedFile>  $files
     */
    protected function storeDocuments(Consultation $consultation, string $ownerRef, array $files, ?string $actorRef): void
    {
        foreach ($files as $file) {
            if (! $file instanceof UploadedFile) {
                continue;
            }

            $path = $file->store("care/consultations/{$consultation->id}/documents", 'public');

            ConsultationDocument::create([
                'owner_ref' => $ownerRef,
                'consultation_id' => $consultation->id,
                'file_path' => $path,
                'original_name' => $file->getClientOriginalName(),
                'mime_type' => $file->getMimeType(),
                'uploaded_by' => $actorRef,
            ]);
        }
    }
}
