<?php

declare(strict_types=1);

require DENTAL_ROOT . '/app/bootstrap.php';
require_login();
require_documentation_schema();
require_permission('sterilization.manage',['super_admin','clinic_admin','dentist','dental_assistant','inventory_officer']);
require DENTAL_ROOT . '/app/layout.php';
require DENTAL_ROOT . '/app/module_helpers.php';
require_once DENTAL_ROOT . '/app/inventory_integrity.php';

$pdo=db();
if(!inventory_schema_ready()){
    render_header('Sterilization');echo '<div class="alert danger">Apply Version 0.5.9.8 before using controlled sterilization and instrument traceability.</div>';render_footer();exit;
}
$user=current_user()??[];$userId=(int)($user['id']??0);$role=active_role();$userBranch=inventory_user_branch_id();$canRelease=in_array($role,['super_admin','clinic_admin','dentist','inventory_officer'],true);
$allBranches=branch_options();$branches=array_values(array_filter($allBranches,static fn(array $b):bool=>$role==='super_admin'||(int)$b['id']===$userBranch));$branchId=(int)($_GET['branch_id']??($userBranch?:($branches[0]['id']??0)));if($role!=='super_admin')$branchId=$userBranch;if($branchId>0)inventory_require_branch($branchId);

function sterilization_batch_hash(array $batch,array $sets): string
{
    return hash('sha256',json_encode([
        'id'=>(int)$batch['id'],'branch_id'=>(int)$batch['branch_id'],'equipment_id'=>(int)$batch['equipment_id'],
        'cycle_no'=>(string)$batch['cycle_no'],'load_no'=>(string)$batch['load_no'],'started_at'=>(string)$batch['cycle_started_at'],
        'completed_at'=>(string)$batch['cycle_completed_at'],'temperature_c'=>(float)$batch['temperature_c'],
        'pressure_kpa'=>(float)$batch['pressure_kpa'],'exposure_minutes'=>(int)$batch['exposure_minutes'],
        'chemical'=>(string)$batch['chemical_indicator_result'],'biological_required'=>(int)$batch['biological_indicator_required'],
        'biological'=>(string)$batch['biological_indicator_result'],'released_by'=>(int)($batch['released_by']??0),
        'release_note'=>(string)($batch['release_note']??''),'sets'=>array_map('intval',$sets),
    ],JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRESERVE_ZERO_FRACTION));
}

