<?php

declare(strict_types=1);

require DENTAL_ROOT . '/app/bootstrap.php';
require_login();
require DENTAL_ROOT . '/app/layout.php';
if (is_file(DENTAL_ROOT . '/app/setup_readiness.php')) { require_once DENTAL_ROOT . '/app/setup_readiness.php'; }

$pdo = db();
if (!app_demo_read_only() && !defined('DENTAL_REMINDER_SHUTDOWN_REGISTERED')) {
    define('DENTAL_REMINDER_SHUTDOWN_REGISTERED', true);
    register_shutdown_function(static function (): void {
        if (function_exists('fastcgi_finish_request')) { @fastcgi_finish_request(); }
        maybe_run_automatic_reminders();
    });
}
$user = current_user() ?? [];
$actualRole = actual_role();
$role = active_role();
$userId = (int) ($user['id'] ?? 0);
$today = date('Y-m-d');
$dashboardBranchId = $actualRole === 'super_admin' ? 0 : signed_user_branch_id();
$advanced = documentation_schema_ready();
$profile = role_profile($role);
$workspace = task_workspace($role);
$taskDentistId = 0;
$taskDentistOptions = [];
$selectedDentistProfile = null;
$assistantDentistIds = ($role === 'dental_assistant' && active_role_can_operate('dental_assistant')) ? task_dentist_scope_ids() : [];
$assistantMarks = $assistantDentistIds ? implode(',', array_fill(0, count($assistantDentistIds), '?')) : '';
$assistantEncounterAssignments=[];
if($role==='dental_assistant'&&active_role_can_operate('dental_assistant')&&encounter_assistant_schema_ready()){
    $stmt=$pdo->prepare('SELECT c.id encounter_id,c.dentist_name,c.assistant_name,c.assistant_assigned_at,c.status encounter_status,p.patient_no,CONCAT(p.first_name," ",p.last_name) patient,q.queue_no,q.status queue_status,q.chair_name,q.service_name,q.treatment_started_at FROM `' . table('clinical_encounters') . '` c JOIN `' . table('patients') . '` p ON p.id=c.patient_id LEFT JOIN `' . table('queue_entries') . '` q ON q.id=c.queue_entry_id WHERE c.assistant_user_id=? AND c.status IN ("draft","pending_review") ORDER BY (q.status="in_treatment") DESC,COALESCE(q.treatment_started_at,c.updated_at,c.created_at) DESC LIMIT 20');
    $stmt->execute([$userId]);$assistantEncounterAssignments=$stmt->fetchAll();
}

if ($role === 'dentist') {
    if (active_role_can_operate('dentist')) {
        $taskDentistId = $userId;
    } else {
        $taskDentistOptions = dentist_options((int)($user['branch_id'] ?? 0) ?: null);
        $requestedDentist = (int)($_GET['dentist_id'] ?? $_SESSION['task_dentist_id'] ?? 0);
        $validIds = array_map(static fn(array $row): int => (int)$row['id'], $taskDentistOptions);
        if (!in_array($requestedDentist, $validIds, true)) $requestedDentist = (int)($validIds[0] ?? 0);
        $taskDentistId = $requestedDentist;
        if ($taskDentistId > 0) $_SESSION['task_dentist_id'] = $taskDentistId;
    }
    if ($taskDentistId > 0) $selectedDentistProfile = dentist_profile($taskDentistId);
}

$count = static function (string $sql, array $params = []) use ($pdo): int {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return (int) $stmt->fetchColumn();
};

[$dashboardPatientScopeSql,$dashboardPatientScopeArgs]=task_patient_scope_condition('p.id');
$patients = $count('SELECT COUNT(*) FROM `' . table('patients') . '` p WHERE p.deleted_at IS NULL'.$dashboardPatientScopeSql,$dashboardPatientScopeArgs);
$appointmentScopeSql='';$appointmentScopeArgs=[];
if($role==='dentist'&&$taskDentistId>0){$appointmentScopeSql=' AND dentist_user_id=?';$appointmentScopeArgs=[$taskDentistId];}
elseif($role==='dental_assistant'&&active_role_can_operate('dental_assistant')){
    if($assistantDentistIds){$appointmentScopeSql=' AND dentist_user_id IN ('.$assistantMarks.')';$appointmentScopeArgs=$assistantDentistIds;}else{$appointmentScopeSql=' AND 1=0';}
}elseif($actualRole!=='super_admin'){
    if($dashboardBranchId>0){$appointmentScopeSql=' AND branch_id=?';$appointmentScopeArgs=[$dashboardBranchId];}else{$appointmentScopeSql=' AND 1=0';}
}
$appointmentsAll = $count('SELECT COUNT(*) FROM `' . table('appointments') . '` WHERE DATE(start_at)=?'.$appointmentScopeSql,array_merge([$today],$appointmentScopeArgs));
$appointmentsOwn = $appointmentsAll;
$futureAppointmentsOwn = $count('SELECT COUNT(*) FROM `' . table('appointments') . '` WHERE start_at>NOW() AND status NOT IN ("cancelled","no_show")'.$appointmentScopeSql,$appointmentScopeArgs);
$currentTreatmentOwn=$readyPatientsOwn=$waitingScreeningOwn=$nextExpectedOwn=$oldClinicalDraftsOwn=$labDueOwn=$scheduleImpactOwn=$diagnosticReviewOwn=0;
if($role==='dentist'&&$taskDentistId>0){
    $currentTreatmentOwn=$count('SELECT COUNT(*) FROM `' . table('queue_entries') . '` WHERE dentist_user_id=? AND status="in_treatment"',[$taskDentistId]);
    $readyPatientsOwn=$count('SELECT COUNT(*) FROM `' . table('queue_entries') . '` WHERE dentist_user_id=? AND status IN ("called","seated")',[$taskDentistId]);
    $waitingScreeningOwn=$count('SELECT COUNT(*) FROM `' . table('queue_entries') . '` WHERE dentist_user_id=? AND status IN ("waiting","called","seated") AND intake_completed_at IS NULL',[$taskDentistId]);
    $nextExpectedOwn=$count('SELECT COUNT(*) FROM `' . table('appointments') . '` a WHERE DATE(a.start_at)=CURDATE() AND a.start_at>=NOW() AND a.dentist_user_id=? AND a.status NOT IN ("cancelled","no_show","completed") AND NOT EXISTS (SELECT 1 FROM `' . table('queue_entries') . '` q WHERE q.appointment_id=a.id AND DATE(q.checked_in_at)=CURDATE())',[$taskDentistId]);
    $oldClinicalDraftsOwn=$count('SELECT COUNT(*) FROM `' . table('clinical_encounters') . '` WHERE dentist_user_id=? AND status IN ("draft","pending_review") AND COALESCE(updated_at,created_at)<DATE_SUB(NOW(),INTERVAL 24 HOUR)',[$taskDentistId]);
    if(schema_table_exists(table('laboratory_cases')))$labDueOwn=$count('SELECT COUNT(*) FROM `' . table('laboratory_cases') . '` WHERE dentist_user_id=? AND status NOT IN ("completed","cancelled") AND due_date IS NOT NULL AND due_date<=DATE_ADD(CURDATE(),INTERVAL 7 DAY)',[$taskDentistId]);
    if(availability_schema_ready())$scheduleImpactOwn=$count('SELECT COUNT(DISTINCT a.id) FROM `' . table('appointments') . '` a JOIN `' . table('doctor_availability_exceptions') . '` e ON e.doctor_user_id=a.dentist_user_id AND e.branch_id=a.branch_id AND e.exception_date=DATE(a.start_at) WHERE a.dentist_user_id=? AND a.start_at>=NOW() AND a.status NOT IN ("cancelled","no_show","completed") AND e.status="submitted" AND e.exception_type IN ("leave","closure","training","unavailable") AND (e.all_day=1 OR (TIME(a.start_at)<COALESCE(e.end_time,"23:59:59") AND TIME(a.end_at)>COALESCE(e.start_time,"00:00:00")))',[$taskDentistId]);
    if(schema_table_exists(table('diagnostic_review_tasks')))$diagnosticReviewOwn=$count('SELECT COUNT(*) FROM `' . table('diagnostic_review_tasks') . '` WHERE assigned_dentist_user_id=? AND status IN ("pending","in_review")',[$taskDentistId]);
}
$incomingClinicalTasksOwn=$acceptedClinicalTasksOwn=$providerPrivilegeMismatchOwn=$clinicDecidesRequests=$pendingClinicalTaskAssignments=$futureProviderPrivilegeMismatch=0;
$uncoveredTreatmentItemCount=$uncoveredTreatmentItemOwn=0;
$providerPrivilegeExceptions=[];
$uncoveredTreatmentItems=[];
if(clinical_task_assignment_schema_ready()){
    $clinicalTaskBranchSql=$actualRole==='super_admin'?'':($dashboardBranchId>0?' AND branch_id=?':' AND 1=0');
    $clinicalTaskBranchArgs=($actualRole!=='super_admin'&&$dashboardBranchId>0)?[$dashboardBranchId]:[];
    $pendingClinicalTaskAssignments=$count('SELECT COUNT(*) FROM `' . table('clinical_task_assignments') . '` WHERE status="pending"'.$clinicalTaskBranchSql,$clinicalTaskBranchArgs);
    if($role==='dentist'&&$taskDentistId>0){
        $incomingClinicalTasksOwn=$count('SELECT COUNT(*) FROM `' . table('clinical_task_assignments') . '` WHERE assigned_dentist_user_id=? AND status="pending"',[$taskDentistId]);
        $acceptedClinicalTasksOwn=$count('SELECT COUNT(*) FROM `' . table('clinical_task_assignments') . '` WHERE assigned_dentist_user_id=? AND status IN ("accepted","in_progress")',[$taskDentistId]);
    }
}
if(public_intake_schema_ready()&&schema_column_exists(table('appointment_requests'),'provider_preference_mode')){
    $clinicRequestBranchSql=$actualRole==='super_admin'?'':($dashboardBranchId>0?' AND branch_id=?':' AND 1=0');
    $clinicRequestBranchArgs=($actualRole!=='super_admin'&&$dashboardBranchId>0)?[$dashboardBranchId]:[];
    $clinicDecidesRequests=$count('SELECT COUNT(*) FROM `' . table('appointment_requests') . '` WHERE patient_type="new" AND provider_preference_mode="clinic_decides" AND status IN ("submitted","needs_verification","reviewing")'.$clinicRequestBranchSql,$clinicRequestBranchArgs);
}
if(schema_column_exists(table('service_catalog'),'required_privilege_code')){
    $sql='SELECT a.id,a.start_at,a.dentist_user_id,a.patient_id,s.name name,s.name service_name,s.provider_type,s.required_privilege_code,u.name dentist_name,CONCAT(pt.first_name," ",pt.last_name) patient_name FROM `' . table('appointments') . '` a JOIN `' . table('service_catalog') . '` s ON s.id=a.service_id LEFT JOIN `' . table('users') . '` u ON u.id=a.dentist_user_id LEFT JOIN `' . table('patients') . '` pt ON pt.id=a.patient_id WHERE a.start_at>=NOW() AND a.status NOT IN ("cancelled","no_show","completed") AND s.provider_type="dentist" AND a.dentist_user_id IS NOT NULL';
    $args=[];if($role==='dentist'&&$taskDentistId>0){$sql.=' AND a.dentist_user_id=?';$args[]=$taskDentistId;}elseif($actualRole!=='super_admin'&&(int)($user['branch_id']??0)>0){$sql.=' AND a.branch_id=?';$args[]=(int)$user['branch_id'];}
    $sql.=' ORDER BY a.start_at LIMIT 250';$stmt=$pdo->prepare($sql);$stmt->execute($args);
    foreach($stmt->fetchAll() as $appointmentRow){$reason=null;if(!dentist_service_readiness((int)$appointmentRow['dentist_user_id'],$appointmentRow,$reason)){$futureProviderPrivilegeMismatch++;if((int)$appointmentRow['dentist_user_id']===$taskDentistId)$providerPrivilegeMismatchOwn++;if(count($providerPrivilegeExceptions)<40)$providerPrivilegeExceptions[]=$appointmentRow+['readiness_reason'=>$reason];}}
}
if(clinical_task_assignment_schema_ready() && schema_table_exists(table('treatment_plan_items')) && schema_table_exists(table('treatment_plans'))){
    // Compatibility for upgraded databases that predate treatment_plans.branch_id.
    $planBranchExpr=schema_column_exists(table('treatment_plans'),'branch_id')
        ? 'COALESCE(p.branch_id,ce.branch_id,pt.branch_id)'
        : 'COALESCE(ce.branch_id,pt.branch_id)';
    $sql='SELECT i.id item_id,i.procedure_name,i.procedure_code,i.status item_status,p.id plan_id,p.patient_id,'.$planBranchExpr.' branch_id,p.dentist_user_id,u.name dentist_name,CONCAT(pt.first_name," ",pt.last_name) patient_name '
        . 'FROM `' . table('treatment_plan_items') . '` i JOIN `' . table('treatment_plans') . '` p ON p.id=i.treatment_plan_id '
        . 'LEFT JOIN `' . table('clinical_encounters') . '` ce ON ce.id=p.encounter_id '
        . 'LEFT JOIN `' . table('users') . '` u ON u.id=p.dentist_user_id LEFT JOIN `' . table('patients') . '` pt ON pt.id=p.patient_id '
        . 'LEFT JOIN `' . table('clinical_task_assignments') . '` cta ON cta.treatment_plan_item_id=i.id AND cta.status IN ("pending","accepted","in_progress") '
        . 'WHERE i.status IN ("accepted","scheduled","in_progress") AND p.status NOT IN ("completed","cancelled","expired") AND p.dentist_user_id IS NOT NULL AND cta.id IS NULL';
    $args=[];
    if($role==='dentist'&&$taskDentistId>0){$sql.=' AND p.dentist_user_id=?';$args[]=$taskDentistId;}
    elseif($actualRole!=='super_admin'&&(int)($user['branch_id']??0)>0){$sql.=' AND '.$planBranchExpr.'=?';$args[]=(int)$user['branch_id'];}
    $sql.=' ORDER BY FIELD(i.priority,"emergency","urgent","important","routine"),i.updated_at DESC,i.id DESC LIMIT 250';
    $stmt=$pdo->prepare($sql);$stmt->execute($args);
    foreach($stmt->fetchAll() as $itemRow){
        $required=clinical_privilege_code_for_text(trim((string)($itemRow['procedure_code']??'').' '.(string)$itemRow['procedure_name']));
        $reason=null;
        if(!dentist_has_clinical_privilege((int)$itemRow['dentist_user_id'],(string)($required?:'consultation'),$reason)){
            $uncoveredTreatmentItemCount++;
            if((int)$itemRow['dentist_user_id']===$taskDentistId)$uncoveredTreatmentItemOwn++;
            if(count($uncoveredTreatmentItems)<40)$uncoveredTreatmentItems[]=$itemRow+['required_privilege'=>$required?:'consultation','readiness_reason'=>$reason];
        }
    }
}
$queueScopeSql='';$queueScopeArgs=[];
if($role==='dentist'&&$taskDentistId>0){$queueScopeSql=' AND dentist_user_id=?';$queueScopeArgs=[$taskDentistId];}
elseif($role==='dental_assistant'&&active_role_can_operate('dental_assistant')){
    if($assistantDentistIds){$queueScopeSql=' AND dentist_user_id IN ('.$assistantMarks.')';$queueScopeArgs=$assistantDentistIds;}else{$queueScopeSql=' AND 1=0';}
}elseif($actualRole!=='super_admin'){
    if($dashboardBranchId>0){$queueScopeSql=' AND branch_id=?';$queueScopeArgs=[$dashboardBranchId];}else{$queueScopeSql=' AND 1=0';}
}
$waitingAll = $count("SELECT COUNT(*) FROM `" . table('queue_entries') . "` WHERE status IN ('waiting','called','seated','in_treatment')".$queueScopeSql,$queueScopeArgs);
$waitingOwn = ($role === 'dentist' && $taskDentistId > 0)
    ? $count("SELECT COUNT(*) FROM `" . table('queue_entries') . "` WHERE dentist_user_id=? AND status IN ('waiting','called','seated','in_treatment')", [$taskDentistId])
    : $waitingAll;
