<?php

namespace App\Services\Care;

use App\Models\Member;

/**
 * Care RBAC: role → abilities + specialty manage / refer / view apps.
 *
 * Authorization must use can() / primaryAppsFor() / referAppsFor() /
 * viewAppsFor() — never role string checks like isDoctor. Legacy `doctor`
 * aliases to general_physician for abilities; desk specialists are narrowed
 * by SpecialtyModuleService keyword matching. `nurse` is a limited floor role
 * (ED / theatre / dialysis nurses are separate).
 *
 * Inheritance: lab_manager ⊃ lab_technician; blood_bank_manager ⊃ blood_bank_officer.
 */
class CarePermissions
{
    /**
     * Child role abilities are merged into the parent (parent wins on duplicates).
     *
     * @var array<string, list<string>>
     */
    protected array $roleInherits = [
        'lab_manager' => ['lab_technician'],
        'blood_bank_manager' => ['blood_bank_officer'],
    ];

    /**
     * Specialty module keys each role may open (manage). Null = no specialty
     * matrix entry (fall back to module config allowlists + desk keywords).
     * Empty list = no specialty manage.
     *
     * @var array<string, list<string>|null>
     */
    protected array $rolePrimaryApps = [
        'super_admin' => null, // all via admin
        'hospital_admin' => null,
        'emergency_physician' => ['emergency', 'radiology', 'pathology', 'blood_bank', 'cardiology'],
        'ed_nurse' => ['emergency', 'blood_bank', 'radiology', 'pathology', 'nursing_services'],
        'general_physician' => [
            'emergency', 'cardiology', 'pediatrics', 'ent', 'dermatology',
            'ophthalmology', 'vaccination', 'child_welfare',
        ],
        'doctor' => [
            'emergency', 'cardiology', 'pediatrics', 'ent', 'dermatology',
            'ophthalmology', 'vaccination', 'child_welfare',
        ], // legacy → GP (desk specialists narrowed elsewhere)
        'surgeon' => ['surgery', 'radiology', 'pathology', 'blood_bank'],
        'theatre_nurse' => ['surgery', 'blood_bank', 'nursing_services'],
        'radiologist' => ['radiology'],
        'radiographer' => ['radiology'],
        'lab_technician' => ['pathology', 'blood_bank'],
        'lab_manager' => ['pathology', 'blood_bank'],
        'pathologist' => ['pathology'],
        'blood_bank_officer' => ['blood_bank'],
        'blood_bank_manager' => ['blood_bank'],
        'pharmacist' => [], // Pharmacy is core, not a specialty module
        'dentist' => ['dentistry'],
        'psychiatrist' => ['psychiatry'],
        'physiotherapist' => ['physiotherapy'],
        'midwife' => ['womens_health', 'nursing_services'],
        'obstetrician' => ['womens_health'],
        'gynecologist' => ['womens_health'],
        'obgyn' => ['womens_health'],
        'labour_ward_nurse' => ['womens_health', 'nursing_services'],
        'pediatrician' => ['pediatrics'],
        'oncologist' => ['oncology', 'infusion'],
        'cardiologist' => ['cardiology'],
        'ent_specialist' => ['ent'],
        'dermatologist' => ['dermatology'],
        'ophthalmologist' => ['ophthalmology'],
        'orthopedist' => ['orthopedics'],
        'podiatrist' => ['podiatry'],
        'fertility_specialist' => ['fertility'],
        'embryologist' => ['fertility'],
        'fertility_nurse' => ['fertility', 'nursing_services'],
        'dialysis_nurse' => ['renal', 'nursing_services'],
        'ambulance_staff' => ['ambulance', 'emergency'],
        'receptionist' => [], // refer only — see roleReferApps
        'billing_officer' => [],
        'cashier' => [],
        'accountant' => [],
        'department_manager' => null, // department analytics — all enabled modules view
        'nurse' => ['vaccination', 'child_welfare', 'infusion', 'womens_health'],
    ];

