<?php

namespace Modules\ClinicCore\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Modules\ClinicCore\Entities\ClcAppointmentDiagnosis;
use Modules\ClinicCore\Entities\ClcAppointmentWaitlist;
use Modules\ClinicCore\Entities\ClcImagingModality;
use Modules\ClinicCore\Entities\ClcImagingReport;
use Modules\ClinicCore\Entities\ClcImagingRequest;
use Modules\ClinicCore\Entities\ClcMedicalAlert;
use Modules\ClinicCore\Entities\ClcOrder;
use Modules\ClinicCore\Entities\ClcOrderItem;
use Modules\ClinicCore\Entities\ClcPatientAllergy;
use Modules\ClinicCore\Entities\ClcPatientInsurance;
use Modules\ClinicCore\Entities\ClcPatientMedication;
use Modules\ClinicCore\Entities\ClcReferral;
use Modules\ClinicCore\Entities\ClcVital;
use Modules\ClinicCore\Entities\Services\MedicalAlertService;

/**
 * Demo enrichment for the "Shifa One" human clinic (business_id 8).
 *
 * Populates the NEW P3–P5 medical surfaces that the deep audit found empty, so
 * that no clinical screen is blank when browsing the demo. SEEDER ONLY — never
 * touches schema. Everything is idempotent (existence checks / updateOrCreate
 * on stable ref/no values such as 'DEMO-...'), so re-running is safe. Each
 * section is isolated in its own try/catch and every non-ClinicCore module's
 * model is guarded with class_exists() so a disabled specialty never aborts the
 * run.
 *
 * Run:
 *   DB_DATABASE=shifa_one_demo php artisan db:seed \
 *     --class="Modules\\ClinicCore\\Database\\Seeders\\MedicalDemoEnrichmentSeeder"
 */
class MedicalDemoEnrichmentSeeder extends Seeder
{
    /** Target demo business. */
    private const BUSINESS_NAME = 'Shifa One';

    private int $businessId;

    public function run(): void
    {
        $business = DB::table('business')->where('name', self::BUSINESS_NAME)->first();
        if (! $business) {
            $this->command->warn(
                "MedicalDemoEnrichmentSeeder: business '" . self::BUSINESS_NAME . "' not found — nothing seeded."
            );
            return;
        }
        $this->businessId = (int) $business->id;
        $this->command->info("MedicalDemoEnrichmentSeeder: enriching business #{$this->businessId} ({$business->name}).");

        // Shared reference data (queried, never created).
        $patients     = DB::table('clc_patients')->where('business_id', $this->businessId)
            ->orderBy('id')->get();
        $appointments = DB::table('clc_appointments')->where('business_id', $this->businessId)
            ->orderBy('id')->get();
        $doctors      = DB::table('clc_doctors')->where('business_id', $this->businessId)
            ->orderBy('id')->get();

        if ($patients->isEmpty()) {
            $this->command->warn('MedicalDemoEnrichmentSeeder: no clc_patients for this business — nothing to enrich.');
            return;
        }

        $completedAppts = $appointments->whereIn('status', ['completed', 'in_progress'])->values();
        if ($completedAppts->isEmpty()) {
            // Fall back to any appointment so diagnosis/order/vitals sections still populate.
            $completedAppts = $appointments;
        }
        $doctorId = $doctors->first()->id ?? null;

        $this->seedAllergies($patients);
        $this->seedMedications($patients);
        $this->seedInsurances($patients);
        $this->seedDiagnoses($patients, $completedAppts, $doctorId);
        $this->seedOrders($patients, $completedAppts, $doctorId);
        $this->seedImaging($patients, $completedAppts, $doctorId);
        $this->seedReferrals($patients, $completedAppts, $doctorId);
        $this->seedVitals($patients, $appointments);
        $this->seedWaitlist($patients, $doctorId);
        $this->seedDental($patients, $doctors);
        $this->seedDermatology($patients);
        $this->seedOphthalmology($patients);

        $this->command->info('MedicalDemoEnrichmentSeeder: done.');
    }

    // ── 1. Structured clinical baseline ──────────────────────────────────────

    /** clc_patient_allergies on ~4 patients incl. one life_threatening → alert. */
    private function seedAllergies($patients): void
    {
        try {
            $alertSvc = app(MedicalAlertService::class);

            // [offset, allergen, type, severity, reaction, source, status]
            $rows = [
                [0, 'Penicillin',   'drug',          'life_threatening', 'Anaphylaxis — throat swelling, hypotension', 'clinical_observation', 'active'],
                [1, 'Sulfa drugs',  'drug',          'severe',           'Stevens-Johnson type rash',                  'patient_reported',     'active'],
                [1, 'Peanuts',      'food',          'moderate',         'Urticaria and lip swelling',                 'patient_reported',     'active'],
                [2, 'Latex',        'latex',         'moderate',         'Contact dermatitis on gloved areas',         'clinical_observation', 'active'],
                [3, 'Pollen',       'environmental', 'mild',             'Seasonal rhinitis, sneezing',                'patient_reported',     'active'],
            ];

            $count = 0;
            foreach ($rows as [$offset, $allergen, $type, $severity, $reaction, $source, $status]) {
                $patient = $patients->get($offset);
                if (! $patient) {
                    continue;
                }

                $allergy = ClcPatientAllergy::updateOrCreate(
                    [
                        'business_id' => $this->businessId,
                        'patient_id'  => $patient->id,
                        'allergen'    => $allergen,
                    ],
                    [
                        'type'      => $type,
                        'severity'  => $severity,
                        'reaction'  => $reaction,
                        'source'    => $source,
                        'status'    => $status,
                        'notes'     => 'DEMO seed',
                    ]
                );

                // Raise/refresh the safety alert via the service (idempotent on
                // source_type+source_id) so severe/life_threatening rows light the banner.
                $alertSvc->syncAllergyAlert($allergy);
                $count++;
            }

            $this->command->info("  · allergies: {$count} rows (severe/life-threatening raise medical alerts).");
        } catch (\Throwable $e) {
            $this->command->warn('  · allergies skipped: ' . $e->getMessage());
        }
    }