$assistantAppointments = ($role === 'dental_assistant' && $actualRole === 'dental_assistant')
    ? ($assistantDentistIds ? $count('SELECT COUNT(*) FROM `' . table('appointments') . '` WHERE DATE(start_at)=? AND dentist_user_id IN (' . $assistantMarks . ')', array_merge([$today], $assistantDentistIds)) : 0)
    : $appointmentsAll;
$assistantWaiting = ($role === 'dental_assistant' && $actualRole === 'dental_assistant')
    ? ($assistantDentistIds ? $count("SELECT COUNT(*) FROM `" . table('queue_entries') . "` WHERE dentist_user_id IN (" . $assistantMarks . ") AND status IN ('waiting','called','seated','in_treatment')", $assistantDentistIds) : 0)
    : $waitingAll;
$cashierBranchSql=$actualRole==='super_admin'?'':($dashboardBranchId>0?' AND branch_id=?':' AND 1=0');$cashierBranchArgs=($actualRole!=='super_admin'&&$dashboardBranchId>0)?[$dashboardBranchId]:[];
$cashierWaiting = $count("SELECT COUNT(*) FROM `" . table('queue_entries') . "` WHERE status IN ('waiting_billing','cashier_called')".$cashierBranchSql,$cashierBranchArgs);
$unpaid = $count("SELECT COUNT(*) FROM `" . table('invoices') . "` WHERE status IN ('unpaid','partial')".$cashierBranchSql,$cashierBranchArgs);
$collectionSql = 'SELECT COALESCE(SUM(py.amount),0) FROM `' . table('payments') . '` py '
    . 'JOIN `' . table('invoices') . '` i ON i.id=py.invoice_id WHERE DATE(py.created_at)=CURDATE()';
$collectionArgs = [];
if ($dashboardBranchId > 0) {
    $collectionSql .= ' AND i.branch_id=?';
    $collectionArgs[] = $dashboardBranchId;
}
$collectionStmt = $pdo->prepare($collectionSql);
$collectionStmt->execute($collectionArgs);
$todayGrossCollections = (float)($collectionStmt->fetchColumn() ?: 0);
$todayReturnedCollections = 0.0;
if (schema_table_exists(table('payment_adjustments'))) {
    $returnSql = 'SELECT COALESCE(SUM(pa.amount),0) FROM `' . table('payment_adjustments') . '` pa '
        . 'JOIN `' . table('invoices') . '` i ON i.id=pa.invoice_id '
        . 'WHERE pa.status="processed" AND DATE(pa.processed_at)=CURDATE()';
    $returnArgs = [];
    if ($dashboardBranchId > 0) {
        $returnSql .= ' AND i.branch_id=?';
        $returnArgs[] = $dashboardBranchId;
    }
    $returnStmt = $pdo->prepare($returnSql);
    $returnStmt->execute($returnArgs);
    $todayReturnedCollections = (float)($returnStmt->fetchColumn() ?: 0);
}
$todayCollections = round($todayGrossCollections - $todayReturnedCollections, 2);
$requestBranchSql=$actualRole==='super_admin'?'':($dashboardBranchId>0?' AND branch_id=?':' AND 1=0');$requestBranchArgs=($actualRole!=='super_admin'&&$dashboardBranchId>0)?[$dashboardBranchId]:[];
$onlineRequests = public_intake_schema_ready()
    ? $count("SELECT COUNT(*) FROM `" . table('appointment_requests') . "` WHERE status IN ('submitted','needs_verification','reviewing')".$requestBranchSql,$requestBranchArgs)
    : 0;
$appointmentAliasScope=str_replace([' dentist_user_id',' branch_id'],[' a.dentist_user_id',' a.branch_id'],$appointmentScopeSql);
$expectedArrivals = $count("SELECT COUNT(*) FROM `" . table('appointments') . "` a WHERE DATE(a.start_at)=CURDATE() AND a.status NOT IN ('cancelled','no_show','completed') AND NOT EXISTS (SELECT 1 FROM `" . table('queue_entries') . "` q WHERE q.appointment_id=a.id AND DATE(q.checked_in_at)=CURDATE())".$appointmentAliasScope,$appointmentScopeArgs);
$lateArrivals = $count("SELECT COUNT(*) FROM `" . table('appointments') . "` a WHERE DATE(a.start_at)=CURDATE() AND a.start_at<NOW() AND a.status NOT IN ('cancelled','no_show','completed') AND NOT EXISTS (SELECT 1 FROM `" . table('queue_entries') . "` q WHERE q.appointment_id=a.id AND DATE(q.checked_in_at)=CURDATE())".$appointmentAliasScope,$appointmentScopeArgs);
$arrivingNextHour = $count("SELECT COUNT(*) FROM `" . table('appointments') . "` a WHERE a.start_at>=NOW() AND a.start_at<DATE_ADD(NOW(),INTERVAL 60 MINUTE) AND a.status NOT IN ('cancelled','no_show','completed') AND NOT EXISTS (SELECT 1 FROM `" . table('queue_entries') . "` q WHERE q.appointment_id=a.id AND DATE(q.checked_in_at)=CURDATE())".$appointmentAliasScope,$appointmentScopeArgs);
$unconfirmedAppointments = $count("SELECT COUNT(*) FROM `" . table('appointments') . "` WHERE start_at>=NOW() AND start_at<DATE_ADD(NOW(),INTERVAL 7 DAY) AND status IN ('requested','pending_confirmation')".$appointmentScopeSql,$appointmentScopeArgs);
$receptionWaiting = $count("SELECT COUNT(*) FROM `" . table('queue_entries') . "` WHERE status='waiting'".$cashierBranchSql,$cashierBranchArgs);
$followupBranchSql=$actualRole==='super_admin'?'':($dashboardBranchId>0?' AND branch_id=?':' AND 1=0');$followupBranchArgs=($actualRole!=='super_admin'&&$dashboardBranchId>0)?[$dashboardBranchId]:[];
$followupsNeedBooking = schema_table_exists(table('follow_ups')) ? $count("SELECT COUNT(*) FROM `" . table('follow_ups') . "` WHERE appointment_id IS NULL AND status IN ('pending','deferred')".$followupBranchSql,$followupBranchArgs) : 0;
$activeWaitlist = $cancelledSlotMatches = 0;
if(schema_table_exists(table('appointment_waitlist'))){
    $activeWaitlist=$count("SELECT COUNT(*) FROM `" . table('appointment_waitlist') . "` WHERE status IN ('waiting','offered')");
    $cancelledSlotMatches=$count("SELECT COUNT(DISTINCT a.id) FROM `" . table('appointments') . "` a WHERE a.status='cancelled' AND a.start_at>=NOW() AND a.start_at<DATE_ADD(NOW(),INTERVAL 60 DAY) AND EXISTS (SELECT 1 FROM `" . table('appointment_waitlist') . "` w WHERE w.branch_id=a.branch_id AND w.status IN ('waiting','offered') AND w.service_id=a.service_id AND DATE(a.start_at) BETWEEN w.earliest_date AND w.latest_date AND (w.dentist_user_id IS NULL OR w.dentist_user_id=a.dentist_user_id))");
}
$pendingIntakes = clinical_sequence_schema_ready()
    ? $count("SELECT COUNT(*) FROM `" . table('patient_intakes') . "` i LEFT JOIN `" . table('appointments') . "` a ON a.id=i.appointment_id WHERE i.status='draft' AND (DATE(a.start_at)=CURDATE() OR DATE(i.created_at)=CURDATE())")
    : 0;
