<?php

declare(strict_types=1);

/**
 * Role-first navigation and persistent patient-flow context.
 * This file intentionally contains no schema changes. It reads the existing
 * appointment, queue, encounter, invoice, and patient state to explain the
 * next human action without altering the underlying workflow engine.
 */

function dental_role_workspace_definition(?string $role = null): array
{
    $role ??= active_role();
    return match ($role) {
        'super_admin' => [
            'label' => 'System Control Center',
            'url' => 'system_setup.php',
            'action' => 'Open system control',
            'responsibility' => 'Keep the installation healthy, secure, current, backed up, and correctly licensed.',
            'handoff' => 'Use a task view for routine clinic work so every action remains attributable to the correct operational lane.',
            'steps' => ['Review system alerts', 'Resolve updates or failures', 'Manage access and protected settings', 'Verify backups and audit exceptions'],
        ],
        'clinic_admin' => [
            'label' => 'Operations Control Center',
            'url' => 'administration.php',
            'action' => 'Open operations control',
            'responsibility' => 'Monitor the clinic, resolve operational exceptions, and keep staffing, schedules, chairs, finance, and supplies ready.',
            'handoff' => 'Clinical decisions remain with the assigned Dentist; payment posting remains with the Cashier.',
            'steps' => ['Review exceptions', 'Resolve staffing and schedule blocks', 'Monitor patient flow and cashier', 'Approve controlled changes'],
        ],
        'dentist' => [
            'label' => 'My Treatment Dashboard',
            'url' => 'doctor_treatment.php',
            'action' => 'Open my treatment dashboard',
            'responsibility' => 'Treat the assigned patient and complete one clinically defensible encounter from review through release.',
            'handoff' => 'After finalization, send verified charges to Cashier or complete an authorized no-payment discharge.',
            'steps' => ['Choose assigned patient', 'Review safety information', 'Assess and diagnose', 'Treat and document', 'Release to Cashier or discharge'],
        ],
        'receptionist' => [
            'label' => 'Front Desk',
            'url' => 'queue.php',
            'action' => 'Open Front Desk',
            'responsibility' => 'Convert requests into appointments, verify arrivals, and move patients through the valid front-desk queue stages.',
            'handoff' => 'Once the patient is seated and screening is ready, responsibility transfers to the clinical team.',
            'steps' => ['Review requests', 'Verify patient', 'Book or confirm', 'Check in', 'Call and seat'],
        ],
        'staff' => [
            'label' => 'General Staff Workspace',
            'url' => 'dashboard.php',
            'action' => 'Open reception and cashier workspace',
            'responsibility' => 'Complete front-desk registration, scheduling, arrival, queue, payment, receipt, checkout, and shift reconciliation duties under one dedicated account.',
            'handoff' => 'Transfer clinical decisions and encounter finalization to the care team; send discounts, refunds, waivers, and other approvals to the Clinic Administrator.',
            'steps' => ['Register or verify the patient', 'Book, confirm, or check in the visit', 'Move the patient through the front-desk queue', 'Open your cashier session and receive authorized payment', 'Issue the receipt, complete checkout, and reconcile the drawer'],
        ],
        'dental_assistant' => [
            'label' => 'Chairside Tasks',
            'url' => 'dashboard.php',
            'action' => 'Open chairside tasks',
            'responsibility' => 'Prepare the patient, chair, instruments, screening, images, and delegated treatment entries for the assigned Dentist.',
            'handoff' => 'Submit all delegated entries to the Dentist; diagnosis, prescription, signature, and discharge remain Dentist-only.',
            'steps' => ['Open assigned patient', 'Review alerts and record screening', 'Prepare chair and instruments', 'Upload images or draft entries', 'Hand record to Dentist'],
        ],
        'cashier' => [
            'label' => 'Cashier Register',
            'url' => 'billing.php',
            'action' => 'Open Cashier Register',
            'responsibility' => 'Complete one focused payment transaction using only verified charges and approved payment methods.',
            'handoff' => 'After payment completion, return future scheduling or recall work to Reception.',
            'steps' => ['Open cashier window', 'Call or search payor', 'Review verified charges', 'Receive payment', 'Issue receipt and close visit'],
        ],
        'inventory_officer' => [
            'label' => 'Inventory Work Queue',
            'url' => 'inventory.php',
            'action' => 'Open inventory work queue',
            'responsibility' => 'Resolve stock, lot, expiry, movement, purchase, and sterilization work in document-state order.',
            'handoff' => 'Clinical consumption is recorded from verified treatment; manual stock issues must not duplicate procedure consumption.',
            'steps' => ['Review alerts and pending documents', 'Receive or issue stock', 'Verify lot and expiry', 'Complete transfer or count', 'Review sterilization and reports'],
        ],
        default => [
            'label' => 'My Workspace',
            'url' => 'dashboard.php',
            'action' => 'Open my workspace',
            'responsibility' => 'Complete work authorized for the signed-in role.',
            'handoff' => 'Escalate work that belongs to another professional or financial role.',
            'steps' => ['Review assigned work', 'Open the task', 'Complete required information', 'Save the result', 'Confirm the handoff'],
        ],
    };
}

