<?php

namespace Database\Seeders;

use App\Account;
use App\AccountTransaction;
use App\Business;
use App\Currency;
use App\User;
use App\Utils\BusinessUtil;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Spatie\Permission\Models\Permission;

/**
 * Builds the full Shifa One human-medical demo dataset on top of a migrated
 * UltimatePOS DB with the ClinicCore / specialty plugins / ClinicLab modules
 * enabled.
 *
 * Uses the canonical creation path (BusinessUtil::createNewBusiness +
 * newBusinessDefaultResources + addLocation) — the same one the registration
 * flow uses — so the business behaves like a real one. Idempotent: re-running
 * reuses the existing Shifa One business unless --fresh is passed.
 *
 * Sections (called from run): foundation → staff → accounts → products →
 * contacts (patients) → clinicReference → clinic (departments/doctors/patients
 * across specialties/appointments/prescriptions/vaccinations/reports) → lab
 * (ClinicLab results) → contracts (care plans) → trade (POS/purchases) →
 * advancedTransfer → ai → passport.
 *
 * NB: every contact mobile is +201092288093 per the demo spec.
 */
class ShifaInstallSeeder extends Seeder
{
    public bool $fresh = false;

    private BusinessUtil $businessUtil;

    private int $businessId;
    private int $ownerId;
    /** @var array<string,int> location code => id */
    private array $loc = [];
    private int $unitId;
    /** @var array<string,int> category name => id */
    private array $cat = [];
    /** @var array<string,array<int,array>> bucket => [ [product_id,variation_id,cost,sell,name], ... ] */
    private array $catalog = ['managed' => [], 'fnb' => [], 'service' => []];
    /** @var array<string,int[]> contact ref => id (suppliers + customers) */
    private array $contactIds = ['supplier' => [], 'customer' => []];
    /** @var int[] staff user ids keyed by username */
    private array $staffIds = [];
    /** @var array<string,int> department name => id */
    private array $dept = [];
    /** @var array<string,int> specialty code => id */
    private array $specialty = [];
    /** @var int[] doctor ids */
    private array $doctorIds = [];
    /** @var array<string,int> specialty code => doctor id */
    private array $deptDoctor = [];
    /** @var array<int,array{patient_id:int,contact_id:int,code:string,name:string}> */
    private array $patients = [];
    /** @var array<string,float> install section => wall-clock seconds */
    private array $timings = [];
    /** @var array<string,string> category name => mock image filename */
    private array $catImageCache = [];
    private const MOBILE = '+201092288093';

    /**
     * The 12 demo patients, each anchored to a specialty (showcases the
     * ClinicCore 45-specialty driver registry). code => [department label,
     * doctor name, profile table, profile column=>value].
     */
    private const SPECIALTY_PLAN = [
        ['cardiology_vascular', 'Cardiology', 'Dr. Yasmin Fahmy', 'cardiology_patient_profiles',
            ['bp_systolic' => 148, 'bp_diastolic' => 94, 'ejection_fraction' => 55, 'rhythm' => 'Sinus rhythm', 'nyha' => 'II']],
        ['pediatrics_newborn', 'Pediatrics', 'Dr. Salma Adel', 'pediatrics_patient_profiles',
            ['weight_percentile' => 60, 'height_percentile' => 55, 'head_circ_percentile' => 50, 'milestones' => 'Age-appropriate', 'immunisation' => 'Up to date']],
        ['dermatology', 'Dermatology', 'Dr. Omar Sherif', 'derma_patient_profiles',
            ['fitzpatrick_type' => 'III', 'skin_type' => 'combination']],
        ['dental', 'Dental', 'Dr. Hany Naguib', 'dental_patient_profiles',
            ['dentition_type' => 'permanent', 'notes' => 'Caries on 26; scaling advised.']],
        ['ophthalmology', 'Ophthalmology', 'Dr. Yasmin Fahmy', 'ophth_patient_profiles',
            ['va_right' => '6/6', 'va_left' => '6/9', 'sphere_right' => -0.50, 'cylinder_right' => -0.25, 'axis_right' => 180,
             'sphere_left' => -0.75, 'cylinder_left' => -0.50, 'axis_left' => 170, 'iop_right' => 16, 'iop_left' => 23, 'wears_glasses' => 1,
             'notes' => 'Left IOP borderline — review.']],
        ['endocrinology', 'Diabetes & Endocrinology', 'Dr. Omar Sherif', 'endocrinology_patient_profiles',
            ['hba1c' => 7.8, 'fasting_glucose' => 142, 'tsh' => 2.1, 'bmi' => 29.4]],
        ['internal_medicine', 'Internal Medicine', 'Dr. Salma Adel', 'internal_medicine_patient_profiles',
            ['bmi' => 27.6, 'comorbidities' => 'Hypertension, Type 2 Diabetes', 'active_problems' => 'BP control, lipid management']],
        ['orthopedics', 'Orthopedics', 'Dr. Hany Naguib', 'orthopedics_patient_profiles',
            ['affected_joint' => 'Right knee', 'rom' => 'Reduced flexion', 'neurovascular' => 'Intact', 'imaging' => 'OA grade 2']],
        ['gynaecology', 'Gynaecology & Infertility', 'Dr. Yasmin Fahmy', 'obgyn_patient_profiles',
            ['gravida' => 2, 'para' => 1, 'abortions' => 0, 'living' => 1, 'menarche_age' => 13, 'lmp_offset_days' => 56,
             'cycle_length_days' => 28, 'cycle_regular' => 1, 'contraception' => 'None', 'is_pregnant' => 1, 'menopause' => 0,
             'notes' => 'Antenatal — first trimester.']],
        ['psychiatry', 'Psychiatry', 'Dr. Omar Sherif', 'psychiatry_patient_profiles',
            ['mood' => 'Low', 'risk_level' => 'Low', 'diagnosis' => 'Generalised anxiety disorder', 'notes' => 'CBT referral.']],
        ['chest_respiratory', 'Chest & Respiratory', 'Dr. Salma Adel', 'pulmonology_patient_profiles',
            ['fev1' => 78, 'fvc' => 85, 'spo2' => 96, 'mmrc' => 1]],
        ['nephrology', 'Nephrology', 'Dr. Hany Naguib', 'nephrology_patient_profiles',
            ['egfr' => 72, 'creatinine' => 1.1, 'ckd_stage' => 'G2', 'dialysis' => 'No']],
    ];

    public function __construct(BusinessUtil $businessUtil)
    {
        $this->businessUtil = $businessUtil;
    }

    public function run(): void
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0');

        $this->section('foundation', fn () => $this->foundation());
        $this->section('staff', fn () => $this->staff());
        $this->section('accounts', fn () => $this->accounts());
        $this->section('products', fn () => $this->products());
        $this->section('contacts', fn () => $this->contacts());
        $this->section('clinicReference', fn () => $this->clinicReference());
        $this->section('clinic', fn () => $this->clinic());
        $this->section('lab', fn () => $this->lab());
        $this->section('contracts', fn () => $this->contracts());
        $this->section('trade', fn () => $this->trade());
        $this->section('advancedTransfer', fn () => $this->advancedTransfer());
        $this->section('ai', fn () => $this->ai());
        $this->section('passport', fn () => $this->passport());

        DB::statement('SET FOREIGN_KEY_CHECKS=1');