$unpublishedScheduleChanges = 0;
if (availability_schema_ready()) {
    if ($role === 'dentist' && $taskDentistId > 0) {
        $unpublishedScheduleChanges = $count("SELECT (SELECT COUNT(*) FROM `" . table('doctor_availability_rules') . "` WHERE doctor_user_id=? AND status IN ('draft','submitted')) + (SELECT COUNT(*) FROM `" . table('doctor_availability_exceptions') . "` WHERE doctor_user_id=? AND status IN ('draft','submitted'))", [$taskDentistId,$taskDentistId]);
    } else {
        $unpublishedScheduleChanges = $count("SELECT (SELECT COUNT(*) FROM `" . table('doctor_availability_rules') . "` WHERE status IN ('draft','submitted')) + (SELECT COUNT(*) FROM `" . table('doctor_availability_exceptions') . "` WHERE status IN ('draft','submitted'))");
    }
}
$reminderAttention=schema_table_exists(table('appointment_reminders'))?$count('SELECT COUNT(*) FROM `' . table('appointment_reminders') . '` r LEFT JOIN `' . table('email_outbox') . '` o ON o.id=r.outbox_id WHERE COALESCE(o.status,r.status) IN ("failed","retry")'):0;
$failedEmails = schema_table_exists(table('email_outbox'))
    ? $count("SELECT COUNT(*) FROM `" . table('email_outbox') . "` WHERE status IN ('failed','retry')")
    : 0;
$emailHealth = ['issues'=>[],'pending'=>0,'retry'=>0,'failed'=>0,'stale_pending'=>0,'overdue_retry'=>0,'stale_processing'=>0];
try { $emailHealth = email_queue_health($pdo); } catch (Throwable $emailHealthError) { $emailHealth['issues'][] = 'Email delivery health could not be evaluated: ' . $emailHealthError->getMessage(); }
$emailDeliveryAttention = (int)($emailHealth['failed']??0) + (int)($emailHealth['stale_pending']??0) + (int)($emailHealth['overdue_retry']??0) + (int)($emailHealth['stale_processing']??0);
if (!empty($emailHealth['issues']) && $emailDeliveryAttention < 1) $emailDeliveryAttention = 1;

$draftClinical = $followupsDue = $lowStock = $expiring = $inventoryItems = $activeStaff = $backupCount = 0;
if ($advanced) {
    $draftClinical = ($role === 'dentist' && $taskDentistId > 0)
        ? $count("SELECT COUNT(*) FROM `" . table('clinical_encounters') . "` WHERE dentist_user_id=? AND status IN ('draft','pending_review')", [$taskDentistId])
        : (($role === 'dental_assistant' && $actualRole === 'dental_assistant')
            ? ($assistantDentistIds ? $count("SELECT COUNT(*) FROM `" . table('clinical_encounters') . "` WHERE dentist_user_id IN (" . $assistantMarks . ") AND status IN ('draft','pending_review')", $assistantDentistIds) : 0)
            : $count("SELECT COUNT(*) FROM `" . table('clinical_encounters') . "` WHERE status IN ('draft','pending_review')"));
    $followupsDue = ($role === 'dentist' && $taskDentistId > 0)
        ? $count("SELECT COUNT(*) FROM `" . table('follow_ups') . "` WHERE dentist_user_id=? AND status IN ('pending','scheduled') AND recommended_date<=CURDATE()", [$taskDentistId])
        : $count("SELECT COUNT(*) FROM `" . table('follow_ups') . "` WHERE status IN ('pending','scheduled') AND recommended_date<=CURDATE()");
    if (schema_table_exists(table('inventory_branch_stocks')) && schema_column_exists(table('inventory_lots'),'branch_id')) {
        $inventoryBranchSql = $dashboardBranchId > 0 ? ' AND bs.branch_id=' . $dashboardBranchId : '';
        $lotBranchSql = $dashboardBranchId > 0 ? ' AND branch_id=' . $dashboardBranchId : '';
        $inventoryItems = $count("SELECT COUNT(*) FROM `" . table('inventory_branch_stocks') . "` bs JOIN `" . table('inventory_items') . "` i ON i.id=bs.item_id WHERE i.status='active'" . $inventoryBranchSql);
        $lowStock = $count("SELECT COUNT(*) FROM `" . table('inventory_branch_stocks') . "` bs JOIN `" . table('inventory_items') . "` i ON i.id=bs.item_id WHERE i.status='active' AND bs.current_stock<=bs.reorder_level" . $inventoryBranchSql);
        $expiring = $count("SELECT COUNT(*) FROM `" . table('inventory_lots') . "` WHERE status='active' AND quantity_remaining>0 AND expiry_date IS NOT NULL AND expiry_date<=DATE_ADD(CURDATE(),INTERVAL 60 DAY)" . $lotBranchSql);
    } else {
        $inventoryItems = $count("SELECT COUNT(*) FROM `" . table('inventory_items') . "` WHERE status='active'");
        $lowStock = $count("SELECT COUNT(*) FROM `" . table('inventory_items') . "` WHERE status='active' AND current_stock<=reorder_level");
        $expiring = $count("SELECT COUNT(*) FROM `" . table('inventory_lots') . "` WHERE status='active' AND quantity_remaining>0 AND expiry_date IS NOT NULL AND expiry_date<=DATE_ADD(CURDATE(),INTERVAL 60 DAY)");
    }
    $backupCount = $count("SELECT COUNT(*) FROM `" . table('backup_logs') . "` WHERE status IN ('created','uploaded')");
}
$activeStaff = $count("SELECT COUNT(*) FROM `" . table('users') . "` WHERE status='active'");