function dental_visit_journey_steps(): array
{
    return [
        'arrival' => 'Arrived',
        'waiting' => 'Waiting',
        'called' => 'Called',
        'seated' => 'Seated',
        'treatment' => 'Treatment',
        'cashier' => 'Cashier',
        'complete' => 'Complete',
    ];
}

function dental_visit_state_guidance(string $status): array
{
    $status = strtolower(trim($status));
    return match ($status) {
        'checked_in', 'arrived' => ['step'=>'arrival','owner'=>'Front Desk','next_owner'=>'Front Desk','action'=>'Verify intake and place the patient in the waiting queue.'],
        'waiting' => ['step'=>'waiting','owner'=>'Front Desk','next_owner'=>'Front Desk / Dental Assistant','action'=>'Call the patient when the provider and chair are ready.'],
        'called' => ['step'=>'called','owner'=>'Front Desk / Dental Assistant','next_owner'=>'Dental Assistant','action'=>'Seat the patient in the assigned chair and confirm the clinical team.'],
        'seated' => ['step'=>'seated','owner'=>'Dental Assistant','next_owner'=>'Dentist','action'=>'Complete safety screening and chair preparation, then hand the patient to the Dentist.'],
        'in_treatment', 'treatment' => ['step'=>'treatment','owner'=>'Dentist','next_owner'=>'Cashier','action'=>'Complete and sign the encounter, then release verified charges to Cashier.'],
        'billing_correction' => ['step'=>'treatment','owner'=>'Dentist / Administrator','next_owner'=>'Cashier','action'=>'Correct the clinical billing source, then return the visit to Cashier.'],
        'waiting_billing', 'billing', 'cashier_called' => ['step'=>'cashier','owner'=>'Cashier','next_owner'=>'Reception','action'=>'Review verified charges, receive payment, issue the receipt, and complete the visit.'],
        'completed', 'paid', 'discharged', 'closed' => ['step'=>'complete','owner'=>'Completed','next_owner'=>'Reception when follow-up is due','action'=>'The visit is complete. Book the recommended follow-up or recall when required.'],
        'cancelled', 'left_before_treatment', 'no_show' => ['step'=>'complete','owner'=>'Front Desk','next_owner'=>'Reception','action'=>'Record the attendance outcome and reschedule or close the request as appropriate.'],
        default => ['step'=>'arrival','owner'=>'Clinic team','next_owner'=>'Responsible role','action'=>'Review the patient journey and complete the next valid workflow transition.'],
    };
}

function dental_flow_context_route(string $route): bool
{
    return in_array($route, [
        'patients','clinical','clinical_day','clinical_workspace','doctor_treatment','odontogram','periodontal','treatment',
        'prescriptions','documents','consents','referrals','laboratory','followups','visit_workflow','billing','queue','inhouse_checkups'
    ], true);
}

function dental_clear_patient_context(?int $patientId = null): void
{
    if ($patientId === null || (int)($_SESSION['dental_patient_context']['patient_id'] ?? 0) === $patientId) {
        unset($_SESSION['dental_patient_context']);
    }
}

function dental_visit_status_is_terminal(string $status): bool
{
    return in_array(strtolower(trim($status)), [
        'completed','paid','discharged','closed','cancelled','left_before_treatment','no_show',
    ], true);
}

