<?php

declare(strict_types=1);

require DENTAL_ROOT . '/app/bootstrap.php';
require_login();
require_roles(['super_admin','clinic_admin','receptionist','staff','dentist','dental_assistant','inventory_officer']);
require DENTAL_ROOT . '/app/layout.php';
require DENTAL_ROOT . '/app/module_helpers.php';
require_once DENTAL_ROOT . '/app/reconciliation_center.php';

if (!reconciliation_schema_ready()) {
    if (actual_role()==='super_admin') { header('Location: system_setup.php?tab=updates'); exit; }
    throw new RuntimeException('The Workflow Reconciliation Center update is pending. Ask the Superuser to apply Version 0.5.9.8.29.');
}

$pdo=db();
$actualRole=actual_role();
$role=active_role();
$userId=(int)(current_user()['id']??0);
$isAdmin=has_role(['super_admin','clinic_admin']);
$taskPreview=task_preview_mode();
$canAdminMutate=$isAdmin&&!$taskPreview;
$branchId=superuser_primary_workspace()?max(0,(int)($_GET['branch_id']??0)):signed_user_branch_id();

if($_SERVER['REQUEST_METHOD']==='GET'&&$canAdminMutate&&!app_demo_read_only()){
    $scanKey='reconciliation_last_scan_'.($branchId>0?$branchId:'all');
    $lastScan=(int)setting_get('system',$scanKey,0);
    $scanInterval=isset($_GET['_reconciliation_live'])?30:60;
    if($lastScan<time()-$scanInterval){
        try{reconciliation_scan_all($pdo,$branchId);setting_set('system',$scanKey,(string)time());}
        catch(Throwable $autoScanError){error_log('Workflow reconciliation auto-scan warning: '.$autoScanError->getMessage());}
    }
}

if($_SERVER['REQUEST_METHOD']==='POST'){
    verify_csrf();
    try{
        $action=(string)($_POST['action']??'');
        if($action==='scan'){
            if(!$canAdminMutate)throw new RuntimeException('Return Task View to the signed-in Administrator or Superuser workspace before running reconciliation.');
            $scan=reconciliation_scan_all($pdo,$branchId);
            setting_set('system','reconciliation_last_scan_'.($branchId>0?$branchId:'all'),(string)time());
            audit('workflow_reconciliation_scan','system',null,['branch_id'=>$branchId,'linked'=>(int)($scan['linked']??0),'detected'=>(int)($scan['detected']??0),'modules'=>$scan['modules']??[]]);
            flash('success','Reconciliation completed. '.number_format((int)($scan['detected']??0)).' current exceptions were checked across patient flow, clinical care, billing, approvals, and inventory.');
        }elseif($action==='resolve'){
            if(!$canAdminMutate)throw new RuntimeException('Return Task View to the signed-in Administrator or Superuser workspace before applying a reconciliation change.');
            $message=visit_resolve_issue($pdo,(int)($_POST['issue_id']??0),(string)($_POST['resolution_action']??''),(string)($_POST['reason']??''),$userId);
            flash('success',$message);
        }elseif($action==='assign'){
            if(!$canAdminMutate)throw new RuntimeException('Return Task View to the signed-in Administrator or Superuser workspace before assigning reconciliation work.');
            $message=reconciliation_assign_issue($pdo,(int)($_POST['issue_id']??0),(int)($_POST['assigned_to_user_id']??0),$userId,(string)($_POST['assignment_reason']??''));
            flash('success',$message);
        }else throw new RuntimeException('Unsupported reconciliation action.');
    }catch(Throwable $e){flash('error',$e->getMessage());}
    $return='visit_exceptions.php';
    foreach(['issue','status','severity','issue_group','responsible_role','q','branch_id'] as $key){$value=$_POST['return_'.$key]??null;if($value!==null&&$value!=='')$return.=(str_contains($return,'?')?'&':'?').rawurlencode($key).'='.rawurlencode((string)$value);}
    if(!str_contains($return,'issue=')&&!empty($_POST['issue_id']))$return.=(str_contains($return,'?')?'&':'?').'issue='.(int)$_POST['issue_id'];
    header('Location: '.$return);exit;
}