$coverageGaps=[];$criticalActions=[];$coverageGapCount=0;$unassignedDoctorChairs=0;$futureResourceConflicts=0;$staleQueue=0;$oldClinicalDrafts=0;$pendingScheduleApprovals=0;$inhouseToday=0;$inhouseAppointments=[];$inhouseServicesWithoutStaff=0;$futureInhouseUnassigned=0;$futureInhouseChairMissing=0;
$visitExceptionCount=0;$visitCriticalCount=0;
if(in_array($role,['super_admin','clinic_admin'],true)){
    try{
        $branchRows=$pdo->query('SELECT id,name FROM `' . table('branches') . '` WHERE is_active=1 ORDER BY name')->fetchAll();
        foreach($branchRows as $branchRow){
            $bid=(int)$branchRow['id'];$branchDoctors=active_dentists($bid);
            foreach($branchDoctors as $doctor){$binding=doctor_chair_binding((int)$doctor['id'],$bid);if(!$binding['valid']){$unassignedDoctorChairs++;if(count($criticalActions)<50)$criticalActions[]=['severity'=>'Critical','issue'=>'Chair assignment','target'=>(string)($doctor['display_name']??$doctor['name']??'Dentist').' · '.(string)$branchRow['name'],'detail'=>(string)($binding['message']??'No valid unique chair is assigned.'),'url'=>'doctor_profile.php?user_id='.(int)$doctor['id']];}}
            for($offset=0;$offset<14;$offset++){
                $date=date('Y-m-d',strtotime('+'.$offset.' days'));
                $hours=branch_hours_for_date($bid,$date);if(!$hours)continue;
                $covered=false;
                foreach($branchDoctors as $doctor){if(doctor_intervals_for_date((int)$doctor['id'],$bid,$date)){$covered=true;break;}}
                if(!$covered){$coverageGapCount++;$gap=['branch_id'=>$bid,'branch_name'=>(string)$branchRow['name'],'date'=>$date,'open_time'=>$hours['open_time'],'close_time'=>$hours['close_time']];if(count($coverageGaps)<30)$coverageGaps[]=$gap;if(count($criticalActions)<50)$criticalActions[]=['severity'=>'Critical','issue'=>'No doctor coverage','target'=>(string)$branchRow['name'].' · '.date('D, M j',strtotime($date)),'detail'=>date('g:i A',strtotime((string)$hours['open_time'])).'–'.date('g:i A',strtotime((string)$hours['close_time'])),'url'=>'doctor_schedule.php?branch_id='.$bid.'&week='.$date];}
            }
        }
        $resourceConflictClause='a1.patient_id=a2.patient_id OR (a1.dentist_user_id IS NOT NULL AND a1.dentist_user_id=a2.dentist_user_id) OR (a1.inhouse_provider_user_id IS NOT NULL AND a1.inhouse_provider_user_id=a2.inhouse_provider_user_id)';
        if(schema_column_exists(table('appointments'),'chair_id'))$resourceConflictClause.=' OR (a1.chair_id IS NOT NULL AND a1.chair_id=a2.chair_id)';
        $resourceConflictClause.=' OR (TRIM(COALESCE(a1.chair_name,""))<>"" AND LOWER(TRIM(a1.chair_name))=LOWER(TRIM(a2.chair_name)))';
        $futureResourceConflicts=$count('SELECT COUNT(*) FROM `' . table('appointments') . '` a1 JOIN `' . table('appointments') . '` a2 ON a1.id<a2.id AND a1.branch_id=a2.branch_id AND a1.start_at<a2.end_at AND a1.end_at>a2.start_at WHERE a1.start_at>=NOW() AND a1.status NOT IN ("cancelled","no_show","completed") AND a2.status NOT IN ("cancelled","no_show","completed") AND ('.$resourceConflictClause.')');
        $staleQueue=$count('SELECT COUNT(*) FROM `' . table('queue_entries') . '` WHERE status IN ("waiting","called","seated","in_treatment") AND checked_in_at<DATE_SUB(NOW(),INTERVAL 120 MINUTE)');
        $oldClinicalDrafts=$count('SELECT COUNT(*) FROM `' . table('clinical_encounters') . '` WHERE status IN ("draft","pending_review") AND COALESCE(updated_at,created_at)<DATE_SUB(NOW(),INTERVAL 24 HOUR)');
        $pendingScheduleApprovals=$count('SELECT COUNT(*) FROM `' . table('doctor_availability_exceptions') . '` WHERE status="submitted" AND exception_type IN ("leave","closure","training","unavailable")');
        if(schema_column_exists(table('appointments'),'provider_type')){
            $inhouseToday=$count('SELECT COUNT(*) FROM `' . table('appointments') . '` WHERE DATE(start_at)=CURDATE() AND provider_type="inhouse" AND status NOT IN ("cancelled","no_show")');
            $stmt=$pdo->query('SELECT a.id,a.start_at,a.service_name,a.status,COALESCE(NULLIF(a.inhouse_provider_name,""),"In-house clinic team") provider_name,CONCAT(p.first_name," ",p.last_name) patient,b.name branch_name FROM `' . table('appointments') . '` a JOIN `' . table('patients') . '` p ON p.id=a.patient_id LEFT JOIN `' . table('branches') . '` b ON b.id=a.branch_id WHERE DATE(a.start_at)=CURDATE() AND a.provider_type="inhouse" AND a.status NOT IN ("cancelled","no_show") ORDER BY a.start_at LIMIT 50');
            $inhouseAppointments=$stmt->fetchAll();
            if(inhouse_resource_schema_ready()){
                foreach($branchRows as $branchRow){
                    $bid=(int)$branchRow['id'];
                    $serviceStmt=$pdo->prepare('SELECT * FROM `' . table('service_catalog') . '` WHERE is_active=1 AND provider_type="inhouse" AND (branch_id IS NULL OR branch_id=?) ORDER BY name');
                    $serviceStmt->execute([$bid]);
                    foreach($serviceStmt->fetchAll() as $serviceRow){
                        if(inhouse_service_staff_candidates($bid,$serviceRow))continue;
                        $inhouseServicesWithoutStaff++;
                        if(count($criticalActions)<50)$criticalActions[]=['severity'=>'Critical','issue'=>'In-house service staffing','target'=>(string)$serviceRow['name'].' · '.(string)$branchRow['name'],'detail'=>'No eligible employee is assigned or available through the fallback role.','url'=>'inhouse_team.php?branch_id='.$bid];
                    }
                }
                $futureInhouseUnassigned=$count('SELECT COUNT(*) FROM `' . table('appointments') . '` WHERE provider_type="inhouse" AND start_at>=NOW() AND status NOT IN ("cancelled","no_show","completed") AND COALESCE(inhouse_provider_user_id,0)=0');
                $futureInhouseChairMissing=$count('SELECT COUNT(*) FROM `' . table('appointments') . '` a JOIN `' . table('service_catalog') . '` s ON s.id=a.service_id WHERE a.provider_type="inhouse" AND s.requires_chair=1 AND a.start_at>=NOW() AND a.status NOT IN ("cancelled","no_show","completed") AND COALESCE(a.chair_id,0)=0');
                if($futureInhouseUnassigned>0)$criticalActions[]=['severity'=>'Critical','issue'=>'Unassigned in-house appointments','target'=>$futureInhouseUnassigned.' future appointment'.($futureInhouseUnassigned===1?'':'s'),'detail'=>'Legacy or manually imported visits do not identify the accountable employee.','url'=>'appointments.php?provider=inhouse&view=upcoming'];
                if($futureInhouseChairMissing>0)$criticalActions[]=['severity'=>'Critical','issue'=>'Missing shared chair','target'=>$futureInhouseChairMissing.' future appointment'.($futureInhouseChairMissing===1?'':'s'),'detail'=>'Chair-required in-house visits have no formal shared chair resource.','url'=>'appointments.php?provider=inhouse&view=upcoming'];
            }
        }
        $providerOne='COALESCE(NULLIF(a1.inhouse_provider_name,""),u1.name,"Unassigned provider")';
        $providerTwo='COALESCE(NULLIF(a2.inhouse_provider_name,""),u2.name,"Unassigned provider")';
        $stmt=$pdo->query('SELECT a1.id appointment_a,a2.id appointment_b,a1.start_at,CONCAT(p1.first_name," ",p1.last_name) patient_a,CONCAT(p2.first_name," ",p2.last_name) patient_b,'.$providerOne.' provider_a,'.$providerTwo.' provider_b,a1.chair_name chair_a,a2.chair_name chair_b FROM `' . table('appointments') . '` a1 JOIN `' . table('appointments') . '` a2 ON a1.id<a2.id AND a1.branch_id=a2.branch_id AND a1.start_at<a2.end_at AND a1.end_at>a2.start_at JOIN `' . table('patients') . '` p1 ON p1.id=a1.patient_id JOIN `' . table('patients') . '` p2 ON p2.id=a2.patient_id LEFT JOIN `' . table('users') . '` u1 ON u1.id=COALESCE(a1.dentist_user_id,a1.inhouse_provider_user_id) LEFT JOIN `' . table('users') . '` u2 ON u2.id=COALESCE(a2.dentist_user_id,a2.inhouse_provider_user_id) WHERE a1.start_at>=NOW() AND a1.status NOT IN ("cancelled","no_show","completed") AND a2.status NOT IN ("cancelled","no_show","completed") AND ('.$resourceConflictClause.') ORDER BY a1.start_at LIMIT 15');
        foreach($stmt->fetchAll() as $conflict){$criticalActions[]=['severity'=>'Critical','issue'=>'Appointment conflict','target'=>(string)$conflict['patient_a'].' / '.(string)$conflict['patient_b'],'detail'=>date('M j, g:i A',strtotime((string)$conflict['start_at'])).' · '.((string)($conflict['chair_a']?:$conflict['provider_a'])),'url'=>'appointments.php?edit_id='.(int)$conflict['appointment_b']];}
        $stmt=$pdo->query('SELECT q.id,q.queue_no,q.status,q.checked_in_at,CONCAT(p.first_name," ",p.last_name) patient FROM `' . table('queue_entries') . '` q JOIN `' . table('patients') . '` p ON p.id=q.patient_id WHERE q.status IN ("waiting","called","seated","in_treatment") AND q.checked_in_at<DATE_SUB(NOW(),INTERVAL 120 MINUTE) ORDER BY q.checked_in_at LIMIT 15');
        foreach($stmt->fetchAll() as $row){$criticalActions[]=['severity'=>'Urgent','issue'=>'Stale patient queue','target'=>(string)$row['queue_no'].' · '.(string)$row['patient'],'detail'=>ucwords(str_replace('_',' ',(string)$row['status'])).' since '.date('M j, g:i A',strtotime((string)$row['checked_in_at'])),'url'=>'queue.php#queue-entry-'.(int)$row['id']];}
        $stmt=$pdo->query('SELECT c.id,c.created_at,c.updated_at,CONCAT(p.first_name," ",p.last_name) patient,u.name dentist FROM `' . table('clinical_encounters') . '` c JOIN `' . table('patients') . '` p ON p.id=c.patient_id LEFT JOIN `' . table('users') . '` u ON u.id=c.dentist_user_id WHERE c.status IN ("draft","pending_review") AND COALESCE(c.updated_at,c.created_at)<DATE_SUB(NOW(),INTERVAL 24 HOUR) ORDER BY COALESCE(c.updated_at,c.created_at) LIMIT 15');
        foreach($stmt->fetchAll() as $row){$criticalActions[]=['severity'=>'Warning','issue'=>'Unfinished clinical record','target'=>(string)$row['patient'].' · '.(string)($row['dentist']?:'Unassigned'),'detail'=>'Last saved '.date('M j, g:i A',strtotime((string)($row['updated_at']?:$row['created_at']))),'url'=>'clinical_workspace.php?encounter_id='.(int)$row['id']];}
        $stmt=$pdo->query('SELECT e.id,e.doctor_user_id,e.exception_date,e.exception_type,e.reason,u.name dentist,b.name branch_name FROM `' . table('doctor_availability_exceptions') . '` e JOIN `' . table('users') . '` u ON u.id=e.doctor_user_id LEFT JOIN `' . table('branches') . '` b ON b.id=e.branch_id WHERE e.status="submitted" AND e.exception_type IN ("leave","closure","training","unavailable") ORDER BY e.exception_date LIMIT 15');
        foreach($stmt->fetchAll() as $row){$criticalActions[]=['severity'=>'Approval','issue'=>'Schedule approval','target'=>(string)$row['dentist'].' · '.date('M j',strtotime((string)$row['exception_date'])),'detail'=>ucwords(str_replace('_',' ',(string)$row['exception_type'])).((string)$row['reason']!==''?' · '.(string)$row['reason']:''),'url'=>'doctor_schedule.php?doctor_id='.(int)($row['doctor_user_id']??0).'&week='.(string)$row['exception_date']];}
        if(!empty($emailHealth['issues'])){
            $deliveryTarget=(int)($emailHealth['pending']??0).' queued · '.(int)($emailHealth['retry']??0).' retry · '.(int)($emailHealth['failed']??0).' failed';
            $criticalActions[]=['severity'=>'Warning','issue'=>'Email delivery health','target'=>$deliveryTarget,'detail'=>implode(' ',array_slice(array_map('strval',(array)$emailHealth['issues']),0,3)),'url'=>'email_settings.php'];
        }
    }catch(Throwable $adminDashboardError){error_log('DentalCare admin critical dashboard fallback: '.$adminDashboardError->getMessage());}
}
if (visit_integrity_schema_ready()) {
    try {
        $visitIssueWhere=' WHERE status IN ("open","in_review")';
        $visitIssueArgs=[];
        if($actualRole!=='super_admin'){
            if($dashboardBranchId>0){$visitIssueWhere.=' AND (branch_id=? OR branch_id IS NULL)';$visitIssueArgs[]=$dashboardBranchId;}
            else{$visitIssueWhere.=' AND 1=0';}
        }
        if(!in_array($role,['super_admin','clinic_admin'],true)){
            $responsibleRole=in_array($role,['receptionist','dentist','dental_assistant','cashier','inventory_officer'],true)?$role:'clinic_admin';
            $visitIssueWhere.=' AND responsible_role=?';$visitIssueArgs[]=$responsibleRole;
        }
        $visitExceptionCount=$count('SELECT COUNT(*) FROM `' . table('visit_integrity_issues') . '`'.$visitIssueWhere,$visitIssueArgs);
        $visitCriticalCount=$count('SELECT COUNT(*) FROM `' . table('visit_integrity_issues') . '`'.$visitIssueWhere.' AND severity IN ("urgent","critical")',$visitIssueArgs);
        if(in_array($role,['super_admin','clinic_admin'],true) && $visitExceptionCount>0){
            $stmt=$pdo->prepare('SELECT id,severity,summary,detail,patient_id FROM `' . table('visit_integrity_issues') . '`'.$visitIssueWhere.' ORDER BY FIELD(severity,"critical","urgent","warning","approval"),last_detected_at DESC LIMIT 20');
            $stmt->execute($visitIssueArgs);
            foreach($stmt->fetchAll() as $issueRow){
                $criticalActions[]=['severity'=>ucfirst((string)$issueRow['severity']),'issue'=>'Visit workflow exception','target'=>(string)$issueRow['summary'],'detail'=>(string)($issueRow['detail']?:'Linked appointment, queue, clinical, billing, or follow-up records require review.'),'url'=>'visit_exceptions.php?issue='.(int)$issueRow['id']];
            }
        }
    }catch(Throwable $visitDashboardError){error_log('DentalCare visit exception dashboard fallback: '.$visitDashboardError->getMessage());}
}

if(in_array($role,['super_admin','clinic_admin'],true)){
    foreach($providerPrivilegeExceptions as $exception){
        $criticalActions[]=['severity'=>'Critical','issue'=>'Provider not clinically ready','target'=>(string)($exception['patient_name']?:'Patient').' · '.date('M j, g:i A',strtotime((string)$exception['start_at'])),'detail'=>(string)($exception['dentist_name']?:'Selected Dentist').' cannot provide '.(string)$exception['service_name'].': '.(string)$exception['readiness_reason'],'url'=>'appointments.php?edit_id='.(int)$exception['id']];
    }
    if($clinicDecidesRequests>0)$criticalActions[]=['severity'=>'Approval','issue'=>'Clinic must choose provider','target'=>$clinicDecidesRequests.' new-patient request'.($clinicDecidesRequests===1?'':'s'),'detail'=>'The patient selected “Let the dental clinic decide.” Assign an available, credentialed Dentist before approval.','url'=>'appointment_requests.php?provider_mode=clinic_decides'];
    if($pendingClinicalTaskAssignments>0)$criticalActions[]=['severity'=>'Urgent','issue'=>'Specific Dentist tasks awaiting acceptance','target'=>$pendingClinicalTaskAssignments.' pending assignment'.($pendingClinicalTaskAssignments===1?'':'s'),'detail'=>'A patient task was routed to another qualified Dentist and is waiting for a decision.','url'=>'clinical_tasks.php'];
    foreach($uncoveredTreatmentItems as $item){
        $criticalActions[]=['severity'=>'Critical','issue'=>'Accepted treatment lacks qualified Dentist','target'=>(string)($item['patient_name']?:'Patient').' · '.(string)$item['procedure_name'],'detail'=>(string)($item['dentist_name']?:'Responsible Dentist').' lacks '.module_status((string)$item['required_privilege']).'. Assign another qualified Dentist for this item: '.(string)($item['readiness_reason']?:'Clinical privilege unavailable.'),'url'=>'clinical_tasks.php?item_id='.(int)$item['item_id']];
    }
}
$dedup=[];$criticalActions=array_values(array_filter($criticalActions,static function(array $row)use(&$dedup):bool{$key=implode('|',[(string)($row['issue']??''),(string)($row['target']??''),(string)($row['url']??'')]);if(isset($dedup[$key]))return false;$dedup[$key]=true;return true;}));
$criticalGroupMeta=[
    'Critical'=>['label'=>'Critical','note'=>'Can stop treatment, booking, credentialed coverage, or resource safety.','tone'=>'critical'],
    'Urgent'=>['label'=>'Urgent','note'=>'Needs prompt operational action to prevent delay or escalation.','tone'=>'urgent'],
    'Warning'=>['label'=>'Warning','note'=>'Requires review, but does not currently block the patient workflow.','tone'=>'warning'],
    'Approval'=>['label'=>'Approval','note'=>'Waiting for an authorized administrative decision.','tone'=>'approval'],
    'Other'=>['label'=>'Other','note'=>'Operational exceptions not assigned to a standard severity.','tone'=>'other'],
];
$criticalActionGroups=array_fill_keys(array_keys($criticalGroupMeta),[]);
foreach($criticalActions as $criticalAction){
    $severity=ucfirst(strtolower(trim((string)($criticalAction['severity']??'Other'))));
    if(!isset($criticalActionGroups[$severity]))$severity='Other';
    $criticalActionGroups[$severity][]=$criticalAction;
}
$adminCriticalCount=$coverageGapCount+$unassignedDoctorChairs+$futureResourceConflicts+$staleQueue+$oldClinicalDrafts+$emailDeliveryAttention+$pendingScheduleApprovals+$inhouseServicesWithoutStaff+$futureInhouseUnassigned+$futureInhouseChairMissing+$futureProviderPrivilegeMismatch+$clinicDecidesRequests+$pendingClinicalTaskAssignments+$uncoveredTreatmentItemCount+$visitExceptionCount;