if($_SERVER['REQUEST_METHOD']==='POST'){
    require_task_action('sterilization.manage');verify_csrf();$action=(string)($_POST['action']??'');
    try{
        if($action==='equipment'){
            $b=(int)$_POST['branch_id'];inventory_require_branch($b);$name=trim((string)$_POST['name']);$last=$_POST['last_service_date']?:null;$next=$_POST['next_service_date']?:null;if($name==='')throw new RuntimeException('Equipment name is required.');if(!$next||$next<date('Y-m-d'))throw new RuntimeException('Enter a current or future next-service date before activating equipment.');if($last&&$last>$next)throw new RuntimeException('Next service date cannot be earlier than the last service date.');
            $pdo->prepare('INSERT INTO `' . table('sterilization_equipment') . '` (branch_id,name,serial_number,manufacturer,model,last_service_date,next_service_date,status,created_by,created_at) VALUES (?,?,?,?,?,?,?,"active",?,NOW())')->execute([$b,$name,trim((string)$_POST['serial_number']),trim((string)$_POST['manufacturer']),trim((string)$_POST['model']),$last,$next,$userId]);audit('sterilization_equipment_created','sterilization_equipment',(int)$pdo->lastInsertId());module_return('sterilization.php','success','Sterilizer equipment added.');
        }
        if($action==='equipment_status'){
            if(!in_array($role,['super_admin','clinic_admin'],true))throw new RuntimeException('Only an Administrator may change sterilizer service status.');
            $id=(int)$_POST['id'];$status=(string)$_POST['status'];$note=trim((string)$_POST['service_note']);if(!in_array($status,['active','maintenance','out_of_service','retired'],true))throw new RuntimeException('Select a valid equipment status.');if(strlen($note)<5)throw new RuntimeException('Document the service or status reason.');
            $pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_equipment') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$eq=$q->fetch();if(!$eq)throw new RuntimeException('Sterilizer equipment not found.');inventory_require_branch((int)$eq['branch_id']);$last=$_POST['last_service_date']?:$eq['last_service_date'];$next=$_POST['next_service_date']?:$eq['next_service_date'];if($status==='active'&&(!$next||$next<date('Y-m-d')))throw new RuntimeException('Enter a current or future next-service date before returning equipment to Active.');if($last&&$next&&$last>$next)throw new RuntimeException('Next service date cannot be earlier than the last service date.');if($status!=='active'){$open=$pdo->prepare('SELECT COUNT(*) FROM `' . table('sterilization_batches') . '` WHERE equipment_id=? AND cycle_status IN ("loaded","running")');$open->execute([$id]);if((int)$open->fetchColumn()>0)throw new RuntimeException('Complete or cancel the open sterilization load before removing this equipment from service.');}$pdo->prepare('UPDATE `' . table('sterilization_equipment') . '` SET status=?,last_service_date=?,next_service_date=?,updated_at=NOW() WHERE id=?')->execute([$status,$last?:null,$next?:null,$id]);$pdo->commit();audit('sterilization_equipment_status_changed','sterilization_equipment',$id,['previous_status'=>$eq['status'],'new_status'=>$status,'service_note'=>$note,'last_service_date'=>$last,'next_service_date'=>$next]);module_return('sterilization.php','success','Sterilizer service status updated.');
        }
        if($action==='instrument_set'){
            $b=(int)$_POST['branch_id'];inventory_require_branch($b);$code=strtoupper(trim((string)$_POST['set_code']));$name=trim((string)$_POST['name']);$contents=trim((string)$_POST['contents']);if($code===''||$name===''||$contents==='')throw new RuntimeException('Set code, name, and complete contents are required.');
            $pdo->prepare('INSERT INTO `' . table('instrument_sets') . '` (branch_id,set_code,name,contents,status,created_by,created_at) VALUES (?,?,?,? ,"dirty",?,NOW())')->execute([$b,$code,$name,$contents,$userId]);audit('instrument_set_created','instrument_set',(int)$pdo->lastInsertId());module_return('sterilization.php','success','Instrument set added as Dirty. Prepare it before loading a cycle.');
        }
        if($action==='prepare_set'){
            $id=(int)$_POST['id'];$note=trim((string)$_POST['note']);if(strlen($note)<5)throw new RuntimeException('Document the cleaning, inspection, and packaging performed.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('instrument_sets') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$set=$q->fetch();if(!$set)throw new RuntimeException('Instrument set not found.');inventory_require_branch((int)$set['branch_id']);if(!in_array((string)$set['status'],['dirty','quarantined'],true))throw new RuntimeException('Only dirty or quarantined sets can be prepared.');$pdo->prepare('UPDATE `' . table('instrument_sets') . '` SET status="prepared",prepared_by=?,prepared_at=NOW(),updated_at=NOW() WHERE id=?')->execute([$userId,$id]);$pdo->commit();audit('instrument_set_prepared','instrument_set',$id,['note'=>$note]);module_return('sterilization.php','success','Instrument set inspected, packaged, and marked Prepared.');
        }
        if($action==='retire_set'){
            if(!in_array($role,['super_admin','clinic_admin','inventory_officer'],true))throw new RuntimeException('Only an Administrator or Inventory Officer may retire an instrument set.');
            $id=(int)$_POST['id'];$reason=trim((string)$_POST['reason']);if(strlen($reason)<5)throw new RuntimeException('Document why the instrument set is being retired.');
            $pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('instrument_sets') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$set=$q->fetch();if(!$set)throw new RuntimeException('Instrument set not found.');inventory_require_branch((int)$set['branch_id']);if(!in_array((string)$set['status'],['dirty','prepared','quarantined'],true))throw new RuntimeException('Only a dirty, prepared, or quarantined set can be retired. Return or complete any active patient or sterilization use first.');$pdo->prepare('UPDATE `' . table('instrument_sets') . '` SET status="retired",updated_at=NOW() WHERE id=?')->execute([$id]);$pdo->commit();audit('instrument_set_retired','instrument_set',$id,['previous_status'=>$set['status'],'reason'=>$reason]);module_return('sterilization.php','success','Instrument set retired. Its prior cycle and patient-use history remain available.');
        }
        if($action==='create_batch'){
            $b=(int)$_POST['branch_id'];inventory_require_branch($b);$equipmentId=(int)$_POST['equipment_id'];$setIds=array_values(array_unique(array_filter(array_map('intval',(array)($_POST['instrument_set_ids']??[])))));if(!$setIds)throw new RuntimeException('Select at least one prepared instrument set.');$cycle=trim((string)$_POST['cycle_no']);$load=trim((string)$_POST['load_no']);if($cycle===''||$load==='')throw new RuntimeException('Cycle and load numbers are required.');
            $pdo->beginTransaction();$dup=$pdo->prepare('SELECT id FROM `' . table('sterilization_batches') . '` WHERE branch_id=? AND cycle_no=? AND load_no=? FOR UPDATE');$dup->execute([$b,$cycle,$load]);if($dup->fetchColumn())throw new RuntimeException('This branch already has the same cycle and load number.');$eq=$pdo->prepare('SELECT * FROM `' . table('sterilization_equipment') . '` WHERE id=? AND branch_id=? FOR UPDATE');$eq->execute([$equipmentId,$b]);$equipment=$eq->fetch();if(!$equipment||(string)$equipment['status']!=='active')throw new RuntimeException('Select active, in-service sterilization equipment.');if(!empty($equipment['next_service_date'])&&(string)$equipment['next_service_date']<date('Y-m-d'))throw new RuntimeException('This sterilizer is overdue for service and cannot be used.');
            $sets=[];foreach($setIds as $setId){$q=$pdo->prepare('SELECT * FROM `' . table('instrument_sets') . '` WHERE id=? AND branch_id=? FOR UPDATE');$q->execute([$setId,$b]);$set=$q->fetch();if(!$set||(string)$set['status']!=='prepared')throw new RuntimeException('Every selected set must be Prepared and belong to this branch.');$sets[]=$set;}
            $pdo->prepare('INSERT INTO `' . table('sterilization_batches') . '` (branch_id,equipment_id,sterilizer_name,cycle_no,load_no,cycle_started_at,operator_user_id,cycle_status,chemical_indicator_result,biological_indicator_required,biological_indicator_result,indicator_result,biological_result,release_status,notes,created_at) VALUES (?,?,?,?,?,NOW(),?,"loaded","pending",?,?,"Pending",?,"pending",?,NOW())')
                ->execute([$b,$equipmentId,$equipment['name'],$cycle,$load,$userId,isset($_POST['biological_indicator_required'])?1:0,isset($_POST['biological_indicator_required'])?'pending':'not_required',isset($_POST['biological_indicator_required'])?'Pending':'Not required',trim((string)$_POST['notes'])]);
            $batchId=(int)$pdo->lastInsertId();$link=$pdo->prepare('INSERT INTO `' . table('sterilization_batch_sets') . '` (batch_id,instrument_set_id,loaded_by,loaded_at) VALUES (?,?,?,NOW())');foreach($sets as $set){$link->execute([$batchId,(int)$set['id'],$userId]);$pdo->prepare('UPDATE `' . table('instrument_sets') . '` SET status="in_cycle",last_batch_id=?,updated_at=NOW() WHERE id=?')->execute([$batchId,(int)$set['id']]);}
            sterilization_record_event($pdo,$batchId,null,'loaded',['equipment_id'=>$equipmentId,'set_ids'=>$setIds],'Load assembled; cycle not yet started.');$pdo->commit();audit('sterilization_batch_loaded','sterilization_batch',$batchId,['sets'=>$setIds]);module_return('sterilization.php','success','Sterilization load created. Start the cycle when the equipment begins running.');
        }
        if($action==='start'){
            $id=(int)$_POST['id'];$pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_batches') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$batch=$q->fetch();if(!$batch)throw new RuntimeException('Batch not found.');inventory_require_branch((int)$batch['branch_id']);if((string)$batch['cycle_status']!=='loaded')throw new RuntimeException('Only a loaded batch can start.');$pdo->prepare('UPDATE `' . table('sterilization_batches') . '` SET cycle_status="running",cycle_started_at=NOW(),operator_user_id=?,release_status="pending" WHERE id=?')->execute([$userId,$id]);sterilization_record_event($pdo,$id,'loaded','running',[],'Sterilizer cycle started.');$pdo->commit();audit('sterilization_cycle_started','sterilization_batch',$id);module_return('sterilization.php','success','Cycle started.');
        }
        if($action==='complete'){
            $id=(int)$_POST['id'];$temp=(float)$_POST['temperature_c'];$pressure=(float)$_POST['pressure_kpa'];$minutes=(int)$_POST['exposure_minutes'];$chemical=(string)$_POST['chemical_indicator_result'];$bio=(string)$_POST['biological_indicator_result'];if($temp<=0||$pressure<=0||$minutes<=0)throw new RuntimeException('Temperature, pressure, and exposure duration are required.');if(!in_array($chemical,['pass','fail'],true))throw new RuntimeException('Record the chemical indicator result.');
            $pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_batches') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$batch=$q->fetch();if(!$batch)throw new RuntimeException('Batch not found.');inventory_require_branch((int)$batch['branch_id']);if((string)$batch['cycle_status']!=='running')throw new RuntimeException('Only a running cycle can be completed.');$bioRequired=(int)$batch['biological_indicator_required']===1;if($bioRequired&&!in_array($bio,['pending','pass','fail'],true))throw new RuntimeException('Record the biological indicator as Pending, Pass, or Fail.');if(!$bioRequired)$bio='not_required';$failed=$chemical==='fail'||$bio==='fail';$next=$failed?'quarantined':'completed';$release=$failed?'quarantined':'pending';$failure=$failed?trim((string)$_POST['failure_reason']):null;if($failed&&strlen((string)$failure)<5)throw new RuntimeException('Document the failed indicator and quarantine reason.');
            $pdo->prepare('UPDATE `' . table('sterilization_batches') . '` SET cycle_status=?,cycle_completed_at=NOW(),temperature_c=?,pressure_kpa=?,exposure_minutes=?,chemical_indicator_result=?,biological_indicator_result=?,indicator_result=?,biological_result=?,completed_by=?,release_status=?,failure_reason=?,notes=? WHERE id=? AND cycle_status="running"')
                ->execute([$next,$temp,$pressure,$minutes,$chemical,$bio,ucfirst($chemical),$bio==='not_required'?'Not required':ucfirst($bio),$userId,$release,$failure,trim((string)$_POST['notes']),$id]);
            if($failed)$pdo->prepare('UPDATE `' . table('instrument_sets') . '` s JOIN `' . table('sterilization_batch_sets') . '` bs ON bs.instrument_set_id=s.id SET s.status="quarantined",s.updated_at=NOW() WHERE bs.batch_id=?')->execute([$id]);sterilization_record_event($pdo,$id,'running',$next,['temperature_c'=>$temp,'pressure_kpa'=>$pressure,'exposure_minutes'=>$minutes,'chemical'=>$chemical,'biological'=>$bio],(string)($failure?:'Cycle completed.'));$pdo->commit();audit('sterilization_cycle_completed','sterilization_batch',$id,['status'=>$next]);module_return('sterilization.php','success',$failed?'Cycle failed an indicator. All sets are quarantined.':'Cycle completed. Independent release is still required.');
        }
        if($action==='biological_result'){
            $id=(int)$_POST['id'];$result=(string)$_POST['result'];$note=trim((string)$_POST['note']);if(!in_array($result,['pass','fail'],true))throw new RuntimeException('Select Pass or Fail.');if(strlen($note)<5)throw new RuntimeException('Enter the biological test reference and result note.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_batches') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$batch=$q->fetch();if(!$batch)throw new RuntimeException('Batch not found.');inventory_require_branch((int)$batch['branch_id']);if((int)$batch['biological_indicator_required']!==1||(string)$batch['cycle_status']!=='completed'||(string)$batch['chemical_indicator_result']!=='pass'||(string)$batch['biological_indicator_result']!=='pending')throw new RuntimeException('Only a completed cycle with a passing chemical indicator and pending biological result can receive this result. Failed or quarantined cycles cannot be reversed.');$next=$result==='fail'?'quarantined':'completed';$pdo->prepare('UPDATE `' . table('sterilization_batches') . '` SET biological_indicator_result=?,biological_result=?,cycle_status=?,release_status=?,failure_reason=CASE WHEN ?="fail" THEN ? ELSE failure_reason END WHERE id=? AND cycle_status="completed" AND biological_indicator_result="pending"')->execute([$result,ucfirst($result),$next,$result==='fail'?'quarantined':'pending',$result,$note,$id]);if($result==='fail')$pdo->prepare('UPDATE `' . table('instrument_sets') . '` s JOIN `' . table('sterilization_batch_sets') . '` bs ON bs.instrument_set_id=s.id SET s.status="quarantined",s.updated_at=NOW() WHERE bs.batch_id=?')->execute([$id]);sterilization_record_event($pdo,$id,'completed',$next,['biological'=>$result],$note);$pdo->commit();audit('sterilization_biological_result','sterilization_batch',$id,['result'=>$result]);module_return('sterilization.php','success','Biological result recorded.');
        }
        if($action==='release'){
            if(!$canRelease)throw new RuntimeException('Dental Assistants may operate cycles but cannot independently release their own loads.');$id=(int)$_POST['id'];$note=trim((string)$_POST['release_note']);if(strlen($note)<5)throw new RuntimeException('Enter the release-review note.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_batches') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$batch=$q->fetch();if(!$batch)throw new RuntimeException('Batch not found.');inventory_require_branch((int)$batch['branch_id']);if((string)$batch['cycle_status']!=='completed')throw new RuntimeException('Only a completed, non-quarantined cycle can be released.');require_independent_actor([(int)$batch['operator_user_id'],(int)$batch['completed_by']], 'operate or complete and independently release the same sterilization load');if((string)$batch['chemical_indicator_result']!=='pass')throw new RuntimeException('Chemical indicator must pass before release.');$bio=(string)$batch['biological_indicator_result'];if((int)$batch['biological_indicator_required']===1&&$bio!=='pass')throw new RuntimeException('Required biological indicator must pass before release.');if((float)$batch['temperature_c']<=0||(float)$batch['pressure_kpa']<=0||(int)$batch['exposure_minutes']<=0)throw new RuntimeException('Physical cycle parameters are incomplete.');$setStmt=$pdo->prepare('SELECT instrument_set_id FROM `' . table('sterilization_batch_sets') . '` WHERE batch_id=? ORDER BY instrument_set_id');$setStmt->execute([$id]);$setIds=array_map('intval',$setStmt->fetchAll(PDO::FETCH_COLUMN));if(!$setIds)throw new RuntimeException('A batch without traceable instrument sets cannot be released.');$hash=sterilization_batch_hash(array_merge($batch,['released_by'=>$userId,'release_note'=>$note]),$setIds);$stmt=$pdo->prepare('UPDATE `' . table('sterilization_batches') . '` SET cycle_status="released",release_status="released",released_by=?,released_at=NOW(),release_note=?,batch_hash=? WHERE id=? AND cycle_status="completed"');$stmt->execute([$userId,$note,$hash,$id]);if($stmt->rowCount()!==1)throw new RuntimeException('The batch changed before release.');$pdo->prepare('UPDATE `' . table('instrument_sets') . '` s JOIN `' . table('sterilization_batch_sets') . '` bs ON bs.instrument_set_id=s.id SET s.status="sterile",s.updated_at=NOW() WHERE bs.batch_id=? AND s.status="in_cycle"')->execute([$id]);sterilization_record_event($pdo,$id,'completed','released',['batch_hash'=>$hash],$note);$pdo->commit();audit('sterilization_batch_released','sterilization_batch',$id,['hash'=>$hash]);module_return('sterilization.php','success','Load independently released. Instrument sets are now available for patient use.');
        }
        if($action==='cancel_batch'){
            $id=(int)$_POST['id'];$reason=trim((string)$_POST['reason']);if(strlen($reason)<5)throw new RuntimeException('Enter a cancellation reason.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT * FROM `' . table('sterilization_batches') . '` WHERE id=? FOR UPDATE');$q->execute([$id]);$batch=$q->fetch();if(!$batch)throw new RuntimeException('Batch not found.');inventory_require_branch((int)$batch['branch_id']);if((string)$batch['cycle_status']!=='loaded')throw new RuntimeException('Only a not-yet-started load can be cancelled.');$pdo->prepare('UPDATE `' . table('sterilization_batches') . '` SET cycle_status="cancelled",release_status="failed",failure_reason=? WHERE id=?')->execute([$reason,$id]);$pdo->prepare('UPDATE `' . table('instrument_sets') . '` s JOIN `' . table('sterilization_batch_sets') . '` bs ON bs.instrument_set_id=s.id SET s.status="prepared",s.updated_at=NOW() WHERE bs.batch_id=?')->execute([$id]);sterilization_record_event($pdo,$id,'loaded','cancelled',[],$reason);$pdo->commit();audit('sterilization_batch_cancelled','sterilization_batch',$id);module_return('sterilization.php','success','Load cancelled before cycle start; sets returned to Prepared.');
        }
        if($action==='assign_set'){
            $setId=(int)$_POST['instrument_set_id'];$patientId=(int)$_POST['patient_id'];$encounterId=(int)($_POST['encounter_id']??0);$procedureId=(int)($_POST['procedure_id']??0);$notes=trim((string)$_POST['notes']);if($patientId<=0)throw new RuntimeException('Select the patient who will receive this instrument set.');if($encounterId<=0&&$procedureId<=0&&strlen($notes)<5)throw new RuntimeException('Document the appointment or general-use context when no encounter or procedure is selected.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT s.*,b.cycle_status,b.batch_hash,b.branch_id batch_branch FROM `' . table('instrument_sets') . '` s JOIN `' . table('sterilization_batches') . '` b ON b.id=s.last_batch_id WHERE s.id=? FOR UPDATE');$q->execute([$setId]);$set=$q->fetch();if(!$set)throw new RuntimeException('Instrument set not found.');inventory_require_branch((int)$set['branch_id']);if((string)$set['status']!=='sterile'||(string)$set['cycle_status']!=='released'||empty($set['batch_hash']))throw new RuntimeException('Only a released, traceable sterile set can be assigned.');$patientStmt=$pdo->prepare('SELECT branch_id,status FROM `' . table('patients') . '` WHERE id=? AND deleted_at IS NULL');$patientStmt->execute([$patientId]);$patient=$patientStmt->fetch();if(!$patient||!in_array((string)$patient['status'],['active','pending'],true)||((int)($patient['branch_id']??0)>0&&(int)$patient['branch_id']!==(int)$set['branch_id']))throw new RuntimeException('The patient is inactive or belongs to another branch.');if($encounterId>0){$e=$pdo->prepare('SELECT patient_id,branch_id FROM `' . table('clinical_encounters') . '` WHERE id=?');$e->execute([$encounterId]);$enc=$e->fetch();if(!$enc||(int)$enc['patient_id']!==$patientId||(int)$enc['branch_id']!==(int)$set['branch_id'])throw new RuntimeException('Encounter, patient, and instrument branch do not match.');}if($procedureId>0){$procedureBranchExpr=inventory_procedure_branch_expression();$p=$pdo->prepare('SELECT pr.patient_id,pr.encounter_id,'.$procedureBranchExpr.' effective_branch_id FROM `' . table('procedures') . '` pr LEFT JOIN `' . table('clinical_encounters') . '` ce ON ce.id=pr.encounter_id JOIN `' . table('patients') . '` p ON p.id=pr.patient_id WHERE pr.id=?');$p->execute([$procedureId]);$pr=$p->fetch();if(!$pr||(int)$pr['patient_id']!==$patientId||(int)($pr['effective_branch_id']??0)!==(int)$set['branch_id'])throw new RuntimeException('Procedure, patient, and instrument branch do not match.');if($encounterId>0&&(int)$pr['encounter_id']!==$encounterId)throw new RuntimeException('The procedure is not part of the selected encounter.');}
            $pdo->prepare('INSERT INTO `' . table('instrument_usage') . '` (instrument_set_id,batch_id,branch_id,patient_id,encounter_id,procedure_id,assigned_by,assigned_at,notes) VALUES (?,?,?,?,?,?,?,NOW(),?)')->execute([$setId,(int)$set['last_batch_id'],(int)$set['branch_id'],$patientId,$encounterId?:null,$procedureId?:null,$userId,$notes]);$usageId=(int)$pdo->lastInsertId();$pdo->prepare('UPDATE `' . table('instrument_sets') . '` SET status="in_use",updated_at=NOW() WHERE id=? AND status="sterile"')->execute([$setId]);$pdo->commit();audit('instrument_set_assigned','instrument_usage',$usageId,['patient_id'=>$patientId,'set_id'=>$setId]);module_return('sterilization.php','success','Sterile instrument set assigned and linked to the patient record.');
        }
        if($action==='return_set'){
            $usageId=(int)$_POST['usage_id'];$note=trim((string)$_POST['note']);if(strlen($note)<5)throw new RuntimeException('Document chairside return/decontamination handoff.');$pdo->beginTransaction();$q=$pdo->prepare('SELECT u.*,s.status FROM `' . table('instrument_usage') . '` u JOIN `' . table('instrument_sets') . '` s ON s.id=u.instrument_set_id WHERE u.id=? FOR UPDATE');$q->execute([$usageId]);$usage=$q->fetch();if(!$usage||$usage['returned_at'])throw new RuntimeException('This instrument use is already closed or missing.');inventory_require_branch((int)$usage['branch_id']);if((string)$usage['status']!=='in_use')throw new RuntimeException('The set is not currently assigned for use.');$pdo->prepare('UPDATE `' . table('instrument_usage') . '` SET returned_by=?,returned_at=NOW(),notes=CONCAT(COALESCE(notes,""),?) WHERE id=?')->execute([$userId,"\nReturn: ".$note,$usageId]);$pdo->prepare('UPDATE `' . table('instrument_sets') . '` SET status="dirty",updated_at=NOW() WHERE id=?')->execute([(int)$usage['instrument_set_id']]);$pdo->commit();audit('instrument_set_returned','instrument_usage',$usageId,['note'=>$note]);module_return('sterilization.php','success','Instrument set returned as Dirty and must complete a new preparation and sterilization cycle.');
        }
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
}

$view=(string)($_GET['view']??'sets');if(!in_array($view,['sets','batches','usage','equipment'],true))$view='sets';
$state=module_management_state('page','per_page',25);$page=$state['page'];$perPage=$state['per_page'];$offset=$state['offset'];$q=trim((string)($_GET['q']??''));$statusFilter=trim((string)($_GET['status']??''));$manageId=max(0,(int)($_GET['manage_id']??0));
$scopeSql=$branchId>0?' AND branch_id='.(int)$branchId:($role==='super_admin'?'':' AND 1=0');
$preparedCount=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('instrument_sets') . '` WHERE status="prepared"'.$scopeSql)->fetchColumn();
$running=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('sterilization_batches') . '` WHERE cycle_status="running"'.$scopeSql)->fetchColumn();
$releasePending=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('sterilization_batches') . '` WHERE cycle_status="completed"'.$scopeSql)->fetchColumn();
$quarantined=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('sterilization_batches') . '` WHERE cycle_status IN ("quarantined","failed")'.$scopeSql)->fetchColumn();
$inUse=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('instrument_sets') . '` WHERE status="in_use"'.$scopeSql)->fetchColumn();
$usableEquipmentSql='SELECT COUNT(*) FROM `' . table('sterilization_equipment') . '` WHERE status="active"'.($branchId>0?' AND branch_id='.(int)$branchId:'').' AND next_service_date IS NOT NULL AND next_service_date>=CURDATE()';
$usableEquipmentCount=(int)$pdo->query($usableEquipmentSql)->fetchColumn();
$sterileCount=(int)$pdo->query('SELECT COUNT(*) FROM `' . table('instrument_sets') . '` WHERE status="sterile"'.$scopeSql)->fetchColumn();