    /** clc_patient_medications — 2-3 home meds on ~4 patients. */
    private function seedMedications($patients): void
    {
        try {
            // [offset, name, dosage, frequency, route, prescriber, ongoing]
            $rows = [
                [0, 'Metformin',     '500 mg',  'BD',       'oral', 'Dr. Amir (GP)',        true],
                [0, 'Amlodipine',    '5 mg',    'OD',       'oral', 'Dr. Amir (GP)',        true],
                [1, 'Atorvastatin',  '20 mg',   'nocte',    'oral', 'Cardiology outpatient', true],
                [1, 'Aspirin',       '81 mg',   'OD',       'oral', 'Cardiology outpatient', true],
                [2, 'Levothyroxine', '75 mcg',  'OD fasting', 'oral', 'Endocrinology clinic', true],
                [3, 'Salbutamol',    '100 mcg', 'PRN',      'inhaled', 'Chest clinic',        true],
                [3, 'Cetirizine',    '10 mg',   'OD',       'oral', 'Self / OTC',            false],
            ];

            $count = 0;
            foreach ($rows as [$offset, $name, $dosage, $frequency, $route, $prescriber, $ongoing]) {
                $patient = $patients->get($offset);
                if (! $patient) {
                    continue;
                }

                ClcPatientMedication::updateOrCreate(
                    [
                        'business_id'     => $this->businessId,
                        'patient_id'      => $patient->id,
                        'medication_name' => $name,
                    ],
                    [
                        'dosage'     => $dosage,
                        'frequency'  => $frequency,
                        'route'      => $route,
                        'prescriber' => $prescriber,
                        'start_date' => Carbon::now()->subMonths(6)->toDateString(),
                        'end_date'   => $ongoing ? null : Carbon::now()->subMonth()->toDateString(),
                        'ongoing'    => $ongoing,
                        'notes'      => 'DEMO seed — home medication (reconciliation).',
                    ]
                );
                $count++;
            }

            $this->command->info("  · home medications: {$count} rows.");
        } catch (\Throwable $e) {
            $this->command->warn('  · medications skipped: ' . $e->getMessage());
        }
    }

    /** clc_patient_insurances — 1 policy card each on ~4 patients. */
    private function seedInsurances($patients): void
    {
        try {
            // [offset, provider, plan_type, copay, pre_auth]
            $rows = [
                [0, 'Bupa Arabia',      'PPO Silver',      50.00, false],
                [1, 'Tawuniya Health',  'Comprehensive',   75.00, true],
                [2, 'MedGulf',          'HMO Basic',       30.00, false],
                [3, 'AXA Cooperative',  'Gold Family',    100.00, true],
            ];

            $count = 0;
            foreach ($rows as [$offset, $provider, $planType, $copay, $preAuth]) {
                $patient = $patients->get($offset);
                if (! $patient) {
                    continue;
                }

                $policyNo = 'DEMO-POL-' . $this->businessId . '-' . $patient->id;

                ClcPatientInsurance::updateOrCreate(
                    [
                        'business_id'   => $this->businessId,
                        'patient_id'    => $patient->id,
                        'policy_number' => $policyNo,
                    ],
                    [
                        'provider_name'     => $provider,
                        'group_number'      => 'GRP-' . str_pad((string) ($offset + 1), 4, '0', STR_PAD_LEFT),
                        'plan_type'         => $planType,
                        'effective_date'    => Carbon::now()->startOfYear()->toDateString(),
                        'expiry_date'       => Carbon::now()->endOfYear()->toDateString(),
                        'copay_amount'      => $copay,
                        'pre_auth_required' => $preAuth,
                        'status'            => 'active',
                        'notes'             => 'DEMO seed — insurance card.',
                    ]
                );
                $count++;
            }

            $this->command->info("  · insurance policies: {$count} rows.");
        } catch (\Throwable $e) {
            $this->command->warn('  · insurances skipped: ' . $e->getMessage());
        }
    }

    // ── 2. Encounter diagnoses ───────────────────────────────────────────────