$stats = [];
$actions = [];
$workTitle = "Today's appointments";
$workType = 'appointments';
$dentistQuery = '';

switch ($role) {
    case 'super_admin':
        $stats = [
            ['Active users',$activeStaff,'All staff accounts'],
            ['Patients',$patients,'Active records'],
            ['Pending requests',$onlineRequests,'Public appointment intake'],
            ['Backups',$backupCount,'Created and uploaded'],
            ['Low stock',$lowStock,'Needs inventory attention'],
            ['Update status',app_update_pending()?'Pending':'Current','Release ' . app_release_version()],
        ];
        $actions = [
            ['system_setup.php','Open System Setup'],['staff.php','Manage roles and staff'],['uploads.php','Open Files & Uploads'],
            ['system_setup.php?tab=backups','Create or upload backup'],['inventory.php','Review inventory'],['license.php','Review license'],
        ];
        break;
    case 'clinic_admin':
        $stats = [
            ["Today's appointments",$appointmentsAll,'Clinic schedule'],['Clinical queue',$waitingAll,'Appointments and walk-ins'],
            ['Online requests',$onlineRequests,'Awaiting review'],['Cashier queue',$cashierWaiting,'Patients waiting'],
            ['Low stock',$lowStock,'Reorder attention'],['Open invoices',$unpaid,'Unpaid and partial'],
        ];
        $actions = [
            ['appointment_requests.php','Review online requests'],['appointments.php','Manage appointments'],['queue.php','Open clinic queue'],
            ['staff.php','Manage staff and dentists'],['inventory.php','Review inventory'],['uploads.php','Open Files & Uploads'],
        ];
        break;
    case 'dentist':
        $dentistOwnView = $actualRole === 'dentist';
        $stats = [
            ['In treatment',$currentTreatmentOwn,'Only one patient may be active'],
            ['Ready to see',$readyPatientsOwn,'Called or seated patients'],
            ['Awaiting screening',$waitingScreeningOwn,'Assigned patients without completed intake'],
            ['Old clinical drafts',$oldClinicalDraftsOwn,'Unfinished more than 24 hours'],
            ['Follow-ups due',$followupsDue,$dentistOwnView ? 'Assigned to you' : 'Due across dentist workflows'],
            ['Future appointments',$futureAppointmentsOwn,'Confirmed and pending visits after today'],
        ];
        $dentistQuery = (!$dentistOwnView && $taskDentistId > 0) ? '?dentist_id=' . $taskDentistId : '';
        $doctorProfileQuery = (!$dentistOwnView && $taskDentistId > 0) ? '?user_id=' . $taskDentistId : '';
        $actions = [
            ['doctor_treatment.php' . $dentistQuery,$dentistOwnView ? 'Open My Treatment Dashboard' : 'Review selected dentist treatment dashboard'],
            ['doctor_profile.php' . $doctorProfileQuery,$dentistOwnView ? 'Open professional credentials' : 'Review professional credentials'],
            ['doctor_schedule.php' . $dentistQuery,$dentistOwnView ? 'Set my availability' : 'Review selected dentist availability'],
            ['appointments.php' . $dentistQuery,$dentistOwnView ? 'View my appointments' : 'Review selected dentist appointments'],
            ['treatment.php','Open treatment plans'],['prescriptions.php','Open prescriptions'],
        ];
        break;
    case 'receptionist':
        $stats = [
            ["Today's appointments",$appointmentsAll,'Scheduled patients'],['Online requests',$onlineRequests,'Awaiting review'],
            ['Clinical queue',$waitingAll,'Checked-in patients'],['Patients',$patients,'Active records'],
        ];
        $actions = [
            ['appointment_requests.php','Review online requests'],['patients.php?action=new','Register patient'],['appointments.php','Book appointment'],
            ['queue.php','Check in appointment or walk-in'],['communications.php','Patient communications'],
        ];
        break;
    case 'staff':
        $stats = [
            ["Today's appointments",$appointmentsAll,'Reception schedule'],['Clinical queue',$waitingAll,'Patients in process'],
            ['Cashier queue',$cashierWaiting,'Patients waiting for payment'],['Open invoices',$unpaid,'Unpaid and partial'],
            ['Net collected today','₱' . number_format($todayCollections,2),'Payments less processed returns'],['Patients',$patients,'Active records'],
        ];
        $actions = [
            ['appointment_requests.php','Review online requests'],['patients.php?action=new','Register patient'],['appointments.php','Book appointment'],
            ['queue.php','Check in appointment or walk-in'],['billing.php','Open cashier register'],['communications.php','Patient communications'],
        ];
        $workTitle = 'Reception and cashier work queue';
        $workType = $cashierWaiting > 0 ? 'cashier' : 'queue';
        break;
    case 'dental_assistant':
        $stats = [
            ['Assigned treatments',count($assistantEncounterAssignments),'Exact encounter assignments'],['Clinical queue',$assistantWaiting,'Patients assigned to supported dentists'],
            ["Today's appointments",$assistantAppointments,'Assigned dentist schedule'],['Sterilization','Open','Track released instrument loads'],
        ];
        $actions = [
            ['queue.php','Open clinic queue'],['clinical.php','Open clinical encounters'],['documents.php','Upload clinical document'],
            ['sterilization.php','Record sterilization cycle'],['patients.php','Find patient'],
        ];
        $workTitle = 'Current patient flow';
        $workType = 'queue';
        break;
    case 'cashier':
        $stats = [
            ['Cashier queue',$cashierWaiting,'Patients waiting by name'],['Open invoices',$unpaid,'Unpaid and partial'],
            ['Net collected today','₱' . number_format($todayCollections,2),'Payments less processed returns'],['All payables',$unpaid,'Search and collect only in Cashier'],
        ];
        $actions = [
            ['billing.php','Open cashier register'],['billing.php?payable_status=open','View all open payables'],['billing.php','Search payor for payment'],
        ];
        $workTitle = 'Patients waiting for cashier';
        $workType = 'cashier';
        break;
    case 'inventory_officer':
        $stats = [
            ['Inventory items',$inventoryItems,'Active catalog'],['Low stock',$lowStock,'At or below reorder level'],
            ['Expiring soon',$expiring,'Within 60 days'],['Sterilization','Open','Cycle and release records'],
        ];
        $actions = [
            ['inventory.php','Open inventory'],['inventory.php','Record stock transaction'],['sterilization.php','Open sterilization'],['reports.php','Inventory reports'],
        ];
        $workTitle = 'Inventory attention list';
        $workType = 'inventory';
        break;
}

$supportRequestAttention=0;
if(support_request_schema_ready()){
    try{
        $supportSql='SELECT COUNT(*) FROM `' . table('support_requests') . '` WHERE status IN ("open","in_review","waiting_user")';
        $supportArgs=[];
        if($actualRole!=='super_admin'&&$dashboardBranchId>0){$supportSql.=' AND branch_id=?';$supportArgs[]=$dashboardBranchId;}
        $supportStmt=$pdo->prepare($supportSql);$supportStmt->execute($supportArgs);$supportRequestAttention=(int)$supportStmt->fetchColumn();
    }catch(Throwable $supportCountError){error_log('Dashboard support request count warning: '.$supportCountError->getMessage());}
}

