<?php

namespace App\Services\Care;

use App\Models\Organization;
use App\Models\SpecialtyClinicalRecord;
use App\Models\Visit;
use Illuminate\Support\Arr;

/**
 * Visit-scoped specialty clinical records (structured JSON payloads + alerts).
 */
class SpecialtyClinicalRecordService
{
    /**
     * Map workspace tab → record_type for modules that have clinical forms.
     *
     * @return array<string, string>
     */
    public function recordTypesForModule(string $moduleKey): array
    {
        $configured = config("care_specialty_clinical.record_types.{$moduleKey}");
        if (is_array($configured) && $configured !== []) {
            return $configured;
        }

        return [
            'clinical_notes' => 'clinical_note',
        ];
    }

    public function recordTypeForTab(string $moduleKey, string $tab): ?string
    {
        return $this->recordTypesForModule($moduleKey)[$tab] ?? null;
    }

    /**
     * Field schemas for forms (AI-ready structured data).
     *
     * @return list<array{name: string, label: string, type: string, options?: list<string>, required?: bool, rows?: int}>
     */
    public function fieldsFor(string $moduleKey, string $recordType): array
    {
        $fields = config("care_specialty_clinical.fields.{$moduleKey}.{$recordType}");
        if (is_array($fields) && $fields !== []) {
            return $fields;
        }

        return [
            ['name' => 'notes', 'label' => 'Clinical notes', 'type' => 'textarea', 'rows' => 4, 'required' => true],
        ];
    }