        $this->reportTimings();
        $this->info("Shifa One business #{$this->businessId} ready (locations: ".implode(', ', array_keys($this->loc)).').');
    }

    private function section(string $name, callable $fn): void
    {
        $t0 = microtime(true);
        $fn();
        $this->timings[$name] = microtime(true) - $t0;
    }

    private function reportTimings(): void
    {
        if (empty($this->timings)) {
            return;
        }
        $sorted = $this->timings;
        arsort($sorted);
        $total = array_sum($sorted);
        $this->info(sprintf('Timing breakdown — total %.1fs (slowest first):', $total));
        foreach ($sorted as $name => $secs) {
            $this->info(sprintf('    %6.2fs  %5.1f%%  %s', $secs, $total > 0 ? $secs / $total * 100 : 0.0, $name));
        }
    }

    // ---- foundation: business + locations + owner -------------------------

    private function foundation(): void
    {
        $existing = Business::where('name', 'Shifa One')->first();
        if ($existing && $this->fresh) {
            $this->info('Removing existing Shifa One business #'.$existing->id.' (--fresh)');
            $this->wipeBusiness($existing->id);
            $existing = null;
        }
        if ($existing) {
            $this->businessId = $existing->id;
            $this->ownerId = $existing->owner_id;
            $this->loadLocations();
            $this->info('Reusing existing Shifa One business #'.$this->businessId);
            return;
        }

        $currencyId = optional(Currency::where('code', 'EGP')->first())->id
            ?? optional(Currency::first())->id ?? 2;

        $owner = User::create_user([
            'surname' => 'Dr', 'first_name' => 'Hossam', 'last_name' => 'Makram',
            'username' => 'shifa', 'email' => 'owner@shifaone.test',
            'password' => '123456', 'language' => 'en',
        ]);
        $this->ownerId = $owner->id;

        $business = $this->businessUtil->createNewBusiness([
            'name' => 'Shifa One',
            'currency_id' => $currencyId,
            'start_date' => Carbon::now()->subYear()->toDateString(),
            'owner_id' => $owner->id,
            'time_zone' => 'Africa/Cairo',
            'fy_start_month' => 1,
            'accounting_method' => 'fifo',
            'tax_label_1' => 'VAT', 'tax_number_1' => 'EG-SHIFA-001',
            'logo' => 'shifa-one-logo.png',
            'enabled_modules' => [
                'purchases', 'add_sale', 'pos_sale', 'stock_transfers', 'stock_adjustment',
                'expenses', 'account', 'service_staff', 'types_of_service',
                'booking', 'subscription',
            ],
        ]);
        $this->businessId = $business->id;

        $owner->business_id = $business->id;
        $owner->save();

        foreach ([
            'sell.view', 'sell.create', 'sell.update', 'sell.delete',
            'access_all_locations', 'view_cash_register', 'close_cash_register',
        ] as $p) {
            Permission::firstOrCreate(['name' => $p, 'guard_name' => 'web']);
        }

        $this->businessUtil->newBusinessDefaultResources($business->id, $owner->id);

        // Locations: main hospital + branch clinic + central pharmacy store.
        foreach ([
            'MAIN' => ['name' => 'Shifa One Hospital — New Cairo', 'city' => 'New Cairo', 'landmark' => '90th Street'],
            'BRANCH' => ['name' => 'Shifa One Clinic — Maadi', 'city' => 'Maadi', 'landmark' => 'Road 9'],
            'STORE' => ['name' => 'Shifa Central Pharmacy Store', 'city' => 'New Cairo', 'landmark' => 'Industrial Zone'],
        ] as $code => $d) {
            $location = $this->businessUtil->addLocation($business->id, array_merge([
                'country' => 'Egypt', 'state' => 'Cairo', 'zip_code' => '11835',
                'website' => 'https://shifaone.test', 'mobile' => self::MOBILE,
            ], $d));
            Permission::firstOrCreate(['name' => 'location.'.$location->id, 'guard_name' => 'web']);
            $this->loc[$code] = $location->id;
        }

        $this->info('Created business "Shifa One" (#'.$business->id.') + '.count($this->loc).' locations; owner login shifa / 123456');
    }

    private function loadLocations(): void
    {
        $byName = DB::table('business_locations')->where('business_id', $this->businessId)->pluck('id', 'name')->toArray();
        $map = [
            'MAIN' => 'Shifa One Hospital — New Cairo',
            'BRANCH' => 'Shifa One Clinic — Maadi',
            'STORE' => 'Shifa Central Pharmacy Store',
        ];
        $this->loc = [];
        foreach ($map as $code => $name) {
            if (isset($byName[$name])) {
                $this->loc[$code] = $byName[$name];
            }
        }
        if (empty($this->loc)) {
            $this->loc = DB::table('business_locations')->where('business_id', $this->businessId)->pluck('id', 'name')->toArray();
        }
    }

    // ---- staff: Essentials employee users ---------------------------------

    private function staff(): void
    {
        $cashierRole = 'Cashier#'.$this->businessId;
        $staff = [
            ['Dr', 'Yasmin', 'Fahmy', 'dr.yasmin.shifa', 'dr1@shifaone.test'],       // physician / cardiology
            ['Dr', 'Omar', 'Sherif', 'dr.omar.shifa', 'dr2@shifaone.test'],          // physician / surgeon
            ['Ms', 'Salma', 'Adel', 'reception.shifa', 'reception@shifaone.test'],   // reception
            ['Ms', 'Hala', 'Mansour', 'nurse.shifa', 'nurse@shifaone.test'],         // nurse
            ['Mr', 'Hany', 'Naguib', 'pharmacist.shifa', 'pharmacy@shifaone.test'],  // pharmacist
            ['Mr', 'Tarek', 'Fouad', 'accountant.shifa', 'accountant@shifaone.test'],// accountant
        ];
        foreach ($staff as [$surname, $first, $last, $username, $email]) {
            $u = User::where('username', $username)->first();
            if (! $u) {
                $u = User::create_user([
                    'surname' => $surname, 'first_name' => $first, 'last_name' => $last,
                    'username' => $username, 'email' => $email, 'password' => '123456', 'language' => 'en',
                ]);
                $u->business_id = $this->businessId;
                $u->save();
                try { $u->assignRole($cashierRole); } catch (\Throwable $e) { /* role optional */ }
            }
            $this->staffIds[$username] = $u->id;
        }
        $this->info('Staff: '.count($this->staffIds).' employee users (login *.shifa / 123456).');
    }

    // ---- accounts + opening balances --------------------------------------

    private function accounts(): void
    {
        if (! class_exists(Account::class)) {
            return;
        }
        $accounts = [
            ['name' => 'Cash Drawer — Reception', 'account_number' => 'SHIFA-CASH-01', 'opening' => 120000],
            ['name' => 'NBE Current Account', 'account_number' => 'SHIFA-NBE-2025', 'opening' => 3500000],
            ['name' => 'CIB USD Account', 'account_number' => 'SHIFA-CIB-USD', 'opening' => 500000],
        ];
        foreach ($accounts as $a) {
            $account = Account::firstOrCreate(
                ['business_id' => $this->businessId, 'account_number' => $a['account_number']],
                ['name' => $a['name'], 'created_by' => $this->ownerId, 'is_closed' => 0],
            );
            $hasOpening = AccountTransaction::where('account_id', $account->id)
                ->where('type', 'opening_balance')->exists();
            if (! $hasOpening && $a['opening'] > 0) {
                AccountTransaction::create([
                    'account_id' => $account->id,
                    'amount' => $a['opening'],
                    'type' => 'opening_balance',
                    'sub_type' => null,
                    'operation_date' => Carbon::now()->subYear(),
                    'created_by' => $this->ownerId,
                ]);
            }
        }
        $this->info('Accounts: '.count($accounts).' created with opening balances.');
    }

    // ---- products: managed-stock + combo + services -----------------------

    private function products(): void
    {
        if (! empty(DB::table('products')->where('business_id', $this->businessId)->where('sku', 'like', 'SHIFA%')->first())) {
            $this->loadCatalog();
            $this->info('Products already present — reusing.');
            return;
        }

        $this->unitId = DB::table('units')->where('business_id', $this->businessId)->value('id')
            ?: DB::table('units')->insertGetId([
                'business_id' => $this->businessId, 'actual_name' => 'Pieces', 'short_name' => 'Pcs',
                'allow_decimal' => 0, 'created_by' => $this->ownerId, 'created_at' => now(), 'updated_at' => now(),
            ]);
        $this->ensureDefaultImage();
        foreach ([
            'Vaccines', 'Pharmacy', 'Medical Supplies', 'Cafeteria',
            'Clinical Services', 'Diagnostic Imaging', 'Laboratory Tests', 'Controlled Drugs',
        ] as $c) {
            $this->cat[$c] = DB::table('categories')->insertGetId([
                'business_id' => $this->businessId, 'name' => $c, 'short_code' => strtoupper(substr(str_replace(' ', '', $c), 0, 4)),
                'category_type' => 'product', 'parent_id' => 0, 'created_by' => $this->ownerId,
                'created_at' => now(), 'updated_at' => now(),
            ]);
        }

        // Managed-stock pharmacy / vaccines / supplies (human meds).
        foreach ([
            ['Tetanus Toxoid Vaccine', 40, 120, 'Vaccines', 200],
            ['Hepatitis B Vaccine', 80, 200, 'Vaccines', 150],
            ['Influenza Vaccine (seasonal)', 90, 220, 'Vaccines', 180],
            ['COVID-19 Vaccine (dose)', 0, 150, 'Vaccines', 220],
            ['MMR Vaccine', 110, 260, 'Vaccines', 120],
            ['Amoxicillin/Clavulanate 1g (14 tabs)', 35, 90, 'Pharmacy', 400],
            ['Azithromycin 500mg (6 tabs)', 45, 120, 'Pharmacy', 300],
            ['Paracetamol 1g (20 tabs)', 12, 35, 'Pharmacy', 600],
            ['Ibuprofen 400mg (30 tabs)', 18, 50, 'Pharmacy', 500],
            ['Amlodipine 5mg (30 tabs)', 22, 70, 'Pharmacy', 350],
            ['Metformin 850mg (30 tabs)', 20, 60, 'Pharmacy', 380],
            ['Atorvastatin 20mg (30 tabs)', 30, 95, 'Pharmacy', 300],
            ['Omeprazole 20mg (14 caps)', 15, 45, 'Pharmacy', 420],
            ['Insulin Glargine (pen)', 180, 380, 'Pharmacy', 120],
            ['Salbutamol Inhaler', 50, 130, 'Pharmacy', 160],
            ['Surgical Gloves (Box 100)', 110, 200, 'Medical Supplies', 140],
            ['IV Cannula 20G (each)', 8, 25, 'Medical Supplies', 800],
            ['Disposable Syringe 5ml (Box)', 60, 130, 'Medical Supplies', 200],
            ['Sterile Gauze Pack', 25, 60, 'Medical Supplies', 300],
            ['Face Mask (Box 50)', 40, 90, 'Medical Supplies', 250],
        ] as [$name, $cost, $sell, $cat, $qty]) {
            $this->catalog['managed'][] = $this->makeProduct($name, 'single', $cost, $sell, [
                'stock' => true, 'category_id' => $this->cat[$cat], 'stock_qty_default' => $qty,
                'image' => $this->categoryImage($cat),
            ]);
        }

        // Controlled drugs as stock-tracked products (the clinical controlled
        // register lives on clc_medicines — flagged in clinicReference()).
        foreach ([
            ['Morphine 10mg/ml (1ml)', 120, 320, 80],
            ['Pethidine 50mg/ml (2ml)', 90, 260, 90],
            ['Diazepam 5mg/ml (2ml)', 50, 150, 120],
            ['Tramadol 100mg (10 caps)', 40, 120, 160],
            ['Phenobarbital 30mg (30 tabs)', 60, 170, 100],
        ] as [$name, $cost, $sell, $qty]) {
            $this->catalog['managed'][] = $this->makeProduct($name, 'single', $cost, $sell, [
                'stock' => true, 'category_id' => $this->cat['Controlled Drugs'], 'stock_qty_default' => $qty,
                'image' => $this->categoryImage('Controlled Drugs'),
            ]);
        }

        // Cafeteria consumables (not stock-tracked) + a combo meal package
        // (proves the F&B / combo product type for the hospital cafeteria).
        $cafImg = $this->categoryImage('Cafeteria');
        $meal = $this->makeProduct('Patient Meal', 'single', 25, 60, ['category_id' => $this->cat['Cafeteria'], 'image' => $cafImg]);
        $juice = $this->makeProduct('Fresh Juice', 'single', 10, 30, ['category_id' => $this->cat['Cafeteria'], 'image' => $cafImg]);
        $snack = $this->makeProduct('Snack & Coffee', 'single', 12, 35, ['category_id' => $this->cat['Cafeteria'], 'image' => $cafImg]);
        $this->catalog['fnb'] = [$meal, $juice, $snack];
        $this->catalog['fnb'][] = $this->makeProduct('Cafeteria Day Package', 'combo', 40, 110, [
            'category_id' => $this->cat['Cafeteria'], 'image' => $cafImg,
            'combo' => [
                ['variation_id' => $meal['variation_id'], 'quantity' => 2, 'unit_id' => $this->unitId],
                ['variation_id' => $juice['variation_id'], 'quantity' => 1, 'unit_id' => $this->unitId],
                ['variation_id' => $snack['variation_id'], 'quantity' => 1, 'unit_id' => $this->unitId],
            ],
        ]);

        // Clinical services (service-staff, not stock-tracked).
        foreach ([
            ['General Consultation', 0, 250], ['Specialist Consultation', 0, 450],
            ['Follow-Up Visit', 0, 150], ['Minor Surgery', 0, 3500],
            ['Wound Dressing', 0, 200], ['Physiotherapy Session', 0, 400],
            ['Vaccination Visit', 0, 150], ['Antenatal Checkup', 0, 350],
            ['Emergency Triage', 0, 500], ['Nursing / Injection', 0, 120],
        ] as [$name, $cost, $sell]) {
            $this->catalog['service'][] = $this->makeProduct($name, 'single', $cost, $sell, [
                'category_id' => $this->cat['Clinical Services'], 'image' => $this->categoryImage('Clinical Services'),
            ]);
        }

        // Diagnostic imaging (billable, service-type).
        foreach ([
            ['X-Ray (per view)', 0, 400], ['CT Scan', 0, 2500], ['MRI Scan', 0, 3500],
            ['Abdominal Ultrasound', 0, 700], ['Echocardiogram', 0, 1200],
            ['ECG / Electrocardiogram', 0, 300], ['Endoscopy', 0, 1800], ['Mammography', 0, 900],
        ] as [$name, $cost, $sell]) {
            $this->catalog['service'][] = $this->makeProduct($name, 'single', $cost, $sell, [
                'category_id' => $this->cat['Diagnostic Imaging'], 'image' => $this->categoryImage('Diagnostic Imaging'),
            ]);
        }

        // Laboratory tests (billable panels; analyzer results seeded in lab()).
        foreach ([
            ['CBC / Complete Blood Count', 0, 200], ['Comprehensive Metabolic Panel', 0, 450],
            ['HbA1c', 0, 250], ['Lipid Profile', 0, 300], ['Thyroid Panel (TSH/T3/T4)', 0, 400],
            ['Urinalysis', 0, 150], ['Liver Function Test', 0, 350], ['Renal Function Test', 0, 350],
        ] as [$name, $cost, $sell]) {
            $this->catalog['service'][] = $this->makeProduct($name, 'single', $cost, $sell, [
                'category_id' => $this->cat['Laboratory Tests'], 'image' => $this->categoryImage('Laboratory Tests'),
            ]);
        }

        $n = count($this->catalog['managed']) + count($this->catalog['fnb']) + count($this->catalog['service']);
        $this->info("Products: {$n} created (pharmacy/vaccines/supplies + controlled drugs + cafeteria combo + services + imaging + lab tests).");
    }

    private function makeProduct(string $name, string $type, float $cost, float $sell, array $opts = []): array
    {
        $enableStock = (bool) ($opts['stock'] ?? false);
        $pid = DB::table('products')->insertGetId([
            'name' => $name, 'business_id' => $this->businessId, 'type' => $type, 'unit_id' => $this->unitId,
            'category_id' => $opts['category_id'] ?? null,
            'sku' => 'SHIFA'.strtoupper(substr(md5($name.$this->businessId), 0, 8)),
            'image' => $opts['image'] ?? null,
            'barcode_type' => 'C128', 'tax_type' => 'exclusive', 'enable_stock' => $enableStock ? 1 : 0,
            'alert_quantity' => $enableStock ? 10 : 0, 'is_inactive' => 0, 'not_for_selling' => 0,
            'created_by' => $this->ownerId, 'created_at' => now(), 'updated_at' => now(),
        ]);
        $pvId = DB::table('product_variations')->insertGetId([
            'name' => 'DUMMY', 'product_id' => $pid, 'is_dummy' => 1, 'created_at' => now(), 'updated_at' => now(),
        ]);
        $vid = DB::table('variations')->insertGetId([
            'name' => 'DUMMY', 'product_id' => $pid, 'sub_sku' => 'SHIFA'.strtoupper(substr(md5($name.$pid), 0, 8)),
            'product_variation_id' => $pvId, 'default_purchase_price' => $cost, 'dpp_inc_tax' => $cost,
            'profit_percent' => 0, 'default_sell_price' => $sell, 'sell_price_inc_tax' => $sell,
            'combo_variations' => json_encode($opts['combo'] ?? []), 'created_at' => now(), 'updated_at' => now(),
        ]);
        if ($enableStock) {
            foreach ($this->loc as $code => $locId) {
                DB::table('variation_location_details')->insert([
                    'product_id' => $pid, 'product_variation_id' => $pvId, 'variation_id' => $vid,
                    'location_id' => $locId, 'qty_available' => $opts['stock_qty_default'] ?? 100,
                    'created_at' => now(), 'updated_at' => now(),
                ]);
            }
        }
        return ['product_id' => (int) $pid, 'variation_id' => (int) $vid, 'cost' => $cost, 'sell' => $sell, 'name' => $name];
    }

    /* ── Mock product imagery (generated, no external assets) ─────────────── */

    private function ensureDefaultImage(): void
    {
        $path = public_path('img/default.png');
        if (! file_exists($path) && function_exists('imagecreatetruecolor')) {
            $this->renderPlaceholder($path, 'Shifa One', [13, 148, 136]);
        }
    }

    private function categoryImage(string $cat): string
    {
        if (isset($this->catImageCache[$cat])) {
            return $this->catImageCache[$cat];
        }
        $slug = trim(preg_replace('/[^a-z0-9]+/i', '-', $cat), '-');
        $file = 'shifa-'.strtolower($slug).'.png';
        $dir = public_path('uploads/'.config('constants.product_img_path', 'img'));
        if (! is_dir($dir)) {
            @mkdir($dir, 0775, true);
        }
        $path = $dir.'/'.$file;
        if (! file_exists($path) && function_exists('imagecreatetruecolor')) {
            $this->renderPlaceholder($path, $cat, $this->categoryColour($cat));
        }
        return $this->catImageCache[$cat] = $file;
    }

    /** @return array{0:int,1:int,2:int} */
    private function categoryColour(string $cat): array
    {
        $palette = [
            'Vaccines' => [16, 185, 129],
            'Pharmacy' => [59, 130, 246],
            'Medical Supplies' => [99, 102, 241],
            'Cafeteria' => [245, 158, 11],
            'Clinical Services' => [13, 148, 136],
            'Diagnostic Imaging' => [139, 92, 246],
            'Laboratory Tests' => [236, 72, 153],
            'Controlled Drugs' => [220, 38, 38],
        ];
        return $palette[$cat] ?? [13, 148, 136];
    }

    private function renderPlaceholder(string $path, string $label, array $rgb): void
    {
        $w = $h = 480;
        $img = imagecreatetruecolor($w, $h);
        [$r, $g, $b] = $rgb;
        imagefilledrectangle($img, 0, 0, $w, $h, imagecolorallocate($img, $r, $g, $b));
        $disc = imagecolorallocatealpha($img, 255, 255, 255, 105);
        imagefilledellipse($img, (int) ($w / 2), (int) ($h / 2 - 24), 240, 240, $disc);
        $white = imagecolorallocate($img, 255, 255, 255);
        $font = 5;
        $lineH = imagefontheight($font) + 8;
        $lines = [];
        $cur = '';
        foreach (explode(' ', $label) as $word) {
            $try = $cur === '' ? $word : $cur.' '.$word;
            if (strlen($try) > 14 && $cur !== '') { $lines[] = $cur; $cur = $word; }
            else { $cur = $try; }
        }
        if ($cur !== '') { $lines[] = $cur; }
        $y = (int) ($h - 150);
        foreach ($lines as $i => $line) {
            $tw = imagefontwidth($font) * strlen($line);
            imagestring($img, $font, (int) (($w - $tw) / 2), $y + $i * $lineH, $line, $white);
        }
        imagepng($img, $path);
        imagedestroy($img);
    }

    private function loadCatalog(): void
    {
        $rows = DB::table('products as p')
            ->join('variations as v', 'v.product_id', '=', 'p.id')
            ->where('p.business_id', $this->businessId)->where('p.sku', 'like', 'SHIFA%')
            ->get(['p.id as product_id', 'v.id as variation_id', 'v.dpp_inc_tax as cost', 'v.sell_price_inc_tax as sell', 'p.name', 'p.category_id', 'p.enable_stock']);
        foreach ($rows as $r) {
            $bucket = $r->enable_stock ? 'managed' : ($r->category_id == ($this->cat['Clinical Services'] ?? -1) ? 'service' : 'fnb');
            $this->catalog[$bucket][] = (array) $r;
        }
    }

    // ---- contacts: suppliers + patient customers --------------------------

    private function contacts(): void
    {
        if (DB::table('contacts')->where('business_id', $this->businessId)->where('type', '!=', 'customer')->where('is_default', 0)->exists()) {
            $this->loadContacts();
            $this->info('Contacts already present — reusing.');
            return;
        }
        $suppliers = [
            ['Cairo Pharma Distributors', 'موزعو القاهرة للأدوية'],
            ['Nile Medical Disposables', 'النيل للمستلزمات الطبية'],
            ['Delta Lab Reagents & Diagnostics', 'دلتا لكواشف المعامل والتشخيص'],
        ];
        foreach ($suppliers as [$name, $nameAr]) {
            $this->contactIds['supplier'][] = $this->makeContact('supplier', $name, $nameAr, $name);
        }
        // Patients (customer contacts). clc_patients are attached in clinic().
        $customers = [
            'Ahmed Saleh', 'Mona Khalil', 'Karim Adel', 'Sara Ibrahim', 'Khaled Hassan',
            'Layla Mahmoud', 'Youssef Nabil', 'Dina Farouk', 'Hany Wagdy', 'Nour Saad',
            'Mostafa Zaki', 'Rania Samir',
        ];
        foreach ($customers as $name) {
            $this->contactIds['customer'][] = $this->makeContact('customer', $name, null);
        }
        $this->info('Contacts: '.count($suppliers).' suppliers + '.count($customers).' patient customers (mobile '.self::MOBILE.').');
    }

    private function makeContact(string $type, string $name, ?string $nameAr = null, ?string $supplierBusiness = null): int
    {
        $ref = $this->businessUtil->setAndGetReferenceCount('contacts', $this->businessId);
        $contactRef = $this->businessUtil->generateReferenceNumber('contacts', $ref, $this->businessId);
        return (int) DB::table('contacts')->insertGetId([
            'business_id' => $this->businessId, 'type' => $type, 'name' => $name,
            'supplier_business_name' => $type === 'supplier' ? ($supplierBusiness ?: $name) : null,
            'mobile' => self::MOBILE, 'contact_id' => $contactRef, 'created_by' => $this->ownerId,
            'credit_limit' => null, 'created_at' => now(), 'updated_at' => now(),
        ]);
    }

    private function loadContacts(): void
    {
        $this->contactIds['supplier'] = DB::table('contacts')->where('business_id', $this->businessId)
            ->whereIn('type', ['supplier', 'both'])->where('is_default', 0)->pluck('id')->all();
        $this->contactIds['customer'] = DB::table('contacts')->where('business_id', $this->businessId)
            ->where('type', 'customer')->where('is_default', 0)->pluck('id')->all();
    }

    // ---- clinic reference: medicines + lookups ----------------------------

    private function clinicReference(): void
    {
        // Global lookups (specialties, exam templates, soap options, system
        // findings, vital ranges) were copied into the DB at install-bootstrap.
        // Business-scoped reference is seeded by the ClinicCore seeders below
        // (they target the first/only business = Shifa One). Idempotent.
        $seeders = [
            \Modules\ClinicCore\Database\Seeders\AppointmentTypesSeeder::class,
            \Modules\ClinicCore\Database\Seeders\AppointmentReasonsSeeder::class,
            \Modules\ClinicCore\Database\Seeders\MedicineCategoriesSeeder::class,
            \Modules\ClinicCore\Database\Seeders\VaccineTypesSeeder::class,
            \Modules\ClinicCore\Database\Seeders\MedicalConditionsSeeder::class,
            \Modules\ClinicCore\Database\Seeders\DefaultMedicinesSeeder::class,
        ];
        foreach ($seeders as $class) {
            if (class_exists($class)) {
                try { $this->call($class); } catch (\Throwable $e) {
                    $this->info('  ('.class_basename($class).' skipped: '.$e->getMessage().')');
                }
            }
        }

        // Flag a few clc_medicines as controlled (drives the controlled-drug
        // dispense gate / register).
        if (Schema::hasTable('clc_medicines') && Schema::hasColumn('clc_medicines', 'controlled_schedule')) {
            foreach ([
                ['Morphine', 'Schedule II'], ['Pethidine', 'Schedule II'],
                ['Diazepam', 'Schedule IV'], ['Tramadol', 'Schedule IV'], ['Phenobarbital', 'Schedule IV'],
            ] as [$name, $schedule]) {
                $existing = DB::table('clc_medicines')->where('business_id', $this->businessId)->where('name', 'like', $name.'%')->first();
                if ($existing) {
                    DB::table('clc_medicines')->where('id', $existing->id)->update(['controlled_schedule' => $schedule, 'updated_at' => now()]);
                } else {
                    DB::table('clc_medicines')->insert([
                        'business_id' => $this->businessId, 'name' => $name, 'generic_name' => $name,
                        'company' => 'Controlled / Restricted', 'unit' => 'unit',
                        'description' => 'Controlled substance — register-tracked.', 'status' => 'active',
                        'controlled_schedule' => $schedule, 'created_at' => now(), 'updated_at' => now(),
                    ]);
                }
            }
        }
        // Human vaccine types (the inherited global lookups are derma-flavoured)
        // — business-scoped so the immunisation demo reads correctly.
        if (Schema::hasTable('clc_vaccine_types')
            && ! DB::table('clc_vaccine_types')->where('business_id', $this->businessId)->exists()) {
            foreach ([
                ['Tetanus Toxoid', 'Generic', 120], ['Hepatitis B', 'Generic', 60],
                ['Influenza (seasonal)', 'Generic', 12], ['COVID-19 Booster', 'Generic', 12],
                ['MMR (Measles/Mumps/Rubella)', 'Generic', 120], ['Pneumococcal', 'Generic', 60],
            ] as [$name, $maker, $validMonths]) {
                DB::table('clc_vaccine_types')->insert([
                    'business_id' => $this->businessId, 'name' => $name, 'manufacturer' => $maker,
                    'category' => 'Human', 'valid_for_months' => $validMonths, 'status' => 'active',
                    'created_at' => now(), 'updated_at' => now(),
                ]);
            }
        }

        $meds = DB::table('clc_medicines')->where('business_id', $this->businessId)->count();
        $this->info("Clinic reference: {$meds} medicines available; controlled drugs flagged.");
    }

    // ---- clinic: departments, doctors, patients, appointments, Rx, vaccines

    private function clinic(): void
    {
        if (! Schema::hasTable('clc_patients')) {
            $this->info('ClinicCore tables absent — skipping clinic section.');
            return;
        }
        if (DB::table('clc_patients')->where('business_id', $this->businessId)->exists()) {
            $this->patients = DB::table('clc_patients')->where('business_id', $this->businessId)
                ->get(['id as patient_id', 'contact_id', 'patient_name as name', 'default_specialty_id'])
                ->map(fn ($r) => (array) $r)->all();
            $this->doctorIds = DB::table('clc_doctors')->where('business_id', $this->businessId)->pluck('id')->all();
            $this->info('Clinic data already present — reusing patients/doctors.');
            return;
        }

        $this->specialty = DB::table('clc_specialties')->whereNull('business_id')->pluck('id', 'code')->toArray();

        // Clinic locations (drive the Location → Department → Doctor cascade).
        $clinicLoc = [];
        if (Schema::hasTable('clc_clinic_locations')) {
            foreach (['MAIN' => 'New Cairo', 'BRANCH' => 'Maadi'] as $code => $cName) {
                $clinicLoc[$code] = DB::table('clc_clinic_locations')->insertGetId([
                    'business_id' => $this->businessId, 'business_location_id' => $this->loc[$code] ?? null,
                    'name' => 'Shifa One — '.$cName, 'address' => $cName.', Cairo', 'phone' => self::MOBILE,
                    'email' => 'clinic@shifaone.test', 'status' => 'active', 'created_at' => now(), 'updated_at' => now(),
                ]);
            }
        }
        $hasDeptLocation = Schema::hasColumn('clc_departments', 'location_id');

        // Departments + a doctor per specialty (alternating clinic locations).
        $i = 0;
        foreach (self::SPECIALTY_PLAN as [$code, $deptName, $doctorName, $profileTable, $profileData]) {
            if (isset($this->dept[$deptName])) { $i++; continue; }
            $bizLoc = $i % 2 === 0 ? ($this->loc['MAIN'] ?? null) : ($this->loc['BRANCH'] ?? null);
            $row = [
                'business_id' => $this->businessId, 'name' => $deptName, 'status' => 'active',
                'created_at' => now(), 'updated_at' => now(),
            ];
            if ($hasDeptLocation) { $row['location_id'] = $bizLoc; }
            $deptId = DB::table('clc_departments')->insertGetId($row);
            $this->dept[$deptName] = $deptId;

            // Link a doctor to a staff physician where the name matches.
            $userId = collect(['dr.yasmin.shifa', 'dr.omar.shifa', 'reception.shifa', 'pharmacist.shifa'])
                ->first(fn ($u) => str_contains($doctorName, ucfirst(explode('.', $u)[1] ?? '')));
            $doctorId = DB::table('clc_doctors')->insertGetId([
                'business_id' => $this->businessId,
                'user_id' => $userId ? ($this->staffIds[$userId] ?? null) : null,
                'department_id' => $deptId, 'name' => $doctorName, 'email' => null, 'phone' => self::MOBILE,
                'specialization' => $deptName, 'consultation_fee' => 450, 'status' => 'active',
                'created_at' => now(), 'updated_at' => now(),
            ]);
            $this->doctorIds[] = $doctorId;
            $this->deptDoctor[$code] = $doctorId;

            if (Schema::hasTable('clc_doctor_departments')) {
                DB::table('clc_doctor_departments')->insert([
                    'doctor_id' => $doctorId, 'department_id' => $deptId, 'created_at' => now(), 'updated_at' => now(),
                ]);
            }
            if (Schema::hasTable('clc_doctor_schedules')) {
                foreach ([0, 1, 2, 3, 4] as $dow) {
                    DB::table('clc_doctor_schedules')->insert([
                        'doctor_id' => $doctorId, 'department_id' => $deptId, 'day_of_week' => $dow,
                        'start_time' => '09:00:00', 'end_time' => '17:00:00',
                        'slot_duration_minutes' => 30, 'status' => 'active', 'created_at' => now(), 'updated_at' => now(),
                    ]);
                }
            }
            $i++;
        }

        // Patients — one per customer contact, each anchored to a specialty.
        $owners = $this->contactIds['customer'];
        $genders = ['male', 'female'];
        $bloodGroups = ['O+', 'A+', 'B+', 'AB+', 'O-', 'A-'];
        foreach ($owners as $idx => $contactId) {
            $plan = self::SPECIALTY_PLAN[$idx % count(self::SPECIALTY_PLAN)];
            [$code, $deptName, $doctorName, $profileTable, $profileData] = $plan;
            $specialtyId = $this->specialty[$code] ?? null;
            $name = DB::table('contacts')->where('id', $contactId)->value('name');
            // OB/Gyn patients must be female; pregnancy demo.
            $gender = $code === 'gynaecology' ? 'female' : $genders[$idx % 2];
            $patientId = DB::table('clc_patients')->insertGetId([
                'business_id' => $this->businessId, 'default_specialty_id' => $specialtyId,
                'contact_id' => $contactId, 'patient_name' => $name, 'gender' => $gender,
                'dob' => Carbon::now()->subYears(18 + ($idx * 4) % 55)->subMonths($idx % 11)->toDateString(),
                'bloodgroup' => $bloodGroups[$idx % count($bloodGroups)],
                'allergies' => $idx % 3 === 0 ? 'Penicillin' : null,
                'chronic_conditions' => $idx % 4 === 0 ? 'Hypertension' : ($idx % 4 === 1 ? 'Type 2 Diabetes' : null),
                'family_history' => $idx % 5 === 0 ? 'Father — IHD' : null,
                'height_cm' => 150 + ($idx * 3) % 40,
                'status' => 'active', 'created_at' => now(), 'updated_at' => now(),
            ]);
            $this->patients[] = ['patient_id' => (int) $patientId, 'contact_id' => (int) $contactId, 'code' => $code, 'name' => $name];

            // Specialty profile row (the plugin's *_patient_profiles extension).
            $this->persistSpecialtyProfile($patientId, $profileTable, $profileData);

            // Dental patients get a couple of tooth-chart findings.
            if ($code === 'dental' && Schema::hasTable('dental_tooth_findings')) {
                foreach ([['26', 'caries'], ['46', 'filled']] as [$tooth, $cond]) {
                    DB::table('dental_tooth_findings')->insert([
                        'clc_patient_id' => $patientId, 'tooth_number' => $tooth, 'condition' => $cond,
                        'surfaces' => null, 'note' => null, 'created_at' => now(), 'updated_at' => now(),
                    ]);
                }
            }
        }

        $this->appointmentsAndRx();
        $this->vaccinationsAndReports();

        $this->info('Clinic: '.count($this->dept).' departments, '.count($this->doctorIds).' doctors, '.count($this->patients).' patients across specialties.');
    }

    /** Insert a specialty plugin's *_patient_profiles row (schema-aware). */
    private function persistSpecialtyProfile(int $patientId, string $table, array $data): void
    {
        if (! Schema::hasTable($table)) {
            return;
        }
        $row = ['clc_patient_id' => $patientId, 'created_at' => now(), 'updated_at' => now()];
        foreach ($data as $col => $val) {
            // OB/Gyn LMP is supplied as an offset so the EDD computes from "today".
            if ($col === 'lmp_offset_days') {
                if (Schema::hasColumn($table, 'lmp_date')) {
                    $row['lmp_date'] = Carbon::now()->subDays((int) $val)->toDateString();
                }
                continue;
            }
            if (Schema::hasColumn($table, $col)) {
                $row[$col] = $val;
            }
        }
        DB::table($table)->insert($row);
    }

    private function appointmentsAndRx(): void
    {
        $typeIds = DB::table('clc_appointment_types')->pluck('id', 'name')->toArray();
        $main = $this->loc['MAIN'] ?? reset($this->loc);
        $meds = DB::table('clc_medicines')->where('business_id', $this->businessId)->limit(10)->get(['id', 'name'])->all();

        // status, dayOffset, type(enum), reason, withRx
        $plan = [
            ['completed', -25, 'checkup', 'Specialist Consultation', true],
            ['completed', -18, 'checkup', 'Follow-Up Visit', false],
            ['completed', -12, 'checkup', 'Antenatal Checkup', true],
            ['completed', -6, 'checkup', 'Lab / Blood Work', true],
            ['no_show', -4, 'checkup', 'Follow-Up Visit', false],
            ['cancelled', -2, 'checkup', 'General Consultation', false],
            ['confirmed', 1, 'checkup', 'Specialist Consultation', false],
            ['confirmed', 4, 'surgery', 'Minor Surgery', false],
            ['pending', 7, 'vaccination', 'Vaccination Visit', false],
            ['pending', 10, 'checkup', 'X-Ray / Imaging', false],
        ];
        $made = 0; $rx = 0;
        foreach ($plan as $i => [$status, $dayOffset, $type, $reason, $withRx]) {
            $patient = $this->patients[$i % count($this->patients)];
            $doctorId = $this->deptDoctor[$patient['code']] ?? $this->doctorIds[$i % count($this->doctorIds)];
            $specialtyId = $this->specialty[$patient['code']] ?? null;
            $date = Carbon::now()->addDays($dayOffset);
            $completed = $status === 'completed';
            $apptId = DB::table('clc_appointments')->insertGetId([
                'business_id' => $this->businessId, 'specialty_id' => $specialtyId, 'location_id' => $main,
                'contact_id' => $patient['contact_id'], 'patient_id' => $patient['patient_id'],
                'doctor_id' => $doctorId, 'department_id' => null,
                'ref_no' => 'SH-APT-'.str_pad((string) ($i + 1), 4, '0', STR_PAD_LEFT),
                'appointment_date' => $date->toDateString(), 'appointment_time' => sprintf('%02d:00:00', 9 + ($i % 7)),
                'duration_minutes' => 30,
                'weight_kg' => $completed ? 72.5 : null,
                'temperature' => $completed ? 37.0 : null,
                'heart_rate' => $completed ? 78 : null,
                'respiratory_rate' => $completed ? 16 : null,
                'systolic_bp' => $completed ? 128 : null,
                'diastolic_bp' => $completed ? 82 : null,
                'spo2' => $completed ? 98 : null,
                'blood_glucose' => $completed ? 110 : null,
                'bmi' => $completed ? 26.4 : null,
                'soap_subjective' => $completed ? 'Patient reports intermittent symptoms over the past week.' : null,
                'soap_objective' => $completed ? 'Vitals stable. Examination largely unremarkable.' : null,
                'soap_assessment' => $completed ? 'Working diagnosis consistent with presenting complaint.' : null,
                'soap_plan' => $completed ? 'Start treatment, lifestyle advice, follow-up in 2 weeks.' : null,
                'reason' => $reason, 'type' => $type,
                'type_ids' => isset($typeIds[$reason]) ? json_encode([$typeIds[$reason]]) : null,
                'status' => $status,
                'consultation_started_at' => $completed ? $date->copy()->setTime(9, 5) : null,
                'consultation_ended_at' => $completed ? $date->copy()->setTime(9, 35) : null,
                'created_at' => now(), 'updated_at' => now(),
            ]);
            $made++;

            if ($withRx && ! empty($meds)) {
                $presId = DB::table('clc_prescriptions')->insertGetId([
                    'business_id' => $this->businessId, 'ref_no' => 'SH-RX-'.str_pad((string) ($i + 1), 4, '0', STR_PAD_LEFT),
                    'appointment_id' => $apptId, 'contact_id' => $patient['contact_id'], 'patient_id' => $patient['patient_id'],
                    'doctor_id' => $doctorId, 'prescribed_date' => $date->toDateString(),
                    'valid_until' => $date->copy()->addDays(30)->toDateString(),
                    'diagnosis' => 'As per consultation', 'status' => 'active',
                    'notes' => 'Take as directed. Complete the full course.',
                    'created_at' => now(), 'updated_at' => now(),
                ]);
                foreach (array_slice($meds, 0, 2) as $k => $m) {
                    DB::table('clc_prescription_items')->insert([
                        'prescription_id' => $presId, 'medicine_id' => $m->id, 'medicine_name' => $m->name,
                        'dosage' => $k === 0 ? '1 tablet' : '1 capsule', 'frequency' => $k === 0 ? 'BID' : 'OD',
                        'duration_days' => 7, 'instructions' => 'After meals', 'quantity' => 14,
                        'created_at' => now(), 'updated_at' => now(),
                    ]);
                }
                $rx++;
            }
        }
        $this->info("Clinic: {$made} appointments (pending→completed/no-show/cancelled), {$rx} prescriptions.");
    }

    private function vaccinationsAndReports(): void
    {
        // Prefer the business's own human vaccine types (seeded in
        // clinicReference); fall back to any global lookup row.
        $vaccTypes = DB::table('clc_vaccine_types')->where('business_id', $this->businessId)->pluck('id')->all();
        if (empty($vaccTypes)) {
            $vaccTypes = DB::table('clc_vaccine_types')->pluck('id')->all();
        }
        $made = 0;
        if ($vaccTypes) {
            foreach ($this->patients as $i => $patient) {
                if ($i % 2 === 1) { continue; } // ~half the patients
                // Stagger administered dates so the later ones fall past their
                // 12-month booster (overdue → drives ClinicWhatsApp recalls).
                $administered = Carbon::now()->subMonths(4 * ($i + 1));
                $nextDue = $administered->copy()->addYear();
                $overdue = $nextDue->isPast();
                DB::table('clc_vaccinations')->insert([
                    'business_id' => $this->businessId, 'patient_id' => $patient['patient_id'], 'contact_id' => $patient['contact_id'],
                    'vaccine_type_id' => $vaccTypes[$i % count($vaccTypes)], 'dose' => '1 dose',
                    'administered_date' => $administered->toDateString(),
                    'next_due_date' => $nextDue->toDateString(),
                    'administered_by' => $this->staffIds['nurse.shifa'] ?? $this->ownerId,
                    'status' => $overdue ? 'overdue' : 'administered',
                    'reminder_sent' => 0, 'notes' => null, 'created_at' => now(), 'updated_at' => now(),
                ]);
                $made++;
            }

            // One booster due in 4 days (status administered) so the
            // ClinicWhatsApp vaccination-recall window (default 7d) fires.
            $p = $this->patients[1] ?? $this->patients[0];
            DB::table('clc_vaccinations')->insert([
                'business_id' => $this->businessId, 'patient_id' => $p['patient_id'], 'contact_id' => $p['contact_id'],
                'vaccine_type_id' => $vaccTypes[0], 'dose' => '1 dose',
                'administered_date' => Carbon::now()->subMonths(12)->addDays(4)->toDateString(),
                'next_due_date' => Carbon::now()->addDays(4)->toDateString(),
                'administered_by' => $this->staffIds['nurse.shifa'] ?? $this->ownerId,
                'status' => 'administered', 'reminder_sent' => 0, 'notes' => 'Annual booster due.',
                'created_at' => now(), 'updated_at' => now(),
            ]);
            $made++;
        }

        $reports = 0;
        if (Schema::hasTable('clc_medical_reports')) {
            foreach (array_slice($this->patients, 0, 3) as $i => $patient) {
                DB::table('clc_medical_reports')->insert([
                    'business_id' => $this->businessId, 'patient_id' => $patient['patient_id'], 'contact_id' => $patient['contact_id'],
                    'doctor_id' => $this->doctorIds[$i % count($this->doctorIds)],
                    'report_type' => 'clinical', 'source' => 'consultation',
                    'report_title' => $patient['name'].' — Health Summary',
                    'findings' => 'Overall stable. Continue current management. Recommend annual checkup.',
                    'created_at' => now(), 'updated_at' => now(),
                ]);
                $reports++;
            }
        }
        $this->info("Clinic: {$made} vaccinations (incl. overdue for recalls), {$reports} medical reports.");
    }

    // ---- lab: ClinicLab linkage (LabCore device + sessions) ---------------

    private function lab(): void
    {
        if (! Schema::hasTable('lab_test_sessions') || empty($this->patients)) {
            return;
        }
        if (DB::table('lab_test_sessions')->where('business_id', $this->businessId)->exists()) {
            $this->info('Lab sessions already present — reusing.');
            return;
        }
        $deviceId = DB::table('lab_devices')->insertGetId([
            'business_id' => $this->businessId, 'vendor' => 'generic', 'model' => 'Chemistry Analyzer',
            'name' => 'Shifa Lab Analyzer', 'connection_type' => 'manual', 'status' => 'active',
            'created_at' => now(), 'updated_at' => now(),
        ]);

        // One completed CBC session (one abnormal) linked to a patient.
        $p = $this->patients[0];
        $this->labSession($deviceId, 'CBC-2001', $p, 'blood', [
            ['WBC', 'White Blood Cells', 11.8, '10^3/uL', 4.0, 11.0, true],
            ['RBC', 'Red Blood Cells', 4.9, '10^6/uL', 4.5, 5.9, false],
            ['HGB', 'Hemoglobin', 14.2, 'g/dL', 13.0, 17.0, false],
            ['HCT', 'Hematocrit', 42.0, '%', 40.0, 50.0, false],
            ['PLT', 'Platelets', 250, '10^3/uL', 150, 400, false],
        ]);
        // One chemistry session linked to the endocrinology patient.
        $p2 = $this->patients[5] ?? $this->patients[0];
        $this->labSession($deviceId, 'CHEM-2002', $p2, 'serum', [
            ['GLU', 'Glucose (fasting)', 142, 'mg/dL', 70, 100, true],
            ['HBA1C', 'HbA1c', 7.8, '%', 4.0, 5.6, true],
            ['CREA', 'Creatinine', 1.0, 'mg/dL', 0.7, 1.3, false],
            ['CHOL', 'Total Cholesterol', 210, 'mg/dL', 0, 200, true],
        ]);

        // One pending worklist order (the "order lab" flow).
        if (Schema::hasTable('lab_worklist')) {
            $p3 = $this->patients[1] ?? $this->patients[0];
            DB::table('lab_worklist')->insert([
                'business_id' => $this->businessId, 'vendor' => 'generic', 'device_id' => $deviceId,
                'sample_no' => 'WL-3001', 'patient_name' => $p3['name'], 'tests' => json_encode(['Lipid Profile']),
                'status' => 'pending', 'contact_id' => $p3['contact_id'], 'clc_patient_id' => $p3['patient_id'],
                'created_by' => $this->ownerId, 'created_at' => now(), 'updated_at' => now(),
            ]);
        }
        $this->info('Lab: 1 analyzer device, 2 completed sessions (abnormal flags) + 1 pending order, all linked to patients.');
    }

    /** Insert one completed lab session + its results. $rows: [code,name,val,unit,lo,hi,abnormal]. */
    private function labSession(int $deviceId, string $sampleNo, array $patient, string $sampleType, array $rows): void
    {
        $sessionId = DB::table('lab_test_sessions')->insertGetId([
            'uuid' => (string) Str::uuid(), 'business_id' => $this->businessId, 'device_id' => $deviceId, 'vendor' => 'generic',
            'sample_no' => $sampleNo, 'patient_name' => $patient['name'], 'sample_type' => $sampleType,
            'test_date' => now()->toDateString(), 'test_time' => now()->toTimeString(), 'source' => 'analyzer',
            'contact_id' => $patient['contact_id'], 'clc_patient_id' => $patient['patient_id'],
            'created_at' => now(), 'updated_at' => now(),
        ]);
        foreach ($rows as [$code, $aname, $val, $unit, $lo, $hi, $abn]) {
            DB::table('lab_test_results')->insert([
                'test_session_id' => $sessionId, 'analyte_code' => $code, 'analyte_name' => $aname,
                'value' => $val, 'unit' => $unit, 'ref_lower' => $lo, 'ref_upper' => $hi,
                'raw_flag' => $abn ? 'H' : null, 'is_abnormal' => $abn ? 1 : 0,
                'created_at' => now(), 'updated_at' => now(),
            ]);
        }
    }

    // ---- contracts: care plans + subscriptions ----------------------------

    private function contracts(): void
    {
        if (! Schema::hasTable('clc_wellness_plans') || empty($this->patients)) {
            return;
        }
        if (DB::table('clc_wellness_plans')->where('business_id', $this->businessId)->exists()) {
            $this->info('Care plans already present — reusing.');
            return;
        }
        $plans = [
            ['Family Health Membership', 'Annual family plan: 4 GP visits, 2 specialist visits, 10% pharmacy discount.', 300, 3200],
            ['Chronic Care — Diabetes', 'Quarterly endocrinology review, HbA1c monitoring, dietitian sessions.', 250, 2600],
        ];
        $planIds = [];
        foreach ($plans as [$name, $desc, $m, $a]) {
            $pid = DB::table('clc_wellness_plans')->insertGetId([
                'business_id' => $this->businessId, 'name' => $name, 'description' => $desc,
                'price_monthly' => $m, 'price_annual' => $a, 'status' => 'active',
                'created_at' => now(), 'updated_at' => now(),
            ]);
            $planIds[] = $pid;
            foreach ([
                ['visits', 'Doctor visits', 6, null],
                ['screenings', 'Screening tests', 2, null],
                ['discount_pct', 'Pharmacy discount', null, 10],
            ] as [$kind, $label, $qty, $val]) {
                DB::table('clc_wellness_plan_benefits')->insert([
                    'plan_id' => $pid, 'kind' => $kind, 'label' => $label,
                    'quantity_per_year' => $qty, 'value' => $val, 'created_at' => now(), 'updated_at' => now(),
                ]);
            }
        }
        $subs = 0;
        foreach (array_slice($this->patients, 0, 4) as $i => $patient) {
            DB::table('clc_wellness_subscriptions')->insert([
                'business_id' => $this->businessId, 'contact_id' => $patient['contact_id'], 'patient_id' => $patient['patient_id'],
                'plan_id' => $planIds[$i % count($planIds)], 'status' => 'active',
                'billing_cycle' => $i % 2 ? 'monthly' : 'annual',
                'started_at' => Carbon::now()->subMonths(2)->toDateString(),
                'renews_at' => Carbon::now()->addMonths(10)->toDateString(),
                'created_by' => $this->ownerId, 'created_at' => now(), 'updated_at' => now(),
            ]);
            $subs++;
        }
        $this->info('Contracts: '.count($planIds)." care plans, {$subs} active subscriptions (membership contracts).");
    }

    // ---- trade: POS sells (varied payment status) + purchase --------------

    private function trade(): void
    {
        if (DB::table('transactions')->where('business_id', $this->businessId)->where('type', 'sell')->where('invoice_no', 'like', 'SHIFA-INV%')->exists()) {
            $this->info('POS sells already present — reusing.');
            return;
        }
        if (empty($this->catalog['service']) && empty($this->catalog['managed'])) {
            $this->loadCatalog();
        }
        $main = $this->loc['MAIN'] ?? reset($this->loc);
        $items = array_merge($this->catalog['service'], $this->catalog['managed'], $this->catalog['fnb']);
        $customers = $this->contactIds['customer'] ?: DB::table('contacts')->where('business_id', $this->businessId)->where('type', 'customer')->pluck('id')->all();

        $plan = [
            ['paid', [0, 4]], ['paid', [1]], ['paid', [10, 3]], ['partial', [2, 5]], ['due', [6]],
        ];
        $made = 0;
        foreach ($plan as $idx => [$payStatus, $itemIdx]) {
            $lines = []; $total = 0.0;
            foreach ($itemIdx as $k) {
                $it = $items[$k % max(count($items), 1)];
                $qty = 1 + ($k % 2);
                $price = (float) $it['sell'];
                $total += $price * $qty;
                $lines[] = ['it' => $it, 'qty' => $qty, 'price' => $price];
            }
            $date = Carbon::now()->subDays(($idx + 1) * 3);
            $txnId = DB::table('transactions')->insertGetId([
                'business_id' => $this->businessId, 'location_id' => $main, 'type' => 'sell', 'status' => 'final',
                'contact_id' => $customers[$idx % count($customers)],
                'invoice_no' => 'SHIFA-INV-'.str_pad((string) ($idx + 1), 4, '0', STR_PAD_LEFT),
                'transaction_date' => $date, 'total_before_tax' => $total, 'tax_amount' => 0,
                'discount_amount' => 0, 'final_total' => $total,
                'payment_status' => $payStatus === 'paid' ? 'paid' : ($payStatus === 'partial' ? 'partial' : 'due'),
                'created_by' => $this->ownerId, 'essentials_duration' => 0,
                'created_at' => $date, 'updated_at' => $date,
            ]);
            foreach ($lines as $ln) {
                DB::table('transaction_sell_lines')->insert([
                    'transaction_id' => $txnId, 'product_id' => $ln['it']['product_id'], 'variation_id' => $ln['it']['variation_id'],
                    'quantity' => $ln['qty'], 'unit_price' => $ln['price'], 'unit_price_before_discount' => $ln['price'],
                    'unit_price_inc_tax' => $ln['price'], 'item_tax' => 0, 'created_at' => $date, 'updated_at' => $date,
                ]);
            }
            $paid = $payStatus === 'paid' ? $total : ($payStatus === 'partial' ? round($total / 2, 2) : 0);
            if ($paid > 0) {
                DB::table('transaction_payments')->insert([
                    'transaction_id' => $txnId, 'business_id' => $this->businessId, 'amount' => $paid,
                    'method' => 'cash', 'paid_on' => $date, 'created_by' => $this->ownerId,
                    'payment_ref_no' => 'SHP-'.($idx + 1), 'created_at' => $date, 'updated_at' => $date,
                ]);
            }
            $made++;
        }

        // One received purchase from a supplier.
        $supplier = $this->contactIds['supplier'][0] ?? DB::table('contacts')->where('business_id', $this->businessId)->where('type', 'supplier')->value('id');
        if ($supplier && ! empty($this->catalog['managed'])) {
            $pdate = Carbon::now()->subDays(20);
            $ptotal = 0.0;
            $plines = array_slice($this->catalog['managed'], 0, 4);
            foreach ($plines as $it) { $ptotal += (float) $it['cost'] * 50; }
            $poId = DB::table('transactions')->insertGetId([
                'business_id' => $this->businessId, 'location_id' => $this->loc['STORE'] ?? $main, 'type' => 'purchase',
                'status' => 'received', 'contact_id' => $supplier, 'ref_no' => 'SHIFA-PO-0001',
                'transaction_date' => $pdate, 'total_before_tax' => $ptotal, 'tax_amount' => 0,
                'final_total' => $ptotal, 'payment_status' => 'paid', 'created_by' => $this->ownerId,
                'essentials_duration' => 0, 'created_at' => $pdate, 'updated_at' => $pdate,
            ]);
            foreach ($plines as $it) {
                DB::table('purchase_lines')->insert([
                    'transaction_id' => $poId, 'product_id' => $it['product_id'], 'variation_id' => $it['variation_id'],
                    'quantity' => 50, 'purchase_price' => $it['cost'], 'purchase_price_inc_tax' => $it['cost'],
                    'created_at' => $pdate, 'updated_at' => $pdate,
                ]);
            }
            DB::table('transaction_payments')->insert([
                'transaction_id' => $poId, 'business_id' => $this->businessId, 'amount' => $ptotal,
                'method' => 'bank_transfer', 'paid_on' => $pdate, 'created_by' => $this->ownerId,
                'payment_ref_no' => 'SHPP-1', 'created_at' => $pdate, 'updated_at' => $pdate,
            ]);
        }
        $this->info("Trade: {$made} POS sells (paid/partial/due) + 1 received purchase.");
    }

    // ---- Advanced Transfer: ensure the owner can see the module menu ------

    private function advancedTransfer(): void
    {
        if (empty(\App\System::getProperty('AdvancedTransfer_version'))) {
            \App\System::addProperty('AdvancedTransfer_version', '1.1.0');
        }
        $perms = [
            'tpsTransfer.access_index_stock_transfers',
            'tpsTransfer.access_add_stock_transfer',
            'tpsTransfer.access_prepare_stock_transfer',
            'tpsTransfer.access_received_stock_transfer',
            'tpsTransfer.access_transactions_stock_transfers',
            'tpsTransfer.access_settings_stock_transfer',
            'tpsTransfer.access_all_locations_from',
            'tpsTransfer.access_all_locations_to',
        ];
        foreach ($perms as $p) {
            Permission::firstOrCreate(['name' => $p, 'guard_name' => 'web']);
        }
        $owner = User::find($this->ownerId);
        if ($owner) {
            try {
                $owner->givePermissionTo($perms);
                $adminRole = \Spatie\Permission\Models\Role::where('name', 'Admin#'.$this->businessId)->first();
                if ($adminRole) { $adminRole->givePermissionTo($perms); }
            } catch (\Throwable $e) {
                $this->info('  (advanced-transfer permission grant skipped: '.$e->getMessage().')');
            }
        }
        $this->info('Advanced Transfer: permissions granted to owner — module menu visible.');
    }

    // ---- AI: point AiCore at the medical_clinic vertical ------------------

    private function ai(): void
    {
        if (! Schema::hasTable('ai_providers') || ! Schema::hasColumn('ai_providers', 'business_vertical')) {
            return;
        }
        DB::table('ai_providers')->updateOrInsert(
            ['business_id' => $this->businessId, 'name' => 'default'],
            ['business_vertical' => 'medical_clinic', 'updated_at' => now(), 'created_at' => now()]
        );
        $this->info('AI: AiCore vertical set to medical_clinic for the business.');
    }

    // ---- OAuth: Passport personal-access client (portal/PWA login) --------

    private function passport(): void
    {
        if (DB::table('oauth_clients')->where('personal_access_client', 1)->exists()) {
            $this->info('Passport personal-access client already present.');
            return;
        }
        \Illuminate\Support\Facades\Artisan::call('passport:client', [
            '--personal' => true, '--name' => 'Shifa One Personal Access', '--no-interaction' => true,
        ]);
        $this->info('Passport personal-access client created (portal / API login → Bearer).');
    }

    // ---- helpers ----------------------------------------------------------

    private function wipeBusiness(int $businessId): void
    {
        $userIds = DB::table('users')->where('business_id', $businessId)->pluck('id')->all();

        // Specialty profile rows (keyed by clc_patient_id).
        $patientIds = Schema::hasTable('clc_patients')
            ? DB::table('clc_patients')->where('business_id', $businessId)->pluck('id')->all() : [];
        if ($patientIds) {
            $profileTables = DB::select(
                "SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name LIKE '%patient_profiles'"
            );
            foreach ($profileTables as $pt) {
                $t = $pt->table_name ?? $pt->TABLE_NAME ?? null;
                if ($t && Schema::hasColumn($t, 'clc_patient_id')) {
                    DB::table($t)->whereIn('clc_patient_id', $patientIds)->delete();
                }
            }
            if (Schema::hasTable('dental_tooth_findings')) {
                DB::table('dental_tooth_findings')->whereIn('clc_patient_id', $patientIds)->delete();
            }
        }
        // Child rows first.
        if (Schema::hasTable('clc_prescription_items')) {
            DB::table('clc_prescription_items')->whereIn('prescription_id',
                DB::table('clc_prescriptions')->where('business_id', $businessId)->select('id'))->delete();
        }
        if (Schema::hasTable('lab_test_results')) {
            DB::table('lab_test_results')->whereIn('test_session_id',
                DB::table('lab_test_sessions')->where('business_id', $businessId)->select('id'))->delete();
        }
        if (Schema::hasTable('clc_wellness_plan_benefits')) {
            DB::table('clc_wellness_plan_benefits')->whereIn('plan_id',
                DB::table('clc_wellness_plans')->where('business_id', $businessId)->select('id'))->delete();
        }
        $doctorIds = Schema::hasTable('clc_doctors')
            ? DB::table('clc_doctors')->where('business_id', $businessId)->pluck('id')->all() : [];
        if ($doctorIds) {
            foreach (['clc_doctor_schedules', 'clc_doctor_departments'] as $t) {
                if (Schema::hasTable($t)) { DB::table($t)->whereIn('doctor_id', $doctorIds)->delete(); }
            }
        }

        $clcTables = [
            'clc_appointments', 'clc_prescriptions', 'clc_vaccinations', 'clc_medical_reports',
            'clc_patient_conditions', 'clc_patients', 'clc_doctors', 'clc_departments', 'clc_clinic_locations',
            'clc_medicines',
            'clc_wellness_subscriptions', 'clc_wellness_plan_benefits', 'clc_wellness_plans',
            'lab_worklist', 'lab_test_results', 'lab_test_sessions', 'lab_devices',
            'ai_providers',
        ];
        foreach ($clcTables as $t) {
            if (Schema::hasTable($t) && Schema::hasColumn($t, 'business_id')) {
                DB::table($t)->where('business_id', $businessId)->delete();
            }
        }
        foreach (['transactions', 'contacts', 'products', 'business_locations', 'roles'] as $t) {
            DB::table($t)->where('business_id', $businessId)->delete();
        }
        if ($userIds) {
            DB::table('users')->whereIn('id', $userIds)->delete();
        }
        DB::table('business')->where('id', $businessId)->delete();
    }

    private function info(string $msg): void
    {
        $this->command?->getOutput()->writeln('  <info>·</info> '.$msg);
    }
}