$dailyTasks = match ($role) {
    'super_admin' => [
        ['dashboard.php#critical-operations','Critical operations',$adminCriticalCount,'Coverage, chair, queue, clinical, delivery, and visit exceptions.','urgent'],
        ['visit_exceptions.php','Visit workflow exceptions',$visitExceptionCount,'Repair broken appointment-to-checkout links and abandoned operational states.','urgent'],
        ['clinical_tasks.php','Pending clinical task routing',$pendingClinicalTaskAssignments,'Specific procedures waiting for a qualified Dentist to accept.','urgent'],
        ['clinical_tasks.php','Accepted treatment without qualified Dentist',$uncoveredTreatmentItemCount,'Accepted or scheduled procedures that need item-specific Dentist assignment.','urgent'],
        ['appointment_requests.php?provider_mode=clinic_decides','Clinic-decides provider requests',$clinicDecidesRequests,'New patients waiting for the clinic to select a provider.','warning'],
        ['dashboard.php#coverage-gaps','Open days without a doctor',$coverageGapCount,'Branch operating dates with no published doctor availability.','warning'],
        ['system_setup.php?tab=updates','System updates',app_update_pending() ? 1 : 0,'Apply pending application and database updates.','urgent'],
        ['help_support.php','Support requests',$supportRequestAttention,'Staff requests waiting for review, response, or resolution.','warning'],
        ['email_settings.php','Email delivery health',$emailDeliveryAttention,'Review stalled, retrying, failed, or misconfigured delivery.','warning'],
        ['reminders.php','Patient reminders',$reminderAttention,'Review automatic reminder rules and delivery.','normal'],
        ['appointment_requests.php','Online requests',$onlineRequests,'Requests waiting for clinic review.','normal'],
        ['inventory.php','Low-stock items',$lowStock,'Supplies at or below reorder level.','warning'],
        ['system_setup.php?tab=backups','Backup control',$backupCount,'Review backups or create today’s backup.','normal'],
    ],
    'clinic_admin' => [
        ['dashboard.php#critical-operations','Critical operations',$adminCriticalCount,'Coverage, chair, queue, clinical, delivery, and visit exceptions.','urgent'],
        ['visit_exceptions.php','Visit workflow exceptions',$visitExceptionCount,'Repair broken appointment-to-checkout links and abandoned operational states.','urgent'],
        ['clinical_tasks.php','Pending clinical task routing',$pendingClinicalTaskAssignments,'Specific procedures waiting for a qualified Dentist to accept.','urgent'],
        ['clinical_tasks.php','Accepted treatment without qualified Dentist',$uncoveredTreatmentItemCount,'Accepted or scheduled procedures that need item-specific Dentist assignment.','urgent'],
        ['appointment_requests.php?provider_mode=clinic_decides','Clinic-decides provider requests',$clinicDecidesRequests,'New patients waiting for the clinic to select a provider.','warning'],
        ['dashboard.php#coverage-gaps','Open days without a doctor',$coverageGapCount,'Branch operating dates with no published doctor availability.','warning'],
        ['appointments.php?provider=inhouse','In-house appointments',$inhouseToday,'Appointments that do not require a dentist.','normal'],
        ['appointment_requests.php','Online requests',$onlineRequests,'Review and convert public requests.','urgent'],
        ['queue.php#expected-patients','Expected arrivals',$expectedArrivals,'Patients booked today who have not checked in.','normal'],
        ['queue.php#expected-patients','Late arrivals',$lateArrivals,'Contact or update patients past their scheduled time.','warning'],
        ['queue.php','Active clinical queue',$waitingAll,'Patients waiting, called, seated, or in treatment.','normal'],
        ['billing.php','Cashier queue',$cashierWaiting,'Patients waiting for billing or payment.','normal'],
        ['doctor_schedule.php','Unpublished schedules',$unpublishedScheduleChanges,'Doctor availability changes still in draft.','warning'],
        ['help_support.php','Support requests',$supportRequestAttention,'Staff requests waiting for review, response, or resolution.','warning'],
        ['reminders.php','Patient reminders',$reminderAttention,'Review appointment, follow-up, and recall delivery.','normal'],
    ],
    'dentist' => [
        ['visit_exceptions.php','Visit exceptions assigned to Dentist',$visitExceptionCount,'Resolve missing clinical completion, invoice handoff, or Dentist-owned visit states.','urgent'],
        ['doctor_treatment.php' . $dentistQuery,'Current treatment',$currentTreatmentOwn,'Reopen the one patient currently in treatment.','urgent'],
        ['followups.php?view=diagnostic','Diagnostic reasons awaiting validation',$diagnosticReviewOwn,'Review staff-recorded patient concerns, validate the diagnostic reason, and close the task when resolved.','urgent'],
        ['clinical_tasks.php','Clinical tasks awaiting your decision',$incomingClinicalTasksOwn,'Accept or decline a specific procedure assigned by the responsible Dentist.','urgent'],
        ['clinical_tasks.php','Accepted delegated tasks',$acceptedClinicalTasksOwn,'Patients and procedures you accepted without taking ownership of the full plan.','normal'],
        ['clinical_tasks.php','My treatment items needing qualified coverage',$uncoveredTreatmentItemOwn,'Accepted procedures you cannot perform until a qualified Dentist accepts the specific task.','urgent'],
        ['doctor_schedule.php?tab=future' . ($taskDentistId>0?'&doctor_id='.$taskDentistId:''),'Appointments blocked by credentials or privilege',$providerPrivilegeMismatchOwn,'A selected service cannot be performed until credentials, privileges, or task routing are corrected.','urgent'],
        ['queue.php','Ready patients',$readyPatientsOwn,'Called or seated patients ready for the dentist.','urgent'],
        ['queue.php#expected-patients','Expected but not arrived',$nextExpectedOwn,'Upcoming patients who have not checked in.','normal'],
        ['clinical.php','Old clinical drafts',$oldClinicalDraftsOwn,'Finish records left open for more than 24 hours.','warning'],
        ['laboratory.php','Laboratory due',$labDueOwn,'Cases overdue or due within seven days.','warning'],
        ['doctor_schedule.php' . $dentistQuery,'Schedule impact',$scheduleImpactOwn,'Future patients affected by submitted leave or unavailable time.','warning'],
        ['doctor_schedule.php?tab=future' . ($taskDentistId>0?'&doctor_id='.$taskDentistId:''),'Future appointments',$futureAppointmentsOwn,'Review upcoming patients, chairs, and calendar entries.','normal'],
    ],
    'receptionist' => [
        ['visit_exceptions.php','Reception visit exceptions',$visitExceptionCount,'Resolve attendance outcomes, duplicate check-ins, and appointment/queue mismatches.','urgent'],
        ['appointment_requests.php','Online requests',$onlineRequests,'Verify identity, prevent duplicates, and convert requests into appointments.','urgent'],
        ['queue.php#expected-patients','Arriving within one hour',$arrivingNextHour,'Prepare check-in and confirm patients who are due soon.','urgent'],
        ['queue.php#expected-patients','Late patients',$lateArrivals,'Contact, reschedule, cancel, or mark no-show after confirmation.','warning'],
        ['appointments.php?view=upcoming&status=pending_confirmation','Unconfirmed appointments',$unconfirmedAppointments,'Confirm visits scheduled within the next seven days.','warning'],
        ['queue.php','Waiting at reception',$receptionWaiting,'Move arrived patients through the valid front-desk queue stages.','normal'],
        ['waitlist.php?match_cancelled=1','Cancelled slots with matches',$cancelledSlotMatches,'Offer newly opened slots to matching waitlisted patients.','urgent'],
        ['waitlist.php','Active appointment waitlist',$activeWaitlist,'Review date, time, service, and continuity preferences.','normal'],
        ['followups.php','Follow-ups needing booking',$followupsNeedBooking,'Book recommendations that do not yet have an appointment.','normal'],
        ['appointments.php?view=today','Today’s appointments',$appointmentsAll,'Review all visits, in-house services, and check-in readiness.','normal'],
    ],
    'staff' => [
        ['billing.php','Cashier queue',$cashierWaiting,'Serve patients released for billing through your own cashier session.','urgent'],
        ['billing.php?payable_status=open','Open payables',$unpaid,'Collect unpaid or partial balances from the Cashier Register.','warning'],
        ['visit_exceptions.php','Reception and checkout exceptions',$visitExceptionCount,'Resolve attendance, queue, chair-release, and checkout mismatches within General Staff scope.','urgent'],
        ['appointment_requests.php','Online requests',$onlineRequests,'Verify identity and convert requests into appointments.','urgent'],
        ['queue.php#expected-patients','Expected arrivals',$expectedArrivals,'Prepare scheduled arrivals and check-in.','normal'],
        ['queue.php','Active front-desk queue',$waitingAll,'Move patients through valid reception stages.','normal'],
        ['waitlist.php','Appointment waitlist',$activeWaitlist,'Review patients waiting for an available schedule.','normal'],
        ['followups.php','Follow-ups needing booking',$followupsNeedBooking,'Book recommendations that do not yet have an appointment.','normal'],
        ['communications.php','Patient communications',$onlineRequests,'Record patient contact and follow-through.','normal'],
    ],
    'dental_assistant' => [
        ['visit_exceptions.php','Assistant visit exceptions',$visitExceptionCount,'Review seating, chair, and queue-state problems assigned to the assistant workflow.','urgent'],
        ['queue.php','Assigned queue',$assistantWaiting,'Prepare patients and chairs for assigned dentists.','urgent'],
        ['clinical.php','Clinical drafts',$draftClinical,'Complete permitted screening and draft entries.','warning'],
        ['queue.php#expected-patients','Expected patients',$assistantAppointments,'Review today’s assigned dentist schedule.','normal'],
        ['sterilization.php','Sterilization workflow',0,'Record cycles and released instruments.','normal'],
        ['followups.php','Follow-ups',$followupsDue,'Review follow-up instructions within your scope.','normal'],
    ],
    'cashier' => [
        ['billing.php','Cashier queue',$cashierWaiting,'Serve patients released for billing.','urgent'],
        ['billing.php?payable_status=open','All open payables',$unpaid,'Collect unpaid or partial balances only from the Cashier Register.','warning'],
        ['billing.php','Payor search','Ready','Search by patient no., name, mobile, email, invoice, or receipt reference inside Cashier.','normal'],
        ['billing.php','Today’s cashier collections','₱' . number_format($todayCollections,2),'Visible as your cashier workspace total, not a financial-report module.','normal'],
    ],
    'inventory_officer' => [
        ['visit_exceptions.php','Inventory reconciliation exceptions',$visitExceptionCount,'Review negative balances, expired lots, missing treatment issues, and transfers in transit.','urgent'],
        ['inventory.php','Low-stock items',$lowStock,'Receive or reorder critical supplies.','urgent'],
        ['inventory.php','Expiring lots',$expiring,'Review lots expiring within 60 days.','warning'],
        ['sterilization.php','Sterilization records',0,'Complete cycle and release documentation.','normal'],
        ['reports.php','Inventory reports',$inventoryItems,'Review stock movement and control reports.','normal'],
    ],
    default => [],
};
$dailyTasks = array_values(array_filter($dailyTasks, static function(array $task): bool { if(!task_link_allowed((string)$task[0]))return false; $value=$task[2]??0; return !is_numeric($value)||(float)$value>0; }));
$actions = array_values(array_filter($actions, static fn(array $action): bool => task_link_allowed((string)$action[0])));

$roleWorkspace = function_exists('dental_role_workspace_definition') ? dental_role_workspace_definition($role) : ['label'=>$profile['name'].' Workspace','url'=>'dashboard.php','action'=>'Open workspace','responsibility'=>$profile['description'],'handoff'=>'Complete the task and confirm the next owner.','steps'=>(array)($workspace['steps']??[])];
$primaryTask = [
    'url'=>(string)$roleWorkspace['url'],
    'label'=>(string)$roleWorkspace['action'],
    'title'=>(string)$roleWorkspace['label'],
    'status'=>'Ready',
    'note'=>(string)$roleWorkspace['responsibility'],
];
if($role==='super_admin'&&app_update_pending())$primaryTask=['url'=>'system_setup.php?tab=updates','label'=>'Apply pending update','title'=>'System update requires attention','status'=>'Required','note'=>'Complete the application and database update before routine operation.'];
elseif($role==='clinic_admin'&&$adminCriticalCount>0)$primaryTask=['url'=>'dashboard.php#critical-operations','label'=>'Resolve critical operations','title'=>'Operations need intervention','status'=>$adminCriticalCount.' open','note'=>'Start with blockers that can stop treatment, scheduling, billing, or safe resource use.'];
elseif($role==='dentist'&&$currentTreatmentOwn>0)$primaryTask=['url'=>'doctor_treatment.php'.$dentistQuery,'label'=>'Continue current treatment','title'=>'A patient is in active treatment','status'=>$currentTreatmentOwn.' active','note'=>'Return to the same encounter before opening another patient.'];
elseif($role==='dentist'&&$readyPatientsOwn>0)$primaryTask=['url'=>'doctor_treatment.php'.$dentistQuery,'label'=>'Open next ready patient','title'=>'Patient ready for Dentist','status'=>$readyPatientsOwn.' ready','note'=>'Choose the assigned seated or called patient from My Treatment Dashboard.'];
elseif($role==='receptionist'&&$onlineRequests>0)$primaryTask=['url'=>'appointment_requests.php','label'=>'Review next request','title'=>'Online requests are waiting','status'=>$onlineRequests.' waiting','note'=>'Verify identity, prevent duplicates, and convert the request into the correct appointment.'];
elseif($role==='receptionist'&&$receptionWaiting>0)$primaryTask=['url'=>'queue.php','label'=>'Continue patient flow','title'=>'Patients are at the Front Desk','status'=>$receptionWaiting.' active','note'=>'Use only the valid next queue action shown for each patient.'];
elseif($role==='staff'&&$cashierWaiting>0)$primaryTask=['url'=>'billing.php','label'=>'Serve next payor','title'=>'Patient waiting for General Staff cashier','status'=>$cashierWaiting.' waiting','note'=>'Use your own cashier session to verify authorized charges, receive payment, issue the receipt, and complete checkout.'];
elseif($role==='staff'&&$onlineRequests>0)$primaryTask=['url'=>'appointment_requests.php','label'=>'Review next request','title'=>'Online requests are waiting','status'=>$onlineRequests.' waiting','note'=>'Verify identity, prevent duplicates, and convert the request into the correct appointment.'];
elseif($role==='staff'&&$receptionWaiting>0)$primaryTask=['url'=>'queue.php','label'=>'Continue patient flow','title'=>'Patients are at the Front Desk','status'=>$receptionWaiting.' active','note'=>'Use only the valid next queue action shown for each patient.'];
elseif($role==='dental_assistant'&&!empty($assistantEncounterAssignments))$primaryTask=['url'=>'clinical_workspace.php?encounter_id='.(int)$assistantEncounterAssignments[0]['encounter_id'],'label'=>'Open assigned patient','title'=>'Chairside treatment assigned','status'=>count($assistantEncounterAssignments).' assigned','note'=>'Complete screening, chairside support, images, and delegated entries for Dentist verification.'];
elseif($role==='dental_assistant'&&$assistantWaiting>0)$primaryTask=['url'=>'queue.php','label'=>'Prepare next patient','title'=>'Assigned patients are waiting','status'=>$assistantWaiting.' waiting','note'=>'Confirm the chair, patient safety information, and Dentist assignment before seating.'];
elseif($role==='cashier'&&$cashierWaiting>0)$primaryTask=['url'=>'billing.php','label'=>'Serve next payor','title'=>'Patient waiting for Cashier','status'=>$cashierWaiting.' waiting','note'=>'Open one focused transaction, verify the authorized bill, receive payment, and close the visit.'];
elseif($role==='inventory_officer'&&($lowStock+$expiring)>0)$primaryTask=['url'=>'inventory.php','label'=>'Resolve inventory alerts','title'=>'Stock requires attention','status'=>($lowStock+$expiring).' alerts','note'=>'Prioritize low-stock and expiring lots before routine transactions.'];
if(!task_link_allowed((string)$primaryTask['url']))$primaryTask=['url'=>'dashboard.php','label'=>'Review available tasks','title'=>(string)$roleWorkspace['label'],'status'=>'Restricted','note'=>'Use the tasks shown below for this signed-in role.'];

