﻿<?php

use App\Http\Controllers\AdmissionController;
use App\Http\Controllers\AllergyMedicationController;
use App\Http\Controllers\AppointmentController;
use App\Http\Controllers\AskBitmedAiController;
use App\Http\Controllers\AttendanceController;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\BedController;
use App\Http\Controllers\CalendarController;
use App\Http\Controllers\CardController;
use App\Http\Controllers\CareActionController;
use App\Http\Controllers\CareTeamController;
use App\Http\Controllers\CdsController;
use App\Http\Controllers\ClinicalEntryController;
use App\Http\Controllers\ClinicalNoteController;
use App\Http\Controllers\ClinicalOrderController;
use App\Http\Controllers\ClinicalResultController;
use App\Http\Controllers\CredentialController;
use App\Http\Controllers\CreditNoteController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\DemoLoginHarnessController;
use App\Http\Controllers\DemoMfaCodeController;
use App\Http\Controllers\DepositController;
use App\Http\Controllers\DocumentCertificateController;
use App\Http\Controllers\DocumentSignatoryController;
use App\Http\Controllers\DocumentTemplateController;
use App\Http\Controllers\DrugAdministrationController;
use App\Http\Controllers\DuplicateReviewController;
use App\Http\Controllers\EmployeeController;
use App\Http\Controllers\EncounterController;
use App\Http\Controllers\GuardianController;
use App\Http\Controllers\IncidentController;
use App\Http\Controllers\InventoryController;
use App\Http\Controllers\InvoiceController;
use App\Http\Controllers\LabOrderController;
use App\Http\Controllers\LabReportController;
use App\Http\Controllers\LeaveRequestController;
use App\Http\Controllers\MessageController;
use App\Http\Controllers\MessagePreferenceController;
use App\Http\Controllers\MessageRoutingController;
use App\Http\Controllers\MetricsController;
use App\Http\Controllers\MfaChallengeController;
use App\Http\Controllers\MfaSettingsController;
use App\Http\Controllers\MfaSetupController;
use App\Http\Controllers\NutritionController;
use App\Http\Controllers\PatientAppointmentsController;
use App\Http\Controllers\PatientController;
use App\Http\Controllers\PatientPortalController;
use App\Http\Controllers\PatientUserLinkController;
use App\Http\Controllers\PaymentAdapterController;
use App\Http\Controllers\PaymentCollectionController;
use App\Http\Controllers\PaymentPointController;
use App\Http\Controllers\PayrollController;
use App\Http\Controllers\PayslipController;
use App\Http\Controllers\ProcurementController;
use App\Http\Controllers\ProductItemController;
use App\Http\Controllers\PublicCertificateVerificationController;
use App\Http\Controllers\PublicSiteController;
use App\Http\Controllers\PublicWebsiteController;
use App\Http\Controllers\QueueController;
use App\Http\Controllers\ReferralController;
use App\Http\Controllers\RefundController;
use App\Http\Controllers\RosterController;
use App\Http\Controllers\ServiceTariffController;
use App\Http\Controllers\Setup\AuditController;
use App\Http\Controllers\Setup\GrantController;
use App\Http\Controllers\Setup\MfaResetController;
use App\Http\Controllers\Setup\ModuleController;
use App\Http\Controllers\Setup\UserController;
use App\Http\Controllers\ShiftController;
use App\Http\Controllers\SpecimenController;
use App\Http\Controllers\SpecimenProcessingController;
use App\Http\Controllers\SponsorshipController;
use App\Http\Controllers\SupplierController;
use App\Http\Controllers\TrainingController;
use App\Http\Controllers\TreatmentHistoryController;
use App\Http\Controllers\UnitConversionController;
use App\Http\Controllers\VitalSignController;
use App\Http\Controllers\WalletController;
use App\Http\Controllers\WardController;
use App\Http\Controllers\WelfareRequestController;
use App\Http\Middleware\RequireMfaChallenge;
use App\Http\Middleware\RequireMfaEnrolment;
use Illuminate\Support\Facades\Route;

// Core routes (guest + auth)
Route::redirect('/', '/dashboard');

Route::get('/__demo/login/{email}', DemoLoginHarnessController::class)
    ->middleware('web')
    ->name('demo-login.harness');

Route::get('/__demo/mfa-code/{email}', DemoMfaCodeController::class)
    ->middleware('web')
    ->name('demo-login.mfa-code');

// O11 / DEC49 â€” the public website. Anyone may read it: the tenant is
// named in the path, and the controller asserts the tenant is active
// with `public.website` enabled before a single row is touched. These
// pages read the four public content tables and nothing else â€” never a
// patient, an encounter, an invoice or a staff account (O11's explicit
// safe interface). The booking POST is throttled because it is the one
// unauthenticated write the surface offers.
Route::get('/site/{tenant}', [PublicSiteController::class, 'home'])
    ->name('public.site.home');
Route::get('/site/{tenant}/services', [PublicSiteController::class, 'services'])
    ->name('public.site.services');
Route::get('/site/{tenant}/clinicians', [PublicSiteController::class, 'clinicians'])
    ->name('public.site.clinicians');
Route::get('/site/{tenant}/careers', [PublicSiteController::class, 'careers'])
    ->name('public.site.careers');
Route::get('/site/{tenant}/book', [PublicSiteController::class, 'book'])
    ->name('public.site.book');
Route::post('/site/{tenant}/book', [PublicSiteController::class, 'storeBooking'])
    ->middleware('throttle:10,1')
    ->name('public.site.book.store');
Route::get('/site/{tenant}/booked/{reference}', [PublicSiteController::class, 'booked'])
    ->name('public.site.booked');
Route::get('/site/{tenant}/search', [PublicSiteController::class, 'search'])
    ->name('public.site.search');

// F18 â€” public certificate verification. Unauthenticated by design, and
// deliberately narrow: the controller loads the certificate without any
// tenant boundary, then renders only reference, certificate type, issue date,
// status and clinic name. No patient, no body, no signatory details. A
// revoked reference stays revoked; a reissue gets a new reference.
Route::get('/verify', [PublicCertificateVerificationController::class, 'index'])
    ->name('public.certificates.verify.index');
Route::get('/verify/{reference}', [PublicCertificateVerificationController::class, 'show'])
    ->name('public.certificates.verify');

Route::middleware('guest')->group(function (): void {
    Route::get('/login', [LoginController::class, 'create'])->name('login');
    Route::post('/login', [LoginController::class, 'store']);
});

Route::post('/logout', [LoginController::class, 'destroy'])
    ->middleware('auth')
    ->name('logout');