    /** clc_appointment_diagnoses — 1-2 ICD-10 diagnoses on ~4 appointments. */
    private function seedDiagnoses($patients, $appointments, ?int $doctorId): void
    {
        try {
            // [appt offset, icd, description, type, confidence]
            $rows = [
                [0, 'I10',   'Essential (primary) hypertension',            'primary',   'confirmed'],
                [0, 'E11.9', 'Type 2 diabetes mellitus without complications', 'secondary', 'confirmed'],
                [1, 'J06.9', 'Acute upper respiratory infection, unspecified', 'primary',  'probable'],
                [2, 'M54.5', 'Low back pain',                               'primary',   'confirmed'],
                [3, 'E78.5', 'Hyperlipidaemia, unspecified',                'primary',   'confirmed'],
            ];

            $count = 0;
            foreach ($rows as [$offset, $icd, $description, $type, $confidence]) {
                $appt = $appointments->get($offset);
                if (! $appt) {
                    continue;
                }

                ClcAppointmentDiagnosis::updateOrCreate(
                    [
                        'business_id'    => $this->businessId,
                        'appointment_id' => $appt->id,
                        'icd_code'       => $icd,
                    ],
                    [
                        'patient_id'     => $appt->patient_id,
                        'description'    => $description,
                        'diagnosis_type' => $type,
                        'confidence'     => $confidence,
                        'diagnosed_by'   => null,
                    ]
                );
                $count++;
            }

            $this->command->info("  · diagnoses: {$count} rows on existing appointments.");
        } catch (\Throwable $e) {
            $this->command->warn('  · diagnoses skipped: ' . $e->getMessage());
        }
    }

    // ── 3. Unified clinical orders ───────────────────────────────────────────

    /**
     * clc_orders + clc_order_items — ~3 orders (lab / imaging / procedure) on
     * existing appointments; a couple already 'resulted' with a result_summary
     * plus one still 'ordered'. Idempotent on a stable DEMO order_no. Direct
     * inserts (no routing) keep the seed self-contained and fail-soft.
     */
    private function seedOrders($patients, $appointments, ?int $doctorId): void
    {
        try {
            // [suffix, appt offset, type, priority, indication, status, items[]]
            //   item = [service_name, service_code, laterality, status, result_summary, abnormal_flag]
            $orders = [
                [
                    '0001', 0, 'lab', 'routine', 'Diabetes + hypertension follow-up', 'resulted',
                    [
                        ['HbA1c',            'LOINC:4548-4', 'na', 'resulted', 'HbA1c 7.8% — above target (goal < 7.0%).', 'high'],
                        ['Lipid panel',      'LOINC:24331-1', 'na', 'resulted', 'LDL 158 mg/dL, HDL 41 mg/dL — dyslipidaemia.', 'high'],
                        ['Serum creatinine', 'LOINC:2160-0', 'na', 'resulted', 'Creatinine 0.9 mg/dL — normal.',             'normal'],
                    ],
                ],
                [
                    '0002', 1, 'imaging', 'urgent', 'Persistent cough — rule out consolidation', 'resulted',
                    [
                        ['Chest X-Ray PA', 'CPT:71046', 'na', 'resulted', 'No focal consolidation; lungs clear.', 'normal'],
                    ],
                ],
                [
                    '0003', 2, 'procedure', 'routine', 'Suture removal after minor laceration repair', 'ordered',
                    [
                        ['Suture removal', 'CPT:15851', 'na', 'ordered', null, null],
                    ],
                ],
            ];

            $orderCount = 0;
            $itemCount  = 0;
            foreach ($orders as [$suffix, $offset, $type, $priority, $indication, $status, $items]) {
                $appt = $appointments->get($offset);
                if (! $appt) {
                    continue;
                }

                $orderNo = 'DEMO-ORD-' . $this->businessId . '-' . $suffix;

                $order = ClcOrder::updateOrCreate(
                    [
                        'business_id' => $this->businessId,
                        'order_no'    => $orderNo,
                    ],
                    [
                        'appointment_id'      => $appt->id,
                        'patient_id'          => $appt->patient_id,
                        'ordering_doctor_id'  => $doctorId,
                        'order_type'          => $type,
                        'priority'            => $priority,
                        'clinical_indication' => $indication,
                        'status'              => $status,
                    ]
                );
                $orderCount++;

                foreach ($items as [$serviceName, $serviceCode, $laterality, $itemStatus, $resultSummary, $abnormalFlag]) {
                    ClcOrderItem::updateOrCreate(
                        [
                            'order_id'     => $order->id,
                            'service_name' => $serviceName,
                        ],
                        [
                            'service_code'   => $serviceCode,
                            'laterality'     => $laterality,
                            'quantity'       => 1,
                            'result_summary' => $resultSummary,
                            'abnormal_flag'  => $abnormalFlag,
                            'status'         => $itemStatus,
                            'note'           => 'DEMO seed',
                        ]
                    );
                    $itemCount++;
                }
            }

            $this->command->info("  · orders: {$orderCount} orders / {$itemCount} items.");
        } catch (\Throwable $e) {
            $this->command->warn('  · orders skipped: ' . $e->getMessage());
        }
    }

    // ── 4. Imaging requests + reports ────────────────────────────────────────