$rows=[];$rowsTotal=0;$managed=null;$managedSets=[];
if($view==='sets'){
 $where=['1=1'];$params=[];if($branchId>0){$where[]='s.branch_id=?';$params[]=$branchId;}if($q!==''){$like=module_like_term($q);$where[]='(s.set_code LIKE ? OR s.name LIKE ? OR s.contents LIKE ?)';array_push($params,$like,$like,$like);}if(in_array($statusFilter,['dirty','prepared','in_cycle','sterile','in_use','quarantined','retired'],true)){$where[]='s.status=?';$params[]=$statusFilter;}$whereSql=implode(' AND ',$where);$from=' FROM `' . table('instrument_sets') . '` s LEFT JOIN `' . table('users') . '` u ON u.id=s.prepared_by LEFT JOIN `' . table('sterilization_batches') . '` b ON b.id=s.last_batch_id ';
 $count=$pdo->prepare('SELECT COUNT(*)'.$from.'WHERE '.$whereSql);$count->execute($params);$rowsTotal=(int)$count->fetchColumn();$stmt=$pdo->prepare('SELECT s.*,u.name prepared_by_name,b.load_no,b.cycle_no'.$from.'WHERE '.$whereSql.' ORDER BY s.name LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($params);$rows=$stmt->fetchAll();if($manageId){$stmt=$pdo->prepare('SELECT s.*,u.name prepared_by_name,b.load_no,b.cycle_no'.$from.'WHERE s.id=?'.($branchId>0?' AND s.branch_id=?':'').' LIMIT 1');$stmt->execute($branchId>0?[$manageId,$branchId]:[$manageId]);$managed=$stmt->fetch()?:null;}
}elseif($view==='batches'){
 $where=['1=1'];$params=[];if($branchId>0){$where[]='s.branch_id=?';$params[]=$branchId;}if($q!==''){$like=module_like_term($q);$where[]='(s.cycle_no LIKE ? OR s.load_no LIKE ? OR s.sterilizer_name LIKE ? OR e.serial_number LIKE ?)';array_push($params,$like,$like,$like,$like);}if(in_array($statusFilter,['loaded','running','completed','released','quarantined','failed','cancelled'],true)){$where[]='s.cycle_status=?';$params[]=$statusFilter;}$whereSql=implode(' AND ',$where);$from=' FROM `' . table('sterilization_batches') . '` s LEFT JOIN `' . table('branches') . '` b ON b.id=s.branch_id LEFT JOIN `' . table('sterilization_equipment') . '` e ON e.id=s.equipment_id LEFT JOIN `' . table('users') . '` op ON op.id=s.operator_user_id LEFT JOIN `' . table('users') . '` co ON co.id=s.completed_by LEFT JOIN `' . table('users') . '` rv ON rv.id=s.released_by ';
 $count=$pdo->prepare('SELECT COUNT(*)'.$from.'WHERE '.$whereSql);$count->execute($params);$rowsTotal=(int)$count->fetchColumn();$stmt=$pdo->prepare('SELECT s.*,b.name branch,e.serial_number,op.name operator,co.name completed_by_name,rv.name released_by_name'.$from.'WHERE '.$whereSql.' ORDER BY s.created_at DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($params);$rows=$stmt->fetchAll();if($manageId){$stmt=$pdo->prepare('SELECT s.*,b.name branch,e.serial_number,op.name operator,co.name completed_by_name,rv.name released_by_name'.$from.'WHERE s.id=?'.($branchId>0?' AND s.branch_id=?':'').' LIMIT 1');$stmt->execute($branchId>0?[$manageId,$branchId]:[$manageId]);$managed=$stmt->fetch()?:null;if($managed){$setStmt=$pdo->prepare('SELECT s.set_code,s.name,s.status FROM `' . table('sterilization_batch_sets') . '` bs JOIN `' . table('instrument_sets') . '` s ON s.id=bs.instrument_set_id WHERE bs.batch_id=? ORDER BY s.name');$setStmt->execute([$manageId]);$managedSets=$setStmt->fetchAll();}}
}elseif($view==='usage'){
 $where=['1=1'];$params=[];if($branchId>0){$where[]='u.branch_id=?';$params[]=$branchId;}if($q!==''){$like=module_like_term($q);$where[]='(p.patient_no LIKE ? OR p.first_name LIKE ? OR p.last_name LIKE ? OR s.set_code LIKE ? OR s.name LIKE ? OR pr.procedure_name LIKE ?)';array_push($params,$like,$like,$like,$like,$like,$like);}if($statusFilter==='open')$where[]='u.returned_at IS NULL';elseif($statusFilter==='returned')$where[]='u.returned_at IS NOT NULL';$whereSql=implode(' AND ',$where);$from=' FROM `' . table('instrument_usage') . '` u JOIN `' . table('instrument_sets') . '` s ON s.id=u.instrument_set_id JOIN `' . table('patients') . '` p ON p.id=u.patient_id LEFT JOIN `' . table('procedures') . '` pr ON pr.id=u.procedure_id LEFT JOIN `' . table('users') . '` a ON a.id=u.assigned_by LEFT JOIN `' . table('users') . '` r ON r.id=u.returned_by ';
 $count=$pdo->prepare('SELECT COUNT(*)'.$from.'WHERE '.$whereSql);$count->execute($params);$rowsTotal=(int)$count->fetchColumn();$stmt=$pdo->prepare('SELECT u.*,s.set_code,s.name set_name,p.patient_no,p.first_name,p.last_name,pr.procedure_name,a.name assigned_by_name,r.name returned_by_name'.$from.'WHERE '.$whereSql.' ORDER BY u.assigned_at DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($params);$rows=$stmt->fetchAll();if($manageId){$stmt=$pdo->prepare('SELECT u.*,s.set_code,s.name set_name,p.patient_no,p.first_name,p.last_name,pr.procedure_name,a.name assigned_by_name,r.name returned_by_name'.$from.'WHERE u.id=?'.($branchId>0?' AND u.branch_id=?':'').' LIMIT 1');$stmt->execute($branchId>0?[$manageId,$branchId]:[$manageId]);$managed=$stmt->fetch()?:null;}
}else{
 $where=['1=1'];$params=[];if($branchId>0){$where[]='e.branch_id=?';$params[]=$branchId;}if($q!==''){$like=module_like_term($q);$where[]='(e.name LIKE ? OR e.serial_number LIKE ? OR e.manufacturer LIKE ? OR e.model LIKE ?)';array_push($params,$like,$like,$like,$like);}if(in_array($statusFilter,['active','maintenance','out_of_service','retired'],true)){$where[]='e.status=?';$params[]=$statusFilter;}$whereSql=implode(' AND ',$where);$count=$pdo->prepare('SELECT COUNT(*) FROM `' . table('sterilization_equipment') . '` e WHERE '.$whereSql);$count->execute($params);$rowsTotal=(int)$count->fetchColumn();$stmt=$pdo->prepare('SELECT e.* FROM `' . table('sterilization_equipment') . '` e WHERE '.$whereSql.' ORDER BY e.name LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($params);$rows=$stmt->fetchAll();if($manageId){$stmt=$pdo->prepare('SELECT e.* FROM `' . table('sterilization_equipment') . '` e WHERE e.id=?'.($branchId>0?' AND e.branch_id=?':'').' LIMIT 1');$stmt->execute($branchId>0?[$manageId,$branchId]:[$manageId]);$managed=$stmt->fetch()?:null;}
}

$sterilizationSafetySummary = function_exists('inventory_completion_risk_summary') ? inventory_completion_risk_summary($pdo, $branchId) : [];
render_header('Sterilization');
?>
<section class="stats"><?php stat_card('Prepared sets',$preparedCount,'Ready to load');stat_card('Running cycles',$running,'Do not open equipment');stat_card('Awaiting release',$releasePending,'Independent review required');stat_card('Quarantined',$quarantined,'Failed or unresolved indicators');stat_card('Sets in use',$inUse,'Linked to patients');?></section>
<?php if($sterilizationSafetySummary): ?>
<?= inventory_completion_card_html($sterilizationSafetySummary) ?>
<?php endif; ?>
<div class="toolbar"><div class="tabs"><a class="<?=$view==='sets'?'active':''?>" href="sterilization.php?view=sets&amp;branch_id=<?=$branchId?>">Instrument sets</a><a class="<?=$view==='batches'?'active':''?>" href="sterilization.php?view=batches&amp;branch_id=<?=$branchId?>">Sterilization loads</a><a class="<?=$view==='usage'?'active':''?>" href="sterilization.php?view=usage&amp;branch_id=<?=$branchId?>">Patient traceability</a><a class="<?=$view==='equipment'?'active':''?>" href="sterilization.php?view=equipment&amp;branch_id=<?=$branchId?>">Equipment</a></div><div><?php if($role==='super_admin'&&count($branches)>1):?><form method="get" class="inline-form"><input type="hidden" name="view" value="<?=e($view)?>"><select name="branch_id" onchange="this.form.submit()"><?php foreach($branches as $b):?><option value="<?=(int)$b['id']?>" <?=$branchId===(int)$b['id']?'selected':''?>><?=e((string)$b['name'])?></option><?php endforeach;?></select></form><?php endif;?><button class="btn ghost" data-open="equipment-form">Equipment</button><button class="btn ghost" data-open="set-form">Instrument set</button><button class="btn primary" data-open="batch-form">New load</button><?php if($sterileCount>0):?><button class="btn primary" data-open="assign-form">Assign to patient</button><?php endif;?></div></div>
<form method="get" class="management-filter-bar"><input type="hidden" name="view" value="<?=e($view)?>"><input type="hidden" name="branch_id" value="<?=$branchId?>"><label>Search<input name="q" value="<?=e($q)?>" placeholder="Set, load, patient, equipment or serial"></label><label>Status<select name="status"><option value="">All statuses</option><?php $statuses=$view==='sets'?['dirty','prepared','in_cycle','sterile','in_use','quarantined','retired']:($view==='batches'?['loaded','running','completed','released','quarantined','failed','cancelled']:($view==='usage'?['open','returned']:['active','maintenance','out_of_service','retired']));foreach($statuses as $st):?><option value="<?=e($st)?>" <?=$statusFilter===$st?'selected':''?>><?=e(module_status($st))?></option><?php endforeach;?></select></label><label>Rows<select name="per_page"><?php foreach([10,25,50,100] as $size):?><option value="<?=$size?>" <?=$perPage===$size?'selected':''?>><?=$size?></option><?php endforeach;?></select></label><button class="btn primary">Apply</button><a class="btn ghost" href="sterilization.php?view=<?=e($view)?>&amp;branch_id=<?=$branchId?>">Reset</a></form>
<div class="card server-list-card"><?php module_render_list_count($rowsTotal,$view);module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','sterilization-list');?><div class="table-wrap"><table><thead><tr><?php if($view==='sets'):?><th>Set</th><th>Contents</th><th>Status</th><th>Last cycle</th><th>Preparation</th><th>Action</th><?php elseif($view==='batches'):?><th>Load</th><th>Equipment</th><th>Cycle evidence</th><th>Indicators</th><th>Status</th><th>Action</th><?php elseif($view==='usage'):?><th>Assigned</th><th>Patient</th><th>Set</th><th>Procedure</th><th>Return</th><th>Action</th><?php else:?><th>Equipment</th><th>Serial / model</th><th>Service</th><th>Status</th><th>Action</th><?php endif;?></tr></thead><tbody><?php foreach($rows as $row):?><tr><?php if($view==='sets'):?><td><strong><?=e((string)$row['set_code'])?></strong><small><?=e((string)$row['name'])?></small></td><td><?=nl2br(e((string)$row['contents']))?></td><td><span class="badge"><?=e(module_status((string)$row['status']))?></span></td><td><?=e($row['cycle_no']&&$row['load_no']?(string)$row['cycle_no'].' / '.(string)$row['load_no']:'—')?></td><td><?=e((string)($row['prepared_by_name']?:'—'))?><small><?=!empty($row['prepared_at'])?e(date('M j, g:i A',strtotime((string)$row['prepared_at']))):''?></small></td><td><a class="btn ghost" href="<?=e(module_management_url(['view'=>'sets','manage_id'=>(int)$row['id'],'page'=>$page,'per_page'=>$perPage,'branch_id'=>$branchId]))?>">Manage</a></td><?php elseif($view==='batches'):?><td><strong><?=e((string)$row['cycle_no'])?> / <?=e((string)$row['load_no'])?></strong><small><?=e((string)($row['branch']?:'—'))?></small></td><td><?=e((string)$row['sterilizer_name'])?><small><?=e((string)($row['serial_number']?:'No serial'))?></small></td><td><?=e((string)($row['temperature_c']?:'—'))?> °C · <?=e((string)($row['pressure_kpa']?:'—'))?> kPa · <?=e((string)($row['exposure_minutes']?:'—'))?> min</td><td>Chemical <?=e(module_status((string)$row['chemical_indicator_result']))?><small>Biological <?=e(module_status((string)$row['biological_indicator_result']))?></small></td><td><span class="badge"><?=e(module_status((string)$row['cycle_status']))?></span></td><td><a class="btn ghost" href="<?=e(module_management_url(['view'=>'batches','manage_id'=>(int)$row['id'],'page'=>$page,'per_page'=>$perPage,'branch_id'=>$branchId]))?>">Manage</a></td><?php elseif($view==='usage'):?><td><?=e(date('M j, Y g:i A',strtotime((string)$row['assigned_at'])))?></td><td><strong><?=e((string)$row['last_name'].', '.(string)$row['first_name'])?></strong><small><?=e((string)$row['patient_no'])?></small></td><td><?=e((string)$row['set_code'].' · '.(string)$row['set_name'])?></td><td><?=e((string)($row['procedure_name']?:'Encounter/general use'))?></td><td><?=$row['returned_at']?e(date('M j, g:i A',strtotime((string)$row['returned_at']))):'Open use'?></td><td><a class="btn ghost" href="<?=e(module_management_url(['view'=>'usage','manage_id'=>(int)$row['id'],'page'=>$page,'per_page'=>$perPage,'branch_id'=>$branchId]))?>">Manage</a></td><?php else:?><td><strong><?=e((string)$row['name'])?></strong><small><?=e((string)$row['manufacturer'])?></small></td><td><?=e(trim((string)($row['serial_number']?:'').' '.(string)($row['model']?:''))?:'—')?></td><td>Last <?=e((string)($row['last_service_date']?:'—'))?><small>Next <?=e((string)($row['next_service_date']?:'—'))?></small></td><td><span class="badge"><?=e(module_status((string)$row['status']))?></span></td><td><a class="btn ghost" href="<?=e(module_management_url(['view'=>'equipment','manage_id'=>(int)$row['id'],'page'=>$page,'per_page'=>$perPage,'branch_id'=>$branchId]))?>">Manage</a></td><?php endif;?></tr><?php endforeach;if(!$rows):?><tr><td colspan="6" class="muted">No records match the current filters.</td></tr><?php endif;?></tbody></table></div><?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','sterilization-list');?></div>

<?php if($managed):?><dialog id="sterilization-manage" class="wide-dialog" data-auto-open-dialog="sterilization-manage"><form method="dialog"><button type="submit" class="dialog-close">×</button></form><div class="dialog-scroll">
<?php if($view==='sets'):?><h2><?=e((string)$managed['set_code'])?> · <?=e((string)$managed['name'])?></h2><p><span class="badge"><?=e(module_status((string)$managed['status']))?></span></p><div class="notice info"><?=nl2br(e((string)$managed['contents']))?></div><?php if(in_array($managed['status'],['dirty','quarantined'],true)):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="prepare_set"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label class="wide">Cleaning, inspection, and packaging note<input name="note" required></label><button class="btn primary">Mark prepared</button></form><?php endif;?><?php if(in_array($role,['super_admin','clinic_admin','inventory_officer'],true)&&in_array($managed['status'],['dirty','prepared','quarantined'],true)):?><hr><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="retire_set"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label class="wide">Retirement reason<input name="reason" required></label><button class="btn danger">Retire set</button></form><?php endif;?>
<?php elseif($view==='batches'):?><h2><?=e((string)$managed['sterilizer_name'])?> · <?=e((string)$managed['cycle_no'])?></h2><p>Load <?=e((string)$managed['load_no'])?> · <span class="badge"><?=e(module_status((string)$managed['cycle_status']))?></span></p><div class="detail-grid"><div><small>Sets</small><strong><?php foreach($managedSets as $setRow):?><?=e((string)$setRow['set_code'])?> <?=e((string)$setRow['name'])?><br><?php endforeach;?></strong></div><div><small>Operator</small><strong><?=e((string)($managed['operator']?:'—'))?></strong></div><div><small>Physical parameters</small><strong><?=e((string)($managed['temperature_c']?:'—'))?> °C · <?=e((string)($managed['pressure_kpa']?:'—'))?> kPa · <?=e((string)($managed['exposure_minutes']?:'—'))?> min</strong></div><div><small>Indicators</small><strong>Chemical <?=e(module_status((string)$managed['chemical_indicator_result']))?> · Biological <?=e(module_status((string)$managed['biological_indicator_result']))?></strong></div></div><?php if($managed['cycle_status']==='loaded'):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><button name="action" value="start" class="btn primary">Start cycle</button><label class="wide">Cancellation reason<input name="reason"></label><button name="action" value="cancel_batch" class="btn danger">Cancel unstarted load</button></form><?php elseif($managed['cycle_status']==='running'):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="complete"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label>Temperature °C<input type="number" step="0.01" name="temperature_c" required></label><label>Pressure kPa<input type="number" step="0.01" name="pressure_kpa" required></label><label>Exposure minutes<input type="number" min="1" name="exposure_minutes" required></label><label>Chemical indicator<select name="chemical_indicator_result"><option value="pass">Pass</option><option value="fail">Fail</option></select></label><label>Biological result<select name="biological_indicator_result"><option value="pending">Pending</option><option value="pass">Pass</option><option value="fail">Fail</option></select></label><label>Failure reason<input name="failure_reason"></label><label class="wide">Cycle note<textarea name="notes"></textarea></label><button class="btn primary">Complete cycle</button></form><?php elseif($managed['cycle_status']==='completed'&&(string)$managed['chemical_indicator_result']==='pass'&&(int)$managed['biological_indicator_required']===1&&(string)$managed['biological_indicator_result']==='pending'):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="biological_result"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label>Biological result<select name="result"><option value="pass">Pass</option><option value="fail">Fail</option></select></label><label class="wide">Test reference and note<input name="note" required></label><button class="btn primary">Record biological result</button></form><?php endif;?><?php if($managed['cycle_status']==='completed'&&$managed['chemical_indicator_result']==='pass'&&((int)$managed['biological_indicator_required']!==1||$managed['biological_indicator_result']==='pass')&&$canRelease&&(int)$managed['operator_user_id']!==$userId&&(int)$managed['completed_by']!==$userId):?><hr><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="release"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label class="wide">Independent release review<input name="release_note" required></label><button class="btn primary">Release load</button></form><?php endif;?>
<?php elseif($view==='usage'):?><h2><?=e((string)$managed['set_code'])?> · <?=e((string)$managed['set_name'])?></h2><p><strong><?=e((string)$managed['last_name'].', '.(string)$managed['first_name'])?></strong> · <?=e((string)$managed['patient_no'])?></p><div class="detail-grid"><div><small>Assigned</small><strong><?=e(date('M j, Y g:i A',strtotime((string)$managed['assigned_at'])))?></strong></div><div><small>Procedure</small><strong><?=e((string)($managed['procedure_name']?:'Encounter/general use'))?></strong></div><div><small>Assigned by</small><strong><?=e((string)($managed['assigned_by_name']?:'—'))?></strong></div><div><small>Returned</small><strong><?=$managed['returned_at']?e(date('M j, Y g:i A',strtotime((string)$managed['returned_at']))):'Not returned'?></strong></div></div><?php if(!$managed['returned_at']):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="return_set"><input type="hidden" name="usage_id" value="<?=(int)$managed['id']?>"><label class="wide">Chairside return / decontamination handoff<input name="note" required></label><button class="btn primary">Return dirty</button></form><?php endif;?>
<?php else:?><h2><?=e((string)$managed['name'])?></h2><p><?=e((string)($managed['serial_number']?:'No serial number'))?> · <span class="badge"><?=e(module_status((string)$managed['status']))?></span></p><?php if(in_array($role,['super_admin','clinic_admin'],true)):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="equipment_status"><input type="hidden" name="id" value="<?=(int)$managed['id']?>"><label>Status<select name="status"><?php foreach(['active'=>'Active','maintenance'=>'Maintenance','out_of_service'=>'Out of service','retired'=>'Retired'] as $value=>$label):?><option value="<?=e($value)?>" <?=$managed['status']===$value?'selected':''?>><?=e($label)?></option><?php endforeach;?></select></label><label>Last service<input type="date" name="last_service_date" value="<?=e((string)$managed['last_service_date'])?>"></label><label>Next service due<input type="date" name="next_service_date" value="<?=e((string)$managed['next_service_date'])?>"></label><label class="wide">Service/status reason<input name="service_note" required></label><button class="btn primary">Update equipment</button></form><?php endif;?><?php endif;?></div></dialog><?php endif;?>

<dialog id="equipment-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Add sterilizer equipment</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="equipment"><label>Branch<select name="branch_id" required><?php foreach($branches as $b):?><option value="<?=(int)$b['id']?>" <?=$branchId===(int)$b['id']?'selected':''?>><?=e((string)$b['name'])?></option><?php endforeach;?></select></label><label>Equipment name<input name="name" required></label><label>Serial number<input name="serial_number"></label><label>Manufacturer<input name="manufacturer"></label><label>Model<input name="model"></label><label>Last service<input type="date" name="last_service_date"></label><label>Next service due<input type="date" name="next_service_date"></label><button class="btn primary">Add equipment</button></form></dialog>
<dialog id="set-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Add instrument set</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="instrument_set"><label>Branch<select name="branch_id" required><?php foreach($branches as $b):?><option value="<?=(int)$b['id']?>" <?=$branchId===(int)$b['id']?'selected':''?>><?=e((string)$b['name'])?></option><?php endforeach;?></select></label><label>Set code<input name="set_code" required></label><label>Set name<input name="name" required></label><label class="wide">Complete contents<textarea name="contents" required></textarea></label><button class="btn primary">Add dirty set</button></form></dialog>
<dialog id="batch-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Create sterilization load</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="create_batch"><label>Branch<select name="branch_id" required><?php foreach($branches as $b):?><option value="<?=(int)$b['id']?>" <?=$branchId===(int)$b['id']?'selected':''?>><?=e((string)$b['name'])?></option><?php endforeach;?></select></label><label>Equipment<?php module_remote_record_picker('equipment_id','sterilization_equipment',$branchId,true,'Search equipment name or serial number');?></label><label>Cycle number<input name="cycle_no" required></label><label>Load number<input name="load_no" required></label><fieldset class="wide"><legend>Prepared instrument sets</legend><?php module_remote_multi_record_picker('instrument_set_ids[]','instrument_set',$branchId,true,'Search set code, name, or contents',['status'=>'prepared']);?></fieldset><label><input type="checkbox" name="biological_indicator_required"> Biological indicator required</label><label class="wide">Load notes<textarea name="notes"></textarea></label><button class="btn primary" <?=$preparedCount<1||$usableEquipmentCount<1?'disabled':''?>>Create loaded batch</button><?php if($preparedCount<1||$usableEquipmentCount<1):?><p class="muted wide"><?=$preparedCount<1?'Prepare at least one instrument set. ':''?><?=$usableEquipmentCount<1?'Record active equipment with a current service date.':''?></p><?php endif;?></form></dialog>
<?php if($sterileCount>0):?><dialog id="assign-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Assign released sterile set to patient</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="assign_set"><label>Instrument set<?php module_remote_record_picker('instrument_set_id','instrument_set',$branchId,true,'Search sterile set code or name',null,['status'=>'sterile']);?></label><label class="wide">Patient<?php module_remote_patient_picker('patient_id',null,true);?></label><label>Encounter<?php module_remote_record_picker('encounter_id','encounter',$branchId,false,'Search patient number or name');?></label><label>Procedure<?php module_remote_record_picker('procedure_id','procedure',$branchId,false,'Search patient number, name, or procedure');?></label><label class="wide">Use note<textarea name="notes"></textarea></label><button class="btn primary">Assign sterile set</button></form></dialog><?php endif;?>
<?php render_footer();