$status=module_safe_status_filter('status',['open','in_review','resolved','ignored'],'open');
$severity=module_safe_status_filter('severity',['warning','urgent','critical','approval']);
$issueGroup=module_safe_status_filter('issue_group',['queue','chair','clinical','care_team','billing','follow_up','inventory','approval','workflow']);
$responsible=module_safe_status_filter('responsible_role',['receptionist','dental_assistant','dentist','cashier','inventory_officer','clinic_admin']);
if(!$isAdmin)$responsible=$role;
$q=trim((string)($_GET['q']??''));
$page=module_page_number();
$perPage=module_page_size('per_page',50);
$where=['i.status=?'];$args=[$status];
if($branchId>0){$where[]='(i.branch_id=? OR i.branch_id IS NULL)';$args[]=$branchId;}
if($severity!==''){$where[]='i.severity=?';$args[]=$severity;}
if($issueGroup!==''){$where[]='i.issue_group=?';$args[]=$issueGroup;}
if($isAdmin&&$responsible!==''){$where[]='i.responsible_role=?';$args[]=$responsible;}
if(!$isAdmin){$where[]='(i.responsible_role=? OR i.assigned_to_user_id=?)';$args[]=$role;$args[]=$userId;}
if($q!==''){$like=module_like_term($q);$where[]='(i.summary LIKE ? OR i.detail LIKE ? OR i.issue_code LIKE ? OR i.issue_group LIKE ? OR p.patient_no LIKE ? OR CONCAT(p.first_name," ",p.last_name) LIKE ? OR v.visit_code LIKE ?)';array_push($args,$like,$like,$like,$like,$like,$like,$like);}
$whereSql=' WHERE '.implode(' AND ',$where);
$countStmt=$pdo->prepare('SELECT COUNT(*) FROM `' . table('visit_integrity_issues') . '` i LEFT JOIN `' . table('patients') . '` p ON p.id=i.patient_id LEFT JOIN `' . table('visits') . '` v ON v.id=i.visit_id'.$whereSql);$countStmt->execute($args);$total=(int)$countStmt->fetchColumn();
$pages=max(1,(int)ceil($total/$perPage));$page=min($page,$pages);$offset=($page-1)*$perPage;
$stmt=$pdo->prepare('SELECT i.*,v.visit_code,v.state_code,v.integrity_status,p.patient_no,CONCAT(p.first_name," ",p.last_name) patient_name,b.name branch_name,au.name assigned_to_name FROM `' . table('visit_integrity_issues') . '` i LEFT JOIN `' . table('visits') . '` v ON v.id=i.visit_id LEFT JOIN `' . table('patients') . '` p ON p.id=i.patient_id LEFT JOIN `' . table('branches') . '` b ON b.id=i.branch_id LEFT JOIN `' . table('users') . '` au ON au.id=i.assigned_to_user_id'.$whereSql.' ORDER BY FIELD(i.severity,"critical","urgent","warning","approval"),COALESCE(i.due_at,i.last_detected_at),i.id DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($args);$rows=$stmt->fetchAll();

$summary=['critical'=>0,'urgent'=>0,'warning'=>0,'approval'=>0,'open'=>0,'overdue'=>0,'assigned'=>0];
$summaryWhere=['status IN ("open","in_review")'];$summaryArgs=[];
if($branchId>0){$summaryWhere[]='(branch_id=? OR branch_id IS NULL)';$summaryArgs[]=$branchId;}
if(!$isAdmin){$summaryWhere[]='(responsible_role=? OR assigned_to_user_id=?)';$summaryArgs[]=$role;$summaryArgs[]=$userId;}
$sumStmt=$pdo->prepare('SELECT severity,COUNT(*) total,SUM(due_at IS NOT NULL AND due_at<NOW()) overdue,SUM(assigned_to_user_id IS NOT NULL) assigned FROM `' . table('visit_integrity_issues') . '` WHERE '.implode(' AND ',$summaryWhere).' GROUP BY severity');$sumStmt->execute($summaryArgs);
foreach($sumStmt->fetchAll() as $r){$summary[(string)$r['severity']]=(int)$r['total'];$summary['open']+=(int)$r['total'];$summary['overdue']+=(int)($r['overdue']??0);$summary['assigned']+=(int)($r['assigned']??0);}

$groupCounts=[];$groupStmt=$pdo->prepare('SELECT issue_group,COUNT(*) total FROM `' . table('visit_integrity_issues') . '` WHERE '.implode(' AND ',$summaryWhere).' GROUP BY issue_group ORDER BY total DESC');$groupStmt->execute($summaryArgs);foreach($groupStmt->fetchAll() as $r)$groupCounts[(string)$r['issue_group']]=(int)$r['total'];

$selected=null;$history=[];$issueId=(int)($_GET['issue']??0);
if($issueId){
    $selected=visit_issue_record($pdo,$issueId,false);
    if($selected&&$branchId>0&&!in_array((int)$selected['branch_id'],[0,$branchId],true))$selected=null;
    if($selected&&!$isAdmin&&(string)$selected['responsible_role']!==$role&&(int)($selected['assigned_to_user_id']??0)!==$userId)$selected=null;
    if($selected&&!empty($selected['visit_id'])){$hist=$pdo->prepare('SELECT h.*,u.name changed_by_name FROM `' . table('visit_state_history') . '` h LEFT JOIN `' . table('users') . '` u ON u.id=h.changed_by WHERE h.visit_id=? ORDER BY h.created_at DESC,h.id DESC LIMIT 30');$hist->execute([(int)$selected['visit_id']]);$history=$hist->fetchAll();}
}

$assignable=[];
if($canAdminMutate){
    $staffSql='SELECT u.id,u.name,r.code role_code,b.name branch_name FROM `' . table('users') . '` u JOIN `' . table('roles') . '` r ON r.id=u.role_id LEFT JOIN `' . table('branches') . '` b ON b.id=u.branch_id WHERE u.status="active"';$staffArgs=[];
    if($selected){$staffSql.=' AND r.code IN (?,"clinic_admin")';$staffArgs[]=(string)($selected['responsible_role']??'clinic_admin');}
    else{$staffSql.=' AND r.code IN ("clinic_admin","receptionist","dentist","dental_assistant","cashier","inventory_officer")';}
    if($branchId>0){$staffSql.=' AND (u.branch_id=? OR u.branch_id IS NULL)';$staffArgs[]=$branchId;}$staffSql.=' ORDER BY FIELD(r.code,"clinic_admin","dentist","dental_assistant","receptionist","cashier","inventory_officer"),u.name';$staff=$pdo->prepare($staffSql);$staff->execute($staffArgs);$assignable=$staff->fetchAll();
}

$entityUrl=function(array $row):string{return match((string)($row['entity_type']??'')){
    'appointment'=>'appointments.php?edit_id='.(int)$row['entity_id'],
    'queue_entry'=>'queue.php#queue-'.(int)$row['entity_id'],
    'clinical_encounter'=>'clinical_workspace.php?encounter_id='.(int)$row['entity_id'],
    'procedure'=>!empty($row['metadata_json'])&&($m=json_decode((string)$row['metadata_json'],true))&&isset($m['encounter_id'])?'clinical_workspace.php?encounter_id='.(int)$m['encounter_id']:'patients.php?action=view&id='.(int)$row['patient_id'],
    'invoice'=>'billing.php?invoice_id='.(int)$row['entity_id'],
    'cashier_session'=>'billing.php?view=exceptions',
    'follow_up'=>'followups.php?manage_id='.(int)$row['entity_id'],
    'inventory_document'=>'inventory.php?view=documents&manage_id='.(int)$row['entity_id'],
    'inventory_lot'=>'inventory.php?view=lots&q='.(int)$row['entity_id'],
    'inventory_item'=>'inventory.php?view=stock&q='.(int)$row['entity_id'],
    'clinical_usage'=>!empty($row['metadata_json'])&&($m=json_decode((string)$row['metadata_json'],true))&&isset($m['encounter_id'])?'clinical_workspace.php?encounter_id='.(int)$m['encounter_id']:'inventory.php',
    'discount_authorization','zero_charge_authorization','payment_adjustment'=>'billing.php?view=exceptions',
    default=>(int)($row['patient_id']??0)>0?'patients.php?action=view&id='.(int)$row['patient_id']:'visit_workflow.php'
};};

$actionsFor=function(array $issue):array{
    $code=(string)$issue['issue_code'];
    return match($code){
        'past_appointment_no_outcome'=>[['mark_no_show','Mark no-show'],['cancel_appointment','Cancel appointment'],['send_to_role','Return to Reception']],
        'operational_appointment_without_queue','queue_missing_appointment','encounter_without_queue','treatment_without_encounter'=>[['reconnect_visit','Reconnect visit records'],['release_chair','Release chair when safe'],['send_to_role','Assign to responsible workflow']],
        'finalized_encounter_missing_invoice','completed_procedure_missing_billing_line'=>[['rebuild_invoice','Rebuild approved invoice'],['send_to_role','Return to Dentist']],
        'duplicate_chair_occupancy','finalized_encounter_chair_occupied','paused_treatment_chair_occupied'=>[['release_chair','Release incorrect chair'],['send_to_role','Return to chair team']],
        'appointment_queue_status_mismatch'=>[['sync_appointment_status','Synchronize appointment to queue'],['send_to_role','Return to Reception']],
        'paid_visit_still_at_cashier'=>[['complete_paid_queue','Complete settled cashier visit'],['send_to_role','Return to Cashier']],
        'abandoned_cashier_lock'=>[['release_cashier_lock','Release cashier lock']],
        'negative_branch_stock'=>[['reconcile_inventory_stock','Recalculate from active lots'],['send_to_role','Assign inventory review']],
        'expired_lot_still_active'=>[['quarantine_lot','Quarantine expired lot'],['send_to_role','Assign inventory review']],
        default=>[['send_to_role','Assign to responsible role']]
    };
};

$groupMeta=[
    'critical'=>['label'=>'Critical','note'=>'Stops or invalidates treatment, patient flow, billing, chair safety, or inventory control.','tone'=>'critical'],
    'urgent'=>['label'=>'Urgent','note'=>'Requires prompt action before the discrepancy escalates or delays another workflow.','tone'=>'urgent'],
    'warning'=>['label'=>'Warning','note'=>'Needs review, but does not currently block the patient or stock workflow.','tone'=>'warning'],
    'approval'=>['label'=>'Approval','note'=>'Independent review is required in the source module; this center never self-approves it.','tone'=>'approval'],
];
$severityRows=['critical'=>[],'urgent'=>[],'warning'=>[],'approval'=>[]];foreach($rows as $row)$severityRows[(string)$row['severity']][]=$row;
$returnFields=['issue'=>$issueId,'status'=>$status,'severity'=>$severity,'issue_group'=>$issueGroup,'responsible_role'=>$responsible,'q'=>$q,'branch_id'=>$branchId];

render_header('Workflow Reconciliation Center');
?>
<div class="toolbar"><div><span class="eyebrow">Operational integrity</span><strong>Workflow Exception and Reconciliation Center</strong><span class="muted">Detect and route queue, chair, clinical, care-team, billing, follow-up, approval, and inventory discrepancies without rewriting signed notes or posted payments.</span></div><div class="toolbar-actions"><a class="btn ghost" href="visit_workflow.php">Patient Journey</a><?php if($canAdminMutate):?><form method="post" class="inline-form"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="scan"><button class="btn primary" type="submit">Scan all workflows</button></form><?php endif;?></div></div>

<div data-reconciliation-live-region="reconciliation-center" data-critical-operations>
<section class="reconciliation-summary-grid" aria-label="Open reconciliation summary">
<a class="reconciliation-summary-card severity-critical" href="<?=e(module_query_url(['severity'=>'critical','status'=>'open','page'=>1]))?>"><span><?=dental_icon('shield')?></span><strong><?=number_format($summary['critical'])?></strong><small>Critical</small></a>
<a class="reconciliation-summary-card severity-urgent" href="<?=e(module_query_url(['severity'=>'urgent','status'=>'open','page'=>1]))?>"><span><?=dental_icon('clock')?></span><strong><?=number_format($summary['urgent'])?></strong><small>Urgent</small></a>
<a class="reconciliation-summary-card severity-warning" href="<?=e(module_query_url(['severity'=>'warning','status'=>'open','page'=>1]))?>"><span><?=dental_icon('clipboard')?></span><strong><?=number_format($summary['warning'])?></strong><small>Warnings</small></a>
<a class="reconciliation-summary-card severity-approval" href="<?=e(module_query_url(['severity'=>'approval','status'=>'open','page'=>1]))?>"><span><?=dental_icon('check')?></span><strong><?=number_format($summary['approval'])?></strong><small>Approvals</small></a>
<div class="reconciliation-summary-card"><span><?=dental_icon('operations')?></span><strong><?=number_format($summary['open'])?></strong><small>Open total</small></div>
<div class="reconciliation-summary-card"><span><?=dental_icon('warning')?></span><strong><?=number_format($summary['overdue'])?></strong><small>Past due</small></div>
</section>

<details class="card reconciliation-category-totals"><summary><span><strong>Exception categories</strong><small>See where the current workload originates.</small></span><span class="badge"><?=count(array_filter($groupCounts))?> groups</span></summary><div class="reconciliation-category-grid"><?php foreach(['queue','chair','clinical','care_team','billing','follow_up','inventory','approval','workflow'] as $group):?><a href="<?=e(module_query_url(['issue_group'=>$group,'status'=>'open','page'=>1]))?>"><strong><?=number_format($groupCounts[$group]??0)?></strong><span><?=e(module_status($group))?></span></a><?php endforeach;?></div></details>

<form method="get" class="card reconciliation-filter-bar"><label><span>Search</span><input type="search" name="q" value="<?=e($q)?>" placeholder="Patient, visit, issue, or record"></label><label><span>Status</span><select name="status"><?php foreach(['open'=>'Open','in_review'=>'In review','resolved'=>'Resolved','ignored'=>'Dismissed'] as $v=>$l):?><option value="<?=e($v)?>" <?=$status===$v?'selected':''?>><?=e($l)?></option><?php endforeach;?></select></label><label><span>Level</span><select name="severity"><option value="">All levels</option><?php foreach($groupMeta as $v=>$m):?><option value="<?=$v?>" <?=$severity===$v?'selected':''?>><?=e($m['label'])?></option><?php endforeach;?></select></label><label><span>Category</span><select name="issue_group"><option value="">All categories</option><?php foreach(['queue','chair','clinical','care_team','billing','follow_up','inventory','approval','workflow'] as $v):?><option value="<?=$v?>" <?=$issueGroup===$v?'selected':''?>><?=e(module_status($v))?></option><?php endforeach;?></select></label><?php if($isAdmin):?><label><span>Responsible role</span><select name="responsible_role"><option value="">All roles</option><?php foreach(['clinic_admin','receptionist','dentist','dental_assistant','cashier','inventory_officer'] as $v):?><option value="<?=$v?>" <?=$responsible===$v?'selected':''?>><?=e(module_status($v))?></option><?php endforeach;?></select></label><?php endif;?><label><span>Items per page</span><select name="per_page"><?php foreach([25,50,100] as $n):?><option value="<?=$n?>" <?=$perPage===$n?'selected':''?>><?=$n?></option><?php endforeach;?></select></label><button class="btn primary">Apply</button><a class="btn ghost" href="visit_exceptions.php">Clear</a></form>

<?php if($selected):?>
<section class="card reconciliation-review-panel"><div class="card-head"><div><span class="eyebrow"><?=e(strtoupper((string)$selected['severity']))?> · <?=e(module_status((string)($selected['issue_group']??'workflow')))?> · <?=e((string)(($selected['visit_code'] ?? '') ?: 'Unlinked record'))?></span><h2><?=e((string)$selected['summary'])?></h2><p><?=e((string)(($selected['patient_name'] ?? '') ?: 'No patient linked'))?><?=!empty($selected['patient_no'])?' · '.e((string)$selected['patient_no']):''?> · <?=e((string)(($selected['branch_name'] ?? '') ?: 'Clinic-wide'))?></p></div><div class="inline-actions"><span class="badge <?=in_array((string)$selected['severity'],['critical','urgent'],true)?'warning':''?>"><?=e(module_status((string)$selected['status']))?></span><a class="btn ghost" href="<?=e($entityUrl($selected))?>"><?= $taskPreview ? 'View source record' : 'Open source record' ?></a><a href="<?=e(module_query_url(['issue'=>null]))?>">Close</a></div></div><div class="alert <?=in_array((string)$selected['severity'],['critical','urgent'],true)?'danger':'warning'?>"><strong><?=e((string)$selected['summary'])?></strong><br><?=e((string)$selected['detail'])?></div>
<div class="reconciliation-detail-grid"><div><span>Issue code</span><strong><?=e(module_status((string)$selected['issue_code']))?></strong></div><div><span>Responsible role</span><strong><?=e(module_status((string)$selected['responsible_role']))?></strong></div><div><span>Assigned to</span><strong><?=e((string)($selected['assigned_to_name']?:'Unassigned'))?></strong></div><div><span>First detected</span><strong><?=e(date('M j, Y g:i A',strtotime((string)$selected['detected_at'])))?></strong></div><div><span>Last detected</span><strong><?=e(date('M j, Y g:i A',strtotime((string)$selected['last_detected_at'])))?></strong></div><div><span>Due</span><strong class="<?=!empty($selected['due_at'])&&strtotime((string)$selected['due_at'])<time()?'danger-text':''?>"><?=!empty($selected['due_at'])?e(date('M j, Y g:i A',strtotime((string)$selected['due_at']))):'No deadline'?></strong></div></div>
<?php if($canAdminMutate&&in_array((string)$selected['status'],['open','in_review'],true)):?><div class="grid-2 reconciliation-actions-grid"><form method="post" class="card inset-card"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="assign"><input type="hidden" name="issue_id" value="<?=(int)$selected['id']?>"><?php foreach($returnFields as $key=>$value):?><input type="hidden" name="return_<?=e($key)?>" value="<?=e((string)$value)?>"><?php endforeach;?><h3>Assign follow-through</h3><label>Staff member<select name="assigned_to_user_id" required><option value="">Choose staff</option><?php foreach($assignable as $staff):?><option value="<?=(int)$staff['id']?>" <?=(int)($selected['assigned_to_user_id']??0)===(int)$staff['id']?'selected':''?>><?=e((string)$staff['name'])?> · <?=e(module_status((string)$staff['role_code']))?><?=!empty($staff['branch_name'])?' · '.e((string)$staff['branch_name']):''?></option><?php endforeach;?></select></label><label>Assignment note<textarea name="assignment_reason" rows="3" required placeholder="State what must be checked or completed."></textarea></label><button class="btn primary" type="submit">Assign exception</button></form><form method="post" class="card inset-card"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="resolve"><input type="hidden" name="issue_id" value="<?=(int)$selected['id']?>"><?php foreach($returnFields as $key=>$value):?><input type="hidden" name="return_<?=e($key)?>" value="<?=e((string)$value)?>"><?php endforeach;?><h3>Audited correction</h3><label>Controlled action<select name="resolution_action" required><option value="">Choose action</option><?php foreach($actionsFor($selected) as [$v,$l]):?><option value="<?=e($v)?>"><?=e($l)?></option><?php endforeach;?><option value="ignore">Dismiss with documented reason</option></select></label><label>Reason<textarea name="reason" rows="3" required placeholder="Explain the source record reviewed and why this action is safe."></textarea></label><button class="btn primary" type="submit">Apply controlled action</button></form></div><?php elseif($taskPreview&&$isAdmin&&in_array((string)$selected['status'],['open','in_review'],true)):?><div class="alert task-preview-action-notice"><strong>Review only.</strong> Assignment and controlled-correction forms are intentionally hidden in Task View preview. Return the Task view selector to your signed-in <?=e((string)role_profile($actualRole)['name'])?> workspace before resolving this exception.</div><?php endif;?>
<?php if($history):?><h3>Canonical visit history</h3><div class="timeline-list"><?php foreach($history as $h):?><article class="timeline-item"><time><?=e(date('M j, Y g:i A',strtotime((string)$h['created_at'])))?></time><div><strong><?=e(module_status((string)$h['previous_state']))?> → <?=e(module_status((string)$h['new_state']))?></strong><p><?=e((string)($h['details']?:module_status((string)$h['action_code'])))?></p><small><?=e((string)($h['changed_by_name']?:'System'))?></small></div></article><?php endforeach;?></div><?php endif;?></section>
<?php endif;?>

<section class="card reconciliation-groups"><div class="card-head"><div><h2>Workflow exceptions</h2><p>Open only the level you need. Critical opens first; Approval items must be completed in their source module.</p></div><div class="critical-operations-summary"><span class="badge <?= $total>0?'warning':'' ?>"><?=number_format($total)?> matching</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><?php module_render_server_pagination($total,$page,$perPage,'page','per_page','exceptions');?>
<div class="critical-operation-groups"><?php foreach($groupMeta as $key=>$meta):$groupRows=$severityRows[$key]??[];if(!$groupRows)continue;?><details class="critical-operation-group severity-<?=e($meta['tone'])?>" data-critical-group <?=$key==='critical'?'open':''?>><summary><span class="critical-group-title"><span class="critical-level-marker" aria-hidden="true"></span><span><strong><?=e($meta['label'])?></strong><small><?=e($meta['note'])?></small></span></span><span class="critical-group-count"><?=count($groupRows)?> item<?=count($groupRows)===1?'':'s'?></span></summary><div class="table-wrap reconciliation-table"><table><thead><tr><th>Record</th><th>Exception</th><th>Owner</th><th>Age / due</th><th>Action</th></tr></thead><tbody><?php foreach($groupRows as $row):$overdue=!empty($row['due_at'])&&strtotime((string)$row['due_at'])<time();?><tr><td><strong><?=e((string)(($row['patient_name'] ?? '') ?: module_status((string)($row['entity_type'] ?? 'record'))))?></strong><small><?=e((string)(($row['patient_no'] ?? '') ?: (($row['branch_name'] ?? '') ?: 'Clinic-wide')))?><?=!empty($row['visit_code'])?' · '.e((string)$row['visit_code']):''?></small></td><td><strong><?=e((string)$row['summary'])?></strong><small><?=e((string)$row['detail'])?></small><span class="reconciliation-source-label"><?=e(module_status((string)($row['issue_group']??'workflow')))?> · <?=e(module_status((string)($row['source_module']??'operations')))?></span></td><td><?=e((string)(($row['assigned_to_name'] ?? '') ?: module_status((string)($row['responsible_role'] ?? 'unassigned'))))?><small><?=!empty($row['assigned_to_name'])?'Assigned':'Responsible role'?></small></td><td><strong><?=e(date('M j, g:i A',strtotime((string)$row['last_detected_at'])))?></strong><small class="<?=$overdue?'danger-text':''?>"><?=$overdue?'Past due':(!empty($row['due_at'])?'Due '.e(date('M j, g:i A',strtotime((string)$row['due_at']))):'No deadline')?></small></td><td><a class="btn ghost compact" href="<?=e(module_query_url(['issue'=>(int)$row['id']]))?>">Review</a></td></tr><?php endforeach;?></tbody></table></div></details><?php endforeach;?><?php if(!$rows):?><div class="critical-operations-clear"><strong>No exceptions match this view.</strong><span>The selected status, severity, category, and role filters are clear.</span></div><?php endif;?></div><?php module_render_server_pagination($total,$page,$perPage,'page','per_page','exceptions');?></section>
</div>
<?php render_footer(); ?>