$dashboardTitle = ($role === 'dentist' && !active_role_can_operate('dentist')) ? 'Dentist Review' : (string)$profile['dashboard'];
render_header($dashboardTitle);
?>
<?php if (app_update_pending() && role_context_has('super_admin')): ?>
<div class="flash error"><strong>Application update pending.</strong> <?php if(actual_role()==='super_admin'):?>Apply all migrations in <a href="system_setup.php?tab=updates">System Control → Updates &amp; Database</a>.<?php else:?>The Superuser must apply the pending application and database update.<?php endif;?></div>
<?php elseif (app_update_pending() && has_role('super_admin')): ?>
<div class="flash error">A system update is pending. Switch Active role back to Superuser to open System Setup.</div>
<?php elseif (app_update_pending()): ?>
<div class="flash error">A system update is pending. Ask the Superuser to open System Setup.</div>
<?php endif; ?>
<?php if (task_role_is_active()): $actualProfile = role_profile($actualRole); ?>
<div class="task-view-notice"><strong>Active role:</strong> You are working in the <?= e((string) $profile['name']) ?> workspace while signed in as <?= e((string) $actualProfile['name']) ?>. Actions remain attributed to your real account.</div>
<?php endif; ?>


<section class="role-command-card card" aria-label="Primary role workflow">
    <div class="role-command-main">
        <div><span class="eyebrow">Your current responsibility</span><h2><?=e((string)$primaryTask['title'])?></h2><p><?=e((string)$primaryTask['note'])?></p><div class="role-command-handoff"><strong>Handoff:</strong> <?=e((string)$roleWorkspace['handoff'])?></div></div>
        <div class="role-command-action"><span class="badge <?=in_array((string)$primaryTask['status'],['Ready','Required'],true)?'warning':''?>"><?=e((string)$primaryTask['status'])?></span><a class="btn primary" href="<?=e((string)$primaryTask['url'])?>"><?=e((string)$primaryTask['label'])?></a></div>
    </div>
    <ol class="role-workflow-lane" aria-label="Role workflow steps"><?php foreach((array)$roleWorkspace['steps'] as $index=>$step):?><li><span><?=($index+1)?></span><small><?=e((string)$step)?></small></li><?php endforeach;?></ol>
</section>

<?php if (in_array($role, ['super_admin','clinic_admin'], true) && function_exists('dental_setup_readiness_snapshot')): $setupSnapshot = dental_setup_readiness_snapshot($pdo); $setupMissing = dental_setup_missing_titles($setupSnapshot); ?>
<section class="card setup-dashboard-indicator">
    <div>
        <span class="eyebrow">Clinic setup</span>
        <h2><?= e((string)$setupSnapshot['percent']) ?>% complete</h2>
        <p><?= $setupMissing ? 'Missing: ' . e(implode(', ', array_slice($setupMissing,0,4))) . (count($setupMissing)>4 ? '…' : '') : 'Baseline setup is complete. Continue with QA and manual role-flow testing.' ?></p>
    </div>
    <div class="status-actions"><a class="btn primary compact" href="setup_wizard.php">Open Setup Wizard</a><?php if(actual_role()==='super_admin' && active_role()==='super_admin'):?><a class="btn ghost compact" href="go_live_checklist.php">Go-Live Checklist</a><?php endif;?></div>
</section>
<?php endif; ?>

<?php if ($role === 'dentist' && !active_role_can_operate('dentist') && $taskDentistOptions): ?>
<section class="card task-target-selector"><form method="get" class="toolbar"><div><strong>Dentist workspace</strong><span class="muted">Choose the dentist whose workload you need to review. You remain signed in as <?= e((string)role_profile($actualRole)['name']) ?>. The selected dentist controls the displayed workload only.</span></div><label>Select dentist<select name="dentist_id" onchange="this.form.submit()"><?php foreach($taskDentistOptions as $doctor): ?><option value="<?= (int)$doctor['id'] ?>" <?= $taskDentistId===(int)$doctor['id']?'selected':'' ?>><?= e((string)($doctor['display_name'] ?? $doctor['name'])) ?></option><?php endforeach; ?></select></label></form></section>
<?php endif; ?>
<section class="role-welcome card">
    <div><span class="eyebrow"><?= e((string) $profile['name']) ?> workspace</span><h2>Welcome, <?= e((string) ($user['name'] ?? 'User')) ?></h2><p><?= e((string) $profile['description']) ?></p></div>
    <div class="role-welcome-tools"><span class="role-pill"><?= e((string) $profile['name']) ?></span><div class="role-guidance-links"><button type="button" class="link-btn" data-open="dashboard-task-guide"><?=dental_icon('clipboard')?> What I need to do</button><button type="button" class="link-btn" data-open="dashboard-scope-guide"><?=dental_icon('shield')?> Scope &amp; limits</button><a href="help_support.php?from=dashboard"><?=dental_icon('help')?> Help &amp; Support</a></div></div>
</section>

<section class="card daily-command-center">
    <div class="card-head"><div><span class="eyebrow">Today · <?=e(date('F j, Y'))?></span><h2>Other work awaiting attention</h2><p>Your primary responsibility is shown above. Use these cards only for additional work that is already waiting.</p></div></div>
    <div class="daily-task-grid">
        <?php foreach(array_slice($dailyTasks,0,8) as [$url,$title,$taskCount,$note,$priority]): $hasAttention = is_numeric($taskCount) && (float)$taskCount > 0; ?>
            <a class="daily-task-card <?=e($hasAttention ? (string)$priority : 'clear')?>" href="<?=e((string)$url)?>">
                <span class="daily-task-count"><?=e((string)$taskCount)?></span>
                <strong><?=e((string)$title)?></strong>
                <small><?=e((string)$note)?></small>
                <em><?= $hasAttention ? 'Open task' : 'Review' ?> →</em>
            </a>
        <?php endforeach; ?>
        <?php if(!$dailyTasks): ?><div class="daily-task-empty">No dashboard tasks are available in this task view.</div><?php endif; ?>
    </div>
</section>


<?php if(in_array($role,['super_admin','clinic_admin'],true)): ?>
<section class="card admin-critical-operations" id="critical-operations" data-critical-operations>
    <div class="card-head critical-operations-head">
        <div><span class="eyebrow">Administration control</span><h2>Critical operations</h2><p>Exceptions are grouped by severity. Open only the level you need, or use Show all for a complete review.</p></div>
        <div class="critical-operations-summary"><span class="badge <?= $adminCriticalCount>0?'warning':'' ?>"><?=$adminCriticalCount?> open</span><div class="button-row"><button type="button" class="btn ghost compact" data-critical-expand-all>Show all</button><button type="button" class="btn ghost compact" data-critical-collapse-all>Hide all</button></div></div>
    </div>
    <details class="critical-category-overview" data-critical-group>
        <summary><span><strong>Operational category totals</strong><small>Coverage, chairs, appointments, clinical records, approvals, and communication.</small></span><span class="critical-group-count">12 categories</span></summary>
        <div class="stats critical-stats">
            <?php stat_card('Coverage gaps',$coverageGapCount,'Open branch dates without a published doctor schedule'); ?>
            <?php stat_card('Chair assignment',$unassignedDoctorChairs,'Active dentist-branch assignments without a valid unique chair'); ?>
            <?php stat_card('Future conflicts',$futureResourceConflicts,'Overlapping patient, dentist, in-house employee, or chair bookings'); ?>
            <?php stat_card('In-house staffing',$inhouseServicesWithoutStaff,'Active in-house service and branch combinations without an eligible employee'); ?>
            <?php stat_card('Legacy assignments',$futureInhouseUnassigned+$futureInhouseChairMissing,'Future in-house visits missing an employee or required shared chair'); ?>
            <?php stat_card('Stale queue',$staleQueue,'Active patients waiting more than two hours'); ?>
            <?php stat_card('Old clinical drafts',$oldClinicalDrafts,'Unfinished records older than 24 hours'); ?>
            <?php stat_card('Schedule approvals',$pendingScheduleApprovals,'Leave or restricted schedule requests awaiting administration'); ?>
            <?php stat_card('Provider readiness',$futureProviderPrivilegeMismatch,'Future visits assigned to a Dentist without the required current credential or privilege'); ?>
            <?php stat_card('Treatment coverage',$uncoveredTreatmentItemCount+$pendingClinicalTaskAssignments,'Accepted procedures without qualified coverage or awaiting assigned-Dentist acceptance'); ?>
            <?php stat_card('Clinic provider choice',$clinicDecidesRequests,'New-patient requests where the clinic must choose the Dentist'); ?>
            <?php stat_card('Email delivery',$emailDeliveryAttention,'Stalled, overdue, failed, or misconfigured delivery'); ?>
        </div>
    </details>
    <div class="critical-operation-groups">
        <?php foreach($criticalGroupMeta as $severityKey=>$groupMeta): $groupRows=$criticalActionGroups[$severityKey]??[]; if(!$groupRows)continue; ?>
            <details class="critical-operation-group severity-<?=e((string)$groupMeta['tone'])?>" data-critical-group <?= $severityKey==='Critical'?'open':'' ?>>
                <summary>
                    <span class="critical-group-title"><span class="critical-level-marker" aria-hidden="true"></span><span><strong><?=e((string)$groupMeta['label'])?></strong><small><?=e((string)$groupMeta['note'])?></small></span></span>
                    <span class="critical-group-count"><?=count($groupRows)?> item<?=count($groupRows)===1?'':'s'?></span>
                </summary>
                <div class="table-wrap critical-group-table"><table><thead><tr><th>Issue</th><th>Record</th><th>Details</th><th>Action</th></tr></thead><tbody><?php foreach(array_slice($groupRows,0,15) as $critical):?><tr><td><strong><?=e((string)$critical['issue'])?></strong></td><td><?=e((string)$critical['target'])?></td><td><?=e((string)$critical['detail'])?></td><td><?php if(task_link_allowed((string)$critical['url'])):?><a href="<?=e((string)$critical['url'])?>">Resolve</a><?php else:?><span class="muted">Restricted</span><?php endif;?></td></tr><?php endforeach;?></tbody></table></div>
                <?php if(count($groupRows)>50):?><small class="muted critical-group-limit">Showing the first 15 of <?=count($groupRows)?> <?=e(strtolower((string)$groupMeta['label']))?> items.</small><?php endif;?>
            </details>
        <?php endforeach; ?>
        <?php if(!$criticalActions):?><div class="critical-operations-clear"><strong>No critical operations are open.</strong><span>Patient flow, schedules, clinical completion, and communication currently have no detailed exceptions.</span></div><?php endif;?>
    </div>