    /**
     * clc_imaging_requests (~2) with one FINAL report (impression + narrative)
     * and one draft. Both non-critical (has_critical = false). Idempotent on a
     * stable DEMO ref_no; the report is keyed 1:1 on imaging_request_id.
     */
    private function seedImaging($patients, $appointments, ?int $doctorId): void
    {
        try {
            $modalityId = ClcImagingModality::forBusiness($this->businessId)
                ->where('status', 'active')
                ->orderByRaw('CASE WHEN business_id IS NULL THEN 1 ELSE 0 END')
                ->orderBy('sort_order')
                ->value('id');

            if (! $modalityId) {
                $this->command->warn('  · imaging skipped: no imaging modality available.');
                return;
            }

            // Prefer real code-named modalities when present.
            $xrModality = ClcImagingModality::forBusiness($this->businessId)->where('code', 'XR')->value('id') ?: $modalityId;
            $usModality = ClcImagingModality::forBusiness($this->businessId)->where('code', 'US')->value('id') ?: $modalityId;

            // [suffix, appt offset, modality_id, body_region, indication, req_status, report?]
            //   report = [status, impression, narrative, recommendation, has_critical]
            $requests = [
                [
                    '0001', 1, $xrModality, 'Chest', 'Persistent productive cough for 2 weeks', 'reported',
                    [
                        'final',
                        'No acute cardiopulmonary abnormality.',
                        'Heart size within normal limits. Lungs are clear with no focal consolidation, effusion or pneumothorax. Bony thorax intact.',
                        'No follow-up imaging required. Correlate clinically.',
                        false,
                    ],
                ],
                [
                    '0002', 2, $usModality, 'Abdomen', 'Right upper quadrant discomfort — rule out gallstones', 'in_progress',
                    [
                        'draft',
                        null,
                        'Study acquired; preliminary review in progress.',
                        null,
                        false,
                    ],
                ],
            ];

            $reqCount = 0;
            $repCount = 0;
            foreach ($requests as [$suffix, $offset, $modId, $bodyRegion, $indication, $reqStatus, $report]) {
                $appt = $appointments->get($offset);
                if (! $appt) {
                    continue;
                }
                $patient = $patients->firstWhere('id', $appt->patient_id) ?? $patients->first();

                $refNo = 'DEMO-IM-' . $this->businessId . '-' . $suffix;

                $request = ClcImagingRequest::updateOrCreate(
                    [
                        'business_id' => $this->businessId,
                        'ref_no'      => $refNo,
                    ],
                    [
                        'patient_id'     => $appt->patient_id,
                        'contact_id'     => $patient->contact_id ?? null,
                        'appointment_id' => $appt->id,
                        'modality_id'    => $modId,
                        'body_region'    => $bodyRegion,
                        'indication'     => $indication,
                        'priority'       => 'routine',
                        'status'         => $reqStatus,
                        'requested_at'   => Carbon::now()->subDays(3),
                        'completed_at'   => $reqStatus === 'reported' ? Carbon::now()->subDays(2) : null,
                        'reported_at'    => $reqStatus === 'reported' ? Carbon::now()->subDays(2) : null,
                    ]
                );
                $reqCount++;

                if ($report) {
                    [$reportStatus, $impression, $narrative, $recommendation, $hasCritical] = $report;
                    $isFinal = in_array($reportStatus, ['final', 'amended'], true);

                    ClcImagingReport::updateOrCreate(
                        ['imaging_request_id' => $request->id],
                        [
                            'business_id'        => $this->businessId,
                            'patient_id'         => $appt->patient_id,
                            'radiologist_id'     => $doctorId,
                            'narrative_findings' => $narrative,
                            'impression'         => $impression,
                            'recommendation'     => $recommendation,
                            'has_critical'       => $hasCritical,
                            'report_status'      => $reportStatus,
                            'signed_by'          => $isFinal ? null : null,
                            'signed_at'          => $isFinal ? Carbon::now()->subDays(2) : null,
                        ]
                    );
                    $repCount++;
                }
            }

            $this->command->info("  · imaging: {$reqCount} requests / {$repCount} reports.");
        } catch (\Throwable $e) {
            $this->command->warn('  · imaging skipped: ' . $e->getMessage());
        }
    }

    // ── 5. Referrals ─────────────────────────────────────────────────────────

    /** clc_referrals — one urgent external-outgoing (cardiology) + one internal. */
    private function seedReferrals($patients, $appointments, ?int $doctorId): void
    {
        try {
            $appt0 = $appointments->get(0);
            $appt1 = $appointments->get(1);

            // [suffix, appt, direction, urgency, reason, clinical_summary, status, extra]
            $rows = [
                [
                    '0001', $appt0, 'external_outgoing', 'urgent',
                    'Uncontrolled hypertension with new-onset exertional chest pain.',
                    'Hypertensive, diabetic patient with LDL 158 mg/dL and HbA1c 7.8%. Reports exertional chest tightness. Referring for cardiology assessment and stress testing.',
                    'sent',
                    [
                        'to_facility_name'   => 'Heart Care Cardiology Center',
                        'to_provider_name'   => 'Dr. Nadia Farouk, Consultant Cardiologist',
                        'requested_services' => ['ECG', 'Echocardiogram', 'Exercise stress test'],
                    ],
                ],
                [
                    '0002', $appt1, 'internal', 'routine',
                    'Recurrent upper respiratory infections — assess for allergic component.',
                    'Patient with recurrent URIs and seasonal rhinitis. Requesting internal allergy/immunology review for allergen testing.',
                    'accepted',
                    [
                        'to_doctor_id'       => $doctorId,
                        'requested_services' => ['Allergy skin-prick panel'],
                    ],
                ],
            ];

            $count = 0;
            foreach ($rows as [$suffix, $appt, $direction, $urgency, $reason, $summary, $status, $extra]) {
                if (! $appt) {
                    continue;
                }

                $referralNo = 'DEMO-REF-' . $this->businessId . '-' . $suffix;

                ClcReferral::updateOrCreate(
                    [
                        'business_id' => $this->businessId,
                        'referral_no' => $referralNo,
                    ],
                    array_merge([
                        'patient_id'         => $appt->patient_id,
                        'appointment_id'     => $appt->id,
                        'direction'          => $direction,
                        'urgency'            => $urgency,
                        'from_doctor_id'     => $doctorId,
                        'reason'             => $reason,
                        'clinical_summary'   => $summary,
                        'status'             => $status,
                        'created_by'         => null,
                    ], $extra)
                );
                $count++;
            }

            $this->command->info("  · referrals: {$count} rows.");
        } catch (\Throwable $e) {
            $this->command->warn('  · referrals skipped: ' . $e->getMessage());
        }
    }

