<?php

namespace App\Services\Care;

use App\Models\Appointment;
use App\Models\Assessment;
use App\Models\AssessmentAnswer;
use App\Models\AssessmentQuestion;
use App\Models\AssessmentScore;
use App\Models\AssessmentTemplate;
use App\Models\Bill;
use App\Models\BillLineItem;
use App\Models\Branch;
use App\Models\Consultation;
use App\Models\Department;
use App\Models\Device;
use App\Models\Drug;
use App\Models\DrugBatch;
use App\Models\InvestigationRequest;
use App\Models\InvestigationType;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\Payment;
use App\Models\Practitioner;
use App\Models\Prescription;
use App\Models\User;
use App\Models\Visit;
use App\Models\VitalSign;
use App\Support\DemoWorld;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

/**
 * Idempotent, plan-aware demo data for a Care tenant (keyed by owner_ref / public_id).
 */
class DemoTenantSeeder
{
    public const DEMO_TEMPLATE_CODE = 'demo_triage';

    /** @var list<string> */
    private const FIRST_NAMES = [
        'Ama', 'Kofi', 'Abena', 'Kwame', 'Akosua', 'Yaw', 'Efua', 'Kojo',
        'Adwoa', 'Kwesi', 'Afia', 'Kwadwo', 'Esi', 'Fiifi', 'Mansa', 'Nana',
    ];

    /** @var list<string> */
    private const LAST_NAMES = [
        'Mensah', 'Asante', 'Osei', 'Boateng', 'Owusu', 'Adjei', 'Appiah', 'Darko',
        'Agyeman', 'Sarpong', 'Nyarko', 'Ankrah', 'Tetteh', 'Quaye', 'Addo', 'Frimpong',
    ];

    /** @var list<string> */
    private const BRANCH_NAMES = [
        'Ridge Clinic', 'Osu Medical Centre', 'East Legon Care', 'Tema Harbour Clinic',
        'Kaneshie Health Hub', 'Spintex Wellness', 'Madina Family Clinic', 'Airport Residential Care',
    ];

    /** @var list<array{name: string, type: string}> */
    private const CORE_DEPARTMENTS = [
        ['name' => 'Outpatient', 'type' => 'outpatient'],
        ['name' => 'General Practice', 'type' => 'general'],
        ['name' => 'Nursing', 'type' => 'nursing'],
        ['name' => 'Specialist Clinic', 'type' => 'specialist'],
    ];

    /** @var list<array{name: string, type: string}> */
    private const PAID_DEPARTMENTS = [
        ['name' => 'Laboratory', 'type' => 'laboratory'],
        ['name' => 'Pharmacy', 'type' => 'pharmacy'],
    ];

    /** @var list<string> */
    private const SPECIALTIES = [
        'General Practice', 'Internal Medicine', 'Paediatrics', 'Obstetrics',
        'Emergency', 'Nursing', 'Laboratory', 'Pharmacy',
    ];

    /**
     * @return array{
     *     organization: Organization,
     *     branches: int,
     *     patients: int,
     *     appointments: int,
     *     plan: string
     * }
     */
    public function seed(User $user, string $plan, bool $reset = false): array
    {
        $plan = $this->normalizePlan($plan);
        $ownerRef = $user->ownerRef();
        $volumes = $this->volumes($plan);

        // Always materialize the onboarded org shell before any wipe so concurrent
        // SSO redirects never land on the blank onboarding form.
        $this->ensureOnboardedShell($user, $plan);

        if ($reset) {
            $this->resetTenant($ownerRef);
            $this->ensureOnboardedShell($user, $plan);
        }

        // No outer transaction — Enterprise volumes can hold row locks for minutes
        // and 500 concurrent settings/login requests (messaging credentials, org updates).
        $organization = $this->upsertOrganization($user, $ownerRef, $plan, $volumes['branches']);
        $this->upsertOwnerMember($organization, $ownerRef);

        $branches = $this->seedBranches($organization, $ownerRef, $volumes);
        // Lab catalog early — survives if later clinical seeding fails mid-run.
        $investigationTypes = $volumes['paid_modules']
            ? $this->seedInvestigationCatalog($organization, $ownerRef)
            : [];
        $this->seedStaffMembers($organization, $ownerRef, $plan, $branches);
        $departments = $this->seedDepartments($branches, $ownerRef, $volumes);
        if (in_array($plan, ['pro', 'enterprise'], true)) {
            $this->seedSpecialtyModules($organization, $ownerRef);
            // Keep $departments as core/paid only. Specialty departments must not feed
            // general GP desks or appointments — those waiters live in seedSpecialtyDemoData.
        }
        $practitioners = $this->seedPractitioners($organization, $branches, $departments, $ownerRef, $volumes);
        $this->linkStaffDoctorsToPractitioners($organization, $ownerRef, $plan, $branches, $practitioners);
        $patients = $this->seedPatients($organization, $branches, $ownerRef, $volumes);
        $appointmentCount = $this->seedAppointmentsAndClinical(
            $organization,
            $branches,
            $departments,
            $practitioners,
            $patients,
            $ownerRef,
            $volumes,
        );
        if (in_array($plan, ['pro', 'enterprise'], true)) {
            $appointmentCount += $this->seedSpecialtyDemoData(
                $organization->fresh(),
                $branches,
                $practitioners,
                $patients,
                $ownerRef,
                $plan,
            );
        }
        $this->assignAllDemoWaiters($organization, $ownerRef, $branches, $practitioners);
        $this->renumberWaitingQueuePositions($organization, $ownerRef, $branches);

        // Catalog first so a later lab-request failure never leaves Lab → Catalog empty.
        if ($volumes['paid_modules']) {
            if ($investigationTypes === []) {
                $investigationTypes = $this->seedInvestigationCatalog($organization, $ownerRef);
            }
            $this->seedPaidModules(
                $organization,
                $branches,
                $practitioners,
                $patients,
                $ownerRef,
                $volumes,
                $investigationTypes,
            );
            $this->seedDevices($organization, $branches, $ownerRef);
        }

        if ($volumes['assessments'] > 0) {
            $this->seedAssessments(
                $organization,
                $branches,
                $practitioners,
                $patients,
                $ownerRef,
                $volumes['assessments'],
            );
        }

        $this->syncQueueTicketsForDemo($organization->fresh(), $branches);

        return [
            'organization' => $organization->fresh(),
            'branches' => count($branches),
            'patients' => count($patients),
            'appointments' => $appointmentCount,
            'plan' => $plan,
        ];
    }

    /**
     * @param  list<Branch>  $branches
     */
    private function syncQueueTicketsForDemo(Organization $organization, array $branches): void
    {
        if (! data_get($organization->settings, 'queue_integration_enabled')) {
            return;
        }

        try {
            $bridge = app(CareQueueBridge::class);
            if (! $bridge->isEnabled($organization)) {
                return;
            }

            // Scoped per branch (not full-org) so demos finish quickly while
            // still leaving waiters with real tickets instead of local positions only.
            foreach ($branches as $branch) {
                try {
                    $bridge->syncMissingAppointmentTickets($organization, (int) $branch->id, 100);
                } catch (\Throwable) {
                    // Continue — page load will retry.
                }

                foreach ([
                    CareQueueContexts::PHARMACY,
                    CareQueueContexts::LABORATORY,
                    CareQueueContexts::BILLING,
                ] as $context) {
                    try {
                        $bridge->syncMissingTickets($organization, $context, (int) $branch->id, 50);
                    } catch (\Throwable) {
                        // Continue — page load will retry for that context.
                    }
                }
            }
        } catch (\Throwable) {
            // Demo seed should not fail if Queue API is unreachable.
        }
    }

    /**
     * Point DemoWorld doctor staff at a real practitioner so Call next / rooms work.
     *
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     */
    private function linkStaffDoctorsToPractitioners(
        Organization $organization,
        string $ownerRef,
        string $plan,
        array $branches,
        array $practitioners,
    ): void {
        $doctorEmail = null;
        $doctorName = null;
        foreach (DemoWorld::staff($plan) as $staff) {
            if (($staff['roles']['care'] ?? null) === 'doctor') {
                $doctorEmail = strtolower((string) $staff['email']);
                $doctorName = (string) $staff['name'];
                break;
            }
        }
        if ($doctorEmail === null || $practitioners === []) {
            return;
        }

        $member = $this->findStaffMemberByEmail($organization, $doctorEmail);

        // Link the demo doctor to a single desk on their home branch (not every site).
        $homeBranchId = (int) ($member?->branch_id ?: ($branches[0]->id ?? 0));
        $practitioner = collect($practitioners)->first(
            fn (Practitioner $p) => (int) $p->branch_id === $homeBranchId,
        ) ?? ($practitioners[0] ?? null);

        if (! $practitioner) {
            return;
        }

        $practitioner->forceFill([
            'user_ref' => $member?->user_ref ?: $doctorEmail,
            'member_id' => $member?->id,
            'name' => $doctorName !== '' ? $doctorName : $practitioner->name,
        ])->save();

        if ($homeBranchId > 0) {
            $practitioner->syncAssignedBranches([$homeBranchId]);
        }
    }

    /**
     * Every waiting / checked-in demo appointment gets a doctor and a clean routing
     * state so Queue sync can issue tickets (no amber Unassigned leftovers).
     *
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     */
    private function assignAllDemoWaiters(
        Organization $organization,
        string $ownerRef,
        array $branches,
        array $practitioners,
    ): void {
        if ($practitioners === []) {
            return;
        }

        $byBranch = [];
        foreach ($practitioners as $practitioner) {
            $byBranch[(int) $practitioner->branch_id][] = $practitioner;
        }

        $waiters = Appointment::withTrashed()
            ->owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->where(function ($q) {
                $q->whereNull('practitioner_id')->orWhere('practitioner_id', 0);
            })
            ->get();

        foreach ($waiters as $index => $appointment) {
            $branchPracs = $byBranch[(int) $appointment->branch_id] ?? $practitioners;
            $practitioner = $branchPracs[$index % max(1, count($branchPracs))] ?? $practitioners[0];

            $appointment->forceFill([
                'practitioner_id' => $practitioner->id,
                'queue_routing_status' => null,
                'deleted_at' => null,
            ])->save();
        }
    }

    /**
     * Give each waiting/checked-in appointment a unique 1..n position per branch.
     *
     * @param  list<Branch>  $branches
     */
    private function renumberWaitingQueuePositions(
        Organization $organization,
        string $ownerRef,
        array $branches,
    ): void {
        foreach ($branches as $branch) {
            $waiters = Appointment::withTrashed()
                ->owned($ownerRef)
                ->where('organization_id', $organization->id)
                ->where('branch_id', $branch->id)
                ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
                ->orderByRaw('COALESCE(waiting_at, checked_in_at, scheduled_at) asc')
                ->orderBy('id')
                ->get();

            foreach ($waiters as $index => $appointment) {
                $position = $index + 1;
                if ((int) $appointment->queue_position === $position) {
                    continue;
                }
                $appointment->forceFill([
                    'queue_position' => $position,
                    'deleted_at' => null,
                ])->save();
            }
        }
    }

    /**
     * Ensure the demo tenant has an onboarded organization + owner member.
     * Safe to call on the SSO request path before redirecting.
     */
    public function ensureOnboardedShell(User $user, string $plan): Organization
    {
        $plan = $this->normalizePlan($plan);
        $ownerRef = $user->ownerRef();
        $volumes = $this->volumes($plan);

        return DB::transaction(function () use ($user, $ownerRef, $plan, $volumes) {
            $organization = $this->upsertOrganization($user, $ownerRef, $plan, $volumes['branches']);
            $this->upsertOwnerMember($organization, $ownerRef);

            return $organization;
        });
    }

    public function resetTenant(string $ownerRef): void
    {
        $orgIds = DB::table('care_organizations')->where('owner_ref', $ownerRef)->pluck('id');

        if (Schema::hasTable('care_financial_obligations')) {
            DB::table('care_financial_obligations')->where('owner_ref', $ownerRef)->delete();
            DB::table('care_visit_stage_advances')->where('owner_ref', $ownerRef)->delete();
            $workflowIds = DB::table('care_facility_workflows')->where('owner_ref', $ownerRef)->pluck('id');
            if ($workflowIds->isNotEmpty()) {
                DB::table('care_workflow_stages')->whereIn('workflow_id', $workflowIds)->delete();
                DB::table('care_facility_workflows')->whereIn('id', $workflowIds)->delete();
            }
        }

        DB::table('care_assessment_answers')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_assessment_scores')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_assessments')->where('owner_ref', $ownerRef)->update(['patient_pathway_id' => null]);
        DB::table('care_patient_pathways')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_assessments')->where('owner_ref', $ownerRef)->delete();

        if ($orgIds->isNotEmpty()) {
            $templateIds = DB::table('care_assessment_templates')
                ->whereIn('organization_id', $orgIds)
                ->pluck('id');
            if ($templateIds->isNotEmpty()) {
                DB::table('care_assessment_questions')->whereIn('template_id', $templateIds)->delete();
                DB::table('care_assessment_templates')->whereIn('id', $templateIds)->delete();
            }
        }

        // Orphaned demo templates left behind by nullOnDelete on prior resets.
        $orphanIds = DB::table('care_assessment_templates')
            ->where('code', self::DEMO_TEMPLATE_CODE)
            ->whereNull('organization_id')
            ->pluck('id');
        if ($orphanIds->isNotEmpty()) {
            DB::table('care_assessment_questions')->whereIn('template_id', $orphanIds)->delete();
            DB::table('care_assessment_templates')->whereIn('id', $orphanIds)->delete();
        }