    /**
     * Specialty modules a role may refer into (beyond manage). Empty / missing = none
     * beyond primary apps. Manage always implies refer.
     *
     * @var array<string, list<string>>
     */
    protected array $roleReferApps = [
        'general_physician' => [
            'dentistry', 'physiotherapy', 'womens_health', 'psychiatry', 'orthopedics',
            'oncology', 'renal', 'surgery', 'radiology', 'pathology', 'blood_bank',
            'infusion', 'podiatry', 'fertility', 'ambulance',
        ],
        'doctor' => [
            'dentistry', 'physiotherapy', 'womens_health', 'psychiatry', 'orthopedics',
            'oncology', 'renal', 'surgery', 'radiology', 'pathology', 'blood_bank',
            'infusion', 'podiatry', 'fertility', 'ambulance',
        ],
        'nurse' => ['emergency', 'pediatrics', 'blood_bank'],
        'pharmacist' => ['oncology', 'infusion', 'womens_health', 'emergency'],
        'receptionist' => [
            'emergency', 'pediatrics', 'vaccination', 'child_welfare', 'dentistry',
            'womens_health', 'ambulance', 'ophthalmology', 'physiotherapy',
        ],
        'obstetrician' => ['fertility'],
        'gynecologist' => ['fertility'],
        'obgyn' => ['fertility'],
        'midwife' => ['fertility'],
    ];

    /**
     * Specialty modules a role may view without refer/manage. Empty / missing = none
     * beyond primary + refer. Manage and refer always imply view.
     *
     * @var array<string, list<string>>
     */
    protected array $roleViewApps = [
        'lab_technician' => ['radiology'],
        'lab_manager' => ['radiology'],
        'pathologist' => ['blood_bank'],
        'pharmacist' => ['oncology', 'infusion', 'womens_health', 'emergency'],
    ];