    // ── 6. Historical vitals (trend) ─────────────────────────────────────────

    /** clc_vitals — 2-3 dated readings per ~3 patients so trend charts show a line. */
    private function seedVitals($patients, $appointments): void
    {
        try {
            // Per patient offset: a series of readings across dates (older → newer)
            // [daysAgo, temp, hr, rr, sys, dia, spo2, weight, height, pain, glucose, glucoseTiming]
            $series = [
                0 => [
                    [60, 36.8, 84, 16, 152, 96, 98, 82.0, 172, 0, 9.4, 'fasting'],
                    [30, 36.7, 80, 16, 146, 92, 98, 81.2, 172, 0, 8.6, 'fasting'],
                    [3,  36.9, 78, 15, 138, 88, 99, 80.5, 172, 0, 7.9, 'fasting'],
                ],
                1 => [
                    [45, 37.9, 96, 20, 128, 82, 96, 68.0, 165, 2, null, null],
                    [14, 37.2, 88, 18, 124, 80, 98, 67.6, 165, 1, null, null],
                    [2,  36.8, 76, 16, 120, 78, 99, 67.4, 165, 0, null, null],
                ],
                2 => [
                    [50, 36.6, 72, 14, 118, 76, 99, 74.0, 178, 4, null, null],
                    [20, 36.6, 74, 14, 122, 78, 99, 74.3, 178, 3, null, null],
                    [5,  36.7, 70, 14, 120, 76, 99, 74.1, 178, 2, null, null],
                ],
            ];

            $count = 0;
            foreach ($series as $offset => $readings) {
                $patient = $patients->get($offset);
                if (! $patient) {
                    continue;
                }

                foreach ($readings as $r) {
                    [$daysAgo, $temp, $hr, $rr, $sys, $dia, $spo2, $weight, $height, $pain, $glucose, $glucoseTiming] = $r;
                    $recordedAt = Carbon::now()->subDays($daysAgo)->setTime(9, 0);

                    // Idempotency: one reading per (patient, recorded_at date). We
                    // check existence rather than updateOrCreate because bmi/gcs
                    // are computed in a model 'saving' hook and we want a real save.
                    $exists = ClcVital::where('business_id', $this->businessId)
                        ->where('patient_id', $patient->id)
                        ->whereDate('recorded_at', $recordedAt->toDateString())
                        ->where('notes', 'DEMO seed')
                        ->exists();
                    if ($exists) {
                        continue;
                    }

                    ClcVital::create([
                        'business_id'    => $this->businessId,
                        'patient_id'     => $patient->id,
                        'appointment_id' => null,
                        'recorded_by'    => null,
                        'recorded_at'    => $recordedAt,
                        'temperature'    => $temp,
                        'heart_rate'     => $hr,
                        'respiratory_rate' => $rr,
                        'bp_systolic'    => $sys,
                        'bp_diastolic'   => $dia,
                        'spo2'           => $spo2,
                        'weight_kg'      => $weight,
                        'height_cm'      => $height,
                        'pain_score'     => $pain,
                        'blood_glucose'  => $glucose,
                        'glucose_timing' => $glucoseTiming,
                        'notes'          => 'DEMO seed',
                    ]);
                    $count++;
                }
            }

            $this->command->info("  · vitals: {$count} historical readings (trend-ready).");
        } catch (\Throwable $e) {
            $this->command->warn('  · vitals skipped: ' . $e->getMessage());
        }
    }

    // ── 7. Waitlist ──────────────────────────────────────────────────────────

    /** clc_appointment_waitlist — 1-2 waiting entries. */
    private function seedWaitlist($patients, ?int $doctorId): void
    {
        try {
            $rows = [
                [0, 'Follow-up for blood pressure review', 5],
                [3, 'New patient — dermatology consult',    10],
            ];

            $count = 0;
            foreach ($rows as [$offset, $reason, $inDays]) {
                $patient = $patients->get($offset);
                if (! $patient) {
                    continue;
                }

                // Idempotency: one demo waiting entry per (patient, reason).
                $exists = ClcAppointmentWaitlist::where('business_id', $this->businessId)
                    ->where('patient_id', $patient->id)
                    ->where('reason', $reason)
                    ->exists();
                if ($exists) {
                    continue;
                }

                ClcAppointmentWaitlist::create([
                    'business_id'    => $this->businessId,
                    'patient_id'     => $patient->id,
                    'contact_id'     => $patient->contact_id ?? null,
                    'doctor_id'      => $doctorId,
                    'preferred_date' => Carbon::now()->addDays($inDays)->toDateString(),
                    'reason'         => $reason,
                    'status'         => 'waiting',
                ]);
                $count++;
            }

            $this->command->info("  · waitlist: {$count} waiting entries.");
        } catch (\Throwable $e) {
            $this->command->warn('  · waitlist skipped: ' . $e->getMessage());
        }
    }