        DB::table('care_dispensing_records')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_payments')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_bill_line_items')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_bills')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_investigation_result_values')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_investigation_attachments')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_investigation_results')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_investigation_requests')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_prescription_items')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_prescriptions')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_drug_batches')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_drugs')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_investigation_types')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_consultation_documents')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_diagnoses')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_vital_signs')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_consultations')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_devices')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_appointments')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_visits')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_patient_allergies')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_patient_conditions')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_patient_family_history')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_emergency_contacts')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_insurance_policies')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_patient_attachments')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_patients')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_practitioner_schedules')->where('owner_ref', $ownerRef)->delete();
        if (Schema::hasTable('care_practitioner_branch')) {
            $practitionerIds = DB::table('care_practitioners')->where('owner_ref', $ownerRef)->pluck('id');
            if ($practitionerIds->isNotEmpty()) {
                DB::table('care_practitioner_branch')->whereIn('practitioner_id', $practitionerIds)->delete();
            }
        }
        DB::table('care_practitioners')->where('owner_ref', $ownerRef)->delete();

        // Keep the owner member + organization so SSO redirects never see
        // "not onboarded" while afterResponse() reseeding is still running.
        DB::table('care_members')
            ->where('owner_ref', $ownerRef)
            ->where('user_ref', '!=', $ownerRef)
            ->delete();
        DB::table('care_departments')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_branches')->where('owner_ref', $ownerRef)->delete();

        DB::table('care_messaging_credentials')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_payment_gateway_settings')->where('owner_ref', $ownerRef)->delete();
        DB::table('care_audit_logs')->where('owner_ref', $ownerRef)->delete();

        $organization = Organization::withTrashed()->where('owner_ref', $ownerRef)->first();
        if ($organization) {
            if ($organization->trashed()) {
                $organization->restore();
            }
            $settings = $organization->settings ?? [];
            $settings['onboarded'] = true;
            $organization->forceFill(['settings' => $settings])->save();
        }
    }

    /**
     * @return array{
     *     branches: int,
     *     departments_per_branch: int,
     *     practitioners: int,
     *     patients: int,
     *     appointments: int,
     *     paid_modules: bool,
     *     assessments: int,
     *     lab_requests: int,
     *     prescriptions: int,
     *     bills: int
     * }
     */
    public function volumes(string $plan): array
    {
        $testing = app()->runningUnitTests();

        return match ($this->normalizePlan($plan)) {
            'free' => [
                'branches' => 1,
                'departments_per_branch' => 2,
                'practitioners' => 3,
                'patients' => $testing ? 12 : 30,
                'appointments' => $testing ? 20 : 50,
                'paid_modules' => false,
                'assessments' => 0,
                'lab_requests' => 0,
                'prescriptions' => 0,
                'bills' => 0,
            ],
            'pro' => [
                'branches' => 3,
                'departments_per_branch' => 3,
                'practitioners' => 8,
                'patients' => $testing ? 24 : 200,
                'appointments' => $testing ? 40 : 300,
                'paid_modules' => true,
                'assessments' => 0,
                'lab_requests' => $testing ? 8 : 40,
                'prescriptions' => $testing ? 8 : 40,
                'bills' => $testing ? 10 : 60,
            ],
            'enterprise' => [
                'branches' => 6,
                'departments_per_branch' => 4,
                'practitioners' => 16,
                'patients' => $testing ? 36 : 500,
                'appointments' => $testing ? 60 : 800,
                'paid_modules' => true,
                'assessments' => $testing ? 12 : 80,
                'lab_requests' => $testing ? 12 : 80,
                'prescriptions' => $testing ? 12 : 80,
                'bills' => $testing ? 15 : 120,
            ],
            default => throw new \InvalidArgumentException("Unsupported plan [{$plan}]"),
        };
    }

    private function normalizePlan(string $plan): string
    {
        $plan = strtolower(trim($plan));

        return in_array($plan, ['free', 'pro', 'enterprise'], true) ? $plan : 'free';
    }

    private function upsertOrganization(User $user, string $ownerRef, string $plan, int $billedBranches): Organization
    {
        $slug = 'demo-'.Str::slug(Str::limit($user->email ?: $ownerRef, 40, ''));
        if ($slug === 'demo-' || $slug === 'demo') {
            $slug = 'demo-'.Str::lower(Str::substr($ownerRef, 0, 8));
        }

        $name = DemoWorld::business($plan)['name'];

        $organization = Organization::withTrashed()
            ->where('owner_ref', $ownerRef)
            ->first();

        $facilityType = $plan === 'enterprise' ? 'hospital' : 'clinic';
        $settings = array_merge($organization?->settings ?? [], [
            'onboarded' => true,
            'facility_type' => $facilityType,
            'facility_category' => $facilityType,
            'plan' => $plan,
            'plan_expires_at' => now()->addYear()->toIso8601String(),
            'billed_branches' => max(1, $billedBranches),
            'demo' => true,
            // Pro/Enterprise demos: Queue on natural role pages + every specialty module.
            // Free / non-demo tenants leave specialty_modules empty (opt-in via Settings).
            'queue_integration_enabled' => in_array($plan, ['pro', 'enterprise'], true),
            'specialty_modules' => in_array($plan, ['pro', 'enterprise'], true)
                ? $this->allSpecialtyFlagsEnabled()
                : [],
            // Demo seed uses the legacy clinical path. Explicitly clear leftover
            // onboarding rollout so a failed race that created a real workflow
            // shell cannot leave an empty gated tenant.
            'rollout' => [
                CareFeatures::WORKFLOW_ENGINE => false,
                CareFeatures::FINANCIAL_GATES => false,
            ],
        ]);
        unset($settings['workflow_template']);

        if ($organization) {
            if ($organization->trashed()) {
                $organization->restore();
            }
            $organization->update([
                'name' => $name,
                'slug' => $slug,
                'timezone' => 'Africa/Accra',
                'settings' => $settings,
            ]);

            return $organization->fresh();
        }

        return Organization::create([
            'owner_ref' => $ownerRef,
            'name' => $name,
            'slug' => $slug,
            'timezone' => 'Africa/Accra',
            'settings' => $settings,
        ]);
    }

    private function upsertOwnerMember(Organization $organization, string $ownerRef): Member
    {
        return Member::query()->updateOrCreate(
            [
                'organization_id' => $organization->id,
                'user_ref' => $ownerRef,
            ],
            [
                'owner_ref' => $ownerRef,
                'role' => 'hospital_admin',
                'branch_id' => null,
            ],
        );
    }

    /**
     * @param  array<string, mixed>  $volumes
     * @return list<Branch>
     */
    private function seedBranches(Organization $organization, string $ownerRef, array $volumes): array
    {
        $catalog = DemoWorld::branches($this->normalizePlan(
            (string) data_get($organization->settings, 'plan', 'free')
        ), $volumes['branches']);
        $branches = [];
        foreach ($catalog as $i => $spec) {
            $code = $spec['code'] !== '' ? $spec['code'] : sprintf('DEMO-BR-%02d', $i + 1);
            $branches[] = Branch::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'code' => $code,
                ],
                [
                    'owner_ref' => $ownerRef,
                    'name' => $spec['name'],
                    'address' => $spec['address'],
                    'phone' => $spec['phone'],
                    'is_active' => true,
                    'deleted_at' => null,
                ],
            );
        }

        return $branches;
    }

    /**
     * Find a demo staff Member by login email, including SSO-remapped public_id user_ref.
     */
    private function findStaffMemberByEmail(Organization $organization, string $email): ?Member
    {
        $email = strtolower(trim($email));
        if ($email === '') {
            return null;
        }

        $publicId = User::query()
            ->whereRaw('LOWER(email) = ?', [$email])
            ->value('public_id');

        return Member::query()
            ->where('organization_id', $organization->id)
            ->where(function ($query) use ($email, $publicId) {
                $query->where('user_ref', $email);
                if (is_string($publicId) && $publicId !== '') {
                    $query->orWhere('user_ref', $publicId);
                }
            })
            ->orderByRaw('CASE WHEN user_ref = ? THEN 0 ELSE 1 END', [
                is_string($publicId) && $publicId !== '' ? $publicId : $email,
            ])
            ->first();
    }

    /**
     * Local Member rows for DemoWorld staff (user_ref = email until SSO remaps to public_id).
     * Multi-branch demos spread staff across branches so each site has a role roster.
     *
     * @param  list<Branch>  $branches
     */
    private function seedStaffMembers(Organization $organization, string $ownerRef, string $plan, array $branches): void
    {
        $hq = $branches[0] ?? null;
        $staffList = DemoWorld::staff($plan);
        foreach ($staffList as $index => $staff) {
            $role = $staff['roles']['care'] ?? null;
            if (! is_string($role) || $role === '') {
                continue;
            }

            $branch = $hq;
            if ($role !== 'hospital_admin' && $branches !== []) {
                // Cashiers always staff HQ so demo unpaid invoices on branch[0] are collectible.
                $branch = $role === 'cashier'
                    ? $hq
                    : $branches[$index % count($branches)];
            }

            $email = strtolower((string) $staff['email']);
            // Prefer remapped public_id when the staff user already exists locally,
            // so reseeds do not orphan SSO sessions keyed by public_id.
            $publicId = User::query()
                ->whereRaw('LOWER(email) = ?', [$email])
                ->value('public_id');
            $userRef = is_string($publicId) && $publicId !== '' ? $publicId : $email;

            $existing = Member::query()
                ->where('organization_id', $organization->id)
                ->where(function ($query) use ($email, $publicId) {
                    $query->where('user_ref', $email);
                    if (is_string($publicId) && $publicId !== '') {
                        $query->orWhere('user_ref', $publicId);
                    }
                })
                ->orderByRaw('CASE WHEN user_ref = ? THEN 0 ELSE 1 END', [$userRef])
                ->first();

            if ($existing) {
                $existing->forceFill([
                    'user_ref' => $userRef,
                    'owner_ref' => $ownerRef,
                    'role' => $role,
                    'branch_id' => $role === 'hospital_admin' ? null : $branch?->id,
                ])->save();

                Member::query()
                    ->where('organization_id', $organization->id)
                    ->where('id', '!=', $existing->id)
                    ->where(function ($query) use ($email, $publicId) {
                        $query->where('user_ref', $email);
                        if (is_string($publicId) && $publicId !== '') {
                            $query->orWhere('user_ref', $publicId);
                        }
                    })
                    ->delete();

                continue;
            }

            Member::query()->create([
                'organization_id' => $organization->id,
                'user_ref' => $userRef,
                'owner_ref' => $ownerRef,
                'role' => $role,
                'branch_id' => $role === 'hospital_admin' ? null : $branch?->id,
            ]);
        }
    }

    /**
     * @param  list<Branch>  $branches
     * @param  array<string, mixed>  $volumes
     * @return array<int, list<Department>> keyed by branch id
     */
    private function seedDepartments(array $branches, string $ownerRef, array $volumes): array
    {
        $catalog = self::CORE_DEPARTMENTS;
        if ($volumes['paid_modules']) {
            $catalog = array_merge($catalog, self::PAID_DEPARTMENTS);
        }

        $byBranch = [];
        foreach ($branches as $branch) {
            $byBranch[$branch->id] = [];
            $count = min($volumes['departments_per_branch'], count($catalog));
            for ($i = 0; $i < $count; $i++) {
                $spec = $catalog[$i];
                $byBranch[$branch->id][] = Department::withTrashed()->updateOrCreate(
                    [
                        'branch_id' => $branch->id,
                        'name' => $spec['name'],
                    ],
                    [
                        'owner_ref' => $ownerRef,
                        'type' => $spec['type'],
                        'is_active' => true,
                        'deleted_at' => null,
                    ],
                );
            }
        }

        return $byBranch;
    }

    /**
     * @return array<string, bool>
     */
    private function allSpecialtyFlagsEnabled(): array
    {
        $flags = [];
        foreach (array_keys(config('care.specialty_modules', [])) as $key) {
            $flags[$key] = true;
        }

        return $flags;
    }

    private function seedSpecialtyModules(Organization $organization, string $ownerRef): void
    {
        $organization = $organization->fresh();
        $queueEnabled = (bool) data_get($organization->settings, 'queue_integration_enabled');

        $service = app(SpecialtyModuleService::class);
        foreach (array_keys($service->catalog()) as $key) {
            try {
                $service->activate($organization->fresh(), $ownerRef, $key);
            } catch (\Throwable) {
                // Demo seed should not fail if activation hits an unexpected error.
            }
        }

        if ($queueEnabled) {
            try {
                app(CareQueueProvisioner::class)->provisionOrganization($organization->fresh());
            } catch (\Throwable) {
                // Demo seed continues; pages lazy-provision on use.
            }
        }
    }

    /**
     * @param  list<Branch>  $branches
     * @return array<int, list<Department>> keyed by branch id
     */
    private function departmentsByBranch(array $branches, string $ownerRef): array
    {
        $byBranch = [];
        foreach ($branches as $branch) {
            $byBranch[$branch->id] = Department::withTrashed()
                ->owned($ownerRef)
                ->where('branch_id', $branch->id)
                ->where('is_active', true)
                ->orderBy('id')
                ->get()
                ->all();
        }

        return $byBranch;
    }

    /**
     * Waiting / completed specialty appointments so each module page has demo traffic.
     *
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     * @param  list<Patient>  $patients
     */
    private function seedSpecialtyDemoData(
        Organization $organization,
        array $branches,
        array $practitioners,
        array $patients,
        string $ownerRef,
        string $plan = 'pro',
    ): int {
        if ($branches === [] || $patients === [] || $practitioners === []) {
            return 0;
        }

        $reasons = [
            'emergency' => ['Chest pain', 'Trauma review', 'Acute fever'],
            'blood_bank' => ['Cross-match request', 'Transfusion review', 'Blood product issue'],
            'dentistry' => ['Toothache', 'Dental cleaning', 'Extraction review'],
            'ophthalmology' => ['Blurry vision', 'Eye exam', 'Contact lens fitting'],
            'physiotherapy' => ['Back pain rehab', 'Post-injury physio', 'Mobility session'],
            'womens_health' => ['Antenatal visit', 'Pregnancy check', 'Postnatal review'],
            'radiology' => ['X-ray referral', 'Ultrasound', 'Imaging review'],
            'cardiology' => ['Chest pain review', 'ECG follow-up', 'Hypertension clinic'],
            'psychiatry' => ['Mental health review', 'Counselling session', 'Medication review'],
            'pediatrics' => ['Well-child visit', 'Fever in child', 'Growth check'],
            'orthopedics' => ['Fracture follow-up', 'Joint pain', 'Cast review'],
            'ent' => ['Ear infection', 'Sore throat', 'Sinus review'],
            'oncology' => ['Chemo day-care', 'Oncology review', 'Symptom management'],
            'renal' => ['Dialysis session', 'Nephrology review', 'Fluid assessment'],
            'surgery' => ['Pre-op assessment', 'Surgical review', 'Wound check'],
            'vaccination' => ['Routine immunization', 'Catch-up vaccine', 'Travel vaccine'],
            'pathology' => ['Biopsy review', 'Histology result', 'Cytology sample'],
            'infusion' => ['IV infusion', 'Iron infusion', 'Day-care therapy'],
            'dermatology' => ['Skin rash', 'Mole review', 'Dermatology procedure'],
            'podiatry' => ['Diabetic foot care', 'Nail procedure', 'Foot pain'],
            'fertility' => ['Fertility consult', 'Cycle review', 'IVF follow-up'],
            'child_welfare' => ['Growth monitoring', 'Well-child CWC', 'Nutrition follow-up'],
            'ambulance' => ['Chest pain response', 'Trauma pickup', 'Facility transfer'],
        ];

        $count = 0;
        $catalog = config('care.specialty_modules', []);
        /** @var array<int, int> $nextPositionByBranch */
        $nextPositionByBranch = [];
        /** @var array<string, Member|null> $moduleMembers */
        $moduleMembers = [];

        foreach ($catalog as $key => $definition) {
            $type = (string) ($definition['department_type'] ?? '');
            if ($type === '') {
                continue;
            }

            $moduleReasons = $reasons[$key] ?? ['Specialty visit'];
            $staffEmail = sprintf('demo-%s-%s@ladill.com', $plan, $key);
            // Resolve by email or SSO-remapped public_id (seedStaffMembers prefers public_id).
            $moduleMembers[$key] = $this->findStaffMemberByEmail($organization, $staffEmail);

            foreach ($branches as $branchIndex => $branch) {
                $department = Department::owned($ownerRef)
                    ->where('branch_id', $branch->id)
                    ->where('type', $type)
                    ->where('is_active', true)
                    ->first();
                if (! $department) {
                    continue;
                }

                $member = $moduleMembers[$key];
                $staff = $member ? \App\Support\DemoWorld::staffByEmail($staffEmail) : null;
                $homeBranchId = (int) ($member?->branch_id ?? 0);
                // One doctor = one branch. Only link the specialty desk on their home site.
                $assignMember = $member && $homeBranchId > 0 && (int) $branch->id === $homeBranchId;
                $stableRef = sprintf('demo-specialty-%s-%s', $key, $branch->id);
                $specialtyMeta = \App\Support\DemoWorld::SPECIALTY_DOCTORS[$key] ?? null;
                $doctorName = is_array($specialtyMeta)
                    ? (string) ($specialtyMeta['name'] ?? ('Dr. '.($definition['label'] ?? ucfirst($key))))
                    : (string) ($specialtyMeta ?? ('Dr. '.($definition['label'] ?? ucfirst($key))));
                if ($assignMember && is_array($staff) && filled($staff['name'] ?? null)) {
                    $practitionerName = (string) $staff['name'];
                } elseif ($assignMember) {
                    $practitionerName = $doctorName;
                } else {
                    // Cover desk for demo traffic at other sites — still a clinician, never a "Clinic" entity.
                    $practitionerName = $doctorName.' · '.$branch->name;
                }
                $practitioner = Practitioner::withTrashed()->updateOrCreate(
                    [
                        'organization_id' => $organization->id,
                        'user_ref' => $stableRef,
                    ],
                    [
                        'owner_ref' => $ownerRef,
                        'branch_id' => $branch->id,
                        'department_id' => $department->id,
                        'member_id' => $assignMember ? $member->id : null,
                        'name' => $practitionerName,
                        'specialty' => (string) ($definition['label'] ?? $key),
                        'room' => 'Bay '.(($branchIndex % 4) + 1),
                        'is_active' => true,
                        'deleted_at' => null,
                    ],
                );
                $practitioner->syncAssignedBranches([(int) $branch->id]);

                foreach ([Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN, Appointment::STATUS_COMPLETED] as $slot => $status) {
                    $patient = $patients[($branchIndex + $slot + $count) % count($patients)];
                    $scheduledAt = now()->subHours(($branchIndex * 3) + $slot + 1);
                    $token = "specialty|{$key}|{$branch->id}|{$slot}";

                    $visit = null;
                    if (in_array($status, [Appointment::STATUS_CHECKED_IN, Appointment::STATUS_WAITING, Appointment::STATUS_COMPLETED], true)) {
                        $visit = Visit::withTrashed()->updateOrCreate(
                            ['uuid' => $this->demoUuid("visit|{$ownerRef}|{$token}")],
                            [
                                'owner_ref' => $ownerRef,
                                'organization_id' => $organization->id,
                                'branch_id' => $branch->id,
                                'patient_id' => $patient->id,
                                'status' => $status === Appointment::STATUS_COMPLETED
                                    ? Visit::STATUS_COMPLETED
                                    : Visit::STATUS_IN_PROGRESS,
                                'checked_in_at' => $scheduledAt->copy()->addMinutes(5),
                                'completed_at' => $status === Appointment::STATUS_COMPLETED
                                    ? $scheduledAt->copy()->addHour()
                                    : null,
                                'checked_in_by' => $ownerRef,
                                'deleted_at' => null,
                            ],
                        );
                    }

                    $inQueue = in_array($status, [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN], true);
                    $queuePosition = null;
                    if ($inQueue) {
                        $nextPositionByBranch[$branch->id] = ($nextPositionByBranch[$branch->id] ?? 0) + 1;
                        $queuePosition = $nextPositionByBranch[$branch->id];
                    }

                    Appointment::withTrashed()->updateOrCreate(
                        ['uuid' => $this->demoUuid("appt|{$ownerRef}|{$token}")],
                        [
                            'owner_ref' => $ownerRef,
                            'organization_id' => $organization->id,
                            'branch_id' => $branch->id,
                            'patient_id' => $patient->id,
                            'practitioner_id' => $practitioner->id,
                            'department_id' => $department->id,
                            'visit_id' => $visit?->id,
                            'type' => Appointment::TYPE_SCHEDULED,
                            'status' => $status,
                            'scheduled_at' => $scheduledAt,
                            'checked_in_at' => $visit?->checked_in_at,
                            'waiting_at' => $inQueue ? $scheduledAt->copy()->addMinutes(5) : null,
                            'completed_at' => $status === Appointment::STATUS_COMPLETED
                                ? $scheduledAt->copy()->addHour()
                                : null,
                            'cancelled_at' => null,
                            'queue_position' => $queuePosition,
                            'reason' => $moduleReasons[$slot % count($moduleReasons)],
                            'created_by' => $ownerRef,
                            'deleted_at' => null,
                        ],
                    );
                    $count++;
                }
            }
        }

        $this->seedDentistryClinicalDemo($organization, $ownerRef);
        $this->seedBloodBankInventoryDemo($organization, $ownerRef);
        $this->seedOphthalmologyClinicalDemo($organization, $ownerRef);
        $this->seedPhysiotherapyClinicalDemo($organization, $ownerRef);
        $this->seedMaternityClinicalDemo($organization, $ownerRef);
        $this->seedRadiologyClinicalDemo($organization, $ownerRef);
        $this->seedCardiologyClinicalDemo($organization, $ownerRef);
        $this->seedPsychiatryClinicalDemo($organization, $ownerRef);
        $this->seedPediatricsClinicalDemo($organization, $ownerRef);
        $this->seedOrthopedicsClinicalDemo($organization, $ownerRef);
        $this->seedEntClinicalDemo($organization, $ownerRef);
        $this->seedOncologyClinicalDemo($organization, $ownerRef);
        $this->seedRenalClinicalDemo($organization, $ownerRef);
        $this->seedSurgeryClinicalDemo($organization, $ownerRef);
        $this->seedVaccinationClinicalDemo($organization, $ownerRef);
        $this->seedPathologyClinicalDemo($organization, $ownerRef);
        $this->seedInfusionClinicalDemo($organization, $ownerRef);
        $this->seedDermatologyClinicalDemo($organization, $ownerRef);
        $this->seedPodiatryClinicalDemo($organization, $ownerRef);
        $this->seedFertilityClinicalDemo($organization, $ownerRef);
        $this->seedChildWelfareClinicalDemo($organization, $ownerRef);
        $this->seedAmbulanceClinicalDemo($organization, $ownerRef);

        return $count;
    }

    /**
     * Visit-scoped facility stock for Blood Bank Inventory tab / reports.
     * Seeds inventory_note on each open BB visit so findForVisit shows stock without manual entry.
     */
    private function seedBloodBankInventoryDemo(Organization $organization, string $ownerRef): void
    {
        // Specialty departments are provisioned without organization_id; scope via branches.
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'blood_bank')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        /** @var array<int, int> $branchOrder */
        $branchOrder = [];

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $branchId = (int) $appointment->branch_id;
            if (! array_key_exists($branchId, $branchOrder)) {
                $branchOrder[$branchId] = count($branchOrder);
            }

            $clinical->upsert(
                $organization,
                $visit,
                'blood_bank',
                'inventory_note',
                $this->demoBloodBankInventoryPayload($branchOrder[$branchId]),
                $ownerRef,
                $ownerRef,
            );
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function demoBloodBankInventoryPayload(int $branchIndex): array
    {
        // First branch: low whole-blood + alert flag so Inventory / reports demo alerts.
        if ($branchIndex === 0) {
            return [
                'whole_blood_units' => 1,
                'packed_rbc_units' => 14,
                'platelet_units' => 7,
                'ffp_units' => 9,
                'cryo_units' => 4,
                'low_stock_alert' => true,
                'notes' => 'Demo morning stock count — whole blood critically low; replenishment requested.',
            ];
        }

        return [
            'whole_blood_units' => 8 + ($branchIndex * 2),
            'packed_rbc_units' => 18 + $branchIndex,
            'platelet_units' => 10 + $branchIndex,
            'ffp_units' => 12 + $branchIndex,
            'cryo_units' => 5,
            'low_stock_alert' => false,
            'notes' => 'Demo facility stock count for blood bank.',
        ];
    }

    /**
     * Seed refraction + eye exam (+ optional plan) on open ophthalmology visits.
     */
    private function seedOphthalmologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'ophthalmology')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $elevated = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'ophthalmology',
                'refraction',
                [
                    'va_od' => $elevated ? '6/60' : '6/9',
                    'va_os' => $elevated ? '6/36' : '6/6',
                    'va_od_near' => 'N8',
                    'va_os_near' => 'N6',
                    'sphere_od' => $elevated ? '-2.50' : '-0.75',
                    'cylinder_od' => '-0.50',
                    'axis_od' => '90',
                    'sphere_os' => $elevated ? '-1.75' : '-0.50',
                    'cylinder_os' => '-0.25',
                    'axis_os' => '85',
                    'add' => '+1.50',
                    'pd' => 62,
                    'notes' => 'Demo refraction for eye care suite.',
                ],
                $ownerRef,
                $ownerRef,
                'refraction',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'ophthalmology',
                'eye_exam',
                [
                    'chief_complaint' => $elevated ? 'Blurry vision and headache' : 'Routine eye exam',
                    'history' => $elevated
                        ? 'Gradual reduction in vision over 2 weeks; intermittent haloes.'
                        : 'Annual review; no acute symptoms.',
                    'va_od' => $elevated ? '6/60' : '6/9',
                    'va_os' => $elevated ? '6/36' : '6/6',
                    'iop_od' => $elevated ? 32 : 16,
                    'iop_os' => $elevated ? 28 : 15,
                    'pupils' => 'Equal, reactive',
                    'extraocular' => 'Full',
                    'anterior_segment' => $elevated ? 'Quiet; possible early cataract OD' : 'Quiet',
                    'fundus' => $elevated ? 'Cup:disc elevated OD; review for glaucoma' : 'Healthy discs',
                    'findings' => $elevated
                        ? 'Elevated IOP with reduced acuity — urgent glaucoma work-up.'
                        : 'Normal examination; refraction updated.',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($elevated) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'ophthalmology',
                    'eye_plan',
                    [
                        'diagnosis' => 'Suspected primary open-angle glaucoma',
                        'laterality' => 'OU',
                        'plan' => 'Start topical IOP-lowering drops; arrange OCT and visual fields.',
                        'medications' => 'Latanoprost 0.005% OU nocte',
                        'procedure_planned' => false,
                        'follow_up' => '2 weeks',
                        'advice' => 'Return sooner if vision worsens or severe eye pain.',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $elevated ? 'plan' : 'exam']);
            }

            $index++;
        }
    }


    /**
     * Seed assessment + plan (+ optional session) on open physiotherapy visits.
     */
    private function seedPhysiotherapyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'physiotherapy')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highPain = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'physiotherapy',
                'pt_assessment',
                [
                    'chief_complaint' => $highPain ? 'Severe low back pain after lifting' : 'Knee stiffness post-injury',
                    'onset' => $highPain ? 'Acute — 3 days ago' : 'Subacute — 3 weeks',
                    'pain_score' => $highPain ? 9 : 4,
                    'body_region' => $highPain ? 'Back / lumbar' : 'Knee',
                    'rom' => $highPain ? 'Lumbar flexion limited, guarded' : 'Knee flexion 90°, extension -5°',
                    'strength' => $highPain ? 'Core activation poor' : 'Quadriceps 4/5',
                    'special_tests' => $highPain ? 'SLR positive right' : 'McMurray negative',
                    'red_flags' => $highPain ? 'Night pain; no saddle anaesthesia' : '',
                    'goals' => $highPain ? 'Reduce pain and restore safe lifting' : 'Restore full ROM and return to sport',
                ],
                $ownerRef,
                $ownerRef,
                'assessment',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'physiotherapy',
                'pt_plan',
                [
                    'diagnosis' => $highPain ? 'Acute lumbar strain' : 'Post-traumatic knee stiffness',
                    'frequency' => '2–3× / week',
                    'duration_weeks' => $highPain ? 4 : 6,
                    'interventions' => $highPain
                        ? 'Manual therapy, core activation, graded activity'
                        : 'ROM drills, strengthening, proprioception',
                    'precautions' => $highPain ? 'Avoid heavy flexion under load' : 'No pivoting until week 4',
                    'goals' => 'Pain <3/10 and functional mobility',
                    'outcome_measures' => 'VAS, Oswestry / LEFS',
                ],
                $ownerRef,
                $ownerRef,
                'treatment_plan',
            );

            if ($highPain) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'physiotherapy',
                    'pt_session',
                    [
                        'session_number' => 1,
                        'modalities' => 'Heat, soft-tissue mobilisation',
                        'exercises' => 'Pelvic tilts, bird-dog, walking',
                        'pain_before' => 9,
                        'pain_after' => 6,
                        'response' => 'Tolerated well; pain eased post-session',
                        'outcome' => 'Completed — continue plan',
                        'next_review' => '2 days',
                        'notes' => 'Demo physio session.',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'session',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highPain ? 'session' : 'treatment_plan']);
            }

            $index++;
        }
    }

    /**
     * Seed ANC history + exam (+ optional plan) on open maternity visits.
     */
    private function seedMaternityClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'womens_health')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'womens_health',
                'anc_history',
                [
                    'gravida' => $highRisk ? 3 : 1,
                    'para' => $highRisk ? 1 : 0,
                    'lmp' => '12 Jan 2026',
                    'edd' => '19 Oct 2026',
                    'gestational_age_weeks' => $highRisk ? 34 : 22,
                    'anc_visit_number' => $highRisk ? 6 : 3,
                    'obstetric_history' => $highRisk ? 'Previous CS; one miscarriage' : 'Primigravida',
                    'medical_history' => $highRisk ? 'Hypertension in pregnancy' : 'None significant',
                    'allergies' => 'None known',
                    'medications' => 'IFAS, calcium',
                    'social_history' => 'Lives with partner; support available',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'womens_health',
                'obstetric_exam',
                [
                    'bp' => $highRisk ? '158/102' : '118/74',
                    'pulse' => $highRisk ? 92 : 78,
                    'weight_kg' => 72,
                    'fundal_height' => $highRisk ? 33 : 22,
                    'presentation' => 'Cephalic',
                    'lie' => 'Longitudinal',
                    'engagement' => $highRisk ? '3/5' : 'Free',
                    'oedema' => $highRisk ? 'Moderate' : 'None',
                    'urine' => $highRisk ? 'Protein +1' : 'NAD',
                    'danger_signs' => $highRisk ? 'Headache; visual spots' : '',
                    'findings' => $highRisk
                        ? 'Hypertension with oedema — escalate high-risk ANC'
                        : 'Normal ANC examination',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'womens_health',
                'fetal_notes',
                [
                    'fetal_heart_rate' => $highRisk ? 168 : 142,
                    'fetal_movements' => $highRisk ? 'Reduced' : 'Normal',
                    'presentation' => 'Cephalic',
                    'liquor' => 'Adequate clinically',
                    'ctg_summary' => $highRisk ? 'Baseline raised; observe' : 'Not indicated',
                    'notes' => 'Demo fetal notes for maternity suite.',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'womens_health',
                    'mat_plan',
                    [
                        'risk_category' => 'High risk',
                        'plan' => 'Urgent senior review; BP control; fetal monitoring; consider admission.',
                        'medications' => 'Continue antihypertensives as prescribed',
                        'education' => 'Return immediately if headache, bleeding, or reduced movements',
                        'postnatal_needed' => false,
                        'follow_up' => 'Same day review',
                        'referral' => 'Obstetric high-risk clinic',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedRadiologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'radiology')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $urgent = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'radiology',
                'imaging_request',
                [
                    'modality' => $urgent ? 'CT' : 'X-ray',
                    'body_part' => $urgent ? 'Chest' : 'Left ankle',
                    'clinical_info' => $urgent
                        ? 'Shortness of breath; rule out PE / infiltrates'
                        : 'Trauma — query fracture after fall',
                    'urgency' => $urgent ? 'STAT' : 'Routine',
                    'contrast' => $urgent ? 'IV' : 'None',
                    'protocol' => $urgent ? 'CTPA protocol' : 'AP / lateral',
                    'referrer' => 'Demo ER / Ortho',
                ],
                $ownerRef,
                $ownerRef,
                'protocol',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'radiology',
                'imaging_acquisition',
                [
                    'technologist' => 'Demo Tech',
                    'equipment' => $urgent ? 'CT 1' : 'XR Bay A',
                    'acquisition_status' => 'Acquired',
                    'contrast_given' => $urgent,
                    'dose_notes' => $urgent ? 'Standard CTPA dose' : 'Low dose extremity',
                    'quality' => 'Diagnostic',
                    'notes' => 'Demo acquisition for radiology suite.',
                ],
                $ownerRef,
                $ownerRef,
                'acquisition',
            );

            if ($urgent) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'radiology',
                    'imaging_report',
                    [
                        'technique' => 'CT pulmonary angiogram with IV contrast',
                        'comparison' => 'None available',
                        'findings' => 'No filling defect in main or segmental pulmonary arteries. Mild basilar atelectasis.',
                        'impression' => 'No CT evidence of pulmonary embolism.',
                        'recommendations' => 'Clinical correlation; consider alternative causes of dyspnoea.',
                        'critical_result' => false,
                    ],
                    $ownerRef,
                    $ownerRef,
                    'reporting',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $urgent ? 'reporting' : 'acquisition']);
            }

            $index++;
        }
    }

    private function seedCardiologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'cardiology')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'cardiology',
                'cardio_history',
                [
                    'history' => $highRisk
                        ? 'Progressive dyspnoea and orthopnoea for 2 weeks'
                        : 'Palpitations intermittent for 1 month',
                    'past_cardiac' => $highRisk ? 'Prior MI 2019' : 'None',
                    'medications' => $highRisk ? 'Aspirin, atorvastatin, bisoprolol' : 'None',
                    'allergies' => 'NKDA',
                    'risk_factors' => $highRisk ? 'Hypertension, diabetes, smoking history' : 'Family history of AF',
                    'family_history' => $highRisk ? 'Father CAD' : 'Mother AF',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'cardiology',
                'cardiac_exam',
                [
                    'chief_complaint' => $highRisk ? 'Dyspnoea / heart failure symptoms' : 'Palpitations',
                    'nyha_class' => $highRisk ? 'III' : 'I',
                    'bp' => $highRisk ? '168/98' : '124/78',
                    'hr' => $highRisk ? 104 : 72,
                    'heart_sounds' => $highRisk ? 'S3; soft pansystolic murmur' : 'Normal S1 S2; no murmur',
                    'ecg_findings' => $highRisk
                        ? 'Sinus tach; Q waves inferiorly; no acute STEMI'
                        : 'Sinus rhythm; occasional PACs',
                    'exam_findings' => $highRisk ? 'Bilateral basal crackles; mild ankle oedema' : 'CVD exam normal',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'cardiology',
                    'cardio_plan',
                    [
                        'diagnosis' => 'Decompensated heart failure (NYHA III)',
                        'plan' => 'Diuresis; echo; review meds; admit if not improving',
                        'medications' => 'Increase loop diuretic; continue GDMT',
                        'procedure_planned' => false,
                        'follow_up' => '48-hour review or sooner if worse',
                        'advice' => 'Daily weights; fluid restriction counselling',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedPsychiatryClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'psychiatry')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'psychiatry',
                'mental_status',
                [
                    'chief_complaint' => $highRisk ? 'Low mood with suicidal ideation' : 'Anxiety and poor sleep',
                    'history' => $highRisk
                        ? '2-week worsening depression; sleep and appetite reduced'
                        : 'Generalised worry for 3 months; work stress',
                    'appearance' => $highRisk ? 'Dishevelled; limited eye contact' : 'Well kempt; cooperative',
                    'mood_affect' => $highRisk ? 'Depressed / flat' : 'Anxious / congruent',
                    'thought' => $highRisk ? 'Hopeless themes; no psychosis' : 'Worried ruminations; no delusions',
                    'perception' => 'No hallucinations',
                    'cognition' => 'Oriented ×3',
                    'insight' => $highRisk ? 'Partial' : 'Good',
                    'mse_summary' => $highRisk
                        ? 'Depressed MSE with active suicidal ideation — high vigilance'
                        : 'Anxious MSE without psychosis or self-harm intent',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'psychiatry',
                'risk_assessment',
                [
                    'overall_risk' => $highRisk ? 'High' : 'Low',
                    'self_harm' => $highRisk ? 'Ideation' : 'None identified',
                    'harm_others' => 'None identified',
                    'safeguarding' => $highRisk ? 'Lives alone; limited support tonight' : 'Supportive partner',
                    'protective_factors' => $highRisk ? 'Engaged with clinic; no prior attempts' : 'Good insight; coping strategies',
                    'risk_summary' => $highRisk
                        ? 'High risk of self-harm — safety planning and close follow-up required'
                        : 'Low risk; outpatient management appropriate',
                    'immediate_actions' => $highRisk
                        ? 'Safety plan; remove means; same-day senior review'
                        : 'Psychoeducation; follow-up in clinic',
                ],
                $ownerRef,
                $ownerRef,
                'risk',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'psychiatry',
                    'psych_plan',
                    [
                        'formulation' => 'Major depressive episode with high self-harm risk in context of isolation',
                        'diagnosis' => 'Major depressive disorder — severe',
                        'plan' => 'Crisis safety plan; consider short admission vs intensive outpatient; start/adjust antidepressant',
                        'medications' => 'Review current AD; discuss options',
                        'therapy' => 'CBT referral when risk settles',
                        'follow_up' => '24–48 hours or sooner if crisis',
                        'crisis_plan' => 'Emergency contacts; crisis line; return to ED if ideation intensifies',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'formulation',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'formulation' : 'risk']);
            }

            $index++;
        }
    }

    private function seedPediatricsClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'pediatrics')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'pediatrics',
                'ped_history',
                [
                    'history' => $highRisk
                        ? 'Fever and poor feeding for 3 days; reduced urine output'
                        : 'Mild cough for 1 week; eating well',
                    'birth_history' => $highRisk ? 'Term; birth weight 2.8 kg' : 'Term; uneventful',
                    'past_medical' => $highRisk ? 'Prior admission for pneumonia' : 'None',
                    'medications' => 'None',
                    'allergies' => 'NKDA',
                    'feeding' => $highRisk ? 'Breast + formula; intake reduced' : 'Age-appropriate diet',
                    'social' => 'Lives with parents',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'pediatrics',
                'pediatric_exam',
                [
                    'chief_complaint' => $highRisk ? 'Fever / possible sepsis' : 'Cough review',
                    'age_months' => $highRisk ? 18 : 48,
                    'weight_kg' => $highRisk ? 8.2 : 16.5,
                    'height_cm' => $highRisk ? 76 : 102,
                    'head_circumference_cm' => $highRisk ? 45 : null,
                    'temperature_c' => $highRisk ? 39.2 : 36.8,
                    'growth_concern' => $highRisk ? 'Underweight' : 'None',
                    'developmental' => $highRisk ? 'Walks; few words' : 'Age-appropriate',
                    'immunization_status' => $highRisk ? 'Up to date except measles' : 'Up to date',
                    'examination' => $highRisk
                        ? 'Irritable; mild dehydration; clear chest; no rash'
                        : 'Well; mild rhinitis; chest clear',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'pediatrics',
                    'ped_plan',
                    [
                        'diagnosis' => 'Febrile illness — rule out serious bacterial infection',
                        'plan' => 'Labs; oral fluids; antipyretic; review same day if worse',
                        'medications' => 'Paracetamol PRN',
                        'treatment_planned' => true,
                        'follow_up' => '24 hours or sooner if red flags',
                        'advice' => 'Hydration; fever advice; return if lethargy or poor feeding',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedOrthopedicsClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'orthopedics')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'orthopedics',
                'ortho_history',
                [
                    'history' => $highRisk
                        ? 'Fall from height; right wrist pain and deformity'
                        : 'Left knee pain after football twist',
                    'mechanism' => $highRisk ? 'FOOSH from ladder' : 'Twisting injury',
                    'past_ortho' => $highRisk ? 'Prior ankle sprain' : 'None',
                    'medications' => 'None',
                    'allergies' => 'NKDA',
                    'comorbidities' => $highRisk ? 'Diabetes' : 'None',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'orthopedics',
                'ortho_exam',
                [
                    'chief_complaint' => $highRisk ? 'Wrist deformity after fall' : 'Knee pain',
                    'side' => $highRisk ? 'Right' : 'Left',
                    'region' => $highRisk ? 'Distal radius' : 'Knee',
                    'rom' => $highRisk ? 'Limited wrist flexion/extension' : 'Full ROM; pain on twist',
                    'neurovascular' => $highRisk ? 'Sensory deficit' : 'Intact',
                    'swelling' => $highRisk ? 'Dinner-fork deformity' : 'Mild effusion',
                    'exam_findings' => $highRisk ? 'Closed injury; soft compartments' : 'McMurray negative; stable ligaments',
                    'fracture_classification' => $highRisk ? 'Colles-type distal radius' : 'Soft tissue strain',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'orthopedics',
                    'ortho_imaging',
                    [
                        'modality' => 'X-ray',
                        'region' => 'Right wrist',
                        'findings' => 'Displaced distal radius fracture; ulnar styloid intact',
                        'impression' => 'Extra-articular distal radius fracture',
                        'notes' => 'AP and lateral views',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'imaging',
                );

                $clinical->upsert(
                    $organization,
                    $visit,
                    'orthopedics',
                    'ortho_plan',
                    [
                        'diagnosis' => 'Displaced distal radius fracture',
                        'plan' => 'Closed reduction and cast; elevate; analgesia',
                        'weight_bearing' => 'N/A — upper limb',
                        'procedure_planned' => true,
                        'follow_up' => '1 week with cast check / x-ray',
                        'advice' => 'Elevation; watch for numbness or colour change',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedEntClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'ent')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'ent',
                'ent_history',
                [
                    'history' => $highRisk
                        ? 'Progressive sore throat and muffled voice; difficulty swallowing'
                        : 'Recurrent ear discharge for 2 months',
                    'ear_symptoms' => $highRisk ? 'None' : 'Left otorrhoea; mild hearing loss',
                    'nose_symptoms' => 'None',
                    'throat_symptoms' => $highRisk ? 'Severe odynophagia; drooling' : 'None',
                    'past_ent' => $highRisk ? 'Prior tonsillitis' : 'Recurrent otitis media',
                    'medications' => $highRisk ? 'None recent' : 'Topical drops PRN',
                    'allergies' => 'NKDA',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'ent',
                'ent_exam',
                [
                    'chief_complaint' => $highRisk ? 'Sore throat / possible peritonsillar abscess' : 'Chronic ear discharge',
                    'ear_findings' => $highRisk ? 'Normal TMs' : 'Left canal moist; TM perforated',
                    'nose_findings' => 'Normal',
                    'throat_findings' => $highRisk
                        ? 'Trismus; unilateral tonsillar bulge; uvula deviation'
                        : 'Oropharynx normal',
                    'hearing' => $highRisk ? 'Grossly normal' : 'Reduced left whispered voice',
                    'neck' => $highRisk ? 'Tender cervical nodes' : 'No lymphadenopathy',
                    'airway_concern' => $highRisk,
                    'exam_summary' => $highRisk
                        ? 'Findings consistent with peritonsillar abscess — airway vigilance'
                        : 'Chronic otitis media with perforation',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'ent',
                    'ent_plan',
                    [
                        'diagnosis' => 'Suspected peritonsillar abscess',
                        'plan' => 'IV antibiotics; needle aspiration / I&D; airway monitoring',
                        'medications' => 'IV antibiotics per protocol; analgesia',
                        'procedure_planned' => true,
                        'follow_up' => 'Inpatient review post-drainage',
                        'advice' => 'NPO until airway safe; urgent escalation if stridor',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }


    private function seedOncologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'oncology')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $highRisk = $index === 0;
            $clinical->upsert($organization, $visit, 'oncology', 'onc_history', [
                'history' => $highRisk
                    ? 'New diagnosis work-up; progressive fatigue and weight loss'
                    : 'On adjuvant regimen; here for cycle review',
                'cancer_history' => $highRisk ? 'Breast mass biopsied last month' : 'Stage II breast Ca — surgery 4 months ago',
                'medications' => $highRisk ? 'Analgesia PRN' : 'Current chemo cycle medications',
                'allergies' => 'NKDA',
                'comorbidities' => $highRisk ? 'Hypertension' : 'None significant',
                'social' => 'Family support available',
            ], $ownerRef, $ownerRef, 'history');
            $clinical->upsert($organization, $visit, 'oncology', 'onc_staging', [
                'diagnosis' => $highRisk ? 'Suspected metastatic breast carcinoma' : 'Breast carcinoma — adjuvant',
                'stage' => $highRisk ? 'cT4 N2 M1' : 'pT2 N1 M0',
                'histology' => 'Invasive ductal carcinoma',
                'performance_status' => $highRisk ? '3' : '1',
                'exam_findings' => $highRisk ? 'Cachexia; axillary nodes; hepatic tenderness' : 'Well; surgical scar healed',
                'sites' => $highRisk ? 'Breast, axilla, liver' : 'None residual known',
            ], $ownerRef, $ownerRef, 'staging');
            if ($highRisk) {
                $clinical->upsert($organization, $visit, 'oncology', 'onc_plan', [
                    'diagnosis' => 'Metastatic breast carcinoma',
                    'plan' => 'Staging imaging; MDT; supportive care; consider palliative systemic therapy',
                    'intent' => 'Palliative',
                    'medications' => 'Analgesia; antiemetics PRN',
                    'treatment_planned' => true,
                    'follow_up' => 'Chemo day-care after MDT',
                    'advice' => 'Return if fever or uncontrolled pain',
                ], $ownerRef, $ownerRef, 'plan');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'staging']);
            }
            $index++;
        }
    }

    private function seedRenalClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'renal')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $highRisk = $index === 0;
            $clinical->upsert($organization, $visit, 'renal', 'renal_history', [
                'history' => $highRisk
                    ? 'Missed last dialysis; progressive dyspnoea and oedema'
                    : 'Routine thrice-weekly haemodialysis review',
                'ckd_stage' => $highRisk ? 'CKD 5D — 2 years on HD' : 'CKD 5D — 8 months on HD',
                'access_history' => 'Left AV fistula',
                'medications' => 'EPO; phosphate binders; antihypertensives',
                'allergies' => 'NKDA',
                'comorbidities' => $highRisk ? 'Diabetes; heart failure' : 'Hypertension',
            ], $ownerRef, $ownerRef, 'history');
            $clinical->upsert($organization, $visit, 'renal', 'renal_exam', [
                'chief_complaint' => $highRisk ? 'Fluid overload / missed dialysis' : 'Routine dialysis session',
                'bp' => $highRisk ? '168/98' : '132/78',
                'weight_kg' => $highRisk ? 78.5 : 65.2,
                'fluid_status' => $highRisk ? 'Overload' : 'Euvolemic',
                'access' => 'Left AVF thrills present',
                'exam_findings' => $highRisk ? 'Bilateral basal crackles; sacral oedema' : 'Chest clear; no oedema',
                'uremic_symptoms' => $highRisk ? 'Nausea; pruritus' : 'None',
            ], $ownerRef, $ownerRef, 'exam');
            if ($highRisk) {
                $clinical->upsert($organization, $visit, 'renal', 'renal_session', [
                    'session_type' => 'Hemodialysis',
                    'pre_weight_kg' => 78.5,
                    'uf_goal_l' => 3.5,
                    'access' => 'Left AVF',
                    'bp_pre' => '168/98',
                    'labs_summary' => 'K 5.8; Cr elevated; Hb 9.1',
                    'complications' => 'Hypotension mid-session anticipated',
                    'notes' => 'Urgent HD for fluid overload after missed session',
                ], $ownerRef, $ownerRef, 'dialysis');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'dialysis' : 'exam']);
            }
            $index++;
        }
    }

    private function seedSurgeryClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'surgery')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $highRisk = $index === 0;
            $clinical->upsert($organization, $visit, 'surgery', 'surg_history', [
                'history' => $highRisk
                    ? 'Right inguinal hernia with intermittent pain; electing repair'
                    : 'Post-appendicectomy wound check',
                'past_surgical' => $highRisk ? 'None' : 'Laparoscopic appendicectomy 10 days ago',
                'medications' => $highRisk ? 'None' : 'Analgesia; antibiotics completed',
                'allergies' => 'NKDA',
                'comorbidities' => $highRisk ? 'Well-controlled diabetes' : 'None',
                'anaesthesia_history' => 'No prior issues',
            ], $ownerRef, $ownerRef, 'history');
            $clinical->upsert($organization, $visit, 'surgery', 'surg_exam', [
                'chief_complaint' => $highRisk ? 'Inguinal hernia — pre-op' : 'Wound review',
                'exam_findings' => $highRisk
                    ? 'Right reducible inguinal hernia; no strangulation'
                    : 'Wound clean; mild erythema; no discharge',
                'asa_class' => $highRisk ? 'II' : 'I',
                'fitness' => 'Fit for day-case anaesthesia',
                'site_marking' => $highRisk,
            ], $ownerRef, $ownerRef, 'exam');
            if ($highRisk) {
                $clinical->upsert($organization, $visit, 'surgery', 'surg_plan', [
                    'diagnosis' => 'Right inguinal hernia',
                    'proposed_procedure' => 'Open mesh hernia repair',
                    'plan' => 'Day-case repair; consent discussed including mesh risks',
                    'consent_obtained' => true,
                    'procedure_planned' => true,
                    'follow_up' => 'Wound check 7–10 days',
                    'advice' => 'NPO from midnight; bring meds list',
                ], $ownerRef, $ownerRef, 'plan');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }
            $index++;
        }
    }

    private function seedVaccinationClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'vaccination')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $alertCase = $index === 0;
            $clinical->upsert($organization, $visit, 'vaccination', 'vax_eligibility', [
                'vaccine' => $alertCase ? 'Yellow fever' : 'Tetanus toxoid',
                'schedule' => $alertCase ? 'Travel' : 'Routine EPI',
                'prior_doses' => $alertCase ? 'None documented' : 'Primary series complete',
                'indications' => $alertCase ? 'Travel certificate required' : 'Antenatal TT booster',
                'contraindications' => true,
                'allergy_history' => 'NKDA',
                'fever_today' => $alertCase,
                'pregnancy' => ! $alertCase,
                'cleared' => ! $alertCase,
            ], $ownerRef, $ownerRef, 'eligibility');
            $clinical->upsert($organization, $visit, 'vaccination', 'vax_consent', [
                'information_given' => true,
                'consent_obtained' => ! $alertCase,
                'consent_type' => 'Verbal',
                'consented_by' => 'Patient',
                'risks_discussed' => true,
                'notes' => $alertCase ? 'Defer pending fever resolution' : 'Consent for TT booster',
            ], $ownerRef, $ownerRef, $alertCase ? 'eligibility' : 'consent');
            if (! $alertCase) {
                $clinical->upsert($organization, $visit, 'vaccination', 'vax_admin', [
                    'vaccine' => 'Tetanus toxoid',
                    'dose' => '0.5 mL',
                    'site' => 'Left arm',
                    'batch' => 'TT-DEMO-'.($index + 1),
                    'expiry' => now()->addYear()->toDateString(),
                    'route' => 'IM',
                    'administered_by' => 'Demo nurse',
                    'adverse_events' => 'None immediate',
                    'next_due' => now()->addMonths(6)->toDateString(),
                    'outcome' => 'Completed — observe',
                ], $ownerRef, $ownerRef, 'administer');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $alertCase ? 'eligibility' : 'administer']);
            }
            $index++;
        }
    }

    private function seedPathologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'pathology')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $urgent = $index === 0;
            $clinical->upsert($organization, $visit, 'pathology', 'path_request', [
                'specimen_type' => $urgent ? 'Breast core biopsy' : 'Cervical cytology',
                'site' => $urgent ? 'Left breast' : 'Cervix',
                'clinical_history' => $urgent
                    ? 'Palpable mass; BIRADS 4; rule out malignancy'
                    : 'Routine Pap smear; previous NILM',
                'urgency' => $urgent ? 'Urgent' : 'Routine',
                'tests_requested' => $urgent ? 'Histo + IHC if indicated' : 'Pap smear',
                'requesting_clinician' => 'Demo clinician',
            ], $ownerRef, $ownerRef, 'request');
            $clinical->upsert($organization, $visit, 'pathology', 'path_specimen', [
                'received_at' => now()->subHours(2)->format('Y-m-d H:i'),
                'container_label' => 'PATH-DEMO-'.($index + 1),
                'adequacy' => $urgent ? 'Adequate' : 'Adequate',
                'fixative' => $urgent ? '10% formalin' : 'Liquid-based cytology',
                'notes' => 'Received sealed and labelled',
            ], $ownerRef, $ownerRef, 'specimen');
            if ($urgent) {
                $clinical->upsert($organization, $visit, 'pathology', 'path_processing', [
                    'gross' => 'Two cores, 1.2 cm aggregate',
                    'blocks' => 'A1–A2',
                    'stains' => 'H&E',
                    'status' => 'Ready for reporting',
                    'notes' => 'Priority processing',
                ], $ownerRef, $ownerRef, 'processing');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $urgent ? 'processing' : 'specimen']);
            }
            $index++;
        }
    }

    private function seedInfusionClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');
        if ($branchIds->isEmpty()) {
            return;
        }
        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'infusion')
            ->where('is_active', true)
            ->pluck('id');
        if ($departmentIds->isEmpty()) {
            return;
        }
        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();
        if ($appointments->isEmpty()) {
            return;
        }
        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;
        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }
            $notFit = $index === 0;
            $clinical->upsert($organization, $visit, 'infusion', 'infusion_assessment', [
                'indication' => $notFit ? 'IV iron — symptomatic anaemia' : 'IV antibiotics — cellulitis',
                'history' => $notFit
                    ? 'Hb 7.8; prior infusion reaction reported'
                    : 'Day 3 of outpatient IV ceftriaxone',
                'allergies' => $notFit ? 'Possible iron sucrose reaction' : 'NKDA',
                'baseline_vitals' => $notFit ? 'BP 88/54; HR 112' : 'BP 118/74; HR 78',
                'access_plan' => 'Peripheral IV',
                'fit_for_infusion' => ! $notFit,
            ], $ownerRef, $ownerRef, 'assessment');
            if (! $notFit) {
                $clinical->upsert($organization, $visit, 'infusion', 'infusion_protocol', [
                    'medication' => 'Ceftriaxone',
                    'dose' => '2 g',
                    'diluent' => '100 mL NS',
                    'rate' => '30 minutes',
                    'duration' => '30 min',
                    'premeds' => 'None',
                    'ordered_by' => 'Demo clinician',
                ], $ownerRef, $ownerRef, 'protocol');
                $clinical->upsert($organization, $visit, 'infusion', 'infusion_session', [
                    'medication' => 'Ceftriaxone',
                    'dose' => '2 g',
                    'rate' => '30 min',
                    'access' => 'Left forearm PIV',
                    'start_time' => now()->subMinutes(20)->format('H:i'),
                    'end_time' => '',
                    'vitals' => 'Stable mid-infusion',
                    'reactions' => 'None',
                    'notes' => 'Infusing without incident',
                    'outcome' => 'In progress',
                ], $ownerRef, $ownerRef, 'session');
            }
            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $notFit ? 'assessment' : 'session']);
            }
            $index++;
        }
    }

    private function seedDermatologyClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'dermatology')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $urgent = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'dermatology',
                'derm_history',
                [
                    'history' => $urgent
                        ? 'Changing pigmented lesion on forearm noticed by partner'
                        : 'Itchy rash on elbows for 6 weeks',
                    'past_skin' => $urgent ? 'Prior atypical naevus excised 2018' : 'Childhood eczema',
                    'medications' => $urgent ? 'None' : 'Emollients PRN',
                    'allergies' => 'NKDA',
                    'triggers' => $urgent ? 'Sun exposure' : 'Dry weather; detergents',
                    'family_history' => $urgent ? 'Mother melanoma' : 'None relevant',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'dermatology',
                'skin_exam',
                [
                    'chief_complaint' => $urgent ? 'Changing mole' : 'Elbow rash',
                    'urgency' => $urgent ? 'Suspected malignancy' : 'Routine',
                    'duration' => $urgent ? '3 months' : '6 weeks',
                    'distribution' => $urgent ? 'Left forearm' : 'Bilateral elbows',
                    'morphology' => $urgent
                        ? 'Asymmetric pigmented plaque with irregular border'
                        : 'Erythematous plaques with silvery scale',
                    'itch_pain' => $urgent ? 'None' : 'Moderate itch',
                    'dermoscopy' => $urgent ? 'Atypical pigment network' : 'Regular scale pattern',
                    'differential' => $urgent ? 'Melanoma vs atypical naevus' : 'Psoriasis vs eczema',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($urgent) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'dermatology',
                    'derm_plan',
                    [
                        'diagnosis' => 'Suspicious pigmented lesion — rule out melanoma',
                        'plan' => 'Excision biopsy; sun protection counselling',
                        'medications' => 'None pending histology',
                        'procedure_planned' => true,
                        'follow_up' => 'Histology results in 10–14 days',
                        'advice' => 'Avoid sunburn; photograph lesion site',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $urgent ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedPodiatryClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'podiatry')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $highRisk = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'podiatry',
                'pod_history',
                [
                    'history' => $highRisk
                        ? 'Non-healing plantar ulcer for 3 weeks'
                        : 'Ingrown toenail with intermittent pain',
                    'diabetes_history' => $highRisk ? 'Type 2 DM 12 years; peripheral neuropathy' : 'No diabetes',
                    'medications' => $highRisk ? 'Metformin, insulin' : 'None',
                    'allergies' => 'NKDA',
                    'footwear' => $highRisk ? 'Ill-fitting sandals' : 'Closed shoes at work',
                    'prior_ulcers' => $highRisk ? 'Prior healed ulcer right foot 2022' : 'None',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'podiatry',
                'foot_exam',
                [
                    'chief_complaint' => $highRisk ? 'Plantar ulcer' : 'Ingrown toenail',
                    'side' => $highRisk ? 'Right' : 'Left',
                    'diabetes' => $highRisk ? 'Active ulcer' : 'N/A',
                    'pulses' => $highRisk ? 'Dorsalis pedis weak' : 'Present bilaterally',
                    'sensation' => $highRisk ? 'Reduced monofilament' : 'Intact',
                    'skin_nails' => $highRisk ? 'Callus surrounding ulcer' : 'Medial nail fold inflamed',
                    'ulcer' => $highRisk ? '2 cm plantar ulcer, clean base' : '',
                    'offloading' => $highRisk ? 'Felt pad interim' : 'Wide toe box advised',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($highRisk) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'podiatry',
                    'pod_plan',
                    [
                        'diagnosis' => 'Diabetic foot ulcer — high risk',
                        'plan' => 'Debridement; offloading; wound care; vascular review if needed',
                        'offloading' => 'Total contact cast or removable boot',
                        'medications' => 'Dressings; analgesia as needed',
                        'procedure_planned' => true,
                        'follow_up' => '1 week wound clinic',
                        'advice' => 'Daily foot check; avoid barefoot walking',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $highRisk ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedFertilityClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'fertility')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $priority = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'fertility',
                'fert_history',
                [
                    'history' => $priority
                        ? 'Trying to conceive 28 months; age 38; prior miscarriage'
                        : 'Trying to conceive 14 months; regular cycles',
                    'trying_months' => $priority ? 28 : 14,
                    'obstetric_history' => $priority ? 'G1P0; miscarriage at 8 weeks 2024' : 'G0P0; cycles 28–30 days',
                    'partner_notes' => $priority ? 'Partner semen analysis pending' : 'Partner well; no known issues',
                    'medications' => 'Folic acid',
                    'allergies' => 'NKDA',
                ],
                $ownerRef,
                $ownerRef,
                'history',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'fertility',
                'fert_exam',
                [
                    'chief_complaint' => $priority ? 'Primary infertility — time-sensitive' : 'Fertility workup',
                    'priority' => $priority ? 'Time-sensitive' : 'Routine',
                    'partner_present' => true,
                    'cycle_day' => $priority ? 3 : 10,
                    'bmi' => $priority ? 27 : 23,
                    'exam_findings' => 'Pelvic exam unremarkable; uterus anteverted',
                    'notes' => $priority ? 'Discussed AMH and early referral pathway' : 'Baseline counselling given',
                ],
                $ownerRef,
                $ownerRef,
                'exam',
            );

            if ($priority) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'fertility',
                    'fert_plan',
                    [
                        'diagnosis' => 'Primary infertility — age-related priority',
                        'plan' => 'Complete AMH/hormones; semen analysis; pelvic scan; consider IUI counselling',
                        'medications' => 'Continue folic acid; ovulation induction if indicated',
                        'cycle_planned' => true,
                        'follow_up' => 'Cycle day 3 labs; review with results',
                        'advice' => 'Timed intercourse education; lifestyle counselling',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $priority ? 'plan' : 'exam']);
            }

            $index++;
        }
    }

    private function seedChildWelfareClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'child_welfare')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $concern = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'child_welfare',
                'cwc_intake',
                [
                    'visit_reason' => $concern ? 'Growth monitoring' : 'Routine well-child',
                    'caregiver' => $concern ? 'Mother — Ama Mensah' : 'Father — Kojo Boateng',
                    'history' => $concern
                        ? 'Weight faltering over 2 months; reduced appetite; breast milk + porridge'
                        : 'Well since last CWC; no illness; immunizations on track',
                    'birth_history' => $concern ? 'Term SVD; birth weight 2.8 kg' : 'Term SVD; birth weight 3.2 kg',
                    'immunization_status' => $concern ? 'Catch-up needed' : 'Up to date',
                    'feeding' => $concern ? 'Breastfeeding + thin porridge twice daily' : 'Breastfeeding; complementary feeds started',
                    'social_context' => $concern ? 'Single caregiver; food insecurity reported' : 'Stable household',
                    'safeguarding_notes' => $concern ? 'Nutrition concern; monitor home situation — clinical record access only' : '',
                    'allergies' => 'NKDA',
                ],
                $ownerRef,
                $ownerRef,
                'intake',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'child_welfare',
                'cwc_assessment',
                [
                    'age_months' => $concern ? 18 : 9,
                    'weight_kg' => $concern ? 8.1 : 8.6,
                    'height_cm' => $concern ? 76 : 70,
                    'head_circumference_cm' => $concern ? 45 : 44,
                    'muac_cm' => $concern ? 11.2 : 13.5,
                    'growth_status' => $concern ? 'Underweight' : 'Normal',
                    'nutrition_risk' => $concern ? 'High' : 'Low',
                    'development' => $concern ? 'Walks with support; 3–4 words' : 'Sits well; babbling',
                    'exam_findings' => $concern
                        ? 'Thin; dry skin; no oedema; alert'
                        : 'Well nourished; soft fontanelle; clear chest',
                    'safeguarding_flag' => $concern ? 'Concern' : 'None',
                    'assessment_summary' => $concern
                        ? 'Underweight with high nutrition risk — counselling and close follow-up'
                        : 'Healthy well-child visit; continue routine CWC schedule',
                ],
                $ownerRef,
                $ownerRef,
                'assessment',
            );

            if ($concern) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'child_welfare',
                    'cwc_plan',
                    [
                        'goals' => 'Restore weight gain; improve dietary diversity; complete catch-up immunizations',
                        'interventions' => 'Nutrition counselling; RUTF if eligible; growth recheck',
                        'counseling' => 'Feeding frequency; hygiene; danger signs',
                        'referrals' => 'Consider nutrition clinic if no weight gain in 2 weeks',
                        'medications' => 'Vitamin A if due; deworming per schedule',
                        'follow_up' => '2 weeks CWC',
                        'advice' => 'Return sooner if fever, diarrhoea, or poor feeding',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'plan',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $concern ? 'plan' : 'assessment']);
            }

            $index++;
        }
    }

    private function seedAmbulanceClinicalDemo(Organization $organization, string $ownerRef): void
    {
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'ambulance')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointments = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
            ->whereNotNull('visit_id')
            ->with('visit')
            ->orderBy('branch_id')
            ->orderBy('id')
            ->get();

        if ($appointments->isEmpty()) {
            return;
        }

        $clinical = app(SpecialtyClinicalRecordService::class);
        $index = 0;

        foreach ($appointments as $appointment) {
            $visit = $appointment->visit;
            if (! $visit) {
                continue;
            }

            $critical = $index === 0;
            $clinical->upsert(
                $organization,
                $visit,
                'ambulance',
                'amb_dispatch',
                [
                    'call_nature' => $critical ? 'Medical emergency' : 'Transfer',
                    'priority' => $critical ? 'Immediate' : 'Routine',
                    'location' => $critical ? 'Kaneshie Market Road' : 'Ridge Hospital ward 3',
                    'caller_info' => $critical ? 'Bystander — 024XXXXXXX' : 'Ward nurse',
                    'crew' => $critical ? 'Paramedic + EMT' : 'EMT crew',
                    'vehicle' => $critical ? 'AMB-01' : 'AMB-02',
                    'dispatch_notes' => $critical
                        ? 'Chest pain, diaphoretic; request ALS response'
                        : 'Stable inter-facility transfer for imaging',
                    'eta_minutes' => $critical ? 8 : 20,
                ],
                $ownerRef,
                $ownerRef,
                'dispatch',
            );

            $clinical->upsert(
                $organization,
                $visit,
                'ambulance',
                'amb_scene',
                [
                    'chief_complaint' => $critical ? 'Chest pain' : 'Inter-facility transfer',
                    'acuity' => $critical ? 'Critical' : 'Stable',
                    'mechanism' => $critical ? 'Sudden onset at rest' : 'Scheduled transfer',
                    'airway_ok' => true,
                    'breathing_ok' => $critical ? false : true,
                    'circulation_ok' => $critical ? false : true,
                    'gcs' => 15,
                    'vitals_summary' => $critical
                        ? 'BP 88/54, HR 118, SpO2 91% on air, RR 28'
                        : 'BP 124/78, HR 82, SpO2 98%, RR 16',
                    'scene_findings' => $critical
                        ? 'Pale, diaphoretic, ongoing central chest pain radiating to left arm'
                        : 'Patient comfortable on stretcher; IV in situ',
                    'hazards' => $critical ? 'Busy roadside' : 'None',
                ],
                $ownerRef,
                $ownerRef,
                'on_scene',
            );

            if ($critical) {
                $clinical->upsert(
                    $organization,
                    $visit,
                    'ambulance',
                    'amb_en_route',
                    [
                        'destination' => 'Korle Bu Teaching Hospital ED',
                        'interventions' => 'Oxygen via NRB; aspirin; IV access; cardiac monitoring',
                        'medications' => 'Aspirin 300 mg PO',
                        'response_to_treatment' => 'Pain partially eased; SpO2 improved to 95%',
                        'monitoring' => 'Continuous ECG; vitals q5min',
                        'deterioration' => false,
                        'eta_facility' => '12 minutes',
                        'notes' => 'Pre-alerted ED resus',
                    ],
                    $ownerRef,
                    $ownerRef,
                    'transport',
                );
            }

            if (! $visit->specialty_stage) {
                $visit->update(['specialty_stage' => $critical ? 'transport' : 'on_scene']);
            }

            $index++;
        }
    }

    private function seedDentistryClinicalDemo(Organization $organization, string $ownerRef): void
    {
        // Specialty departments are provisioned without organization_id; scope via branches.
        $branchIds = Branch::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->pluck('id');

        if ($branchIds->isEmpty()) {
            return;
        }

        $departmentIds = Department::owned($ownerRef)
            ->whereIn('branch_id', $branchIds)
            ->where('type', 'dental')
            ->where('is_active', true)
            ->pluck('id');

        if ($departmentIds->isEmpty()) {
            return;
        }

        $appointment = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN, Appointment::STATUS_IN_CONSULTATION])
            ->whereNotNull('visit_id')
            ->with(['patient', 'visit'])
            ->latest('id')
            ->first();

        if (! $appointment?->patient || ! $appointment->visit) {
            return;
        }

        $charts = app(\App\Services\Care\Dentistry\DentalChartService::class);
        $plans = app(\App\Services\Care\Dentistry\DentalTreatmentPlanService::class);
        $procedures = app(\App\Services\Care\Dentistry\DentalProcedureService::class);

        $chart = $charts->chartForPatient($organization, $appointment->patient, $ownerRef);
        $charts->updateTooth($chart, '16', [
            'status' => 'present',
            'surfaces' => ['O'],
            'conditions' => ['caries'],
            'notes' => 'Demo occlusal caries',
        ], $ownerRef, $ownerRef, $appointment->visit);

        $plan = $plans->ensurePlan($organization, $appointment->patient, $ownerRef, $appointment->visit, $ownerRef);
        if ($plan->items()->count() === 0) {
            $plans->addItem($plan, $organization, [
                'procedure_code' => 'den.fill',
                'tooth_code' => '16',
                'surfaces' => ['O'],
                'priority' => 10,
                'notes' => 'Demo filling',
            ], $ownerRef, $ownerRef);
            $plans->accept($plan->fresh('items'), $ownerRef, $ownerRef);
        }

        $completedPatient = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('department_id', $departmentIds)
            ->where('status', Appointment::STATUS_COMPLETED)
            ->whereNotNull('visit_id')
            ->with('visit')
            ->latest('id')
            ->first();

        if ($completedPatient?->visit && ! \App\Models\DentalProcedure::owned($ownerRef)->where('visit_id', $completedPatient->visit_id)->exists()) {
            try {
                $procedures->complete($organization, $completedPatient->visit, [
                    'procedure_code' => 'den.fill',
                    'tooth_code' => '26',
                    'surfaces' => ['O'],
                    'anesthesia' => 'Local',
                    'bill' => true,
                    'notes' => 'Demo completed fill',
                ], $ownerRef, $ownerRef);
            } catch (\Throwable) {
                // Billing may be constrained in partial seeds — chart/plan still valuable.
            }
        }

        $perio = app(\App\Services\Care\Dentistry\DentalPerioService::class);
        if (! \App\Models\DentalPerioExam::owned($ownerRef)->where('patient_id', $appointment->patient_id)->exists()) {
            $perio->saveExam($organization, $appointment->patient, $ownerRef, [
                ['tooth_code' => '16', 'surface' => 'B', 'probing_mm' => 3, 'bleeding' => true, 'plaque' => false],
                ['tooth_code' => '16', 'surface' => 'L', 'probing_mm' => 2, 'bleeding' => false, 'plaque' => true],
            ], $appointment->visit, 'Demo perio chart', $ownerRef);
        }

        $lab = app(\App\Services\Care\Dentistry\DentalLabCaseService::class);
        if (! \App\Models\DentalLabCase::owned($ownerRef)->where('patient_id', $appointment->patient_id)->exists()) {
            $lab->create($organization, $appointment->patient, $ownerRef, [
                'case_type' => 'crown',
                'lab_name' => 'Demo Dental Lab',
                'tooth_codes' => ['16'],
                'status' => 'ordered',
                'due_at' => now()->addDays(14)->toDateString(),
                'notes' => 'Demo crown case',
            ], $appointment->visit, $ownerRef);
        }

        $recalls = app(\App\Services\Care\Dentistry\DentalRecallService::class);
        if (! \App\Models\DentalRecall::owned($ownerRef)->where('patient_id', $appointment->patient_id)->exists()) {
            $recalls->schedule($organization, $appointment->patient, $ownerRef, [
                'due_on' => now()->addMonths(6)->toDateString(),
                'reason' => '6-month check',
                'notes' => 'Demo recall',
            ], $ownerRef);
        }

        if (! $appointment->visit->specialty_stage) {
            $appointment->visit->update(['specialty_stage' => 'chair']);
        }
    }

    /**
     * @param  list<Branch>  $branches
     * @param  array<int, list<Department>>  $departments
     * @param  array<string, mixed>  $volumes
     * @return list<Practitioner>
     */
    private function seedPractitioners(
        Organization $organization,
        array $branches,
        array $departments,
        string $ownerRef,
        array $volumes,
    ): array {
        $practitioners = [];
        for ($i = 0; $i < $volumes['practitioners']; $i++) {
            $branch = $branches[$i % count($branches)];
            $branchDepts = $departments[$branch->id] ?? [];
            $department = $branchDepts[$i % max(1, count($branchDepts))] ?? null;
            $key = sprintf('demo-prac-%s-%02d', $ownerRef, $i + 1);

            $practitioners[] = Practitioner::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'user_ref' => $key,
                ],
                [
                    'owner_ref' => $ownerRef,
                    'branch_id' => $branch->id,
                    'department_id' => $department?->id,
                    'member_id' => null,
                    'name' => 'Dr. '.self::FIRST_NAMES[$i % count(self::FIRST_NAMES)].' '.self::LAST_NAMES[$i % count(self::LAST_NAMES)],
                    'specialty' => self::SPECIALTIES[$i % count(self::SPECIALTIES)],
                    'room' => 'Consultation Room '.($i + 1),
                    'is_active' => true,
                    'deleted_at' => null,
                ],
            );
            $practitioners[array_key_last($practitioners)]->syncAssignedBranches([(int) $branch->id]);
        }

        return $practitioners;
    }

    /**
     * @param  list<Branch>  $branches
     * @param  array<string, mixed>  $volumes
     * @return list<Patient>
     */
    private function seedPatients(
        Organization $organization,
        array $branches,
        string $ownerRef,
        array $volumes,
    ): array {
        $worldPeople = DemoWorld::people();
        $patients = [];
        for ($i = 0; $i < $volumes['patients']; $i++) {
            $branch = $branches[$i % count($branches)];
            $number = sprintf('DEMO-%s-%05d', Str::upper(Str::substr(md5($ownerRef), 0, 4)), $i + 1);
            $person = $worldPeople[$i] ?? null;
            if ($person === null) {
                $base = $worldPeople[$i % count($worldPeople)];
                $seq = $i + 1;
                $person = [
                    'first_name' => $base['first_name'],
                    'last_name' => $base['last_name'],
                    'gender' => $base['gender'],
                    'dob' => $base['dob'],
                    'phone' => '+23324'.str_pad((string) (1000000 + $seq), 7, '0', STR_PAD_LEFT),
                    'email' => sprintf('patient.%d@demo.ladill.com', $seq),
                    'national_id' => sprintf('GHA-729%06d', $seq),
                    'city' => $base['city'],
                    'address' => $base['address'],
                ];
            }

            $patients[] = Patient::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'patient_number' => $number,
                ],
                [
                    'uuid' => $this->demoUuid("patient|{$ownerRef}|{$i}"),
                    'owner_ref' => $ownerRef,
                    'branch_id' => $branch->id,
                    'first_name' => $person['first_name'],
                    'last_name' => $person['last_name'],
                    'gender' => $person['gender'],
                    'date_of_birth' => $person['dob'],
                    'phone' => $person['phone'],
                    'email' => $person['email'],
                    'national_id' => $person['national_id'],
                    'city' => $person['city'],
                    'region' => 'Greater Accra',
                    'address' => $person['address'],
                    'deleted_at' => null,
                ],
            );
        }

        return $patients;
    }

    /**
     * @param  list<Branch>  $branches
     * @param  array<int, list<Department>>  $departments
     * @param  list<Practitioner>  $practitioners
     * @param  list<Patient>  $patients
     * @param  array<string, mixed>  $volumes
     */
    private function seedAppointmentsAndClinical(
        Organization $organization,
        array $branches,
        array $departments,
        array $practitioners,
        array $patients,
        string $ownerRef,
        array $volumes,
    ): int {
        $statuses = [
            Appointment::STATUS_SCHEDULED,
            Appointment::STATUS_CHECKED_IN,
            Appointment::STATUS_WAITING,
            Appointment::STATUS_COMPLETED,
            Appointment::STATUS_COMPLETED,
            Appointment::STATUS_CANCELLED,
        ];
        $reasons = ['Follow-up', 'New complaint', 'Review labs', 'Chronic care'];
        $symptoms = ['Fever', 'Cough', 'Headache', 'Fatigue'];

        $count = 0;
        for ($i = 0; $i < $volumes['appointments']; $i++) {
            $patient = $patients[$i % count($patients)];
            $practitioner = $practitioners[$i % count($practitioners)];
            $branch = collect($branches)->first(
                fn (Branch $b) => (int) $b->id === (int) $practitioner->branch_id,
            ) ?? $branches[$i % count($branches)];
            $branchDepts = $departments[(int) $practitioner->branch_id] ?? ($departments[$branch->id] ?? []);
            // Align department to the practitioner desk so Queue tickets stay consultation-context.
            $department = collect($branchDepts)->first(
                fn (Department $dept) => (int) $dept->id === (int) ($practitioner->department_id ?? 0),
            ) ?? ($branchDepts[$i % max(1, count($branchDepts))] ?? null);
            $status = $statuses[$i % count($statuses)];
            $scheduledAt = now()->subDays(14)->addHours($i * 3);

            $visit = null;
            if (in_array($status, [Appointment::STATUS_CHECKED_IN, Appointment::STATUS_WAITING, Appointment::STATUS_COMPLETED], true)) {
                $visit = Visit::withTrashed()->updateOrCreate(
                    ['uuid' => $this->demoUuid("visit|{$ownerRef}|{$i}")],
                    [
                        'owner_ref' => $ownerRef,
                        'organization_id' => $organization->id,
                        'branch_id' => $branch->id,
                        'patient_id' => $patient->id,
                        'status' => $status === Appointment::STATUS_COMPLETED
                            ? Visit::STATUS_COMPLETED
                            : Visit::STATUS_IN_PROGRESS,
                        'checked_in_at' => $scheduledAt->copy()->addMinutes(5),
                        'completed_at' => $status === Appointment::STATUS_COMPLETED
                            ? $scheduledAt->copy()->addHour()
                            : null,
                        'checked_in_by' => $ownerRef,
                        'deleted_at' => null,
                    ],
                );
            }

            Appointment::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("appt|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'patient_id' => $patient->id,
                    'practitioner_id' => $practitioner->id,
                    'department_id' => $department?->id,
                    'visit_id' => $visit?->id,
                    'type' => $i % 5 === 0 ? Appointment::TYPE_WALK_IN : Appointment::TYPE_SCHEDULED,
                    'status' => $status,
                    'scheduled_at' => $scheduledAt,
                    'checked_in_at' => $visit?->checked_in_at,
                    'waiting_at' => in_array($status, [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN], true)
                        ? $scheduledAt->copy()->addMinutes(5)
                        : null,
                    'queue_position' => in_array($status, [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN], true)
                        ? ($i + 1)
                        : null,
                    'completed_at' => $status === Appointment::STATUS_COMPLETED
                        ? $scheduledAt->copy()->addHour()
                        : null,
                    'cancelled_at' => $status === Appointment::STATUS_CANCELLED
                        ? $scheduledAt->copy()->subHour()
                        : null,
                    'reason' => $reasons[$i % count($reasons)],
                    'created_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );

            if ($visit && $status === Appointment::STATUS_COMPLETED) {
                $consultation = Consultation::withTrashed()->updateOrCreate(
                    ['uuid' => $this->demoUuid("consult|{$ownerRef}|{$i}")],
                    [
                        'owner_ref' => $ownerRef,
                        'visit_id' => $visit->id,
                        'appointment_id' => null,
                        'practitioner_id' => $practitioner->id,
                        'patient_id' => $patient->id,
                        'status' => Consultation::STATUS_COMPLETED,
                        'symptoms' => $symptoms[$i % count($symptoms)],
                        'clinical_notes' => 'Demo consultation notes.',
                        'started_at' => $scheduledAt->copy()->addMinutes(15),
                        'completed_at' => $scheduledAt->copy()->addHour(),
                        'completed_by' => $ownerRef,
                        'deleted_at' => null,
                    ],
                );

                VitalSign::query()->updateOrCreate(
                    [
                        'consultation_id' => $consultation->id,
                        'recorded_at' => $scheduledAt->copy()->addMinutes(10),
                    ],
                    [
                        'owner_ref' => $ownerRef,
                        'bp_systolic' => 110 + ($i % 30),
                        'bp_diastolic' => 70 + ($i % 15),
                        'pulse' => 60 + ($i % 40),
                        'temperature' => 36.5 + (($i % 10) / 10),
                        'weight_kg' => 55 + ($i % 40),
                        'height_cm' => 150 + ($i % 40),
                        'spo2' => 95 + ($i % 5),
                        'respiratory_rate' => 14 + ($i % 6),
                        'recorded_by' => $ownerRef,
                    ],
                );
            }

            $count++;
        }

        return $count;
    }

    /**
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     * @param  list<Patient>  $patients
     * @param  array<string, mixed>  $volumes
     */
    /**
     * Demo hardware inventory: browser wedge scanner + agent thermometer per branch (Pro+).
     *
     * @param  list<Branch>  $branches
     */
    private function seedDevices(Organization $organization, array $branches, string $ownerRef): void
    {
        $deviceService = app(DeviceService::class);

        foreach ($branches as $i => $branch) {
            Device::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'name' => 'Demo barcode scanner',
                ],
                [
                    'owner_ref' => $ownerRef,
                    'type' => 'barcode_scanner',
                    'connection_mode' => Device::MODE_BROWSER_WEDGE,
                    'status' => Device::STATUS_OFFLINE,
                    'device_token_hash' => null,
                    'metadata' => ['demo' => true],
                    'deleted_at' => null,
                ],
            );

            $thermo = Device::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'name' => 'Demo thermometer',
                ],
                [
                    'owner_ref' => $ownerRef,
                    'type' => 'thermometer',
                    'connection_mode' => Device::MODE_AGENT,
                    'status' => Device::STATUS_OFFLINE,
                    'metadata' => ['demo' => true, 'note' => 'Token issued on first seed only if missing'],
                    'deleted_at' => null,
                ],
            );

            if (! $thermo->device_token_hash) {
                // Deterministic demo token so sales can reuse docs examples after reseed.
                $plain = 'demo-care-device-'.$organization->id.'-'.$branch->id.'-thermo';
                $thermo->update(['device_token_hash' => $deviceService->hashToken($plain)]);
            }
        }
    }

    /**
     * @return list<InvestigationType>
     */
    private function seedInvestigationCatalog(Organization $organization, string $ownerRef): array
    {
        $types = [];
        foreach ([
            ['name' => 'Fasting Blood Glucose', 'code' => 'FBG', 'category' => 'blood', 'unit' => 'mmol/L', 'low' => 3.9, 'high' => 6.1, 'price' => 2500],
            ['name' => 'Full Blood Count', 'code' => 'FBC', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 4500],
            ['name' => 'Malaria Parasite', 'code' => 'MP', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 1500],
            ['name' => 'Urinalysis', 'code' => 'UA', 'category' => 'urine', 'unit' => null, 'low' => null, 'high' => null, 'price' => 2000],
            ['name' => 'HbA1c', 'code' => 'HBA1C', 'category' => 'blood', 'unit' => '%', 'low' => null, 'high' => 5.7, 'price' => 6000],
            ['name' => 'Lipid Profile', 'code' => 'LIPID', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 5500],
            ['name' => 'Liver Function Test', 'code' => 'LFT', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 5000],
            ['name' => 'Kidney Function Test', 'code' => 'KFT', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 5000],
            ['name' => 'Blood Group & Rh', 'code' => 'BGRH', 'category' => 'blood', 'unit' => null, 'low' => null, 'high' => null, 'price' => 1800],
            ['name' => 'Pregnancy Test (Urine)', 'code' => 'HCG', 'category' => 'urine', 'unit' => null, 'low' => null, 'high' => null, 'price' => 1200],
            ['name' => 'Stool Routine Examination', 'code' => 'SRE', 'category' => 'stool', 'unit' => null, 'low' => null, 'high' => null, 'price' => 2200],
            ['name' => 'Chest X-Ray', 'code' => 'CXR', 'category' => 'xray', 'unit' => null, 'low' => null, 'high' => null, 'price' => 8000],
        ] as $spec) {
            $types[] = InvestigationType::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'code' => $spec['code'],
                ],
                [
                    'owner_ref' => $ownerRef,
                    'name' => $spec['name'],
                    'category' => $spec['category'],
                    'unit' => $spec['unit'],
                    'reference_low' => $spec['low'],
                    'reference_high' => $spec['high'],
                    'price_minor' => $spec['price'],
                    'is_active' => true,
                    'deleted_at' => null,
                ],
            );
        }

        return $types;
    }

    /**
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     * @param  list<Patient>  $patients
     * @param  list<InvestigationType>  $types
     */
    private function seedPaidModules(
        Organization $organization,
        array $branches,
        array $practitioners,
        array $patients,
        string $ownerRef,
        array $volumes,
        array $types = [],
    ): void {
        if ($types === []) {
            $types = $this->seedInvestigationCatalog($organization, $ownerRef);
        }

        $drugs = [];
        foreach ([
            ['name' => 'Amoxicillin 500mg', 'unit' => 'capsule', 'price' => 1500],
            ['name' => 'Paracetamol 500mg', 'unit' => 'tablet', 'price' => 50],
            ['name' => 'Metformin 500mg', 'unit' => 'tablet', 'price' => 80],
            ['name' => 'Amlodipine 5mg', 'unit' => 'tablet', 'price' => 120],
        ] as $i => $spec) {
            $drug = Drug::withTrashed()->updateOrCreate(
                [
                    'organization_id' => $organization->id,
                    'sku' => 'DEMO-DRUG-'.($i + 1),
                ],
                [
                    'owner_ref' => $ownerRef,
                    'branch_id' => $branches[0]->id,
                    'name' => $spec['name'],
                    'generic_name' => $spec['name'],
                    'unit' => $spec['unit'],
                    'unit_price_minor' => $spec['price'],
                    'reorder_level' => 20,
                    'is_active' => true,
                    'deleted_at' => null,
                ],
            );
            DrugBatch::query()->updateOrCreate(
                [
                    'drug_id' => $drug->id,
                    'batch_number' => 'DEMO-BATCH-'.($i + 1),
                ],
                [
                    'owner_ref' => $ownerRef,
                    'expiry_date' => now()->addYear()->toDateString(),
                    'quantity_on_hand' => 200,
                    'cost_minor' => (int) ($spec['price'] * 0.6),
                    'received_at' => now()->subDays(10),
                ],
            );
            $drugs[] = $drug;
        }

        if ($patients === [] || $practitioners === [] || $types === [] || $branches === []) {
            return;
        }

        for ($i = 0; $i < $volumes['lab_requests']; $i++) {
            $patient = $patients[$i % count($patients)];
            $branch = $branches[$i % count($branches)];
            $practitioner = $practitioners[$i % count($practitioners)];
            $type = $types[$i % count($types)];

            $visit = Visit::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("lab-visit|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'patient_id' => $patient->id,
                    'status' => Visit::STATUS_IN_PROGRESS,
                    'checked_in_at' => now()->subDays($i % 10),
                    'checked_in_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );

            $consultation = Consultation::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("lab-consult|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'visit_id' => $visit->id,
                    'practitioner_id' => $practitioner->id,
                    'patient_id' => $patient->id,
                    'status' => Consultation::STATUS_DRAFT,
                    'started_at' => now()->subDays($i % 10),
                    'deleted_at' => null,
                ],
            );

            InvestigationRequest::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("lab-req|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'visit_id' => $visit->id,
                    'consultation_id' => $consultation->id,
                    'patient_id' => $patient->id,
                    'investigation_type_id' => $type->id,
                    'practitioner_id' => $practitioner->id,
                    'status' => InvestigationRequest::STATUS_PENDING,
                    'priority' => 'routine',
                    'clinical_notes' => 'Demo lab request',
                    'requested_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );
        }

        for ($i = 0; $i < $volumes['prescriptions']; $i++) {
            $patient = $patients[$i % count($patients)];
            $branch = $branches[$i % count($branches)];
            $practitioner = $practitioners[$i % count($practitioners)];
            $drug = $drugs[$i % count($drugs)];

            $visit = Visit::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("rx-visit|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'patient_id' => $patient->id,
                    'status' => Visit::STATUS_IN_PROGRESS,
                    'checked_in_at' => now()->subDays($i % 7),
                    'checked_in_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );

            $consultation = Consultation::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("rx-consult|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'visit_id' => $visit->id,
                    'practitioner_id' => $practitioner->id,
                    'patient_id' => $patient->id,
                    'status' => Consultation::STATUS_DRAFT,
                    'started_at' => now()->subDays($i % 7),
                    'deleted_at' => null,
                ],
            );

            $prescription = Prescription::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("rx|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'visit_id' => $visit->id,
                    'consultation_id' => $consultation->id,
                    'patient_id' => $patient->id,
                    'practitioner_id' => $practitioner->id,
                    'status' => Prescription::STATUS_ACTIVE,
                    'prescribed_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );

            $prescription->items()->updateOrCreate(
                [
                    'prescription_id' => $prescription->id,
                    'name' => $drug->name,
                ],
                [
                    'owner_ref' => $ownerRef,
                    'drug_id' => $drug->id,
                    'dosage' => '1 '.$drug->unit,
                    'frequency' => 'BD',
                    'duration' => '5 days',
                    'quantity' => 10,
                    'sort_order' => 0,
                ],
            );
        }

        $branchCount = max(1, count($branches));
        for ($i = 0; $i < $volumes['bills']; $i++) {
            $patient = $patients[$i % count($patients)];
            $branch = $branches[$i % $branchCount];
            $fee = 5000 + (($i % 5) * 1000);
            // Payment mix is per-branch (not i%3 with branchCount=3), otherwise HQ
            // only ever gets paid invoices and the Ridge cashier has nothing to collect.
            $slot = intdiv($i, $branchCount) % 3;
            if ($slot === 0) {
                $status = Bill::STATUS_PAID;
                $amountPaid = $fee;
                $balance = 0;
            } elseif ($slot === 1) {
                $status = Bill::STATUS_PARTIAL;
                $amountPaid = (int) floor($fee / 2);
                $balance = $fee - $amountPaid;
            } else {
                $status = Bill::STATUS_OPEN;
                $amountPaid = 0;
                $balance = $fee;
            }

            $visit = Visit::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("bill-visit|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'patient_id' => $patient->id,
                    'status' => Visit::STATUS_COMPLETED,
                    'checked_in_at' => now()->subDays($i % 12),
                    'completed_at' => now()->subDays($i % 12)->addHour(),
                    'checked_in_by' => $ownerRef,
                    'deleted_at' => null,
                ],
            );

            $bill = Bill::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("bill|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'visit_id' => $visit->id,
                    'patient_id' => $patient->id,
                    'invoice_number' => sprintf('DEMO-INV-%s-%04d', Str::upper(Str::substr(md5($ownerRef), 0, 4)), $i + 1),
                    'status' => $status,
                    'subtotal_minor' => $fee,
                    'discount_minor' => 0,
                    'tax_minor' => 0,
                    'total_minor' => $fee,
                    'amount_paid_minor' => $amountPaid,
                    'balance_minor' => $balance,
                    'created_by' => $ownerRef,
                    'finalized_at' => now()->subDays($i % 12),
                    'deleted_at' => null,
                ],
            );

            BillLineItem::query()->updateOrCreate(
                [
                    'bill_id' => $bill->id,
                    'description' => 'Consultation fee',
                ],
                [
                    'owner_ref' => $ownerRef,
                    'type' => 'consultation',
                    'quantity' => 1,
                    'unit_price_minor' => $fee,
                    'total_minor' => $fee,
                ],
            );

            $paymentUuid = $this->demoUuid("pay|{$ownerRef}|{$i}");
            if ($amountPaid > 0) {
                Payment::query()->updateOrCreate(
                    ['uuid' => $paymentUuid],
                    [
                        'owner_ref' => $ownerRef,
                        'bill_id' => $bill->id,
                        'amount_minor' => $amountPaid,
                        'method' => Payment::METHOD_CASH,
                        'status' => Payment::STATUS_PAID,
                        'reference' => 'DEMO-PAY-'.($i + 1),
                        'paid_at' => now()->subDays($i % 12),
                        'recorded_by' => $ownerRef,
                    ],
                );
            } else {
                Payment::query()->where('uuid', $paymentUuid)->delete();
            }
        }
    }

    /**
     * @param  list<Branch>  $branches
     * @param  list<Practitioner>  $practitioners
     * @param  list<Patient>  $patients
     */
    private function seedAssessments(
        Organization $organization,
        array $branches,
        array $practitioners,
        array $patients,
        string $ownerRef,
        int $count,
    ): void {
        $template = AssessmentTemplate::withTrashed()->updateOrCreate(
            [
                'code' => self::DEMO_TEMPLATE_CODE,
                'version' => 1,
            ],
            [
                'organization_id' => $organization->id,
                'name' => 'Demo Triage Screen',
                'category' => AssessmentTemplate::CATEGORY_SCREENING,
                'description' => 'Lightweight demo assessment for enterprise analytics.',
                'is_current' => true,
                'is_active' => true,
                'scoring_strategy' => 'sum_items',
                'deleted_at' => null,
            ],
        );

        $question = AssessmentQuestion::query()->updateOrCreate(
            [
                'template_id' => $template->id,
                'code' => 'severity',
            ],
            [
                'section' => 'Triage',
                'label' => 'Severity (0-10)',
                'answer_type' => AssessmentQuestion::TYPE_SCALE,
                'is_required' => true,
                'sort_order' => 1,
                'score_key' => 'severity',
                'options' => ['min' => 0, 'max' => 10],
            ],
        );

        for ($i = 0; $i < $count; $i++) {
            $patient = $patients[$i % count($patients)];
            $branch = $branches[$i % count($branches)];
            $practitioner = $practitioners[$i % count($practitioners)];
            $scoreValue = (float) ($i % 11);

            $assessment = Assessment::withTrashed()->updateOrCreate(
                ['uuid' => $this->demoUuid("assess|{$ownerRef}|{$i}")],
                [
                    'owner_ref' => $ownerRef,
                    'organization_id' => $organization->id,
                    'branch_id' => $branch->id,
                    'patient_id' => $patient->id,
                    'template_id' => $template->id,
                    'practitioner_id' => $practitioner->id,
                    'status' => Assessment::STATUS_COMPLETED,
                    'assessed_at' => now()->subDays($i % 30),
                    'completed_at' => now()->subDays($i % 30),
                    'completed_by' => $ownerRef,
                    'started_by' => $ownerRef,
                    'notes' => 'Demo assessment',
                    'deleted_at' => null,
                ],
            );

            AssessmentAnswer::query()->updateOrCreate(
                [
                    'assessment_id' => $assessment->id,
                    'question_id' => $question->id,
                ],
                [
                    'owner_ref' => $ownerRef,
                    'value_number' => $scoreValue,
                ],
            );

            AssessmentScore::query()->updateOrCreate(
                ['assessment_id' => $assessment->id],
                [
                    'owner_ref' => $ownerRef,
                    'template_code' => self::DEMO_TEMPLATE_CODE,
                    'total_score' => $scoreValue,
                    'max_score' => 10,
                    'subscores' => ['severity' => $scoreValue],
                    'severity_label' => $scoreValue >= 7 ? 'high' : ($scoreValue >= 4 ? 'moderate' : 'low'),
                    'computed_at' => now()->subDays($i % 30),
                ],
            );
        }
    }

    private function demoUuid(string $key): string
    {
        $hex = md5('ladill-care-demo|'.$key);

        return sprintf(
            '%s-%s-%s-%s-%s',
            substr($hex, 0, 8),
            substr($hex, 8, 4),
            substr($hex, 12, 4),
            substr($hex, 16, 4),
            substr($hex, 20, 12),
        );
    }
}