</section>
<section class="grid-2 admin-coverage-grid">
    <article class="card" id="coverage-gaps"><div class="card-head"><div><h2>Open days without a doctor schedule</h2><p>The branch is open, but no dentist has published working hours.</p></div><a href="doctor_schedule.php">Open schedule administration</a></div><div class="table-wrap"><table><thead><tr><th>Date</th><th>Branch</th><th>Clinic hours</th><th>Action</th></tr></thead><tbody><?php foreach($coverageGaps as $gap):?><tr><td><strong><?=e(date('D, M j',strtotime((string)$gap['date'])))?></strong></td><td><?=e((string)$gap['branch_name'])?></td><td><?=e(date('g:i A',strtotime((string)$gap['open_time'])).'–'.date('g:i A',strtotime((string)$gap['close_time'])))?></td><td><a href="doctor_schedule.php?branch_id=<?=(int)$gap['branch_id']?>&week=<?=e((string)$gap['date'])?>">Build coverage</a></td></tr><?php endforeach;if(!$coverageGaps):?><tr><td colspan="4" class="muted">Every open date in the next 14 days has at least one published doctor schedule.</td></tr><?php endif;?></tbody></table></div><?php if($coverageGapCount>count($coverageGaps)):?><small class="muted">Showing the first <?=count($coverageGaps)?> of <?=$coverageGapCount?> gaps.</small><?php endif;?></article>
    <article class="card"><div class="card-head"><div><h2>Today’s in-house appointments</h2><p>Dentist-independent services with an accountable clinic employee and, when required, a shared in-house chair.</p></div><a href="appointments.php?provider=inhouse">Open appointments</a></div><div class="table-wrap"><table><thead><tr><th>Time</th><th>Patient</th><th>Service</th><th>Status</th></tr></thead><tbody><?php foreach($inhouseAppointments as $item):?><tr><td><?=e(date('g:i A',strtotime((string)$item['start_at'])))?></td><td><strong><?=e((string)$item['patient'])?></strong><br><small><?=e((string)($item['branch_name']?:'Clinic'))?></small></td><td><?=e((string)$item['service_name'])?><br><small><?=e((string)$item['provider_name'])?></small></td><td><span class="badge"><?=e(ucwords(str_replace('_',' ',(string)$item['status'])))?></span></td></tr><?php endforeach;if(!$inhouseAppointments):?><tr><td colspan="4" class="muted">No in-house appointments are scheduled today.</td></tr><?php endif;?></tbody></table></div></article>
</section>
<?php endif; ?>

<section class="stats role-stats" data-live-queue-region="dashboard-stats">
<?php foreach ($stats as [$label,$value,$note]) stat_card((string)$label,(string)$value,(string)$note); ?>
</section>

<?php if($role==='dental_assistant'&&$actualRole==='dental_assistant'):?>
<section class="card assistant-assignment-workspace" data-live-queue-region="assistant-assigned-treatments"><div class="card-head"><div><h2>My assigned treatments</h2><p>Open the exact patient encounter assigned by the responsible Dentist. Treatment entries remain pending until Dentist verification.</p></div><span class="badge"><?=count($assistantEncounterAssignments)?></span></div><div class="compact-flow-list"><?php foreach($assistantEncounterAssignments as $assignment):?><article class="compact-flow-patient"><div><strong><?=e((string)$assignment['patient'])?></strong><small><?=e((string)$assignment['patient_no'])?> · Dentist: <?=e((string)$assignment['dentist_name'])?> · <?=e((string)($assignment['chair_name']?:'Chair not recorded'))?> · <?=e((string)($assignment['service_name']?:'Dental treatment'))?></small></div><div><span class="badge <?=($assignment['queue_status']??'')==='in_treatment'?'success':'warning'?>"><?=e(module_status((string)($assignment['queue_status']?:$assignment['encounter_status'])))?></span><a class="btn primary compact" href="clinical_workspace.php?encounter_id=<?=(int)$assignment['encounter_id']?>">Open treatment</a></div></article><?php endforeach;?><?php if(!$assistantEncounterAssignments):?><div class="queue-empty">No active treatment is assigned directly to your Dental Assistant account.</div><?php endif;?></div></section>
<?php endif;?>

<section class="grid-2 role-dashboard-grid" data-live-queue-region="dashboard-role-flow">
<div class="card">
    <div class="card-head"><h2><?= e($workTitle) ?></h2></div>
    <div class="table-wrap">
    <?php if ($workType === 'cashier'): ?>
        <table><thead><tr><th>Patient</th><th>Queue</th><th>Service</th><th>Balance</th></tr></thead><tbody>
        <?php $stmt=$pdo->query("SELECT q.id queue_entry_id,p.id patient_id,q.queue_no,q.service_name,CONCAT(p.first_name,' ',p.last_name) patient,COALESCE(SUM(i.amount-i.paid_amount),0) balance FROM `".table('queue_entries')."` q JOIN `".table('patients')."` p ON p.id=q.patient_id LEFT JOIN `".table('invoices')."` i ON i.queue_entry_id=q.id AND i.status IN ('unpaid','partial') WHERE q.status IN ('waiting_billing','cashier_called') GROUP BY q.id,p.first_name,p.last_name ORDER BY q.billing_at,q.checked_in_at LIMIT 20"); $rows=$stmt->fetchAll(); foreach($rows as $row): ?>
        <tr><td><strong><?=e($row['patient'])?></strong><br><a href="billing.php?patient_id=<?=(int)$row['patient_id']?>&amp;queue_entry_id=<?=(int)$row['queue_entry_id']?>">Open in Cashier</a></td><td><?=e($row['queue_no'])?></td><td><?=e($row['service_name']?:'Dental service')?></td><td>₱<?=e(number_format((float)$row['balance'],2))?></td></tr>
        <?php endforeach; if(!$rows): ?><tr><td colspan="4" class="muted">No patients are waiting for cashier.</td></tr><?php endif; ?>
        </tbody></table>
    <?php elseif ($workType === 'inventory' && $advanced): ?>
        <table><thead><tr><th>Item</th><th>Stock</th><th>Reorder</th><th>Next expiry</th></tr></thead><tbody>
        <?php $stmt=$pdo->query("SELECT i.name,i.current_stock,i.reorder_level,i.unit,(SELECT MIN(l.expiry_date) FROM `".table('inventory_lots')."` l WHERE l.item_id=i.id AND l.status='active' AND l.quantity_remaining>0) next_expiry FROM `".table('inventory_items')."` i WHERE i.status='active' AND (i.current_stock<=i.reorder_level OR EXISTS(SELECT 1 FROM `".table('inventory_lots')."` x WHERE x.item_id=i.id AND x.status='active' AND x.quantity_remaining>0 AND x.expiry_date<=DATE_ADD(CURDATE(),INTERVAL 60 DAY))) ORDER BY i.current_stock<=i.reorder_level DESC,i.name LIMIT 20"); $rows=$stmt->fetchAll(); foreach($rows as $row): ?>
        <tr><td><strong><?=e($row['name'])?></strong></td><td class="<?=((float)$row['current_stock']<=(float)$row['reorder_level'])?'danger-text':''?>"><?=e($row['current_stock'])?> <?=e($row['unit'])?></td><td><?=e($row['reorder_level'])?></td><td><?=e($row['next_expiry']?:'—')?></td></tr>
        <?php endforeach; if(!$rows): ?><tr><td colspan="4" class="muted">No low-stock or expiring items.</td></tr><?php endif; ?>
        </tbody></table>
    <?php elseif ($workType === 'queue'): ?>
        <table><thead><tr><th>Patient</th><th>Queue</th><th>Service</th><th>Status</th></tr></thead><tbody>
        <?php
        $queueWorkSql="SELECT q.queue_no,q.service_name,q.status,CONCAT(p.first_name,' ',p.last_name) patient FROM `".table('queue_entries')."` q JOIN `".table('patients')."` p ON p.id=q.patient_id WHERE q.status IN ('waiting','called','seated','in_treatment')";
        $queueWorkArgs=[];
        if($role==='dental_assistant' && $actualRole==='dental_assistant'){
            if($assistantDentistIds){$queueWorkSql.=' AND q.dentist_user_id IN ('.$assistantMarks.')';$queueWorkArgs=$assistantDentistIds;}else{$queueWorkSql.=' AND 1=0';}
        }
        $queueWorkSql.=" ORDER BY (q.checked_in_at<CURDATE()) DESC,FIELD(q.priority,'emergency','urgent','regular'),q.checked_in_at LIMIT 20";
        $stmt=$pdo->prepare($queueWorkSql);$stmt->execute($queueWorkArgs);$rows=$stmt->fetchAll(); foreach($rows as $row): ?>
        <tr><td><strong><?=e($row['patient'])?></strong></td><td><?=e($row['queue_no'])?></td><td><?=e($row['service_name']?:'Dental service')?></td><td><span class="badge"><?=e(ucwords(str_replace('_',' ',$row['status'])))?></span></td></tr>
        <?php endforeach; if(!$rows): ?><tr><td colspan="4" class="muted">No patients are currently in the clinical queue.</td></tr><?php endif; ?>
        </tbody></table>
    <?php else: ?>
        <table><thead><tr><th>Time</th><th>Patient</th><th>Service</th><th>Status</th></tr></thead><tbody>
        <?php
        $sql='SELECT a.*,CONCAT(p.first_name," ",p.last_name) patient FROM `'.table('appointments').'` a JOIN `'.table('patients').'` p ON p.id=a.patient_id WHERE DATE(a.start_at)=?'.$appointmentAliasScope;
        $args=array_merge([$today],$appointmentScopeArgs);$sql.=' ORDER BY a.start_at LIMIT 30';
        $stmt=$pdo->prepare($sql);$stmt->execute($args);$rows=$stmt->fetchAll(); foreach($rows as $row): ?>
        <tr><td><?=e(date('g:i A',strtotime($row['start_at'])))?></td><td><strong><?=e($row['patient'])?></strong></td><td><?=e($row['service_name'])?></td><td><span class="badge"><?=e(ucwords(str_replace('_',' ',$row['status'])))?></span></td></tr>
        <?php endforeach; if(!$rows): ?><tr><td colspan="4" class="muted">No appointments scheduled today.</td></tr><?php endif; ?>
        </tbody></table>
    <?php endif; ?>
    </div>
</div>
<div class="card">
    <div class="card-head"><h2>My quick actions</h2></div>
    <div class="quick-actions role-actions"><?php foreach($actions as [$url,$label]):?><a href="<?=e($url)?>"><?=e($label)?></a><?php endforeach;?><?php if(!$actions):?><p class="muted">No additional actions are available in this task view.</p><?php endif;?></div>
</div>
</section>
<dialog id="dashboard-task-guide" class="role-guidance-dialog"><div class="dialog-body"><button type="button" class="dialog-close" data-close>×</button><span class="eyebrow"><?=e((string)($workspace['label']??$profile['name']))?> task guide</span><h2>What I need to do</h2><ol class="task-steps dialog-task-steps"><?php foreach((array)($workspace['steps'] ?? []) as $step):?><li><?=e((string)$step)?></li><?php endforeach;?></ol><div class="dialog-actions"><a class="btn primary" href="help_support.php?from=dashboard"><?=dental_icon('help')?> Open Help &amp; Support</a><button class="btn ghost" type="button" data-close>Close</button></div></div></dialog>
<dialog id="dashboard-scope-guide" class="role-guidance-dialog"><div class="dialog-body"><button type="button" class="dialog-close" data-close>×</button><span class="eyebrow"><?=e((string)($workspace['label']??$profile['name']))?> task guide</span><h2>Scope and limits</h2><p class="role-guide-purpose"><?=e((string)($workspace['purpose']??$profile['description']))?></p><div class="alert role-guide-limit"><strong>Limit</strong><span><?=e((string)($workspace['limits']??'Actions remain subject to your signed-in account permissions.'))?></span></div><?php if(task_preview_mode()):?><p class="muted">Task View changes the operational perspective only. Transactions remain attributed to <?=e((string)($user['name']??'the signed-in user'))?>.</p><?php endif;?><div class="dialog-actions"><a class="btn primary" href="help_support.php?from=dashboard"><?=dental_icon('help')?> Open Help &amp; Support</a><button class="btn ghost" type="button" data-close>Close</button></div></div></dialog>
<?php render_footer();