    public function findForVisit(Visit $visit, string $moduleKey, string $recordType): ?SpecialtyClinicalRecord
    {
        $keys = [$moduleKey];
        if ($moduleKey === 'womens_health') {
            $keys[] = 'maternity'; // legacy clinical rows
        }

        return SpecialtyClinicalRecord::owned($visit->owner_ref)
            ->where('visit_id', $visit->id)
            ->whereIn('module_key', $keys)
            ->where('record_type', $recordType)
            ->orderByDesc('id')
            ->first();
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    public function upsert(
        Organization $organization,
        Visit $visit,
        string $moduleKey,
        string $recordType,
        array $payload,
        string $ownerRef,
        ?string $actorRef = null,
        ?string $stageCode = null,
        string $status = SpecialtyClinicalRecord::STATUS_ACTIVE,
    ): SpecialtyClinicalRecord {
        $visit->loadMissing('patient');
        $sanitized = $this->sanitizePayload($moduleKey, $recordType, $payload);
        $alerts = $this->deriveAlerts($moduleKey, $recordType, $sanitized);

        $record = $this->findForVisit($visit, $moduleKey, $recordType);

        $attributes = [
            'owner_ref' => $ownerRef,
            'organization_id' => $organization->id,
            'branch_id' => $visit->branch_id,
            'patient_id' => $visit->patient_id,
            'consultation_id' => $visit->consultations()->orderByDesc('id')->value('id'),
            'module_key' => $moduleKey,
            'record_type' => $recordType,
            'status' => $status,
            'stage_code' => $stageCode,
            'payload' => $sanitized,
            'alerts' => $alerts,
            'recorded_by' => $actorRef ?? $ownerRef,
            'recorded_at' => now(),
        ];

        if ($record) {
            $record->update($attributes);

            return $record->fresh();
        }

        return SpecialtyClinicalRecord::create(array_merge($attributes, [
            'visit_id' => $visit->id,
        ]));
    }

    /**
     * @param  array<string, mixed>  $input
     * @return array<string, mixed>
     */
    public function sanitizePayload(string $moduleKey, string $recordType, array $input): array
    {
        $allowed = collect($this->fieldsFor($moduleKey, $recordType))->pluck('name')->all();
        $out = [];

        foreach ($allowed as $name) {
            if (! array_key_exists($name, $input)) {
                continue;
            }
            $value = $input[$name];
            if (is_string($value)) {
                $value = trim($value);
            }
            if ($value === '' || $value === null) {
                continue;
            }
            if ($value === '1' || $value === '0' || $value === 'true' || $value === 'false') {
                // keep as string for form round-trip; normalize booleans below via field type
            }
            $out[$name] = $value;
        }

        foreach ($this->fieldsFor($moduleKey, $recordType) as $field) {
            $name = $field['name'];
            if (! array_key_exists($name, $out)) {
                continue;
            }
            if (($field['type'] ?? '') === 'boolean') {
                $out[$name] = filter_var($out[$name], FILTER_VALIDATE_BOOLEAN);
            }
            if (($field['type'] ?? '') === 'number') {
                $out[$name] = is_numeric($out[$name]) ? 0 + $out[$name] : null;
                if ($out[$name] === null) {
                    unset($out[$name]);
                }
            }
        }

        return $out;
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return list<array{code: string, severity: string, message: string}>
     */
    public function deriveAlerts(string $moduleKey, string $recordType, array $payload): array
    {
        $alerts = [];

        if ($moduleKey === 'emergency' && $recordType === 'triage') {
            $acuity = (string) ($payload['acuity'] ?? '');
            if (str_starts_with($acuity, '1') || str_starts_with($acuity, '2')) {
                $alerts[] = [
                    'code' => 'er.high_acuity',
                    'severity' => 'critical',
                    'message' => 'High-acuity triage — prioritise resus / emergency bay.',
                ];
            }
            if (($payload['airway_ok'] ?? true) === false
                || ($payload['breathing_ok'] ?? true) === false
                || ($payload['circulation_ok'] ?? true) === false) {
                $alerts[] = [
                    'code' => 'er.abc_compromise',
                    'severity' => 'critical',
                    'message' => 'ABC compromise flagged on triage.',
                ];
            }
            $pain = (int) ($payload['pain_score'] ?? 0);
            if ($pain >= 8) {
                $alerts[] = [
                    'code' => 'er.severe_pain',
                    'severity' => 'warning',
                    'message' => 'Severe pain score (≥8).',
                ];
            }
        }

        if ($moduleKey === 'blood_bank' && $recordType === 'blood_request') {
            $urgency = (string) ($payload['urgency'] ?? '');
            if (str_contains(strtolower($urgency), 'emergency') || str_contains(strtolower($urgency), 'massive')) {
                $alerts[] = [
                    'code' => 'bb.massive_transfusion',
                    'severity' => 'critical',
                    'message' => 'Emergency / massive transfusion request.',
                ];
            } elseif (str_contains(strtolower($urgency), 'urgent')) {
                $alerts[] = [
                    'code' => 'bb.urgent_request',
                    'severity' => 'warning',
                    'message' => 'Urgent blood product request.',
                ];
            }
            if (($payload['crossmatch_status'] ?? '') === 'Incompatible') {
                $alerts[] = [
                    'code' => 'bb.incompatible',
                    'severity' => 'critical',
                    'message' => 'Cross-match incompatible — do not issue.',
                ];
            }
        }

        if ($moduleKey === 'blood_bank' && $recordType === 'inventory_note') {
            if (! empty($payload['low_stock_alert'])) {
                $alerts[] = [
                    'code' => 'bb.low_stock',
                    'severity' => 'warning',
                    'message' => 'Blood products flagged as low stock.',
                ];
            }
            foreach (['whole_blood_units', 'packed_rbc_units', 'platelet_units', 'ffp_units', 'cryo_units'] as $key) {
                if (isset($payload[$key]) && (int) $payload[$key] <= 2) {
                    $alerts[] = [
                        'code' => 'bb.unit_critical',
                        'severity' => 'warning',
                        'message' => str_replace('_', ' ', $key).' at or below 2 units.',
                    ];
                }
            }
        }

        if ($moduleKey === 'blood_bank' && $recordType === 'transfusion_note') {
            $reaction = strtolower((string) ($payload['reaction'] ?? ''));
            if (str_contains($reaction, 'severe') || str_contains($reaction, 'stop')) {
                $alerts[] = [
                    'code' => 'bb.transfusion_reaction',
                    'severity' => 'critical',
                    'message' => 'Severe transfusion reaction — stop and escalate.',
                ];
            }
            if (($payload['vitals_ok'] ?? true) === false) {
                $alerts[] = [
                    'code' => 'bb.vitals_concern',
                    'severity' => 'warning',
                    'message' => 'Transfusion vitals not acceptable.',
                ];
            }
        }

        if ($moduleKey === 'ophthalmology' && in_array($recordType, ['eye_exam', 'refraction'], true)) {
            $iopOd = isset($payload['iop_od']) ? (float) $payload['iop_od'] : null;
            $iopOs = isset($payload['iop_os']) ? (float) $payload['iop_os'] : null;
            if (($iopOd !== null && $iopOd >= 30) || ($iopOs !== null && $iopOs >= 30)) {
                $alerts[] = [
                    'code' => 'eye.critical_iop',
                    'severity' => 'critical',
                    'message' => 'Critical IOP (≥30 mmHg) — urgent glaucoma / acute review.',
                ];
            } elseif (($iopOd !== null && $iopOd >= 24) || ($iopOs !== null && $iopOs >= 24)) {
                $alerts[] = [
                    'code' => 'eye.elevated_iop',
                    'severity' => 'warning',
                    'message' => 'Elevated IOP (≥24 mmHg).',
                ];
            }

            foreach (['va_od', 'va_os'] as $vaKey) {
                $va = strtolower(trim((string) ($payload[$vaKey] ?? '')));
                if ($va === '') {
                    continue;
                }
                if (preg_match('/\b(cf|hm|pl|nlp|counting|hand\s*motion|perception)\b/i', $va)
                    || preg_match('/^6\/(60|90|120)\b/', $va)
                    || preg_match('/^20\/(200|400)\b/', $va)) {
                    $alerts[] = [
                        'code' => 'eye.low_vision',
                        'severity' => 'warning',
                        'message' => 'Low visual acuity recorded ('.$vaKey.').',
                    ];
                    break;
                }
            }
        }

        if ($moduleKey === 'ophthalmology' && $recordType === 'eye_procedure') {
            $outcome = strtolower((string) ($payload['outcome'] ?? ''));
            if (str_contains($outcome, 'complication') || str_contains($outcome, 'aborted')) {
                $alerts[] = [
                    'code' => 'eye.procedure_concern',
                    'severity' => 'warning',
                    'message' => 'Procedure outcome needs review (complication or aborted).',
                ];
            }
        }

        if ($moduleKey === 'physiotherapy' && $recordType === 'pt_assessment') {
            $pain = (int) ($payload['pain_score'] ?? 0);
            if ($pain >= 8) {
                $alerts[] = [
                    'code' => 'pt.severe_pain',
                    'severity' => 'warning',
                    'message' => 'Severe pain score (≥8) on physio assessment.',
                ];
            }
            $red = trim((string) ($payload['red_flags'] ?? ''));
            if ($red !== '') {
                $alerts[] = [
                    'code' => 'pt.red_flags',
                    'severity' => 'critical',
                    'message' => 'Physio red flags recorded — review urgently.',
                ];
            }
        }

        if ($moduleKey === 'womens_health' && $recordType === 'obstetric_exam') {
            $danger = trim((string) ($payload['danger_signs'] ?? ''));
            if ($danger !== '') {
                $alerts[] = [
                    'code' => 'mat.danger_signs',
                    'severity' => 'critical',
                    'message' => 'Maternity danger signs flagged.',
                ];
            }
            $bp = (string) ($payload['bp'] ?? '');
            if (preg_match('/(\d{2,3})\s*[\/]\s*(\d{2,3})/', $bp, $m)) {
                $sys = (int) $m[1];
                $dia = (int) $m[2];
                if ($sys >= 160 || $dia >= 110) {
                    $alerts[] = [
                        'code' => 'mat.severe_hypertension',
                        'severity' => 'critical',
                        'message' => 'Severe hypertension on obstetric exam.',
                    ];
                } elseif ($sys >= 140 || $dia >= 90) {
                    $alerts[] = [
                        'code' => 'mat.hypertension',
                        'severity' => 'warning',
                        'message' => 'Elevated BP on obstetric exam.',
                    ];
                }
            }
        }

        if ($moduleKey === 'womens_health' && $recordType === 'fetal_notes') {
            $fhr = isset($payload['fetal_heart_rate']) ? (int) $payload['fetal_heart_rate'] : null;
            if ($fhr !== null && ($fhr < 110 || $fhr > 160)) {
                $alerts[] = [
                    'code' => 'mat.abnormal_fhr',
                    'severity' => 'critical',
                    'message' => 'Fetal heart rate outside 110–160 bpm.',
                ];
            }
            $movements = strtolower((string) ($payload['fetal_movements'] ?? ''));
            if (str_contains($movements, 'reduced') || str_contains($movements, 'absent')) {
                $alerts[] = [
                    'code' => 'mat.reduced_movements',
                    'severity' => 'critical',
                    'message' => 'Reduced or absent fetal movements.',
                ];
            }
        }

        if ($moduleKey === 'womens_health' && $recordType === 'mat_plan') {
            $risk = strtolower((string) ($payload['risk_category'] ?? ''));
            if (str_contains($risk, 'high')) {
                $alerts[] = [
                    'code' => 'mat.high_risk',
                    'severity' => 'warning',
                    'message' => 'High-risk maternity care plan.',
                ];
            }
        }

        if ($moduleKey === 'radiology' && $recordType === 'imaging_request') {
            $urgency = strtolower((string) ($payload['urgency'] ?? ''));
            if (str_contains($urgency, 'stat')) {
                $alerts[] = [
                    'code' => 'rad.stat',
                    'severity' => 'critical',
                    'message' => 'STAT imaging request.',
                ];
            } elseif (str_contains($urgency, 'urgent')) {
                $alerts[] = [
                    'code' => 'rad.urgent',
                    'severity' => 'warning',
                    'message' => 'Urgent imaging request.',
                ];
            }
        }

        if ($moduleKey === 'radiology' && $recordType === 'imaging_report') {
            if (! empty($payload['critical_result'])) {
                $alerts[] = [
                    'code' => 'rad.critical_result',
                    'severity' => 'critical',
                    'message' => 'Critical / unexpected imaging finding recorded.',
                ];
            }
        }

        if ($moduleKey === 'cardiology' && $recordType === 'cardiac_exam') {
            $nyha = (string) ($payload['nyha_class'] ?? '');
            if (in_array($nyha, ['III', 'IV'], true)) {
                $alerts[] = [
                    'code' => 'car.nyha_high',
                    'severity' => $nyha === 'IV' ? 'critical' : 'warning',
                    'message' => 'NYHA class '.$nyha.' on cardiac exam.',
                ];
            }
            $ecg = strtolower((string) ($payload['ecg_findings'] ?? ''));
            if (str_contains($ecg, 'stemi') || str_contains($ecg, 'st elevation') || str_contains($ecg, 'vt') || str_contains($ecg, 'vf')) {
                $alerts[] = [
                    'code' => 'car.ecg_critical',
                    'severity' => 'critical',
                    'message' => 'Critical ECG findings recorded.',
                ];
            }
        }

        if ($moduleKey === 'dermatology' && $recordType === 'skin_exam') {
            $urgency = (string) ($payload['urgency'] ?? '');
            if (in_array($urgency, ['High', 'Suspected malignancy'], true)) {
                $alerts[] = [
                    'code' => 'der.urgency_high',
                    'severity' => $urgency === 'Suspected malignancy' ? 'critical' : 'warning',
                    'message' => 'Skin exam urgency: '.$urgency.'.',
                ];
            }
        }

        if ($moduleKey === 'ambulance' && $recordType === 'amb_scene') {
            $acuity = (string) ($payload['acuity'] ?? '');
            if (in_array($acuity, ['Critical', 'Urgent'], true)) {
                $alerts[] = [
                    'code' => 'amb.acuity_high',
                    'severity' => $acuity === 'Critical' ? 'critical' : 'warning',
                    'message' => 'Scene acuity: '.$acuity.'.',
                ];
            }
            if (array_key_exists('circulation_ok', $payload) && in_array($payload['circulation_ok'], [false, 0, '0'], true)) {
                $alerts[] = [
                    'code' => 'amb.circulation_unstable',
                    'severity' => 'critical',
                    'message' => 'Circulation marked unstable on scene.',
                ];
            }
        }

        if ($moduleKey === 'ambulance' && $recordType === 'amb_en_route') {
            if (! empty($payload['deterioration'])) {
                $alerts[] = [
                    'code' => 'amb.deterioration',
                    'severity' => 'critical',
                    'message' => 'Patient deterioration noted en route.',
                ];
            }
        }

        if ($moduleKey === 'podiatry' && $recordType === 'foot_exam') {
            $risk = (string) ($payload['diabetes'] ?? '');
            if (in_array($risk, ['High', 'Active ulcer'], true)) {
                $alerts[] = [
                    'code' => 'pod.diabetes_high',
                    'severity' => $risk === 'Active ulcer' ? 'critical' : 'warning',
                    'message' => 'Diabetic foot risk: '.$risk.'.',
                ];
            }
        }

        if ($moduleKey === 'fertility' && $recordType === 'fert_exam') {
            $priority = (string) ($payload['priority'] ?? '');
            if (in_array($priority, ['Urgent', 'Time-sensitive'], true)) {
                $alerts[] = [
                    'code' => 'fer.priority_high',
                    'severity' => $priority === 'Urgent' ? 'critical' : 'warning',
                    'message' => 'Fertility priority: '.$priority.'.',
                ];
            }
        }

        if ($moduleKey === 'pediatrics' && $recordType === 'pediatric_exam') {
            $concern = (string) ($payload['growth_concern'] ?? '');
            if ($concern !== '' && $concern !== 'None') {
                $alerts[] = [
                    'code' => 'ped.growth_concern',
                    'severity' => in_array($concern, ['Wasting', 'Underweight'], true) ? 'critical' : 'warning',
                    'message' => 'Growth concern: '.$concern.'.',
                ];
            }
            $temp = (float) ($payload['temperature_c'] ?? 0);
            if ($temp >= 39.0) {
                $alerts[] = [
                    'code' => 'ped.fever_high',
                    'severity' => 'critical',
                    'message' => 'High fever recorded ('.$temp.'°C).',
                ];
            } elseif ($temp >= 38.0) {
                $alerts[] = [
                    'code' => 'ped.fever',
                    'severity' => 'warning',
                    'message' => 'Fever recorded ('.$temp.'°C).',
                ];
            }
        }

        if ($moduleKey === 'orthopedics' && $recordType === 'ortho_exam') {
            $nv = strtolower((string) ($payload['neurovascular'] ?? ''));
            if (str_contains($nv, 'vascular compromise')) {
                $alerts[] = [
                    'code' => 'ort.nv_vascular',
                    'severity' => 'critical',
                    'message' => 'Vascular compromise on ortho exam.',
                ];
            } elseif (str_contains($nv, 'deficit')) {
                $alerts[] = [
                    'code' => 'ort.nv_deficit',
                    'severity' => 'warning',
                    'message' => 'Neurovascular deficit recorded.',
                ];
            }
        }

        if ($moduleKey === 'ent' && $recordType === 'ent_exam') {
            if (! empty($payload['airway_concern'])) {
                $alerts[] = [
                    'code' => 'ent.airway_concern',
                    'severity' => 'critical',
                    'message' => 'Airway concern flagged on ENT exam.',
                ];
            }
        }


        if ($moduleKey === 'oncology' && $recordType === 'onc_staging') {
            $ecog = (string) ($payload['performance_status'] ?? '');
            if (in_array($ecog, ['3', '4'], true)) {
                $alerts[] = [
                    'code' => 'onc.ecog_high',
                    'severity' => 'warning',
                    'message' => 'High ECOG performance status ('.$ecog.') — review treatment fitness.',
                ];
            }
        }

        if ($moduleKey === 'vaccination' && $recordType === 'vax_eligibility') {
            if (empty($payload['cleared']) && ! empty($payload['vaccine'])) {
                $alerts[] = [
                    'code' => 'vax.not_cleared',
                    'severity' => 'critical',
                    'message' => 'Vaccine requested but patient is not cleared to vaccinate.',
                ];
            }
            if (! empty($payload['fever_today'])) {
                $alerts[] = [
                    'code' => 'vax.fever',
                    'severity' => 'warning',
                    'message' => 'Fever recorded on vaccination eligibility screen.',
                ];
            }
        }

        if ($moduleKey === 'vaccination' && $recordType === 'vax_observation') {
            $aefi = strtolower((string) ($payload['aefi'] ?? ''));
            if (str_contains($aefi, 'severe')) {
                $alerts[] = [
                    'code' => 'vax.aefi_severe',
                    'severity' => 'critical',
                    'message' => 'Severe AEFI recorded — escalate and document follow-up.',
                ];
            }
        }

        if ($moduleKey === 'pathology' && $recordType === 'path_request') {
            $urgency = strtolower((string) ($payload['urgency'] ?? ''));
            if (str_contains($urgency, 'urgent') || str_contains($urgency, 'intra')) {
                $alerts[] = [
                    'code' => 'path.urgent',
                    'severity' => 'warning',
                    'message' => 'Urgent / intra-op pathology request — prioritise processing.',
                ];
            }
        }

        if ($moduleKey === 'pathology' && $recordType === 'path_specimen') {
            $adequacy = strtolower((string) ($payload['adequacy'] ?? ''));
            if (str_contains($adequacy, 'inadequate')) {
                $alerts[] = [
                    'code' => 'path.inadequate',
                    'severity' => 'critical',
                    'message' => 'Specimen marked inadequate — notify requesting clinician.',
                ];
            }
        }

        if ($moduleKey === 'infusion' && $recordType === 'infusion_assessment') {
            if (empty($payload['fit_for_infusion']) && ! empty($payload['indication'])) {
                $alerts[] = [
                    'code' => 'inf.not_fit',
                    'severity' => 'critical',
                    'message' => 'Indication recorded but patient not marked fit for infusion.',
                ];
            }
        }

        if ($moduleKey === 'infusion' && in_array($recordType, ['infusion_session', 'infusion_monitoring'], true)) {
            $reaction = strtolower((string) ($payload['reactions'] ?? $payload['reaction'] ?? ''));
            if (str_contains($reaction, 'severe') || str_contains($reaction, 'stop')) {
                $alerts[] = [
                    'code' => 'inf.reaction',
                    'severity' => 'critical',
                    'message' => 'Infusion reaction / stop flagged — review interventions.',
                ];
            }
        }

        if ($moduleKey === 'renal' && $recordType === 'renal_exam') {
            $fluid = strtolower((string) ($payload['fluid_status'] ?? ''));
            if (str_contains($fluid, 'overload')) {
                $alerts[] = [
                    'code' => 'ren.fluid_overload',
                    'severity' => 'warning',
                    'message' => 'Fluid overload recorded on renal exam.',
                ];
            }
        }

        if ($moduleKey === 'surgery' && $recordType === 'surg_plan') {
            if (empty($payload['consent_obtained']) && ! empty($payload['procedure_planned'])) {
                $alerts[] = [
                    'code' => 'sur.consent_missing',
                    'severity' => 'critical',
                    'message' => 'Procedure planned without documented consent.',
                ];
            }
        }

        if ($moduleKey === 'psychiatry' && $recordType === 'risk_assessment') {
            $level = strtolower((string) ($payload['overall_risk'] ?? ''));
            if (str_contains($level, 'imminent')) {
                $alerts[] = [
                    'code' => 'psy.risk_imminent',
                    'severity' => 'critical',
                    'message' => 'Imminent risk — escalate immediately.',
                ];
            } elseif (str_contains($level, 'high')) {
                $alerts[] = [
                    'code' => 'psy.risk_high',
                    'severity' => 'critical',
                    'message' => 'High psychiatric risk recorded.',
                ];
            }
            $selfHarm = strtolower((string) ($payload['self_harm'] ?? ''));
            if (str_contains($selfHarm, 'intent') || str_contains($selfHarm, 'attempt')) {
                $alerts[] = [
                    'code' => 'psy.self_harm',
                    'severity' => 'critical',
                    'message' => 'Self-harm intent or recent attempt recorded.',
                ];
            }
        }

        if ($moduleKey === 'child_welfare' && $recordType === 'cwc_assessment') {
            $flag = strtolower((string) ($payload['safeguarding_flag'] ?? ''));
            if ($flag === 'escalated') {
                $alerts[] = [
                    'code' => 'cwc.safeguarding',
                    'severity' => 'critical',
                    'message' => 'Safeguarding escalated — follow facility protocol.',
                ];
            } elseif ($flag === 'concern') {
                $alerts[] = [
                    'code' => 'cwc.safeguarding',
                    'severity' => 'warning',
                    'message' => 'Safeguarding concern recorded on CWC assessment.',
                ];
            }
            $nutrition = strtolower((string) ($payload['nutrition_risk'] ?? ''));
            if ($nutrition === 'high') {
                $alerts[] = [
                    'code' => 'cwc.nutrition_risk',
                    'severity' => 'warning',
                    'message' => 'High nutrition risk on child welfare assessment.',
                ];
            }
            $growth = strtolower((string) ($payload['growth_status'] ?? ''));
            if (in_array($growth, ['wasted', 'underweight'], true)) {
                $alerts[] = [
                    'code' => 'cwc.growth',
                    'severity' => 'warning',
                    'message' => 'Abnormal growth status: '.$payload['growth_status'].'.',
                ];
            }
        }

        return $alerts;
    }

    /**
     * @return list<SpecialtyClinicalRecord>
     */
    public function forVisit(Visit $visit, string $moduleKey): array
    {
        return SpecialtyClinicalRecord::owned($visit->owner_ref)
            ->where('visit_id', $visit->id)
            ->where('module_key', $moduleKey)
            ->orderBy('record_type')
            ->get()
            ->all();
    }

    /**
     * @return list<array{code: string, severity: string, message: string}>
     */
    public function alertsForVisit(Visit $visit, string $moduleKey): array
    {
        $alerts = [];
        foreach ($this->forVisit($visit, $moduleKey) as $record) {
            foreach ($record->activeAlerts() as $alert) {
                $alerts[] = $alert;
            }
        }

        return $alerts;
    }

    public function countOpenByType(Organization $organization, string $moduleKey, string $recordType, ?int $branchId = null): int
    {
        return SpecialtyClinicalRecord::owned($organization->owner_ref)
            ->where('organization_id', $organization->id)
            ->where('module_key', $moduleKey)
            ->where('record_type', $recordType)
            ->whereIn('status', [SpecialtyClinicalRecord::STATUS_DRAFT, SpecialtyClinicalRecord::STATUS_ACTIVE])
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->count();
    }

    /**
     * Validation rules derived from field schema.
     *
     * @return array<string, list<string>>
     */
    public function validationRules(string $moduleKey, string $recordType): array
    {
        $rules = [];
        foreach ($this->fieldsFor($moduleKey, $recordType) as $field) {
            $name = 'payload.'.$field['name'];
            $fieldRules = [];
            if (! empty($field['required'])) {
                $fieldRules[] = 'required';
            } else {
                $fieldRules[] = 'nullable';
            }
            $fieldRules[] = match ($field['type'] ?? 'text') {
                'number' => 'numeric',
                'boolean' => 'boolean',
                'textarea', 'text', 'select' => 'string',
                default => 'string',
            };
            if (($field['type'] ?? '') === 'select' && ! empty($field['options'])) {
                $fieldRules[] = 'in:'.implode(',', $field['options']);
            }
            if (in_array($field['type'] ?? '', ['text', 'textarea'], true)) {
                $fieldRules[] = 'max:5000';
            }
            $rules[$name] = $fieldRules;
        }

        return $rules;
    }

    /**
     * @param  array<string, mixed>  $validated
     * @return array<string, mixed>
     */
    public function payloadFromRequest(array $validated): array
    {
        return Arr::get($validated, 'payload', []);
    }
}
