<?php

namespace Modules\ClinicLab\Entities\Services;

use Modules\ClinicCore\Entities\ClcPatient;
use Modules\LabCore\Entities\LabWorklist;
use Modules\LabCore\Entities\LabTestSession;

/**
 * Bridges a shared ClinicCore patient to LabCore's order queue (lab_worklist)
 * and results (lab_test_sessions). Keeps all LabCore/ClinicCore code untouched —
 * it only reads their models and writes the linkage columns this module added.
 */
class ClinicLabService
{
    /**
     * Create a pending lab order (worklist entry) for a ClinicCore patient.
     * The LabBridge agent picks it up and sends it to the analyzer.
     */
    public function createOrder(ClcPatient $patient, array $params): LabWorklist
    {
        $tests = $params['tests'] ?? null;
        if (is_string($tests)) {
            $tests = array_values(array_filter(array_map('trim', explode(',', $tests))));
        }

        $order = LabWorklist::create([
            'business_id'  => $patient->business_id,
            'device_id'    => $params['device_id'] ?? null,
            'agent_id'     => $params['agent_id'] ?? null,
            'vendor'       => $params['vendor'] ?? 'manual',
            'sample_no'    => ($params['sample_no'] ?? null) ?: $this->generateSampleNo($patient),
            'patient_id'   => (string) $patient->id,
            'patient_name' => $patient->patient_name ?: optional($patient->contact)->name,
            'species'      => null, // human
            'sex'          => $patient->gender && $patient->gender !== 'unknown' ? $patient->gender : null,
            'age'          => $patient->dob ? (int) $patient->dob->age : null,
            'tests'        => $tests ?: null,
            'status'       => 'pending',
            'contact_id'   => $patient->contact_id,
            'created_by'   => auth()->id(),
        ]);

        // Linkage columns aren't in LabCore's $fillable — set them directly so
        // we don't have to modify the LabCore model.
        $order->clc_patient_id = $patient->id;
        $order->clc_appointment_id = $params['clc_appointment_id'] ?? null;
        $order->save();

        return $order;
    }

    /** Pending/sent orders for a patient (most recent first). */
    public function ordersForPatient(ClcPatient $patient)
    {
        return LabWorklist::where('business_id', $patient->business_id)
            ->where(function ($q) use ($patient) {
                $q->where('clc_patient_id', $patient->id)
                  ->orWhere('contact_id', $patient->contact_id);
            })
            ->orderByDesc('id')->get();
    }

    /**
     * Result sessions for a patient. Reconciles any sessions whose sample_no
     * matches one of this patient's orders but that arrived without the
     * clc_patient_id set (the agent only echoes sample_no/patient_id).
     */
    public function resultsForPatient(ClcPatient $patient)
    {
        $this->reconcile($patient);

        return LabTestSession::with(['results'])
            ->where('business_id', $patient->business_id)
            ->where(function ($q) use ($patient) {
                $q->where('clc_patient_id', $patient->id)
                  ->orWhere('contact_id', $patient->contact_id);
            })
            ->orderByDesc('test_date')->orderByDesc('test_time')->get();
    }

    /** Backfill clc_patient_id on sessions that match this patient's orders. */
    protected function reconcile(ClcPatient $patient): void
    {
        $sampleNos = LabWorklist::where('clc_patient_id', $patient->id)
            ->pluck('sample_no')->filter()->values();
        if ($sampleNos->isEmpty()) return;

        LabTestSession::whereNull('clc_patient_id')
            ->where('business_id', $patient->business_id)
            ->whereIn('sample_no', $sampleNos)
            ->update(['clc_patient_id' => $patient->id]);
    }

    protected function generateSampleNo(ClcPatient $patient): string
    {
        return 'CLC' . $patient->id . '-' . now()->format('ymdHis');
    }
}