    // ── 8. DentalClinic ──────────────────────────────────────────────────────

    private function seedDental($patients, $doctors): void
    {
        try {
            if (! class_exists(\Modules\DentalClinic\Entities\DentalToothFinding::class)) {
                $this->command->warn('  · dental skipped: DentalClinic module not installed.');
                return;
            }

            $patient = $this->patientForSpecialty($patients, 'dental');
            if (! $patient) {
                $this->command->warn('  · dental skipped: no patient available.');
                return;
            }
            $doctorId = $doctors->first()->id ?? null;

            // Tooth findings: caries on 26, filled 46 with perio fields.
            $findings = [
                [26, 'carious', 'MO', 'Occlusal caries, moderate depth.', null, null, null, 'none'],
                [46, 'filled',  'O',  'Amalgam filling, intact.',         3,    false, 0,  'none'],
            ];
            $findingCount = 0;
            foreach ($findings as [$tooth, $condition, $surfaces, $note, $probing, $bop, $mobility, $endo]) {
                \Modules\DentalClinic\Entities\DentalToothFinding::updateOrCreate(
                    [
                        'clc_patient_id' => $patient->id,
                        'tooth_number'   => $tooth,
                    ],
                    [
                        'condition'           => $condition,
                        'surfaces'            => $surfaces,
                        'note'                => $note,
                        'notation_system'     => 'universal',
                        'probing_depth_mm'    => $probing,
                        'bleeding_on_probing' => $bop,
                        'mobility'            => $mobility,
                        'endo_status'         => $endo,
                    ]
                );
                $findingCount++;
            }

            // Procedures (guarded — separate table/model).
            $procCount = 0;
            if (class_exists(\Modules\DentalClinic\Entities\DentalProcedure::class)) {
                $procedures = [
                    [26, 'MO', 'D2392', 'Composite restoration — two surface', 'Local infiltration', 8],
                    [46, null, 'D1110', 'Adult prophylaxis (scaling & polish)', null, 3],
                ];
                foreach ($procedures as [$tooth, $surfaces, $cdt, $name, $anaesthesia, $daysAgo]) {
                    // Idempotency: one demo procedure per (patient, cdt_code, tooth).
                    $exists = \Modules\DentalClinic\Entities\DentalProcedure::where('business_id', $this->businessId)
                        ->where('patient_id', $patient->id)
                        ->where('cdt_code', $cdt)
                        ->where('tooth_number', $tooth)
                        ->exists();
                    if ($exists) {
                        continue;
                    }
                    \Modules\DentalClinic\Entities\DentalProcedure::create([
                        'business_id'     => $this->businessId,
                        'patient_id'      => $patient->id,
                        'appointment_id'  => null,
                        'tooth_number'    => $tooth,
                        'surfaces'        => $surfaces,
                        'cdt_code'        => $cdt,
                        'procedure_name'  => $name,
                        'anesthesia_type' => $anaesthesia,
                        'performed_by'    => $doctorId,
                        'performed_at'    => Carbon::now()->subDays($daysAgo),
                    ]);
                    $procCount++;
                }
            }

            // Treatment plan (3-row plan sharing plan_name).
            $planCount = 0;
            if (class_exists(\Modules\DentalClinic\Entities\DentalTreatmentPlanItem::class)) {
                $planName = 'Comprehensive care plan';
                $items = [
                    [26, 'D2392', 'Composite restoration #26', 'soon',    350.00, 'completed', 1],
                    [46, 'D1110', 'Scaling & polish',          'elective', 200.00, 'planned',  2],
                    [16, 'D2740', 'Crown #16',                 'urgent',  1800.00, 'planned',  3],
                ];
                foreach ($items as [$tooth, $cdt, $name, $urgency, $cost, $status, $sort]) {
                    \Modules\DentalClinic\Entities\DentalTreatmentPlanItem::updateOrCreate(
                        [
                            'business_id'    => $this->businessId,
                            'patient_id'     => $patient->id,
                            'plan_name'      => $planName,
                            'procedure_name' => $name,
                        ],
                        [
                            'tooth_number'   => $tooth,
                            'cdt_code'       => $cdt,
                            'urgency'        => $urgency,
                            'estimated_cost' => $cost,
                            'status'         => $status,
                            'sort_order'     => $sort,
                        ]
                    );
                    $planCount++;
                }
            }

            // Recall (1 due).
            $recallCount = 0;
            if (class_exists(\Modules\DentalClinic\Entities\DentalRecall::class)) {
                $exists = \Modules\DentalClinic\Entities\DentalRecall::where('business_id', $this->businessId)
                    ->where('patient_id', $patient->id)
                    ->where('reason', 'Perio maintenance')
                    ->exists();
                if (! $exists) {
                    \Modules\DentalClinic\Entities\DentalRecall::create([
                        'business_id'     => $this->businessId,
                        'patient_id'      => $patient->id,
                        'due_date'        => Carbon::now()->subDays(2)->toDateString(),
                        'interval_months' => 6,
                        'reason'          => 'Perio maintenance',
                        'status'          => 'due',
                        'notes'           => 'DEMO seed — recall now due.',
                    ]);
                    $recallCount = 1;
                }
            }

            $this->command->info(
                "  · dental (patient #{$patient->id}): {$findingCount} findings / {$procCount} procedures / {$planCount} plan items / {$recallCount} recall."
            );
        } catch (\Throwable $e) {
            $this->command->warn('  · dental skipped: ' . $e->getMessage());
        }
    }