    /**
     * @var array<string, list<string>>
     */
    protected array $roleAbilities = [
        'super_admin' => ['*'],
        'hospital_admin' => ['*'],

        'emergency_physician' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'lab.view', 'lab.results.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'emergency.queue.view', 'emergency.discharge',
            'radiology.request', 'pathology.request', 'bloodbank.request',
            'cardiology.view',
            'queue.patient_board',
            'service_queues.console',
        ],
        'ed_nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'emergency.queue.view',
            'radiology.request', 'pathology.request', 'bloodbank.request',
            // cannot: emergency.discharge, consultations.manage
            'service_queues.console',
        ],
        'general_physician' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'lab.view', 'lab.results.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'emergency.queue.view', 'emergency.discharge',
            'cardiology.view', 'pediatrics.view', 'ent.view', 'dermatology.view',
            'ophthalmology.view',
            'queue.patient_board',
            'service_queues.console',
        ],
        // Legacy alias — same abilities as general_physician (desk specialists
        // still narrowed by SpecialtyModuleService keyword matching).
        'doctor' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'lab.view', 'lab.results.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'emergency.queue.view', 'emergency.discharge',
            'cardiology.view', 'pediatrics.view', 'ent.view', 'dermatology.view',
            'ophthalmology.view',
            'queue.patient_board',
            'service_queues.console',
        ],
        'surgeon' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'lab.view', 'lab.results.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'radiology.request', 'pathology.request', 'bloodbank.request',
            'service_queues.console',
        ],
        'theatre_nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'bloodbank.request',
            'service_queues.console',
        ],
        'radiologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request',
            'lab.view', 'lab.results.view',
            'radiology.view', 'radiology.report',
            'assessments.view', 'assessments.capture',
            'service_queues.console',
        ],
        'radiographer' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'radiology.view', 'radiology.capture',
            'service_queues.console',
        ],
        'lab_technician' => [
            'dashboard.view', 'patients.view',
            'lab.view', 'lab.manage',
            'pathology.view', 'pathology.result.enter',
            'blood_bank.view', 'blood_bank.issue',
            // cannot: pathology.result.approve, lab.catalog.manage
            'service_queues.console',
        ],
        'lab_manager' => [
            'lab.catalog.manage',
            'pathology.result.approve',
            'analytics.lab.view',
            'lab.admin',
            'blood_bank.manage',
        ],
        'pathologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'lab.view', 'lab.manage', 'lab.results.view',
            'pathology.view', 'pathology.result.enter', 'pathology.result.approve',
            'assessments.view', 'assessments.capture',
            'service_queues.console',
        ],
        'blood_bank_officer' => [
            'dashboard.view', 'patients.view',
            'blood_bank.view', 'blood_bank.issue',
            'service_queues.console',
        ],
        'blood_bank_manager' => [
            'blood_bank.manage',
            'blood_bank.inventory',
            'blood_bank.reports',
        ],
        'pharmacist' => [
            'dashboard.view', 'patients.view',
            'prescriptions.view', 'prescriptions.dispense',
            'pharmacy.view', 'pharmacy.manage',
            'service_queues.console',
        ],
        'dentist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'dentistry.view', 'dentistry.manage',
            'service_queues.console',
        ],
        'psychiatrist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'prescriptions.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'psychiatry.view', 'psychiatry.manage',
            'service_queues.console',
        ],
        'physiotherapist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'physiotherapy.view', 'physiotherapy.manage',
            'service_queues.console',
        ],
        'midwife' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'womens_health.view', 'womens_health.manage',
            'service_queues.console',
        ],
        'obstetrician' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'womens_health.view', 'womens_health.manage',
            'service_queues.console',
        ],
        'gynecologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'womens_health.view', 'womens_health.manage',
            'service_queues.console',
        ],
        'obgyn' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'womens_health.view', 'womens_health.manage',
            'service_queues.console',
        ],
        'labour_ward_nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'womens_health.view', 'womens_health.manage',
            'service_queues.console',
        ],
        'pediatrician' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'pediatrics.view', 'pediatrics.manage',
            'queue.patient_board',
            'service_queues.console',
        ],
        'oncologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'oncology.view', 'oncology.manage',
            'infusion.view', 'infusion.manage',
            'service_queues.console',
        ],
        'cardiologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'lab.view', 'lab.results.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'cardiology.view', 'cardiology.manage',
            'service_queues.console',
        ],
        'ent_specialist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'ent.view', 'ent.manage',
            'service_queues.console',
        ],
        'dermatologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'dermatology.view', 'dermatology.manage',
            'service_queues.console',
        ],
        'ophthalmologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'ophthalmology.view', 'ophthalmology.manage',
            'service_queues.console',
        ],
        'orthopedist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'orthopedics.view', 'orthopedics.manage',
            'service_queues.console',
        ],
        'podiatrist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'podiatry.view', 'podiatry.manage',
            'service_queues.console',
        ],
        'fertility_specialist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'consultations.manage',
            'investigations.request', 'prescriptions.manage',
            'vitals.manage',
            'assessments.view', 'assessments.capture', 'assessments.manage',
            'pathways.manage',
            'fertility.view', 'fertility.manage',
            'service_queues.console',
        ],
        'embryologist' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'fertility.view', 'fertility.manage',
            'service_queues.console',
        ],
        'fertility_nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'fertility.view', 'fertility.manage',
            'service_queues.console',
        ],
        'dialysis_nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'renal.view', 'renal.manage',
            'service_queues.console',
        ],
        'ambulance_staff' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'vitals.manage',
            'assessments.view', 'assessments.capture',
            'ambulance.view', 'ambulance.manage',
            'emergency.queue.view',
            'service_queues.console',
        ],
        'receptionist' => [
            'dashboard.view', 'patients.view', 'patients.manage',
            'appointments.view', 'appointments.manage', 'queue.manage',
            'service_queues.console',
        ],
        'billing_officer' => [
            'dashboard.view', 'patients.view',
            'bills.view', 'bills.manage',
            'insurance.view', 'patient_accounts.view',
            // cannot: payments.manage (receive payments)
            'service_queues.console',
        ],
        // Cashiers collect payments — cannot edit invoices (no bills.manage).
        'cashier' => [
            'dashboard.view', 'patients.view',
            'bills.view',
            'payments.manage', 'payments.refund', 'receipts.view',
            'service_queues.console',
        ],
        'accountant' => [
            'dashboard.view', 'bills.view',
            'reports.finance.view', 'reports.finance.export',
            'accounting.view', 'accounting.gl', 'accounting.reconcile',
            'audit.view', 'audit.export',
        ],
        'department_manager' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view',
            'analytics.department.view',
            'reports.finance.view',
            'service_queues.console',
        ],
        // Legacy floor nurse — vaccination / CWC / infusion / maternity manage.
        'nurse' => [
            'dashboard.view', 'patients.view', 'appointments.view',
            'consultations.view', 'vitals.manage',
            'assessments.view', 'assessments.capture',
            'service_queues.console',
        ],
    ];

    /** Ward / nursing-ops roles (charge / unit leads) — not the legacy floor `nurse`. */
    protected array $nursingOpsRoles = [
        'ed_nurse',
        'theatre_nurse',
        'labour_ward_nurse',
        'fertility_nurse',
        'dialysis_nurse',
        'midwife',
    ];

    /** Clinical roles that staff the floor (queues, specialty desks, counters). */
    protected array $floorCareRoles = [
        'emergency_physician', 'ed_nurse', 'general_physician', 'doctor',
        'surgeon', 'theatre_nurse', 'radiologist', 'radiographer',
        'lab_technician', 'lab_manager', 'pathologist',
        'blood_bank_officer', 'blood_bank_manager',
        'pharmacist', 'dentist', 'psychiatrist', 'physiotherapist',
        'midwife', 'obstetrician', 'gynecologist', 'obgyn', 'labour_ward_nurse',
        'pediatrician', 'oncologist',
        'cardiologist', 'ent_specialist', 'dermatologist', 'ophthalmologist',
        'orthopedist', 'podiatrist', 'fertility_specialist', 'embryologist', 'fertility_nurse',
        'dialysis_nurse', 'ambulance_staff', 'receptionist', 'cashier', 'billing_officer',
        'nurse',
    ];

    /** Roles that get practitioner desks / multi-branch clinical assignment. */
    protected array $clinicalPractitionerRoles = [
        'emergency_physician', 'general_physician', 'doctor', 'surgeon',
        'radiologist', 'pathologist', 'dentist', 'psychiatrist',
        'physiotherapist', 'midwife', 'obstetrician', 'gynecologist', 'obgyn',
        'labour_ward_nurse', 'pediatrician', 'oncologist',
        'cardiologist', 'ent_specialist', 'dermatologist', 'ophthalmologist',
        'orthopedist', 'podiatrist', 'fertility_specialist', 'embryologist', 'fertility_nurse',
        'ed_nurse', 'theatre_nurse', 'dialysis_nurse', 'ambulance_staff',
        'radiographer', 'lab_technician', 'lab_manager',
        'blood_bank_officer', 'blood_bank_manager', 'pharmacist', 'nurse',
    ];

    public function can(?Member $member, string $ability): bool
    {
        if ($member === null) {
            return false;
        }

        $abilities = $this->resolvedAbilities((string) $member->role);

        if (in_array('*', $abilities, true)) {
            return true;
        }

        return in_array($ability, $abilities, true);
    }

    /**
     * @return list<string>
     */
    public function resolvedAbilities(string $role): array
    {
        $seen = [];
        $merged = [];

        $stack = [$role];
        while ($stack !== []) {
            $current = array_pop($stack);
            if (isset($seen[$current])) {
                continue;
            }
            $seen[$current] = true;

            foreach ($this->roleInherits[$current] ?? [] as $parent) {
                $stack[] = $parent;
            }

            foreach ($this->roleAbilities[$current] ?? [] as $ability) {
                $merged[$ability] = true;
            }
        }

        return $this->withNursingStationAbility(
            $this->withNursingOpsAbilities(
                $this->withDerivedAbilities(array_keys($merged)),
                $role,
            ),
            $role,
        );
    }

    /**
     * Clinical vitals unlock charting surfaces; ward nursing hub abilities are role-gated.
     *
     * @param  list<string>  $abilities
     * @return list<string>
     */
    protected function withDerivedAbilities(array $abilities): array
    {
        if (in_array('*', $abilities, true)) {
            return $abilities;
        }

        // Clinical vitals → charting surfaces (not the Nursing Services admin hub).
        if (in_array('vitals.manage', $abilities, true)) {
            $abilities[] = 'mar.view';
            $abilities[] = 'mar.administer';
            $abilities[] = 'nursing.notes.view';
            $abilities[] = 'nursing.notes.manage';
            $abilities[] = 'nursing.handover.view';
            $abilities[] = 'nursing.handover.manage';
            $abilities[] = 'nursing.assessments.view';
            $abilities[] = 'nursing.assessments.manage';
            $abilities[] = 'nursing.performance.view';
            $abilities[] = 'nursing.my_shifts.view';
        }

        if (in_array('analytics.department.view', $abilities, true)) {
            $abilities[] = 'inpatient.placement.view';
            $abilities[] = 'mar.view';
            $abilities[] = 'nursing.notes.view';
            $abilities[] = 'nursing.handover.view';
            $abilities[] = 'nursing.assessments.view';
            $abilities[] = 'nursing.performance.view';
            $abilities[] = 'nursing.roster.view';
            $abilities[] = 'nursing.services.view';
            $abilities[] = 'nursing.my_shifts.view';
        }

        return array_values(array_unique($abilities));
    }

    /**
     * Ward nursing abilities (care units, roster manage, Nursing Services hub).
     * Applied only for nursing-ops roles — not ambulance / GP / specialty MDs.
     *
     * @param  list<string>  $abilities
     * @return list<string>
     */
    protected function withNursingOpsAbilities(array $abilities, ?string $role): array
    {
        if (in_array('*', $abilities, true)) {
            return $abilities;
        }

        if ($role === null || ! in_array($role, $this->nursingOpsRoles, true)) {
            return $abilities;
        }

        if (! in_array('vitals.manage', $abilities, true)) {
            return $abilities;
        }

        $abilities[] = 'inpatient.placement.view';
        $abilities[] = 'inpatient.placement.manage';
        $abilities[] = 'nursing.roster.view';
        $abilities[] = 'nursing.roster.manage';
        $abilities[] = 'nursing.services.view';

        return array_values(array_unique($abilities));
    }

    /**
     * Bedside nurse station (My care unit) — floor + ward nursing roles with clinical vitals.
     * Not granted to GPs / ambulance solely because they also chart vitals.
     *
     * @param  list<string>  $abilities
     * @return list<string>
     */
    protected function withNursingStationAbility(array $abilities, ?string $role): array
    {
        if (in_array('*', $abilities, true)) {
            return $abilities;
        }

        $nursingRoles = [
            'nurse',
            'ed_nurse',
            'theatre_nurse',
            'labour_ward_nurse',
            'fertility_nurse',
            'dialysis_nurse',
            'midwife',
        ];

        if ($role === null || ! in_array($role, $nursingRoles, true)) {
            return $abilities;
        }

        if (! in_array('vitals.manage', $abilities, true)) {
            return $abilities;
        }

        $abilities[] = 'nursing.station.view';

        return array_values(array_unique($abilities));
    }

    public function isAdmin(?Member $member): bool
    {
        return $member !== null && in_array($member->role, ['super_admin', 'hospital_admin'], true);
    }

    /**
     * Primary specialty module keys for this member's role (manage).
     *
     * @return list<string>|null  null = unrestricted specialty (admins / dept manager use other rules)
     */
    public function primaryAppsFor(?Member $member): ?array
    {
        if ($member === null) {
            return [];
        }

        if ($this->isAdmin($member)) {
            return null;
        }

        $role = (string) $member->role;
        if (! array_key_exists($role, $this->rolePrimaryApps)) {
            return [];
        }

        return $this->rolePrimaryApps[$role];
    }

    /**
     * Specialty modules this role may refer into (beyond manage).
     *
     * @return list<string>
     */
    public function referAppsFor(?Member $member): array
    {
        if ($member === null || $this->isAdmin($member)) {
            return [];
        }

        $role = (string) $member->role;
        if ($role === 'department_manager') {
            return [];
        }

        return array_values(array_unique($this->roleReferApps[$role] ?? []));
    }

    /**
     * Specialty modules this role may view without refer/manage.
     *
     * @return list<string>
     */
    public function viewAppsFor(?Member $member): array
    {
        if ($member === null || $this->isAdmin($member)) {
            return [];
        }

        $role = (string) $member->role;

        return array_values(array_unique($this->roleViewApps[$role] ?? []));
    }

    /**
     * Whether the role matrix lists this specialty module as a primary app.
     */
    public function roleHasPrimaryApp(?Member $member, string $moduleKey): bool
    {
        $apps = $this->primaryAppsFor($member);
        if ($apps === null) {
            return true;
        }

        return in_array($moduleKey, $apps, true);
    }

    /**
     * Single-desk specialist roles (hide Specialty nav group).
     */
    public function isSingleAppSpecialist(?Member $member): bool
    {
        $apps = $this->primaryAppsFor($member);
        if ($apps === null) {
            return false;
        }

        return count($apps) === 1;
    }

    /**
     * Floor care: queues, specialty clinics, lab/pharmacy counters.
     * Facility admins manage settings/staff — they do not staff waiting lines.
     */
    public function handlesFloorCare(?Member $member): bool
    {
        if ($member === null || $this->isAdmin($member)) {
            return false;
        }

        return in_array($member->role, $this->floorCareRoles, true);
    }

    /**
     * Dedicated patient-flow board (GP Queue home / call-next board).
     * Permission-gated — not a role string check. Facility admins do not staff this board.
     */
    public function canAccessPatientQueue(?Member $member): bool
    {
        if ($member === null || $this->isAdmin($member)) {
            return false;
        }

        return $this->can($member, 'queue.patient_board');
    }

    /**
     * @return list<string>
     */
    public function clinicalPractitionerRoles(): array
    {
        return $this->clinicalPractitionerRoles;
    }

    /**
     * @return list<string>
     */
    public function floorCareRoles(): array
    {
        return $this->floorCareRoles;
    }

    /**
     * Roles that may switch / multi-site like legacy doctors (practitioner branches).
     */
    public function usesPractitionerBranchScope(?Member $member): bool
    {
        if ($member === null) {
            return false;
        }

        return $this->can($member, 'queue.patient_board')
            || in_array($member->role, [
                'emergency_physician', 'general_physician', 'doctor', 'surgeon',
                'radiologist', 'pathologist', 'dentist', 'psychiatrist',
                'pediatrician', 'oncologist', 'physiotherapist', 'midwife',
                'obstetrician', 'gynecologist', 'obgyn', 'fertility_specialist',
                'cardiologist', 'ent_specialist', 'dermatologist', 'ophthalmologist',
                'orthopedist', 'podiatrist',
            ], true);
    }

    /**
     * Care cashiers collect via Billing, not Ladill POS. Strip POS from
     * Identity app grants so the suite launcher hides it.
     *
     * @param  list<string>|null  $slugs  Null = unrestricted owner access.
     * @return list<string>|null
     */
    public function filterAllowedAppSlugs(?Member $member, ?array $slugs): ?array
    {
        if ($slugs === null || $member?->role !== 'cashier') {
            return $slugs;
        }

        return array_values(array_filter(
            array_map('strval', $slugs),
            fn (string $slug) => $slug !== 'pos',
        ));
    }

    /**
     * @param  array{full_access?: bool, apps?: list<string>, show_hub?: bool, show_billing?: bool}  $access
     * @return array{full_access?: bool, apps?: list<string>, show_hub?: bool, show_billing?: bool}
     */
    public function filterStaffAppAccess(?Member $member, array $access): array
    {
        if ($member?->role !== 'cashier') {
            return $access;
        }

        $apps = $this->filterAllowedAppSlugs($member, array_values(array_map(
            'strval',
            (array) ($access['apps'] ?? []),
        ))) ?? [];

        $access['apps'] = $apps;

        if (empty($access['full_access']) && count($apps) <= 1) {
            $access['show_hub'] = false;
        }

        return $access;
    }
}