function dental_patient_workflow_is_active(?array $queue, ?array $encounter, ?array $openInvoice, string $route, bool $cashierLock = false): bool
{
    $queueStatus = strtolower(trim((string)($queue['status'] ?? '')));
    if ($queue && $queueStatus !== '' && !dental_visit_status_is_terminal($queueStatus)) return true;

    $encounterStatus = strtolower(trim((string)($encounter['status'] ?? '')));
    if ($encounter && !in_array($encounterStatus, ['finalized','addendum','completed','closed','cancelled'], true)) return true;

    // An old unpaid balance is not an active clinical patient. It becomes an
    // active task only inside the Cashier workspace or while a Cashier session
    // has explicitly locked the patient.
    if ($route === 'billing' && ($openInvoice || $cashierLock)) return true;
    return false;
}

function dental_flow_patient_context(string $route = ''): ?array
{
    if (!current_user() || !dental_flow_context_route($route)) return null;
    if (isset($_GET['clear_patient_context'])) { dental_clear_patient_context(); return null; }
    if (!schema_table_exists(table('patients'))) return null;

    $patientId = max(0, (int)($_GET['patient_id'] ?? $_POST['patient_id'] ?? 0));
    $encounterId = max(0, (int)($_GET['encounter_id'] ?? $_POST['encounter_id'] ?? 0));
    $queueId = max(0, (int)($_GET['queue_entry_id'] ?? $_POST['queue_entry_id'] ?? $_GET['queue_id'] ?? $_POST['queue_id'] ?? 0));
    $explicit = $patientId > 0 || $encounterId > 0 || $queueId > 0;

    if ($route === 'patients' && in_array((string)($_GET['action'] ?? ''), ['view','edit'], true)) {
        $patientId = max($patientId, (int)($_GET['id'] ?? 0));
        $explicit = $patientId > 0;
    }

    try {
        $pdo = db();
        $encounter = null;
        $queue = null;
        if ($encounterId > 0 && schema_table_exists(table('clinical_encounters'))) {
            $stmt = $pdo->prepare('SELECT * FROM `' . table('clinical_encounters') . '` WHERE id=? LIMIT 1');
            $stmt->execute([$encounterId]);
            $encounter = $stmt->fetch() ?: null;
            if ($encounter) {
                $patientId = (int)$encounter['patient_id'];
                $queueId = (int)($encounter['queue_entry_id'] ?? 0);
            }
        }
        if ($queueId > 0 && schema_table_exists(table('queue_entries'))) {
            $stmt = $pdo->prepare('SELECT * FROM `' . table('queue_entries') . '` WHERE id=? LIMIT 1');
            $stmt->execute([$queueId]);
            $queue = $stmt->fetch() ?: null;
            if ($queue) $patientId = (int)$queue['patient_id'];
        }

        $savedContextAge = time() - (int)($_SESSION['dental_patient_context']['updated_at'] ?? 0);
        if ($savedContextAge > 28800) unset($_SESSION['dental_patient_context']);
        if ($patientId < 1 && !$explicit && !empty($_SESSION['dental_patient_context']['patient_id'])) {
            $patientId = (int)$_SESSION['dental_patient_context']['patient_id'];
            $encounterId = (int)($_SESSION['dental_patient_context']['encounter_id'] ?? 0);
            $queueId = (int)($_SESSION['dental_patient_context']['queue_entry_id'] ?? 0);
        }
        if ($patientId < 1) return null;

        $stmt = $pdo->prepare('SELECT id,patient_no,first_name,middle_name,last_name,branch_id,status FROM `' . table('patients') . '` WHERE id=? AND deleted_at IS NULL LIMIT 1');
        $stmt->execute([$patientId]);
        $patient = $stmt->fetch();
        if (!$patient || !task_patient_scope_allowed($patientId)) return null;

        if (!$queue && schema_table_exists(table('queue_entries'))) {
            $stmt = $pdo->prepare('SELECT * FROM `' . table('queue_entries') . '` WHERE patient_id=? AND status NOT IN ("completed","cancelled","left_before_treatment","no_show") ORDER BY checked_in_at DESC,id DESC LIMIT 1');
            $stmt->execute([$patientId]);
            $queue = $stmt->fetch() ?: null;
            if ($queue) $queueId = (int)$queue['id'];
        }
        if (!$encounter && schema_table_exists(table('clinical_encounters'))) {
            if ($queueId > 0) {
                $stmt = $pdo->prepare('SELECT * FROM `' . table('clinical_encounters') . '` WHERE queue_entry_id=? ORDER BY id DESC LIMIT 1');
                $stmt->execute([$queueId]);
            } else {
                $stmt = $pdo->prepare('SELECT * FROM `' . table('clinical_encounters') . '` WHERE patient_id=? ORDER BY id DESC LIMIT 1');
                $stmt->execute([$patientId]);
            }
            $encounter = $stmt->fetch() ?: null;
            if ($encounter) $encounterId = (int)$encounter['id'];
        }

        $openInvoice = null;
        if (schema_table_exists(table('invoices'))) {
            $invoiceStmt = $pdo->prepare('SELECT id,status,amount,paid_amount,queue_entry_id FROM `' . table('invoices') . '` WHERE patient_id=? AND status IN ("draft","unpaid","partial") AND amount-paid_amount>0.001 ORDER BY created_at DESC,id DESC LIMIT 1');
            $invoiceStmt->execute([$patientId]);
            $openInvoice = $invoiceStmt->fetch() ?: null;
        }

        $cashierLock = false;
        if ($route === 'billing' && role_context_has('cashier') && schema_table_exists(table('cashier_sessions')) && schema_column_exists(table('cashier_sessions'),'locked_patient_id')) {
            $lockStmt = $pdo->prepare('SELECT COUNT(*) FROM `' . table('cashier_sessions') . '` WHERE branch_id=? AND cashier_user_id=? AND status="open" AND locked_patient_id=?');
            $lockStmt->execute([(int)(current_user()['branch_id'] ?? 0),(int)(current_user()['id'] ?? 0),$patientId]);
            $cashierLock = (int)$lockStmt->fetchColumn() > 0;
        }

        $workflowActive = dental_patient_workflow_is_active($queue, $encounter, $openInvoice, $route, $cashierLock);

        // A persisted context is only valid while a real queue, encounter, or
        // Cashier transaction remains active. Completed patient records must
        // never remain pinned as the next patient's active context.
        if (!$explicit && !$workflowActive) {
            dental_clear_patient_context($patientId);
            return null;
        }

        $status = (string)($queue['status'] ?? '');
        if ($status === '' && $encounter) {
            if (in_array((string)$encounter['status'], ['finalized','addendum'], true)) {
                $status = ((string)($encounter['completion_destination'] ?? '') === 'no_payment' || !$openInvoice) ? 'completed' : 'waiting_billing';
            } else {
                $status = 'in_treatment';
            }
        }
        if ($status === '' && $openInvoice) $status = 'waiting_billing';
        if ($status === '') $status = 'completed';

        if ($route === 'billing' && role_context_has('cashier')) {
            $queueObligation = $queue && in_array((string)($queue['status'] ?? ''), ['waiting_billing','cashier_called'], true);
            if (!$queueObligation && !$cashierLock && !$openInvoice) {
                dental_clear_patient_context($patientId);
                return null;
            }
            $status = 'waiting_billing';
            $workflowActive = true;
        }

        if (!$workflowActive) dental_clear_patient_context($patientId);

        $guide = dental_visit_state_guidance($status);
        $name = trim(implode(' ', array_filter([(string)$patient['first_name'], (string)($patient['middle_name'] ?? ''), (string)$patient['last_name']])));
        $activeRole = active_role();
        $contextMode = $workflowActive ? 'workflow' : 'view';
        $context = [
            'mode' => $contextMode,
            'context_label' => $workflowActive ? 'Active patient' : 'Viewing patient',
            'patient_id' => $patientId,
            'patient_no' => (string)$patient['patient_no'],
            'patient_name' => $name,
            'queue_entry_id' => $queueId,
            'queue_no' => (string)($queue['queue_no'] ?? ''),
            'queue_status' => $status,
            'chair_name' => (string)($queue['chair_name'] ?? ''),
            'service_name' => (string)($queue['service_name'] ?? ''),
            'dentist_name' => (string)($queue['dentist_name'] ?? $encounter['dentist_name'] ?? ''),
            'assistant_name' => (string)($encounter['assistant_name'] ?? ''),
            'encounter_id' => $encounterId,
            'current_step' => $workflowActive ? (string)$guide['step'] : '',
            'current_owner' => $workflowActive ? (string)$guide['owner'] : 'Patient record',
            'next_owner' => $workflowActive ? (string)$guide['next_owner'] : '',
            'next_action' => $workflowActive
                ? (string)$guide['action']
                : 'No active visit is attached to this record view. Starting another task will not reuse this patient automatically.',
            'patient_url' => $activeRole === 'cashier' ? '' : 'patients.php?action=view&id=' . $patientId,
            'journey_url' => 'visit_workflow.php?patient_id=' . $patientId,
            'queue_url' => $queueId > 0 ? 'queue.php?queue_entry_id=' . $queueId . '&patient_id=' . $patientId : 'queue.php?patient_id=' . $patientId,
            'encounter_url' => $encounterId > 0 ? 'clinical_workspace.php?encounter_id=' . $encounterId : 'clinical.php?patient_id=' . $patientId,
            'chart_url' => 'odontogram.php?patient_id=' . $patientId . ($encounterId > 0 ? '&encounter_id=' . $encounterId : ''),
            'plan_url' => 'treatment.php?patient_id=' . $patientId . ($encounterId > 0 ? '&encounter_id=' . $encounterId : ''),
            'documents_url' => 'documents.php?patient_id=' . $patientId . ($encounterId > 0 ? '&encounter_id=' . $encounterId : ''),
            'billing_url' => 'billing.php?patient_id=' . $patientId . ($queueId > 0 ? '&queue_entry_id=' . $queueId : '') . ($openInvoice ? '&invoice_id=' . (int)$openInvoice['id'] : ''),
            'open_invoice_id' => (int)($openInvoice['id'] ?? 0),
        ];
        $context['primary_url'] = $workflowActive ? match ((string)$guide['step']) {
            'cashier' => (string)$context['billing_url'],
            'treatment' => (string)$context['encounter_url'],
            'waiting', 'called', 'seated', 'arrival' => (string)$context['queue_url'],
            default => (string)$context['patient_url'],
        } : (string)$context['patient_url'];
        $context['primary_label'] = $workflowActive ? match ((string)$guide['step']) {
            'cashier' => 'Continue cashier task',
            'treatment' => 'Open today’s visit',
            'waiting', 'called', 'seated', 'arrival' => 'Continue patient flow',
            default => 'Open patient record',
        } : 'Open patient record';
        $context['allow_manual_clear'] = $workflowActive && !($route === 'billing' && role_context_has('cashier'));

        if ($explicit && $workflowActive) {
            $_SESSION['dental_patient_context'] = [
                'patient_id' => $patientId,
                'encounter_id' => $encounterId,
                'queue_entry_id' => $queueId,
                'mode' => 'workflow',
                'updated_at' => time(),
            ];
        }
        return $context;
    } catch (Throwable $exception) {
        error_log('DentalCare patient-flow context fallback: ' . $exception->getMessage());
        return null;
    }
}