    // ── 9. DermatologyClinic ─────────────────────────────────────────────────

    private function seedDermatology($patients): void
    {
        try {
            if (! class_exists(\Modules\DermatologyClinic\Entities\DermaLesion::class)) {
                $this->command->warn('  · dermatology skipped: DermatologyClinic module not installed.');
                return;
            }

            $patient = $this->patientForSpecialty($patients, 'dermatology');
            if (! $patient) {
                $this->command->warn('  · dermatology skipped: no patient available.');
                return;
            }

            // 2 lesions — one benign, one with ABCDE flags suggesting biopsy.
            $lesions = [
                [
                    'DEMO-LESION-benign', 'left_arm', 30, 40, 'macule', 4.0, 'Uniform brown',
                    'well_defined', 'stable',
                    ['asymmetry' => false, 'border' => false, 'color' => false, 'diameter' => false, 'evolving' => false],
                    'Benign-appearing junctional naevus. Uniform pigment, regular network.',
                    'active', false, null,
                ],
                [
                    'DEMO-LESION-suspicious', 'back', 55, 60, 'plaque', 9.5, 'Variegated brown-black',
                    'irregular', 'growing',
                    ['asymmetry' => true, 'border' => true, 'color' => true, 'diameter' => true, 'evolving' => true],
                    'Asymmetric lesion with irregular borders, colour variation and > 6 mm diameter. ABCDE positive — excisional biopsy recommended.',
                    'monitoring', false, null,
                ],
            ];

            $lesionCount = 0;
            foreach ($lesions as $l) {
                [$tag, $region, $px, $py, $morphology, $size, $color, $border, $evolution, $abcde, $notes, $status, $biopsyTaken, $biopsyType] = $l;

                // Idempotency: dermoscopy_notes tag doubles as the stable key (photos
                // aren't present, so we key on region + a tagged note prefix).
                $existing = \Modules\DermatologyClinic\Entities\DermaLesion::where('business_id', $this->businessId)
                    ->where('patient_id', $patient->id)
                    ->where('body_region', $region)
                    ->where('dermoscopy_notes', 'like', $tag . '%')
                    ->first();

                $attrs = [
                    'business_id'      => $this->businessId,
                    'patient_id'       => $patient->id,
                    'appointment_id'   => null,
                    'body_region'      => $region,
                    'position_x'       => $px,
                    'position_y'       => $py,
                    'morphology'       => $morphology,
                    'size_mm'          => $size,
                    'color'            => $color,
                    'border'           => $border,
                    'evolution'        => $evolution,
                    'abcde_flags'      => $abcde,
                    'dermoscopy_notes' => $tag . ' — ' . $notes,
                    'biopsy_taken'     => $biopsyTaken,
                    'biopsy_type'      => $biopsyType,
                    'status'           => $status,
                ];

                if ($existing) {
                    $existing->fill($attrs)->save();
                } else {
                    \Modules\DermatologyClinic\Entities\DermaLesion::create($attrs);
                }
                $lesionCount++;
            }

            // PASI trend — 3 dated scores (improving on treatment).
            $scoreCount = 0;
            if (class_exists(\Modules\DermatologyClinic\Entities\DermaScore::class)) {
                $scores = [
                    [90, 'pasi', 18.4],
                    [45, 'pasi', 11.2],
                    [7,  'pasi', 5.6],
                ];
                foreach ($scores as [$daysAgo, $type, $value]) {
                    $recordedAt = Carbon::now()->subDays($daysAgo)->setTime(10, 0);
                    $exists = \Modules\DermatologyClinic\Entities\DermaScore::where('business_id', $this->businessId)
                        ->where('patient_id', $patient->id)
                        ->where('score_type', $type)
                        ->whereDate('recorded_at', $recordedAt->toDateString())
                        ->exists();
                    if ($exists) {
                        continue;
                    }
                    \Modules\DermatologyClinic\Entities\DermaScore::create([
                        'business_id' => $this->businessId,
                        'patient_id'  => $patient->id,
                        'score_type'  => $type,
                        'value'       => $value,
                        'recorded_at' => $recordedAt,
                    ]);
                    $scoreCount++;
                }
            }

            $this->command->info(
                "  · dermatology (patient #{$patient->id}): {$lesionCount} lesions / {$scoreCount} PASI scores."
            );
        } catch (\Throwable $e) {
            $this->command->warn('  · dermatology skipped: ' . $e->getMessage());
        }
    }

    // ── 10. OphthalmologyClinic ──────────────────────────────────────────────