// Authenticated session + MFA
Route::middleware(['auth', RequireMfaChallenge::class, RequireMfaEnrolment::class])->group(function (): void {
    Route::get('/mfa/setup', [MfaSetupController::class, 'show'])->name('mfa.setup');
    Route::post('/mfa/store-secret', [MfaSetupController::class, 'storeSecret'])->name('mfa.store-secret');
    Route::post('/mfa/confirm', [MfaSetupController::class, 'confirm'])->name('mfa.confirm');
    Route::post('/mfa/cancel', [MfaSetupController::class, 'cancel'])->name('mfa.cancel');

    Route::get('/mfa/challenge', [MfaChallengeController::class, 'show'])->name('mfa.challenge');
    Route::post('/mfa/verify', [MfaChallengeController::class, 'verify'])->name('mfa.verify');

    Route::get('/settings/two-step', [MfaSettingsController::class, 'index'])->name('mfa.settings');
    Route::post('/settings/two-step/codes', [MfaSettingsController::class, 'regenerateCodes'])->name('mfa.regenerate-codes');

    // C01 / C02 Ã¢â‚¬â€ patient identity and guardians (module: frontdesk.patients).
    Route::get('/patients', [PatientController::class, 'index'])->middleware('module:frontdesk.patients', 'permission:patients.view')->name('patients.index');
    Route::get('/patients/new', [PatientController::class, 'create'])->middleware('module:frontdesk.patients', 'permission:patients.create')->name('patients.create');
    Route::post('/patients', [PatientController::class, 'store'])->middleware('module:frontdesk.patients', 'permission:patients.create')->name('patients.store');
    Route::get('/patients/{patient}', [PatientController::class, 'show'])->middleware('module:frontdesk.patients', 'permission:patients.view')->name('patients.show');
    Route::get('/patients/{patient}/edit', [PatientController::class, 'edit'])->middleware('module:frontdesk.patients', 'permission:patients.amend')->name('patients.edit');
    Route::put('/patients/{patient}', [PatientController::class, 'update'])->middleware('module:frontdesk.patients', 'permission:patients.amend')->name('patients.update');
    Route::get('/duplicates', [DuplicateReviewController::class, 'index'])->middleware('module:frontdesk.patients', 'permission:patients.merge')->name('duplicates.index');
    Route::get('/duplicates/{review}', [DuplicateReviewController::class, 'show'])->middleware('module:frontdesk.patients', 'permission:patients.merge')->name('duplicates.show');
    Route::post('/duplicates/{review}/merge', [DuplicateReviewController::class, 'merge'])->middleware('module:frontdesk.patients', 'permission:patients.merge')->name('duplicates.merge');
    Route::post('/duplicates/{review}/dismiss', [DuplicateReviewController::class, 'dismiss'])->middleware('module:frontdesk.patients', 'permission:patients.merge')->name('duplicates.dismiss');
    Route::post('/patients/{patient}/unmerge', [DuplicateReviewController::class, 'unmerge'])->middleware('module:frontdesk.patients', 'permission:patients.merge')->name('patients.unmerge');
    Route::post('/patients/{patient}/guardians', [GuardianController::class, 'store'])->middleware('module:frontdesk.patients', 'permission:patients.manage_guardians')->name('patients.guardians.store');
    Route::post('/patients/{patient}/guardians/{guardian}/verify', [GuardianController::class, 'verify'])->middleware('module:frontdesk.patients', 'permission:patients.manage_guardians')->name('patients.guardians.verify');
    Route::post('/patients/{patient}/guardians/{guardian}/revoke', [GuardianController::class, 'revoke'])->middleware('module:frontdesk.patients', 'permission:patients.manage_guardians')->name('patients.guardians.revoke');

    // DEC19 â€” the login â†” patient binding. The write door is `patients.amend`:
    // binding a login changes who may see the record, so it is an amendment of
    // the record's access facts. Dual confirmation (a second, different person)
    // is enforced by the service and is not a permission question.
    Route::post('/patients/{patient}/portal-links', [PatientUserLinkController::class, 'store'])->middleware('module:frontdesk.patients', 'permission:patients.amend')->name('patients.portal-links.store');
    Route::post('/patients/{patient}/portal-links/{link}/confirm', [PatientUserLinkController::class, 'confirm'])->middleware('module:frontdesk.patients', 'permission:patients.amend')->name('patients.portal-links.confirm');
    Route::post('/patients/{patient}/portal-links/{link}/revoke', [PatientUserLinkController::class, 'revoke'])->middleware('module:frontdesk.patients', 'permission:patients.amend')->name('patients.portal-links.revoke');

    Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');

    // C03 Ã¢â‚¬â€ patient-linked appointment booking (module: scheduling.appointments).
    Route::get('/appointments', [AppointmentController::class, 'index'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.view')
        ->name('appointments.index');
    Route::post('/appointments', [AppointmentController::class, 'store'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('appointments.store');
    Route::get('/appointments/{appointment}', [AppointmentController::class, 'show'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.view')
        ->name('appointments.show');
    Route::put('/appointments/{appointment}', [AppointmentController::class, 'update'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('appointments.update');
    Route::post('/appointments/{appointment}/cancel', [AppointmentController::class, 'cancel'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('appointments.cancel');
    Route::post('/appointments/{appointment}/no-show', [AppointmentController::class, 'noShow'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('appointments.no-show');
    Route::post('/appointments/{appointment}/complete', [AppointmentController::class, 'complete'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('appointments.complete');

    // C04 Ã¢â‚¬â€ OPD queue and patient journey (module: scheduling.appointments).
    Route::get('/queue', [QueueController::class, 'index'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.view')
        ->name('queue.index');
    Route::post('/queue', [QueueController::class, 'store'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.store');
    Route::post('/queue/check-in/{appointment}', [QueueController::class, 'checkIn'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.check-in');
    Route::post('/queue/{entry}/call', [QueueController::class, 'call'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.call');
    Route::post('/queue/{entry}/complete', [QueueController::class, 'complete'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.complete');
    Route::post('/queue/{entry}/no-show', [QueueController::class, 'noShow'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.no-show');
    Route::post('/queue/{entry}/cancel', [QueueController::class, 'cancel'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.manage')
        ->name('queue.cancel');

    // C05 Ã¢â‚¬â€ longitudinal record: encounters, notes and entries.
    Route::get('/encounters', [EncounterController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.index');
    Route::post('/encounters', [EncounterController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.store');
    Route::get('/encounters/{encounter}', [EncounterController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.show');
    Route::post('/encounters/{encounter}/close', [EncounterController::class, 'close'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.close');
    Route::post('/encounters/{encounter}/notes', [ClinicalNoteController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.notes.store');
    Route::put('/notes/{note}', [ClinicalNoteController::class, 'update'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('notes.update');
    Route::post('/notes/{note}/sign', [ClinicalNoteController::class, 'sign'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('notes.sign');
    Route::post('/notes/{note}/amend', [ClinicalNoteController::class, 'amend'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('notes.amend');
    Route::post('/encounters/{encounter}/entries', [ClinicalEntryController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('encounters.entries.store');
    Route::put('/entries/{entry}', [ClinicalEntryController::class, 'update'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('entries.update');
    Route::get('/patients/{patient?}/record', [EncounterController::class, 'record'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.record');
    Route::post('/patients/{patient}/record/open', [EncounterController::class, 'openForPatient'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.record.open');

    // C06 Ã¢â‚¬â€ allergies and medication history (same module and door as C05;
    // patient-scoped verbs re-checked in AllergyMedicationService).
    Route::get('/patients/{patient}/allergies', [AllergyMedicationController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.allergies');
    Route::post('/patients/{patient}/allergies', [AllergyMedicationController::class, 'recordAllergy'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.allergies.record');
    Route::put('/allergies/{allergy}', [AllergyMedicationController::class, 'updateAllergy'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('allergies.update');
    Route::get('/patients/{patient}/allergies/{allergy}/edit', [AllergyMedicationController::class, 'edit'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.allergies.edit');
    Route::post('/allergies/{allergy}/entered-in-error', [AllergyMedicationController::class, 'enteredInError'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('allergies.entered-in-error');
    Route::post('/patients/{patient}/allergies/assess-none', [AllergyMedicationController::class, 'assessNone'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.allergies.assess-none');
    Route::post('/patients/{patient}/allergies/reset-unknown', [AllergyMedicationController::class, 'resetUnknown'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.allergies.reset-unknown');
    Route::post('/patients/{patient}/medications', [AllergyMedicationController::class, 'recordMedication'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.medications.record');
    Route::post('/medications/{medication}/discontinue', [AllergyMedicationController::class, 'discontinueMedication'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('medications.discontinue');
    Route::post('/medications/{medication}/reconcile', [AllergyMedicationController::class, 'reconcileMedication'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('medications.reconcile');

    // C07 Ã¢â‚¬â€ clinical orders (same module and door as C05/C06; the
    // patient-scoped `clinical.order` verb is re-checked in ClinicalOrderService).
    Route::get('/patients/{patient}/orders', [ClinicalOrderController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.orders');
    Route::post('/patients/{patient}/orders', [ClinicalOrderController::class, 'place'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.orders.place');
    Route::post('/orders/{order}/amend', [ClinicalOrderController::class, 'amend'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('orders.amend');
    Route::post('/orders/{order}/start', [ClinicalOrderController::class, 'start'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('orders.start');
    Route::post('/orders/{order}/fulfill', [ClinicalOrderController::class, 'fulfill'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('orders.fulfill');
    Route::post('/orders/{order}/cancel', [ClinicalOrderController::class, 'cancel'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('orders.cancel');
    Route::post('/orders/{order}/entered-in-error', [ClinicalOrderController::class, 'enteredInError'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('orders.entered-in-error');

    // C08 Ã¢â‚¬â€ results review and follow-up. As with C07 the doors use
    // non-patient-scoped permissions and the service re-checks the
    // patient-scoped verbs (`clinical.result` for review, `diagnostics.deliver`
    // for delivery) against the assigned-care relationship.
    Route::get('/results', [ClinicalResultController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('results.index');
    Route::get('/results/deliveries', [ClinicalResultController::class, 'deliveryQueue'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('results.deliveries');
    Route::get('/patients/{patient}/results', [ClinicalResultController::class, 'patientHistory'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('patients.results');
    Route::post('/orders/{order}/results', [ClinicalResultController::class, 'deliver'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('orders.results.deliver');
    Route::post('/orders/{order}/results/reject', [ClinicalResultController::class, 'reject'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('orders.results.reject');
    Route::post('/results/{result}/assign-owner', [ClinicalResultController::class, 'assignOwner'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('results.assign-owner');
    Route::post('/results/{result}/acknowledge', [ClinicalResultController::class, 'acknowledge'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('results.acknowledge');
    Route::post('/results/{result}/act', [ClinicalResultController::class, 'act'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('results.act');
    Route::post('/results/{result}/close', [ClinicalResultController::class, 'close'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('results.close');

    // C09 Ã¢â‚¬â€ governed clinical decision support. The register page sits behind
    // `clinical.access`; governance verbs (`clinical.cds_manage`) and the
    // patient-scoped `clinical.guidance` verb are re-checked in the service.
    Route::get('/cds', [CdsController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('cds.index');
    Route::post('/cds/rules', [CdsController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('cds.rules.store');
    Route::post('/cds/rules/{rule}/submit', [CdsController::class, 'submit'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('cds.rules.submit');
    Route::post('/cds/rules/{rule}/approve', [CdsController::class, 'approve'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('cds.rules.approve');
    Route::post('/cds/rules/{rule}/retire', [CdsController::class, 'retire'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('cds.rules.retire');
    Route::post('/patients/{patient}/guidance/{rule}/acknowledge', [CdsController::class, 'acknowledge'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('guidance.acknowledge');
    Route::post('/patients/{patient}/guidance/{rule}/override', [CdsController::class, 'override'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('guidance.override');

    // C10 Ã¢â‚¬â€ referral coordination. As with C07Ã¢â‚¬â€œC09 the doors use
    // `clinical.access`; the service re-checks the patient-scoped
    // `clinical.referral` verb when requesting and `referrals.manage` for
    // lifecycle actions.
    Route::get('/referrals', [ReferralController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.index');
    Route::get('/referrals/{referral}', [ReferralController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.show');
    Route::post('/patients/{patient}/referrals', [ReferralController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.store');
    Route::post('/referrals/{referral}/attach', [ReferralController::class, 'attach'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.attach');
    Route::post('/referrals/{referral}/accept', [ReferralController::class, 'accept'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.accept');
    Route::post('/referrals/{referral}/schedule', [ReferralController::class, 'schedule'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.schedule');
    Route::post('/referrals/{referral}/complete', [ReferralController::class, 'complete'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.complete');
    Route::post('/referrals/{referral}/decline', [ReferralController::class, 'decline'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.decline');
    Route::post('/referrals/{referral}/return', [ReferralController::class, 'returnToSender'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('referrals.return');

    // L01 Ã¢â‚¬â€ specimen lifecycle. Until the whole laboratory module is delivered
    // (L01Ã¢â‚¬â€œL03), these doors sit behind the delivered parent module exactly as
    // the C07/C08 laboratory-facing order and result routes do; they move to
    // `module:diagnostics.laboratory` when L03 lands. The doors use the coarse
    // `patients.view` for the same reason the C08 delivery queue does:
    // `diagnostics.view` is a patient-scoped verb, so it cannot open a
    // department worklist. SpecimenService re-checks `diagnostics.view` in the
    // assigned-care context for every read, and `diagnostics.collect` /
    // `diagnostics.process` for every action, so a viewer can never act.
    // Registration takes the patient from the route, so the patient association
    // can never be chosen by the submitter.
    Route::get('/specimens', [SpecimenController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.index');
    Route::get('/specimens/{specimen}', [SpecimenController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.show');
    Route::post('/patients/{patient}/specimens', [SpecimenController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('patients.specimens.store');
    Route::post('/specimens/{specimen}/collect', [SpecimenController::class, 'collect'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.collect');
    Route::post('/specimens/{specimen}/receive', [SpecimenController::class, 'receive'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.receive');
    Route::post('/specimens/{specimen}/transport', [SpecimenController::class, 'transport'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.transport');
    Route::post('/specimens/{specimen}/store', [SpecimenController::class, 'storeInStorage'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.store');
    Route::post('/specimens/{specimen}/reject', [SpecimenController::class, 'reject'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.reject');
    Route::post('/specimens/{specimen}/recollect', [SpecimenController::class, 'recollect'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.recollect');
    Route::post('/specimens/{specimen}/aliquot', [SpecimenController::class, 'aliquot'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.aliquot');
    Route::post('/specimens/{specimen}/dispose', [SpecimenController::class, 'dispose'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.dispose');

    // L02 Ã¢â‚¬â€ laboratory processing & quality control. Until the whole laboratory
    // module is delivered (L01Ã¢â‚¬â€œL03), these doors sit behind the delivered parent
    // module exactly as the L01 routes do. The doors use the coarse
    // `patients.view` for the same reason the C08 delivery queue does:
    // `diagnostics.view` is a patient-scoped verb, so it cannot open a
    // department worklist. SpecimenProcessingService re-checks `diagnostics.process`
    // in assigned-care context for every action, so a viewer can never act.
    Route::get('/specimens/process', [SpecimenProcessingController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.process.index');
    Route::get('/specimens/process/{specimen}', [SpecimenProcessingController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.process.show');
    Route::post('/specimens/process/{specimen}', [SpecimenProcessingController::class, 'process'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('specimens.process');
    Route::post('/analyser-runs/{analyserRun}/override', [SpecimenProcessingController::class, 'override'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('analyser-runs.override');
    Route::post('/analyser-runs/{analyserRun}/release', [SpecimenProcessingController::class, 'release'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('analyser-runs.release');

    // L03 Ã¢â‚¬â€ laboratory report authorisation. Until the laboratory module is
    // fully delivered (L01Ã¢â‚¬â€œL03), these doors sit behind the delivered parent
    // module exactly as the C08 delivery queue does. The doors use the coarse
    // `patients.view` for the same reason the C08 delivery queue does:
    // `diagnostics.view` is a patient-scoped verb, so it cannot open a
    // department worklist. LabReportAuthorisationService re-checks
    // `diagnostics.authorise` in assigned-care context for every action, so
    // a viewer can never act.
    Route::get('/lab-reports', [LabReportController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.index');
    Route::get('/lab-reports/{report}', [LabReportController::class, 'show'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.show');
    Route::post('/lab-reports/{report}/authorise', [LabReportController::class, 'authorise'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.authorise');
    Route::post('/lab-reports/{report}/amend', [LabReportController::class, 'amend'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.amend');

    // C11 - the disclosure decision is separate from authorisation. The route
    // door is the record-reading door; the service re-checks
    // `diagnostics.release_to_patient` and refuses preliminary, unsigned,
    // superseded and cross-tenant reports.
    Route::post('/lab-reports/{report}/release', [LabReportController::class, 'release'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.release');
    Route::post('/lab-reports/{report}/release/withdraw', [LabReportController::class, 'withdrawRelease'])
        ->middleware('module:clinical.records', 'permission:patients.view')
        ->name('lab-reports.release.withdraw');

    // C03 Ã¢â‚¬â€ the patient's own appointment view and self-booking.
    Route::get('/my/appointments', [PatientAppointmentsController::class, 'index'])
        ->middleware('module:frontdesk.patients', 'permission:appointments.book_own')
        ->name('patient.appointments');
    Route::post('/my/appointments', [PatientAppointmentsController::class, 'store'])
        ->middleware('module:frontdesk.patients', 'permission:appointments.book_own')
        ->name('patient.appointments.store');

    // C11 - the patient web portal.
    //
    // Every page is addressed by the **binding**, never by a patient or record
    // id: `/my/{link}/...` with a numeric constraint, so a guessed identifier
    // in the path resolves to nothing. The controller re-resolves the link
    // against the signed-in account (live guardian scopes included) and each
    // section enforces its own scope. `portal.access` is the door into the
    // portal; it is not a substitute for a binding, and it never widens one.
    Route::middleware(['module:frontdesk.patients', 'permission:portal.access'])->group(function (): void {
        Route::get('/my', [PatientPortalController::class, 'home'])
            ->name('portal.home');
        Route::get('/my/{link}', [PatientPortalController::class, 'show'])
            ->whereNumber('link')
            ->name('portal.show');
        Route::get('/my/{link}/appointments', [PatientPortalController::class, 'appointments'])
            ->whereNumber('link')
            ->name('portal.appointments');
        Route::post('/my/{link}/appointments', [PatientPortalController::class, 'storeAppointment'])
            ->whereNumber('link')
            ->name('portal.appointments.store');
        Route::get('/my/{link}/bills', [PatientPortalController::class, 'bills'])
            ->whereNumber('link')
            ->name('portal.bills');
        Route::get('/my/{link}/bills/{reference}', [PatientPortalController::class, 'bill'])
            ->whereNumber('link')
            ->name('portal.bill');
        Route::get('/my/{link}/results', [PatientPortalController::class, 'results'])
            ->whereNumber('link')
            ->name('portal.results');
        Route::get('/my/{link}/results/{reference}', [PatientPortalController::class, 'result'])
            ->whereNumber('link')
            ->name('portal.result');
        Route::get('/my/{link}/documents', [PatientPortalController::class, 'documents'])
            ->whereNumber('link')
            ->name('portal.documents');
        Route::get('/my/{link}/documents/{reference}', [PatientPortalController::class, 'document'])
            ->whereNumber('link')
            ->name('portal.document');
        Route::get('/my/{link}/documents/{reference}/print', [PatientPortalController::class, 'documentPrint'])
            ->whereNumber('link')
            ->name('portal.document.print');
    });

    // C12 - secure messaging and notifications (DEC52). Staff clinical
    // inbox: send/receive messages, route and log delivery, honour
    // per-user preferences. Modelled as a local sink; no external
    // notification provider is contracted in this build.
    Route::middleware(['module:engagement.messaging', 'permission:engagement.message'])->group(function (): void {
        Route::get('/messages', [MessageController::class, 'index'])->name('messages.index');
        Route::get('/messages/preferences', [MessagePreferenceController::class, 'edit'])->name('message-preferences.edit');
        Route::put('/messages/preferences', [MessagePreferenceController::class, 'update'])->name('message-preferences.update');
        Route::get('/messages/routing', [MessageRoutingController::class, 'index'])->name('message-routing.index');
        Route::get('/messages/{message}', [MessageController::class, 'show'])->name('messages.show');
        Route::post('/messages', [MessageController::class, 'store'])->name('messages.store');
    });

    // C13 - feedback and complaints (DEC53). Classify comments, assign
    // owner/deadline, track resolution and response; feedback never silently
    // alters signed clinical records â€” any clinical amendment requires its
    // own explicit release door.
    Route::middleware('permission:feedback.submit')->group(function (): void {
        Route::get('/feedback', [FeedbackComplaintController::class, 'index'])->name('feedback.index');
        Route::post('/feedback', [FeedbackComplaintController::class, 'store'])->name('feedback.store');
        Route::get('/feedback/{item}', [FeedbackComplaintController::class, 'show'])->name('feedback.show');
        Route::put('/feedback/{item}', [FeedbackComplaintController::class, 'update'])->name('feedback.update');
        Route::get('/feedback/{item}/affinity', [FeedbackComplaintController::class, 'checkClinicalAffinity'])->name('feedback.affinity');
    });

    // Finance & Payments - Wallet, Cards, Payment Points
    Route::middleware('module:finance.billing')->group(function (): void {
        Route::resource('wallets', WalletController::class);

        // DEC22 â€” the bound login's own patient-namespace wallet. The
        // resource above keeps meaning the caller's self-service wallet for
        // everyone; this entry resolves the DEC19 binding instead, so a
        // patient reaches the account the till debits and the invoice QR
        // codes live in. A route gate cannot say "or your own record" (the
        // same reason `/wallets` carries no permission door), so the binding
        // is checked in the controller: an unbound login is handed an empty
        // state, never an account.
        Route::get('my/wallet', [WalletController::class, 'mine'])
            ->name('patient.wallet');

        // B06 â€” QR payment needs two halves. A cashier scans the QR at the till; the
        // patient has to be able to produce one. `generateQr()` lived in the wallet
        // service but had no route, so the scan half could only be exercised against
        // a QR a document certificate had happened to create.
        Route::get('wallets/{wallet}/qrs', [WalletController::class, 'qrs'])
            ->name('wallets.qrs');
        Route::get('wallets/{wallet}/qrs/create', [WalletController::class, 'createQr'])
            ->name('wallets.qrs.create');
        Route::post('wallets/{wallet}/qrs', [WalletController::class, 'storeQr'])
            ->name('wallets.qrs.store');
        // B06 â€” cards. `index`/`create`/`store` are already scoped to the
        // signed-in user's own wallet inside the controller, so they stay open
        // for self-service. `show`/`edit`/`update`/`destroy` take a card by ID
        // and are checked in the controller, which is where the ownership rule
        // lives â€” a route gate cannot say "or your own record".
        Route::resource('cards', CardController::class);

        // Payment points are clinic configuration, not a cashier's screen: the
        // till they operate is `payments.collect`. A module gate alone left
        // every account in the tenant able to open and rewrite them.
        //
        // Two details that cost a run each, both silent:
        //  - `Route::resource()->middleware()` takes ONE argument. Passing
        //    'module:x', 'permission:y' drops the permission with no error â€”
        //    unlike `Route::get()`, whose `middleware()` uses func_get_args().
        //  - `create` must be declared BEFORE `{point}`, or `/payment-points/create`
        //    is swallowed by the show route and answers 404.
        Route::resource('payment-points', PaymentPointController::class)
            ->only(['create', 'store'])
            ->parameters(['payment-points' => 'point'])
            ->middleware(['module:finance.billing', 'permission:configuration.manage']);
        Route::resource('payment-points', PaymentPointController::class)
            ->only(['index', 'show'])
            ->parameters(['payment-points' => 'point'])
            ->middleware(['module:finance.billing', 'permission:configuration.view']);
        Route::resource('payment-points', PaymentPointController::class)
            ->only(['edit', 'update', 'destroy'])
            ->parameters(['payment-points' => 'point'])
            ->middleware(['module:finance.billing', 'permission:configuration.manage']);
        // Money-moving doors carry `finance.collect` explicitly. The module
        // gate says the clinic has billing switched on; it says nothing about
        // whether this particular operator may take money at a counter.
        Route::middleware('permission:finance.collect')->group(function (): void {
            Route::post('payment-points/{point}/collect', [PaymentPointController::class, 'collect'])->name('payment-points.collect');
            Route::post('payments/tap-to-pay', [PaymentPointController::class, 'tapToPay'])->name('payments.tap-to-pay');
            Route::post('payments/qr', [PaymentPointController::class, 'qrPayment'])->name('payments.qr');

            // B03 â€” the till. One field takes an invoice number, a patient
            // number or a ten-digit account number, and the payer's
            // outstanding invoices are listed for collection.
            Route::get('/payments/collect', [PaymentCollectionController::class, 'index'])
                ->name('payments.collect.index');
            Route::post('/payments/collect', [PaymentCollectionController::class, 'store'])
                ->name('payments.collect.store');
        });

        // B06 â€” refunds and credit notes (DEC23 / DEC24). Reading the review
        // page needs `refunds.view`; asking for money back is the cashier's
        // `finance.refund` door; and the door that actually moves money out â€”
        // approving a refund, which performs the reversal atomically â€” is the
        // separate `refunds.approve`, held by the finance supervisor and
        // manager only. The "someone else" rule lives in `RefundService`,
        // because no route gate can express it.
        Route::get('/refunds', [RefundController::class, 'index'])
            ->middleware('permission:refunds.view')
            ->name('refunds.index');
        Route::middleware('permission:finance.refund')->group(function (): void {
            Route::post('/invoices/{reference}/refunds', [RefundController::class, 'store'])
                ->name('invoices.refunds.store');
            Route::post('/invoices/{reference}/credit-notes', [CreditNoteController::class, 'store'])
                ->name('invoices.credit-notes.store');
        });
        Route::middleware('permission:refunds.approve')->group(function (): void {
            Route::post('/refunds/{refund}/approve', [RefundController::class, 'approve'])
                ->name('refunds.approve');
            Route::post('/refunds/{refund}/reject', [RefundController::class, 'reject'])
                ->name('refunds.reject');
            Route::post('/credit-notes/{note}/void', [CreditNoteController::class, 'void'])
                ->name('credit-notes.void');
        });
    });

    // B05 + B06 closing â€” deposits, allocations and cashier shifts (DEC34â€“DEC36).
    Route::middleware(['module:finance.billing', 'permission:deposits.view'])->group(function (): void {
        Route::get('/deposits', [DepositController::class, 'index'])
            ->name('deposits.index');
        // Reading who sponsors whom is the same door as reading the register
        // it sits beside: `deposits.view`.
        Route::get('/sponsorships', [SponsorshipController::class, 'index'])
            ->name('sponsorships.index');
        // An enrolment is what later authorises money to be booked against the
        // clinic, so creating or ending one is `deposits.manage` â€” the cashier
        // who can take money cannot quietly put their own employer on a bill.
        Route::post('/sponsorships', [SponsorshipController::class, 'store'])
            ->middleware('permission:deposits.manage')
            ->name('sponsorships.store');
        Route::post('/sponsorships/{sponsor}/end', [SponsorshipController::class, 'end'])
            ->middleware('permission:deposits.manage')
            ->name('sponsorships.end');
        // Withdrawing a promise is the same management act, and it is a state
        // change rather than a delete: what was promised, and when it was
        // released, stays readable.
        Route::post('/sponsorships/allocations/{allocation}/release', [SponsorshipController::class, 'release'])
            ->middleware('permission:deposits.manage')
            ->name('sponsorships.release');
        // Taking a deposit and applying one are `finance.collect`, the same
        // permission the till takes money under.
        Route::post('/deposits', [DepositController::class, 'store'])
            ->middleware('permission:finance.collect')
            ->name('deposits.store');
        Route::post('/deposits/{deposit}/apply', [DepositController::class, 'apply'])
            ->middleware('permission:finance.collect')
            ->name('deposits.apply');
        // Asking for money back is `finance.refund` on the invoice side, and a
        // deposit refund is the same act against the funding register row.
        Route::post('/deposits/refund', [RefundController::class, 'store'])
            ->middleware('permission:finance.refund')
            ->name('deposits.refunds.store');
        // Registering a payer is a management act behind `deposits.manage`,
        // so the cashier who can see the page cannot silently add a sponsor.
        Route::post('/deposits/payers', [DepositController::class, 'payerStore'])
            ->middleware('permission:deposits.manage')
            ->name('deposits.payerStore');
        // Retiring a payer is the same management act: it stops the name being
        // offered on new deposits, and never touches deposits already taken
        // under it.
        Route::post('/deposits/payers/{payer}/deactivate', [DepositController::class, 'payerDeactivate'])
            ->middleware('permission:deposits.manage')
            ->name('deposits.payers.deactivate');
        // Cashier shift management (DEC35): open, count, and close shifts.
        Route::middleware('permission:cashier.shift')->group(function (): void {
            Route::post('/cashier-shifts', [CashierShiftController::class, 'store'])
                ->name('cashier-shifts.store');
            Route::post('/cashier-shifts/{shift}/count', [CashierShiftController::class, 'count'])
                ->middleware('permission:cashier.shift')
                ->name('cashier-shifts.count');
            Route::post('/cashier-shifts/{shift}/close', [CashierShiftController::class, 'close'])
                ->middleware('permission:cashier.shift')
                ->name('cashier-shifts.close');
        });
        // Payment allocation model: linking invoices to sponsors (DEC37â€“DEC38).
        Route::middleware('permission:payment.allocate')->group(function (): void {
            Route::post('/payment-allocations', [PaymentAllocationController::class, 'store'])
                ->name('payment-allocations.store');
            Route::post('/payment-allocations/{allocation}/apply', [PaymentAllocationController::class, 'apply'])
                ->middleware('permission:payment.allocate')
                ->name('payment-allocations.apply');
        });
    });

    // B07 â€” insurance & claims (DEC31â€“DEC33). Insurance carrier registration,
    // claim lodgement, adjudication and payment. Splits sponsorship beyond
    // internal accounts into formal insurance claims with carrier review.
    Route::middleware('permission:claims.view')->group(function (): void {
        Route::get('/claims', [ClaimController::class, 'index'])
            ->name('claims.index');
        Route::post('/claims', [ClaimController::class, 'create'])
            ->name('claims.create');
        Route::get('/claims/{claim}', [ClaimController::class, 'show'])
            ->name('claims.show');
        Route::put('/claims/{claim}', [ClaimController::class, 'update'])
            ->name('claims.update');
    });

    Route::middleware('permission:claims.approve')->group(function (): void {
        Route::post('/claims/{claim}/approve', [ClaimController::class, 'approve'])
            ->name('claims.approve');
    });

    Route::middleware('permission:claims.reject')->group(function (): void {
        Route::post('/claims/{claim}/reject', [ClaimController::class, 'reject'])
            ->name('claims.reject');
    });

    Route::middleware('permission:claims.pay')->group(function (): void {
        Route::post('/claims/{claim}/pay', [ClaimController::class, 'pay'])
            ->name('claims.pay');
    });

    Route::middleware('permission:claims.close')->group(function (): void {
        Route::post('/claims/{claim}/close', [ClaimController::class, 'close'])
            ->name('claims.close');
    });

    Route::middleware(['module:finance.billing', 'permission:finance.view'])->group(function (): void {
        Route::get('/shifts', [ShiftController::class, 'index'])
            ->name('shifts.index');
        Route::post('/shifts/{point}', [ShiftController::class, 'open'])
            ->middleware('permission:finance.collect')
            ->name('shifts.open');
        Route::post('/shifts/{shift}/close', [ShiftController::class, 'close'])
            ->middleware('permission:finance.collect')
            ->name('shifts.close');
    });

    // O06 â€” the staff register and leave (DEC40).
    //
    // Reading personnel data is `employees.view`; curating it is
    // `employees.manage`. Neither is implied by clinical or finance doors
    // (F04, O06: personnel data is restricted separately from patient
    // data). Leave follows the DEC23 two-person shape: `leave.submit`
    // records a request, `leave.approve` decides it, and the decider must
    // be a second person.
    Route::middleware(['module:hr.staff', 'permission:employees.view'])->group(function (): void {
        Route::get('/employees', [EmployeeController::class, 'index'])
            ->name('employees.index');
        // `create` is declared before `{employee}` so it is not swallowed by
        // the binding, the same trap the catalogue resource records.
        Route::get('/employees/create', [EmployeeController::class, 'create'])
            ->middleware('permission:employees.manage')
            ->name('employees.create');
        Route::post('/employees', [EmployeeController::class, 'store'])
            ->middleware('permission:employees.manage')
            ->name('employees.store');
        Route::get('/employees/{employee}', [EmployeeController::class, 'show'])
            ->name('employees.show');
        Route::get('/employees/{employee}/edit', [EmployeeController::class, 'edit'])
            ->middleware('permission:employees.manage')
            ->name('employees.edit');
        Route::put('/employees/{employee}', [EmployeeController::class, 'update'])
            ->middleware('permission:employees.manage')
            ->name('employees.update');
    });

    Route::middleware(['module:hr.staff', 'permission:leave.view'])->group(function (): void {
        Route::get('/leave', [LeaveRequestController::class, 'index'])
            ->name('leave.index');
        Route::post('/leave', [LeaveRequestController::class, 'store'])
            ->middleware('permission:leave.submit')
            ->name('leave.store');
        Route::post('/leave/{leaveRequest}/approve', [LeaveRequestController::class, 'approve'])
            ->middleware('permission:leave.approve')
            ->name('leave.approve');
        Route::post('/leave/{leaveRequest}/reject', [LeaveRequestController::class, 'reject'])
            ->middleware('permission:leave.approve')
            ->name('leave.reject');
        // Cancelling holds the recording door: a recorder withdraws their
        // own request, and the service additionally lets the decision door
        // withdraw anyone's.
        Route::post('/leave/{leaveRequest}/cancel', [LeaveRequestController::class, 'cancel'])
            ->middleware('permission:leave.submit')
            ->name('leave.cancel');
    });

    // O07 â€” credentials and practice standing (DEC41).
    //
    // Reading the credential register is `credentials.view`; recording one
    // and moving its status is `credentials.manage` â€” a clinician does not
    // administer their own licence, so neither door is implied by any
    // clinical capability. The suspension effect is enforced in the
    // Authorizer per request, never here: the routes only manage rows.
    Route::middleware(['module:hr.staff', 'permission:credentials.view'])->group(function (): void {
        Route::get('/credentials', [CredentialController::class, 'index'])
            ->name('credentials.index');
        // `create` is declared before `{credential}` so it is not swallowed
        // by the binding, the same trap the other catalogues record.
        Route::get('/credentials/create', [CredentialController::class, 'create'])
            ->middleware('permission:credentials.manage')
            ->name('credentials.create');
        Route::post('/credentials', [CredentialController::class, 'store'])
            ->middleware('permission:credentials.manage')
            ->name('credentials.store');
        Route::get('/credentials/{credential}', [CredentialController::class, 'show'])
            ->name('credentials.show');
        Route::get('/credentials/{credential}/edit', [CredentialController::class, 'edit'])
            ->middleware('permission:credentials.manage')
            ->name('credentials.edit');
        Route::put('/credentials/{credential}', [CredentialController::class, 'update'])
            ->middleware('permission:credentials.manage')
            ->name('credentials.update');
        Route::post('/credentials/{credential}/status', [CredentialController::class, 'status'])
            ->middleware('permission:credentials.manage')
            ->name('credentials.status');
    });

    // O06 remainder â€” attendance, welfare and payroll (DEC42, DEC39).
    //
    // Attendance has two write doors that never race: `attendance.checkin`
    // touches only the actor's own row for today, `attendance.manage`
    // marks anyone's row and corrects an existing one with a stated
    // reason. The index takes either door â€” the self-only account sees a
    // page carrying its own card and no register.
    //
    // Welfare copies the leave shape (DEC23): `welfare.submit` records,
    // `welfare.approve` decides, the decider is a second person.
    //
    // Payroll is the DEC39 sandbox: `payroll.manage` builds and finalises
    // runs (finalise = second person), `payroll.view` reads registers and
    // run detail, and `payroll.view_own` is the self-service payslip door
    // that can never read another person's pay.
    Route::middleware(['module:hr.staff'])->group(function (): void {
        Route::get('/attendance', [AttendanceController::class, 'index'])
            ->name('attendance.index');
        Route::post('/attendance', [AttendanceController::class, 'store'])
            ->middleware('permission:attendance.manage')
            ->name('attendance.store');
        Route::post('/attendance/check-in', [AttendanceController::class, 'selfCheckin'])
            ->middleware('permission:attendance.checkin')
            ->name('attendance.checkin');
        Route::post('/attendance/check-out', [AttendanceController::class, 'selfCheckout'])
            ->middleware('permission:attendance.checkin')
            ->name('attendance.checkout');

        Route::get('/welfare', [WelfareRequestController::class, 'index'])
            ->name('welfare.index');
        Route::post('/welfare', [WelfareRequestController::class, 'store'])
            ->middleware('permission:welfare.submit')
            ->name('welfare.store');
        Route::post('/welfare/{welfareRequest}/approve', [WelfareRequestController::class, 'approve'])
            ->middleware('permission:welfare.approve')
            ->name('welfare.approve');
        Route::post('/welfare/{welfareRequest}/reject', [WelfareRequestController::class, 'reject'])
            ->middleware('permission:welfare.approve')
            ->name('welfare.reject');
        Route::post('/welfare/{welfareRequest}/cancel', [WelfareRequestController::class, 'cancel'])
            ->middleware('permission:welfare.submit')
            ->name('welfare.cancel');

        Route::get('/payroll', [PayrollController::class, 'index'])
            ->middleware('permission:payroll.view')
            ->name('payroll.index');
        // `components` and `create` are declared before `{payrollRun}` so
        // they are not swallowed by the binding â€” the recurring trap.
        Route::get('/payroll/components', [PayrollController::class, 'components'])
            ->middleware('permission:payroll.view')
            ->name('payroll.components');
        Route::post('/payroll/components', [PayrollController::class, 'storeComponent'])
            ->middleware('permission:payroll.manage')
            ->name('payroll.components.store');
        // DEC39's gate: signing off the statutory rates is its own door and
        // its own act â€” never part of the component form (never a form field).
        Route::post.*sign-off
        Route::get.*employee-list
        Route::get.*employee-export', [PayrollController::class, 'signOffComponents'])
            ->middleware('permission:payroll.sign_off')
            ->name('payroll.components.sign_off');
        Route::get('/payroll/create', [PayrollController::class, 'create'])
            ->middleware('permission:payroll.manage')
            ->name('payroll.create');
        Route::post('/payroll', [PayrollController::class, 'store'])
            ->middleware('permission:payroll.manage')
            ->name('payroll.store');
        Route::get('/payroll/{payrollRun}', [PayrollController::class, 'show'])
            ->middleware('permission:payroll.view')
            ->name('payroll.show');
        Route::get('/payroll/{payrollRun}/payslips/{payslip}', [PayrollController::class, 'payslip'])
            ->middleware('permission:payroll.view')
            ->name('payroll.payslip');
        Route::post('/payroll/{payrollRun}/finalise', [PayrollController::class, 'finalise'])
            ->middleware('permission:payroll.manage')
            ->name('payroll.finalise');

        Route::get('/payslips', [PayslipController::class, 'index'])
            ->middleware('permission:payroll.view_own')
            ->name('payslips.index');
        Route::get('/payslips/{payslip}', [PayslipController::class, 'show'])
            ->middleware('permission:payroll.view_own')
            ->name('payslips.show');

        // O07 remainder â€” dated duty rosters (DEC43). The index takes any
        // of the three doors and is enforced inside the controller, the
        // same way attendance does it: the self-only account lands on a
        // page with its own duties and no register. Every write needs
        // `rosters.manage`, carries a stated reason when it changes an
        // existing row, and is refused by name when it would overlap
        // another active duty of the same person or a day of approved
        // leave. No GET ever mutates a roster row. `amend` names its duty
        // in the form body rather than the URL so the form needs no
        // script to target the row it shows.
        Route::get('/rosters', [RosterController::class, 'index'])
            ->name('rosters.index');
        Route::post('/rosters', [RosterController::class, 'store'])
            ->middleware('permission:rosters.manage')
            ->name('rosters.store');
        Route::post('/rosters/amend', [RosterController::class, 'amend'])
            ->middleware('permission:rosters.manage')
            ->name('rosters.amend');
        Route::post('/rosters/{rosterAssignment}/cancel', [RosterController::class, 'cancel'])
            ->middleware('permission:rosters.manage')
            ->name('rosters.cancel');
    });

    // O10 / DEC44 â€” reporting and business intelligence (module:
    // reporting.bi). The index and each report accept any of several read
    // doors, which route middleware cannot express (one route, several
    // acceptable permissions), so that check lives in the controller with
    // an audited refusal â€” the rosters shape. The CSV export is the route
    // middleware does gate: `metrics.export` sits on top of the report's
    // own read door (F04), and every read and every export writes its own
    // audit row (F05). The file itself is streamed, never stored.
    Route::middleware(['module:reporting.bi'])->group(function (): void {
        Route::get('/metrics', [MetricsController::class, 'index'])
            ->name('metrics.index');
        Route::get('/metrics/{report}', [MetricsController::class, 'show'])
            ->where('report', '[a-z0-9.\-]+')
            ->name('metrics.show');
        Route::get('/metrics/{report}/export', [MetricsController::class, 'export'])
            ->where('report', '[a-z0-9.\-]+')
            ->middleware('permission:metrics.export')
            ->name('metrics.export');
    });

    // O01a / DEC47 â€” inventory core (module: operations.inventory).
    //
    // One register, one ledger: the item list derives stock on hand from
    // the append-only movement ledger at read time, the item page carries
    // the log plus the receipt/issue/count forms, and conversions live on
    // their own page because they are configuration (F08), not stock.
    // Reading needs a read door; every write needs the single SENSITIVE
    // `inventory.manage` door (DEC47) â€” nothing here is self-service.
    // `create` and `suppliers` are declared before `{inventoryItem}` so
    // the literal segments are never swallowed by model binding.
    Route::middleware(['module:operations.inventory'])->group(function (): void {
        Route::get('/inventory', [InventoryController::class, 'index'])
            ->middleware('permission:stock_movements.view')
            ->name('stock-movements.index');
        Route::get('/inventory/create', [InventoryController::class, 'create'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.create');
        Route::post('/inventory', [InventoryController::class, 'store'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.store');
        Route::get('/inventory/suppliers', [SupplierController::class, 'index'])
            ->middleware('permission:stock_movements.view')
            ->name('suppliers.index');
        Route::get('/inventory/suppliers/create', [SupplierController::class, 'create'])
            ->middleware('permission:inventory.manage')
            ->name('suppliers.create');
        Route::post('/inventory/suppliers', [SupplierController::class, 'store'])
            ->middleware('permission:inventory.manage')
            ->name('suppliers.store');
        Route::get('/inventory/suppliers/{supplier}/edit', [SupplierController::class, 'edit'])
            ->middleware('permission:inventory.manage')
            ->name('suppliers.edit');
        Route::put('/inventory/suppliers/{supplier}', [SupplierController::class, 'update'])
            ->middleware('permission:inventory.manage')
            ->name('suppliers.update');
        Route::get('/inventory/{inventoryItem}', [InventoryController::class, 'show'])
            ->middleware('permission:stock_movements.view')
            ->name('stock-movements.show');
        Route::get('/inventory/{inventoryItem}/edit', [InventoryController::class, 'edit'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.edit');
        Route::put('/inventory/{inventoryItem}', [InventoryController::class, 'update'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.update');
        // Movements and counts are POST-only; the ledger has no edit or
        // delete route at all â€” append-only is enforced by the URL surface.
        Route::post('/inventory/{inventoryItem}/movements', [InventoryController::class, 'storeMovement'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.movements.store');
        Route::post('/inventory/{inventoryItem}/counts', [InventoryController::class, 'storeCount'])
            ->middleware('permission:inventory.manage')
            ->name('inventory.counts.store');

        Route::get('/unit-conversions', [UnitConversionController::class, 'index'])
            ->middleware('permission:unit_conversions.view')
            ->name('unit-conversions.index');
        Route::post('/unit-conversions', [UnitConversionController::class, 'store'])
            ->middleware('permission:inventory.manage')
            ->name('unit-conversions.store');
        Route::post('/unit-conversions/{conversion}/retire', [UnitConversionController::class, 'retire'])
            ->middleware('permission:inventory.manage')
            ->name('unit-conversions.retire');
    });

    // O01b / DEC47 â€” the procurement chain (module: operations.procurement).
    //
    // Requisition -> decision -> order -> receipt -> supplier return, with
    // the separation of duties the decision names: a requisition is raised
    // through `procurement.requisition`, decided through the SENSITIVE
    // `procurement.approve` door by a different person (the service refuses
    // the raiser regardless), and executed through `purchase_orders.manage`.
    // `purchase_orders.view` reads every page. The route name and read door
    // are the ones NavigationBuilder has always expected for this item.
    // All the `requisitions` literals are declared before `{purchaseOrder}`
    // so model binding never swallows them.
    Route::middleware(['module:operations.procurement'])->group(function (): void {
        Route::get('/purchase-orders', [ProcurementController::class, 'index'])
            ->middleware('permission:purchase_orders.view')
            ->name('purchase-orders.index');
        Route::get('/purchase-orders/requisitions/create', [ProcurementController::class, 'createRequisition'])
            ->middleware('permission:procurement.requisition')
            ->name('purchase-orders.requisitions.create');
        Route::post('/purchase-orders/requisitions', [ProcurementController::class, 'storeRequisition'])
            ->middleware('permission:procurement.requisition')
            ->name('purchase-orders.requisitions.store');
        // Deciding needs the separate SENSITIVE door; executing the order
        // needs `purchase_orders.manage`. Three doors, three acts.
        Route::post('/purchase-orders/requisitions/{requisition}/approve', [ProcurementController::class, 'approve'])
            ->middleware('permission:procurement.approve')
            ->name('purchase-orders.requisitions.approve');
        Route::post('/purchase-orders/requisitions/{requisition}/reject', [ProcurementController::class, 'reject'])
            ->middleware('permission:procurement.approve')
            ->name('purchase-orders.requisitions.reject');
        Route::post('/purchase-orders/requisitions/{requisition}/order', [ProcurementController::class, 'createOrder'])
            ->middleware('permission:purchase_orders.manage')
            ->name('purchase-orders.requisitions.order');
        Route::get('/purchase-orders/requisitions/{requisition}', [ProcurementController::class, 'requisitionShow'])
            ->middleware('permission:purchase_orders.view')
            ->name('purchase-orders.requisitions.show');
        Route::post('/purchase-orders/{purchaseOrder}/receive', [ProcurementController::class, 'receive'])
            ->middleware('permission:purchase_orders.manage')
            ->name('purchase-orders.receive');
        Route::post('/purchase-orders/{purchaseOrder}/cancel', [ProcurementController::class, 'cancel'])
            ->middleware('permission:purchase_orders.manage')
            ->name('purchase-orders.cancel');
        Route::post('/purchase-orders/{purchaseOrder}/returns', [ProcurementController::class, 'storeReturn'])
            ->middleware('permission:purchase_orders.manage')
            ->name('purchase-orders.returns');
        Route::get('/purchase-orders/{purchaseOrder}', [ProcurementController::class, 'orderShow'])
            ->middleware('permission:purchase_orders.view')
            ->name('purchase-orders.show');
    });

    // O09 / DEC48 â€” quality, incidents and infection prevention
    // (module: quality.incidents).
    //
    // Report -> review (the F10 separation-of-duties *instance*: the
    // reporter is never the reviewer) -> corrective actions assigned to
    // their owner -> close once all are complete. Overdue is derived from
    // `due_at` at read time and never stored, so no route writes it.
    //
    // The literals are declared before `{incident}` exactly as the
    // requisition block learned: a binding route registered earlier would
    // swallow `/incidents/create` and `/incidents/definitions` and 404 them.
    Route::middleware(['module:quality.incidents'])->group(function (): void {
        Route::get('/incidents', [IncidentController::class, 'index'])
            ->middleware('permission:incidents.view')
            ->name('incidents.index');
        Route::get('/incidents/create', [IncidentController::class, 'create'])
            ->middleware('permission:incidents.report')
            ->name('incidents.create');
        Route::post('/incidents', [IncidentController::class, 'store'])
            ->middleware('permission:incidents.report')
            ->name('incidents.store');
        Route::get('/incidents/definitions', [IncidentController::class, 'definitions'])
            ->middleware('permission:incidents.view')
            ->name('incidents.definitions');
        Route::post('/incidents/definitions', [IncidentController::class, 'storeDefinition'])
            ->middleware('permission:incidents.manage')
            ->name('incidents.definitions.store');
        Route::post('/incidents/definitions/{definition}/retire', [IncidentController::class, 'retireDefinition'])
            ->middleware('permission:incidents.manage')
            ->name('incidents.definitions.retire');
        Route::get('/incidents/{incident}', [IncidentController::class, 'show'])
            ->middleware('permission:incidents.view')
            ->name('incidents.show');
        Route::post('/incidents/{incident}/review', [IncidentController::class, 'review'])
            ->middleware('permission:incidents.review')
            ->name('incidents.review');
        Route::post('/incidents/{incident}/close', [IncidentController::class, 'close'])
            ->middleware('permission:incidents.review')
            ->name('incidents.close');
        Route::post('/incident-actions/{action}/complete', [IncidentController::class, 'completeAction'])
            ->middleware('permission:incidents.complete')
            ->name('incidents.actions.complete');
    });

    // O08 / DEC48 â€” training and residency (module: hr.training).
    //
    // `create` is declared before `{rotation}` so it is not swallowed, the
    // same shape as the incidents block above. The register reads through
    // either the register door or the own-view door (the `|` route
    // syntax), and own-view scoping happens in the controller â€” a rotation
    // that is not yours answers 404, never 403, so its existence never
    // leaks.
    Route::middleware(['module:hr.training'])->group(function (): void {
        Route::get('/training', [TrainingController::class, 'index'])
            ->middleware('permission:training.view|training.view_own')
            ->name('training.index');
        Route::get('/training/competencies', [TrainingController::class, 'competencies'])
            ->middleware('permission:training.view|training.view_own')
            ->name('training.competencies');
        Route::get('/training/rotations/create', [TrainingController::class, 'create'])
            ->middleware('permission:training.manage')
            ->name('training.rotations.create');
        Route::post('/training/rotations', [TrainingController::class, 'store'])
            ->middleware('permission:training.manage')
            ->name('training.rotations.store');
        Route::get('/training/rotations/{rotation}', [TrainingController::class, 'show'])
            ->middleware('permission:training.view|training.view_own')
            ->name('training.rotation.show');
        Route::post('/training/rotations/{rotation}/activities', [TrainingController::class, 'storeActivity'])
            ->middleware('permission:training.record')
            ->name('training.rotation.activities.store');
        Route::post('/training/rotations/{rotation}/evaluation', [TrainingController::class, 'storeEvaluation'])
            ->middleware('permission:training.evaluate')
            ->name('training.rotation.evaluation.store');
        Route::post('/training/evaluations/{evaluation}/countersign', [TrainingController::class, 'countersignEvaluation'])
            ->middleware('permission:training.countersign')
            ->name('training.evaluations.countersign');
        Route::post('/training/competencies', [TrainingController::class, 'storeCompetency'])
            ->middleware('permission:training.manage')
            ->name('training.competencies.store');
        Route::post('/training/competencies/{competency}/retire', [TrainingController::class, 'retireCompetency'])
            ->middleware('permission:training.manage')
            ->name('training.competencies.retire');
        Route::post('/training/competencies/{competency}/records', [TrainingController::class, 'startCompetencyRecord'])
            ->middleware('permission:training.evaluate')
            ->name('training.competencies.records.store');
        Route::post('/training/competency-records/{record}/assess', [TrainingController::class, 'assessCompetencyRecord'])
            ->middleware('permission:training.evaluate')
            ->name('training.records.assess');
        Route::post('/training/competency-records/{record}/countersign', [TrainingController::class, 'countersignCompetencyRecord'])
            ->middleware('permission:training.countersign')
            ->name('training.records.countersign');
    });

    // F18 â€” documents and certificates (module: documents.certificates).
    //
    // Six doors, six separated acts: view the register, print the patient-named
    // document, issue/reissue, sign, revoke, and maintain templates and the
    // signatory register. `new` is declared before the `{certificate}` wildcard
    // (the training/incidents shape), and the print route is a separate read
    // door because it produces a document naming the patient.
    Route::middleware(['module:documents.certificates'])->group(function (): void {
        Route::get('/documents/certificates', [DocumentCertificateController::class, 'index'])
            ->middleware('permission:document_certificates.view')
            ->name('document-certificates.index');
        Route::get('/documents/certificates/new', [DocumentCertificateController::class, 'create'])
            ->middleware('permission:document_certificates.issue')
            ->name('document-certificates.create');
        Route::post('/documents/certificates', [DocumentCertificateController::class, 'store'])
            ->middleware('permission:document_certificates.issue')
            ->name('document-certificates.store');
        Route::get('/documents/certificates/{certificate}/print', [DocumentCertificateController::class, 'print'])
            ->middleware('permission:document_certificates.print')
            ->name('document-certificates.print');
        Route::get('/documents/certificates/{certificate}', [DocumentCertificateController::class, 'show'])
            ->middleware('permission:document_certificates.view')
            ->name('document-certificates.show');
        Route::post('/documents/certificates/{certificate}/sign', [DocumentCertificateController::class, 'sign'])
            ->middleware('permission:document_certificates.sign')
            ->name('document-certificates.sign');
        Route::post('/documents/certificates/{certificate}/revoke', [DocumentCertificateController::class, 'revoke'])
            ->middleware('permission:document_certificates.revoke')
            ->name('document-certificates.revoke');
        Route::post('/documents/certificates/{certificate}/reissue', [DocumentCertificateController::class, 'reissue'])
            ->middleware('permission:document_certificates.issue')
            ->name('document-certificates.reissue');

        Route::get('/documents/templates', [DocumentTemplateController::class, 'index'])
            ->middleware('permission:document_templates.view|document_templates.manage')
            ->name('document-templates.index');
        Route::get('/documents/templates/create', [DocumentTemplateController::class, 'create'])
            ->middleware('permission:document_templates.manage')
            ->name('document-templates.create');
        Route::post('/documents/templates', [DocumentTemplateController::class, 'store'])
            ->middleware('permission:document_templates.manage')
            ->name('document-templates.store');
        Route::get('/documents/templates/{template}/edit', [DocumentTemplateController::class, 'edit'])
            ->middleware('permission:document_templates.manage')
            ->name('document-templates.edit');
        Route::post('/documents/templates/{template}', [DocumentTemplateController::class, 'update'])
            ->middleware('permission:document_templates.manage')
            ->name('document-templates.update');
        Route::post('/documents/templates/{template}/retire', [DocumentTemplateController::class, 'retire'])
            ->middleware('permission:document_templates.manage')
            ->name('document-templates.retire');

        Route::get('/documents/signatories', [DocumentSignatoryController::class, 'index'])
            ->middleware('permission:document_signatories.manage')
            ->name('document-signatories.index');
        Route::post('/documents/signatories', [DocumentSignatoryController::class, 'store'])
            ->middleware('permission:document_signatories.manage')
            ->name('document-signatories.store');
        Route::post('/documents/signatories/{signatory}/retire', [DocumentSignatoryController::class, 'retire'])
            ->middleware('permission:document_signatories.manage')
            ->name('document-signatories.retire');
    });

    // O11 / DEC49 â€” the public website's management surface. Three
    // audiences, three doors: the register reads through the OR set (a
    // custom role holding only the publish door can still reach it),
    // drafting and retiring are `public_services.manage`, and publishing
    // is `engagement.publish` alone â€” the service refuses anyone
    // publishing a draft they wrote themselves. Booking confirm/cancel is
    // ordinary work on the manage door.
    Route::middleware(['module:public.website'])->group(function (): void {
        Route::get('/public-website', [PublicWebsiteController::class, 'index'])
            ->middleware('permission:public_services.view|public_services.manage|engagement.publish')
            ->name('public-website.index');
        Route::get('/public-website/bookings', [PublicWebsiteController::class, 'bookings'])
            ->middleware('permission:public_services.view|public_services.manage|engagement.publish')
            ->name('public-website.bookings');
        Route::post('/public-website/{type}', [PublicWebsiteController::class, 'store'])
            ->middleware('permission:public_services.manage')
            ->whereIn('type', ['service', 'clinician', 'career', 'slot'])
            ->name('public-website.store');
        Route::get('/public-website/{type}/{id}/edit', [PublicWebsiteController::class, 'edit'])
            ->middleware('permission:public_services.manage')
            ->whereIn('type', ['service', 'clinician', 'career', 'slot'])
            ->name('public-website.edit');
        Route::put('/public-website/{type}/{id}', [PublicWebsiteController::class, 'update'])
            ->middleware('permission:public_services.manage')
            ->whereIn('type', ['service', 'clinician', 'career', 'slot'])
            ->name('public-website.update');
        Route::post('/public-website/{type}/{id}/publish', [PublicWebsiteController::class, 'publish'])
            ->middleware('permission:engagement.publish')
            ->whereIn('type', ['service', 'clinician', 'career', 'slot'])
            ->name('public-website.publish');
        Route::post('/public-website/{type}/{id}/retire', [PublicWebsiteController::class, 'retire'])
            ->middleware('permission:public_services.manage')
            ->whereIn('type', ['service', 'clinician', 'career', 'slot'])
            ->name('public-website.retire');
        Route::post('/public-website/bookings/{booking}/confirm', [PublicWebsiteController::class, 'confirmBooking'])
            ->middleware('permission:public_services.manage')
            ->name('public-website.bookings.confirm');
        Route::post('/public-website/bookings/{booking}/cancel', [PublicWebsiteController::class, 'cancelBooking'])
            ->middleware('permission:public_services.manage')
            ->name('public-website.bookings.cancel');
    });

    // Product/Service Catalogue.
    //
    // Reading prices and administering them are different permissions. The
    // single ungated resource let a patient account create catalogue items â€”
    // verified by probe, one row actually written.
    //
    // `create` is declared first so it is not swallowed by `{item}`, and the
    // middleware is an array because `Route::resource()->middleware()` takes
    // one argument and silently discards the rest.
    Route::resource('product-items', ProductItemController::class)
        ->only(['create', 'store'])
        ->middleware(['module:finance.billing', 'permission:configuration.manage'])
        ->parameters(['product-items' => 'item']);
    Route::resource('product-items', ProductItemController::class)
        ->only(['index', 'show'])
        ->middleware(['module:finance.billing', 'permission:service_tariffs.view'])
        ->parameters(['product-items' => 'item']);
    Route::resource('product-items', ProductItemController::class)
        ->only(['edit', 'update', 'destroy'])
        ->middleware(['module:finance.billing', 'permission:configuration.manage'])
        ->parameters(['product-items' => 'item']);

    // Wards Management â€” same split: the register is clinical-adjacent and
    // readable by anyone who can see a bed; changing it is configuration.
    Route::resource('wards', WardController::class)
        ->only(['create', 'store'])
        ->middleware(['module:clinical.records', 'permission:configuration.manage']);
    Route::resource('wards', WardController::class)
        ->only(['index', 'show'])
        ->middleware(['module:clinical.records', 'permission:beds.view']);
    Route::resource('wards', WardController::class)
        ->only(['edit', 'update', 'destroy'])
        ->middleware(['module:clinical.records', 'permission:configuration.manage']);

    // Drug Administrations.
    //
    // The administer/withhold/mark-missed actions below already carried
    // `clinical.access`; the CRUD resource around them carried only a module
    // gate, so the screens were wide open while the buttons were not.
    //
    // One resource call keeps Laravel's canonical order (index, create, store,
    // show, ...), so `create` cannot be swallowed by `{administration}`.
    Route::resource('drug-administrations', DrugAdministrationController::class)
        ->middleware(['module:clinical.records', 'permission:clinical.access'])
        ->parameters(['drug-administrations' => 'administration']);
    Route::post('drug-administrations/{administration}/administer', [DrugAdministrationController::class, 'administer'])->middleware('module:clinical.records', 'permission:clinical.access')->name('drug-administrations.administer');
    Route::post('drug-administrations/{administration}/withhold', [DrugAdministrationController::class, 'withhold'])->middleware('module:clinical.records', 'permission:clinical.access')->name('drug-administrations.withhold');
    Route::post('drug-administrations/{administration}/mark-missed', [DrugAdministrationController::class, 'markMissed'])->middleware('module:clinical.records', 'permission:clinical.access')->name('drug-administrations.mark-missed');

    // C01 â€” care actions, care team and treatment history.
    // These back "who treated this patient, what did they do, and what was
    // prescribed". They previously had tables and profile tabs but no write
    // path, so nothing outside the demo seeder could create a row.
    // Writes require `clinical.create` (doctor, nurse); reads need `clinical.view`.
    Route::get('care-actions', [CareActionController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-actions.index');
    Route::get('care-actions/create', [CareActionController::class, 'create'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-actions.create');
    Route::post('care-actions', [CareActionController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-actions.store');
    Route::get('care-actions/{careAction}/edit', [CareActionController::class, 'edit'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-actions.edit');
    Route::put('care-actions/{careAction}', [CareActionController::class, 'update'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-actions.update');

    Route::get('care-team', [CareTeamController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-team.index');
    Route::get('care-team/create', [CareTeamController::class, 'create'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-team.create');
    Route::post('care-team', [CareTeamController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-team.store');
    Route::post('care-team/{member}/end', [CareTeamController::class, 'end'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-team.end');
    Route::post('care-team/{member}/reactivate', [CareTeamController::class, 'reactivate'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('care-team.reactivate');

    Route::get('treatment-history', [TreatmentHistoryController::class, 'index'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('treatment-history.index');
    Route::get('treatment-history/create', [TreatmentHistoryController::class, 'create'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('treatment-history.create');
    Route::post('treatment-history', [TreatmentHistoryController::class, 'store'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('treatment-history.store');
    Route::get('treatment-history/{entry}/edit', [TreatmentHistoryController::class, 'edit'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('treatment-history.edit');
    Route::put('treatment-history/{entry}', [TreatmentHistoryController::class, 'update'])
        ->middleware('module:clinical.records', 'permission:clinical.access')
        ->name('treatment-history.update');

    Route::get('/calendar', [CalendarController::class, 'index'])
        ->middleware('module:scheduling.appointments', 'permission:appointments.view')
        ->name('calendar.index');

    Route::get('/nutrition', [NutritionController::class, 'index'])
        ->middleware('module:nutrition.department', 'permission:nutrition.view')
        ->name('nutrition.index');

    Route::get('/ask', [AskBitmedAiController::class, 'index'])
        ->middleware('module:ai.assistant', 'permission:ai.ask')
        ->name('ask.index');

    Route::post('/ask', [AskBitmedAiController::class, 'ask'])
        ->middleware('module:ai.assistant', 'permission:ai.ask')
        ->name('ask.ask');

    Route::post('/ask/new', [AskBitmedAiController::class, 'start'])
        ->middleware('module:ai.assistant', 'permission:ai.ask')
        ->name('ask.start');

    Route::get('/setup/modules', [ModuleController::class, 'index'])
        ->middleware('module:core.platform', 'permission:modules.view')
        ->name('setup.modules');

    Route::get('/setup/users', [UserController::class, 'index'])
        ->middleware('module:core.identity', 'permission:roles.view')
        ->name('setup.users');

    // F04 â€” assign and revoke access. Viewing the register needs
    // `roles.view`; changing who holds what needs `roles.manage`, which is a
    // deliberately higher bar than being allowed to look.
    Route::get('/setup/users/{user}/grants', [GrantController::class, 'index'])
        ->middleware('module:core.identity', 'permission:roles.view')
        ->name('setup.users.grants');

    Route::middleware('permission:roles.manage')->group(function (): void {
        Route::post('/setup/users/{user}/grants', [GrantController::class, 'store'])
            ->name('setup.users.grants.store');

        Route::post('/setup/grants/{grant}/revoke', [GrantController::class, 'revoke'])
            ->name('setup.grants.revoke');
    });

    Route::get('/setup/audit', [AuditController::class, 'index'])
        ->middleware('module:core.audit')
        ->name('setup.audit');

    Route::post('/setup/users/{user}/mfa-reset', [MfaResetController::class, 'reset'])
        ->middleware('module:core.identity', 'permission:identity.manage')
        ->name('setup.users.mfa-reset');
});

// B01 Ã¢â‚¬â€ Versioned service catalogue and tariffs.
// Configure payer/facility/service tariffs, packages, discounts, tax treatment,
// effective dates; price changes do not retroactively alter issued invoices.
// Doors use module gating + permission verbs; service-level checks enforce real
// permissions (F08 / EX02 versioned configuration precedent).
Route::middleware('auth')->group(function (): void {
    // Reading the price list and changing it are separate doors. `RequirePermission`
    // takes a single slug â€” it does not accept a comma list, it would look one up
    // as a literal key and deny everybody â€” so the split is done with two groups.
    // Registration order is deliberate and must be preserved: `/service-tariffs/create`
    // has to be declared before `/service-tariffs/{service_key}`, or "create" is
    // swallowed as a service key.
    Route::middleware('permission:service_tariffs.view')
        ->get('/service-tariffs', [ServiceTariffController::class, 'index'])
        ->name('service-tariff.index');

    Route::middleware('permission:configuration.manage')->group(function (): void {
        Route::get('/service-tariffs/create', [ServiceTariffController::class, 'create'])
            ->name('service-tariff.create');
        Route::post('/service-tariffs', [ServiceTariffController::class, 'store'])
            ->name('service-tariff.store');
    });

    Route::middleware('permission:service_tariffs.view')
        ->get('/service-tariffs/{service_key}', [ServiceTariffController::class, 'show'])
        ->name('service-tariff.show');

    Route::middleware('permission:configuration.manage')->group(function (): void {
        Route::get('/service-tariffs/{service_key}/edit', [ServiceTariffController::class, 'edit'])
            ->name('service-tariff.edit');
        Route::put('/service-tariffs/{service_key}', [ServiceTariffController::class, 'update'])
            ->name('service-tariff.update');
        Route::post('/service-tariffs/{service_key}/publish', [ServiceTariffController::class, 'publish'])
            ->name('service-tariff.publish');
        Route::post('/service-tariffs/{service_key}/retire', [ServiceTariffController::class, 'retire'])
            ->name('service-tariff.retire');
    });
});

// B02 Ã¢â‚¬â€ Charge capture and invoice lifecycle.
// Authorized charges linked to services and patient; track draft/issued/part-paid/paid/voided
// states; prevent repeated event processing from creating duplicate charges.
// Doors use module gating + permission verbs; service-level checks enforce real
// permissions (F08 / EX02 versioned configuration precedent).
Route::middleware(['auth', 'permission:invoices.view'])->group(function (): void {
    Route::get('/invoices', [InvoiceController::class, 'index'])
        ->name('invoice.index');
    Route::get('/invoices/create', [InvoiceController::class, 'create'])
        ->name('invoice.create');
    Route::post('/invoices', [InvoiceController::class, 'store'])
        ->name('invoice.store');
    Route::get('/invoices/{reference}', [InvoiceController::class, 'show'])
        ->name('invoice.show');
    Route::get('/invoices/download/{reference}', [InvoiceController::class, 'download'])
        ->name('invoice.download');

      Route::get('/invoices/qr/{reference}', [InvoiceController::class, 'qr']);
        ->middleware('permission:invoices.download');
    Route::put('/invoices/{reference}', [InvoiceController::class, 'update'])
        ->name('invoice.update');
    Route::post('/invoices/{reference}/void', [InvoiceController::class, 'void'])
        ->name('invoice.void');

    // B10 â€” invoice edit double-approval workflow. Managers propose an
    // edit; a supervisor and then an approving manager must both approve
    // before the change is applied.
    Route::middleware(['auth', 'permission:invoices.edit'])->group(function (): void {
        Route::post('/invoices/{reference}/propose-edit', [InvoiceController::class, 'proposeEdit'])
            ->name('invoice.propose-edit');
    });
    Route::middleware(['auth', 'permission:invoices.approve'])->group(function (): void {
        Route::post('/invoice-approvals/{approval}/approve', [InvoiceController::class, 'approve'])
            ->name('invoice-approvals.approve');
    });
});

// B04 Ã¢â‚¬â€ Payment adapters and verification.
// Configure supported gateways including prioritized candidates Paystack, Flutterwave, Stripe, PayPal, OPay;
// verify callbacks/server status; deduplicate retries; show pending/failed/settled states;
// sandbox vs verified labelling (DEC32). Public links use independent unguessable tokens;
// tenant isolation on all payment operations; lookup never grants clinical access.
Route::middleware(['auth', 'permission:payment_adapters.view'])->group(function (): void {
    Route::get('/payment-adapters', [PaymentAdapterController::class, 'index'])
        ->name('payment-adapter.index');
    Route::get('/payment-adapters/create', [PaymentAdapterController::class, 'create'])
        ->name('payment-adapter.create');
    Route::post('/payment-adapters', [PaymentAdapterController::class, 'store'])
        ->name('payment-adapter.store');
    Route::get('/payment-adapters/{id}', [PaymentAdapterController::class, 'show'])
        ->name('payment-adapter.show');
});

// Clinical workflows: Vital signs, Lab orders, Admissions, Bed management
Route::middleware(['auth', RequireMfaChallenge::class, RequireMfaEnrolment::class])->group(function (): void {
    // Vitals
    Route::post('/patients/{patient}/vitals', [VitalSignController::class, 'store'])
        ->middleware('permission:vitals.create')
        ->name('vitals.store');
    Route::get('/patients/{patient}/vitals/{vital}/edit', [VitalSignController::class, 'edit'])
        ->middleware('permission:vitals.create')
        ->name('vitals.edit');
    Route::put('/patients/{patient}/vitals/{vital}', [VitalSignController::class, 'update'])
        ->middleware('permission:vitals.create')
        ->name('vitals.update');

    // Lab orders
    Route::get('/patients/{patient}/lab-orders/create', [LabOrderController::class, 'create'])
        ->middleware('permission:lab.orders.create')
        ->name('lab-orders.create');
    Route::post('/patients/{patient}/lab-orders', [LabOrderController::class, 'store'])
        ->middleware('permission:lab.orders.create')
        ->name('lab-orders.store');
    Route::get('/lab-orders/{labOrder}', [LabOrderController::class, 'show'])
        ->middleware('permission:lab.orders.view')
        ->name('lab-orders.show');
    Route::post('/lab-orders/{labOrder}/collect', [LabOrderController::class, 'collect'])
        ->middleware('permission:lab.orders.collect')
        ->name('lab-orders.collect');
    Route::post('/lab-orders/{labOrder}/complete', [LabOrderController::class, 'complete'])
        ->middleware('permission:lab.orders.verify')
        ->name('lab-orders.complete');

    // Admissions
    Route::get('/patients/{patient}/admissions/create', [AdmissionController::class, 'create'])
        ->middleware('permission:admissions.create')
        ->name('admissions.create');
    Route::post('/patients/{patient}/admissions', [AdmissionController::class, 'store'])
        ->middleware('permission:admissions.create')
        ->name('admissions.store');
    Route::get('/admissions/{admission}', [AdmissionController::class, 'show'])
        ->middleware('permission:admissions.view')
        ->name('admissions.show');
    Route::get('/admissions/{admission}/discharge', [AdmissionController::class, 'dischargeForm'])
        ->middleware('permission:admissions.discharge')
        ->name('admissions.discharge.form');
    Route::post('/admissions/{admission}/discharge', [AdmissionController::class, 'discharge'])
        ->middleware('permission:admissions.discharge')
        ->name('admissions.discharge');

    // Bed management
    Route::get('/beds', [BedController::class, 'index'])
        ->middleware('permission:beds.view')
        ->name('beds.index');
    Route::get('/beds/{bed}', [BedController::class, 'show'])
        ->middleware('permission:beds.view')
        ->name('beds.show');
    Route::post('/beds/{bed}/assign', [BedController::class, 'assign'])
        ->middleware('permission:beds.assign')
        ->name('beds.assign');
    Route::post('/bed-assignments/{bedAssignment}/transfer', [BedController::class, 'transfer'])
        ->middleware('permission:beds.transfer')
        ->name('beds.transfer');
    Route::post('/bed-assignments/{bedAssignment}/release', [BedController::class, 'release'])
        ->middleware('permission:beds.release')
        ->name('beds.release');
});