function dental_flow_contextual_url(string $url, ?array $context): string
{
    if (!$context || trim($url) === '' || preg_match('#^(?:https?:)?//#i', $url) || str_starts_with($url, '#')) return $url;
    $parts = parse_url($url);
    if ($parts === false) return $url;
    $path = (string)($parts['path'] ?? '');
    $route = dental_sidebar_route_key_from_path($path);
    $supported = ['patients','clinical','odontogram','periodontal','treatment','prescriptions','documents','consents','referrals','laboratory','followups','visit_workflow'];
    if (!in_array($route, $supported, true)) return $url;
    $query = [];
    if (!empty($parts['query'])) parse_str((string)$parts['query'], $query);
    if ($route === 'patients') {
        if (!isset($query['action'])) $query['action'] = 'view';
        if (!isset($query['id'])) $query['id'] = (int)$context['patient_id'];
    } else {
        $query['patient_id'] ??= (int)$context['patient_id'];
        if (in_array($route, ['odontogram','periodontal','treatment','prescriptions','documents','consents','referrals','laboratory'], true)
            && (int)($context['encounter_id'] ?? 0) > 0) {
            $query['encounter_id'] ??= (int)$context['encounter_id'];
        }
    }
    $fragment = (string)($parts['fragment'] ?? '');
    return $path . ($query ? '?' . http_build_query($query) : '') . ($fragment !== '' ? '#' . $fragment : '');
}