    private function seedOphthalmology($patients): void
    {
        try {
            if (! class_exists(\Modules\OphthalmologyClinic\Entities\OpthEyeExam::class)) {
                $this->command->warn('  · ophthalmology skipped: OphthalmologyClinic module not installed.');
                return;
            }

            $patient = $this->patientForSpecialty($patients, 'ophthalmology');
            if (! $patient) {
                $this->command->warn('  · ophthalmology skipped: no patient available.');
                return;
            }

            $alertSvc = app(MedicalAlertService::class);

            // 3 dated exams; the most recent has IOP > 30 (critical) → trend + alert.
            // [daysAgo, va_od, va_os, iop_od, iop_os, cup_disc_od, cup_disc_os, dr_od, dr_os]
            $exams = [
                [180, '6/9', '6/9',  18.0, 17.0, 0.40, 0.40, 'R0', 'R0'],
                [90,  '6/9', '6/12', 24.0, 22.0, 0.50, 0.50, 'R1', 'R0'],
                [7,   '6/12', '6/12', 32.0, 29.0, 0.65, 0.60, 'R1', 'R1'],
            ];

            $examCount = 0;
            $criticalExam = null;
            foreach ($exams as [$daysAgo, $vaOd, $vaOs, $iopOd, $iopOs, $cupOd, $cupOs, $drOd, $drOs]) {
                $examinedAt = Carbon::now()->subDays($daysAgo)->setTime(11, 0);

                $existing = \Modules\OphthalmologyClinic\Entities\OpthEyeExam::where('business_id', $this->businessId)
                    ->where('patient_id', $patient->id)
                    ->whereDate('examined_at', $examinedAt->toDateString())
                    ->where('notes', 'DEMO seed')
                    ->first();

                $attrs = [
                    'business_id'    => $this->businessId,
                    'patient_id'     => $patient->id,
                    'appointment_id' => null,
                    'va_od'          => $vaOd,
                    'va_os'          => $vaOs,
                    'iop_od'         => $iopOd,
                    'iop_os'         => $iopOs,
                    'iop_method'     => 'goldmann',
                    'cup_disc_od'    => $cupOd,
                    'cup_disc_os'    => $cupOs,
                    'dr_grade_od'    => $drOd,
                    'dr_grade_os'    => $drOs,
                    'maculopathy_od' => 'M0',
                    'maculopathy_os' => 'M0',
                    'notes'          => 'DEMO seed',
                    'examined_at'    => $examinedAt,
                ];

                if ($existing) {
                    $existing->fill($attrs)->save();
                    $exam = $existing;
                } else {
                    $exam = \Modules\OphthalmologyClinic\Entities\OpthEyeExam::create($attrs);
                }
                $examCount++;

                if ($exam->iopCritical()) {
                    $criticalExam = $exam;
                }
            }

            // Raise a critical_value alert for the high-IOP exam (idempotent on source).
            if ($criticalExam) {
                $alertSvc->raise([
                    'business_id' => $this->businessId,
                    'patient_id'  => $patient->id,
                    'alert_type'  => 'critical_value',
                    'severity'    => 'critical',
                    'title'       => 'Critical IOP: ' . rtrim(rtrim((string) $criticalExam->iop_od, '0'), '.') . ' mmHg (OD)',
                    'details'     => 'Intraocular pressure above 30 mmHg — urgent glaucoma review required.',
                    'source_type' => 'opth_eye_exams',
                    'source_id'   => (int) $criticalExam->id,
                ]);
            }

            // 1 glasses prescription.
            $rxCount = 0;
            if (class_exists(\Modules\OphthalmologyClinic\Entities\OpthGlassesRx::class)) {
                $exists = \Modules\OphthalmologyClinic\Entities\OpthGlassesRx::where('business_id', $this->businessId)
                    ->where('patient_id', $patient->id)
                    ->where('notes', 'DEMO seed')
                    ->exists();
                if (! $exists) {
                    \Modules\OphthalmologyClinic\Entities\OpthGlassesRx::create([
                        'business_id'  => $this->businessId,
                        'patient_id'   => $patient->id,
                        'sphere_od'    => -1.25,
                        'sphere_os'    => -1.50,
                        'cylinder_od'  => -0.50,
                        'cylinder_os'  => -0.75,
                        'axis_od'      => 180,
                        'axis_os'      => 175,
                        'add_od'       => 2.00,
                        'add_os'       => 2.00,
                        'pd_mm'        => 63.0,
                        'lens_type'    => 'progressive',
                        'notes'        => 'DEMO seed',
                        'prescribed_at' => Carbon::now()->subDays(7),
                    ]);
                    $rxCount = 1;
                }
            }

            $this->command->info(
                "  · ophthalmology (patient #{$patient->id}): {$examCount} exams / {$rxCount} glasses Rx (critical IOP alert raised)."
            );
        } catch (\Throwable $e) {
            $this->command->warn('  · ophthalmology skipped: ' . $e->getMessage());
        }
    }

    // ── Helpers ──────────────────────────────────────────────────────────────

    /**
     * Pick a patient whose default_specialty maps to the given specialty code
     * (business-scoped or global). Falls back to the first patient so the
     * specialty section always has something to render.
     */
    private function patientForSpecialty($patients, string $code)
    {
        try {
            $specialtyIds = DB::table('clc_specialties')
                ->where('code', $code)
                ->where(function ($q) {
                    $q->where('business_id', $this->businessId)->orWhereNull('business_id');
                })
                ->pluck('id')
                ->all();

            if ($specialtyIds && \Illuminate\Support\Facades\Schema::hasColumn('clc_patients', 'default_specialty_id')) {
                $match = $patients->first(fn ($p) => in_array($p->default_specialty_id ?? null, $specialtyIds, true));
                if ($match) {
                    return $match;
                }
            }
        } catch (\Throwable $e) {
            // fall through to default
        }

        return $patients->first();
    }
}
