<?php

declare(strict_types=1);

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

$pdo=db();
$user=current_user()??[];
$taskRole=active_role();
$actualRole=actual_role();
$role=$taskRole;
$canSchedule=task_context_action_allowed('followups.schedule')&&has_role(['super_admin','clinic_admin','receptionist','staff']);
$canRecommend=active_role_is('dentist')&&task_context_action_allowed('followups.recommend');
$canManageFollowupStatus=task_context_action_allowed('followups.schedule')&&has_role(['super_admin','clinic_admin','receptionist','staff']);
$canDeferFollowup=$canManageFollowupStatus;
$canCreateRecall=$canManageFollowupStatus;
$canManageRecallStatus=$canManageFollowupStatus;
$canRequestDiagnostic=task_context_action_allowed('diagnostic.request')&&has_role(['super_admin','clinic_admin','receptionist','staff','dental_assistant']);
$canValidateDiagnostic=active_role_is('dentist')&&task_context_action_allowed('diagnostic.validate');
$diagnosticReady=schema_table_exists(table('diagnostic_review_tasks'));
$canViewDiagnostic=$diagnosticReady&&has_role(['super_admin','clinic_admin','receptionist','staff','dental_assistant','dentist']);
$canViewRecalls=$role!=='cashier';
$patientId=(int)($_GET['patient_id']??0);
$contextEncounterId=(int)($_GET['encounter_id']??0);
$contextAppointmentId=(int)($_GET['appointment_id']??0);
$view=(string)($_GET['view']??'followups');
if(!in_array($view,['followups','recalls','diagnostic'],true))$view='followups';
if($view==='diagnostic'&&!$canViewDiagnostic)$view='followups';
if($view==='recalls'&&!$canViewRecalls)$view='followups';
$scheduleId=(int)($_GET['schedule_id']??0);
$scheduleRecallId=(int)($_GET['schedule_recall_id']??0);
$error='';
$selectedPatient=$patientId>0?require_patient($patientId):null;
if($patientId>0&&!task_patient_scope_allowed($patientId))access_denied('This patient is outside the active task workspace.');
$branchId=(int)($user['branch_id']??0);
if($branchId<1)$branchId=(int)($pdo->query('SELECT id FROM `' . table('branches') . '` WHERE is_active=1 ORDER BY id LIMIT 1')->fetchColumn()?:0);
$dentists=dentist_options($branchId?:null);
$services=clinic_services_catalog($branchId?:null);

function followup_record(PDO $pdo,int $id): array
{
    $stmt=$pdo->prepare('SELECT f.*,p.first_name,p.last_name,p.patient_no,p.email FROM `' . table('follow_ups') . '` f JOIN `' . table('patients') . '` p ON p.id=f.patient_id WHERE f.id=?');$stmt->execute([$id]);$row=$stmt->fetch();if(!$row)throw new RuntimeException('Follow-up record not found.');require_patient((int)$row['patient_id']);if(!task_patient_scope_allowed((int)$row['patient_id']))throw new RuntimeException('This patient is outside the active task workspace.');if(active_role_is('dentist')&&(int)($row['dentist_user_id']??0)>0&&(int)$row['dentist_user_id']!==(int)(current_user()['id']??0))throw new RuntimeException('This follow-up is assigned to another dentist.');return $row;
}
function recall_record(PDO $pdo,int $id): array
{
    $stmt=$pdo->prepare('SELECT r.*,p.first_name,p.last_name,p.patient_no,p.email FROM `' . table('recalls') . '` r JOIN `' . table('patients') . '` p ON p.id=r.patient_id WHERE r.id=?');$stmt->execute([$id]);$row=$stmt->fetch();if(!$row)throw new RuntimeException('Recall record not found.');require_patient((int)$row['patient_id']);if(!task_patient_scope_allowed((int)$row['patient_id']))throw new RuntimeException('This patient is outside the active task workspace.');return $row;
}
function followup_find_doctor(array $dentists,int $id): ?array{foreach($dentists as $d)if((int)$d['id']===$id)return $d;return null;}
function followup_find_service(array $services,int $id): ?array{foreach($services as $s)if((int)$s['id']===$id)return $s;return null;}
function followup_validate_patient_relation(PDO $pdo,string $tableName,int $recordId,int $patientId,string $label): ?int
{
    if($recordId<1)return null;
    if(!schema_table_exists($tableName)||!schema_column_exists($tableName,'patient_id'))throw new RuntimeException($label.' linkage is not available until the required database update is applied.');
    $stmt=$pdo->prepare('SELECT id FROM `' . $tableName . '` WHERE id=? AND patient_id=? LIMIT 1');
    $stmt->execute([$recordId,$patientId]);
    if(!(int)$stmt->fetchColumn())throw new RuntimeException('The selected '.$label.' does not belong to this patient.');
    return $recordId;
}
function followup_create_appointment(PDO $pdo,array $source,string $sourceType,array $dentists,array $services,int $defaultBranch): array
{
    if(!schema_column_exists(table('appointments'),'chair_id'))throw new RuntimeException('Apply the v0.5.8.4 database update from System Setup before scheduling a follow-up or recall.');
    $patientId=(int)$source['patient_id'];
    $doctorId=(int)($_POST['dentist_user_id']??0);
    $serviceId=(int)($_POST['service_id']??0);
    $date=trim((string)($_POST['date']??''));
    $time=substr(trim((string)($_POST['time']??'')),0,5);
    $branch=(int)($_POST['branch_id']??$source['branch_id']??$defaultBranch);
    $service=followup_find_service($services,$serviceId);
    if(!$service||$date===''||$time===''||$branch<1)throw new RuntimeException('Choose a service, date, and available time.');
    $providerType=service_provider_type($service);
    if($providerType==='inhouse')$doctorId=0;
    $doctor=$providerType==='dentist'?followup_find_doctor($dentists,$doctorId):null;
    if($providerType==='dentist'&&!$doctor)throw new RuntimeException('Choose an available dentist for this follow-up service.');

    $transferRequested=$providerType==='dentist'&&patient_dentist_transfer_allowed()&&isset($_POST['change_continuity_dentist']);
    $transferReason=trim((string)($_POST['continuity_change_reason']??''));
    $bypassRequested=$providerType==='dentist'&&schedule_bypass_allowed()&&can('appointments.schedule_bypass',['super_admin','clinic_admin'])&&isset($_POST['schedule_bypass']);
    $bypassReason=trim((string)($_POST['schedule_bypass_reason']??''));
    if($bypassRequested){
        if($bypassReason==='')throw new RuntimeException('Enter the schedule bypass reason.');
        $manualTime=substr(trim((string)($_POST['bypass_time']??'')),0,5);
        $manualDoctor=(int)($_POST['bypass_dentist_user_id']??0);
        if(!preg_match('/^(?:[01]\d|2[0-3]):[0-5]\d$/',$manualTime)||$manualDoctor<1)throw new RuntimeException('Choose the bypass dentist and manual time.');
        $time=$manualTime;$doctorId=$manualDoctor;$doctor=followup_find_doctor($dentists,$doctorId);
        if(!$doctor)throw new RuntimeException('The selected bypass dentist is unavailable at this branch.');
    }
    if($providerType==='dentist')validate_patient_dentist_continuity($patientId,$doctorId,$transferRequested,$transferReason);

    $continuityDoctor=patient_primary_dentist($patientId);
    $preferredDate=(string)($source['recommended_date']??$source['due_date']??'');
    $preferredTime=substr((string)($source['preferred_time']??''),0,5);
    $preferredDoctor=(int)($source['dentist_user_id']??($continuityDoctor['id']??0));
    $scheduleChanged=($preferredDate!==''&&$date!==$preferredDate)||($preferredTime!==''&&$time!==$preferredTime)||($providerType==='dentist'&&$preferredDoctor>0&&$doctorId!==$preferredDoctor);
    $rescheduleNote=trim((string)($_POST['reschedule_note']??''));
    if($scheduleChanged&&$rescheduleNote==='')throw new RuntimeException('Enter a reschedule note for the changed follow-up schedule.');
    if(!validate_service_slot($branch,$service,$date,$time,$doctorId)&&!$bypassRequested)throw new RuntimeException($providerType==='inhouse'?'The selected in-house follow-up time is no longer available. Choose another time.':'The selected follow-up time is no longer available. Choose another time or ask an Administrator to use Schedule Bypass.');

    $start=$date.' '.$time.':00';
    $end=date('Y-m-d H:i:s',strtotime($start.' +'.(int)$service['duration_minutes'].' minutes'));
    $chair='';$chairId=0;$inhouseProviderId=0;
    $providerName=$providerType==='dentist'?(string)($doctor['display_name']??$doctor['name']):'In-house clinic team';
    if($providerType==='dentist'){
        $binding=doctor_chair_binding($doctorId,$branch);
        if(!$binding['valid'])throw new RuntimeException((string)$binding['issue'].' Assign a unique chair before scheduling this follow-up.');
        $chair=(string)$binding['chair_name'];$chairId=(int)($binding['chair_id']??0);
    }else{
        if(!inhouse_resource_schema_ready())throw new RuntimeException('Apply Version 0.5.8.4 in System Setup before scheduling an in-house follow-up.');
        $assignment=inhouse_slot_assignment($branch,$date,$time,$service,0,(int)($_POST['inhouse_provider_user_id']??0),(int)($_POST['chair_id']??0));
        if(empty($assignment['valid']))throw new RuntimeException((string)($assignment['issue']??'No in-house resource is available.'));
        $inhouseProviderId=(int)$assignment['provider_id'];$providerName=(string)$assignment['provider_name'];$chairId=(int)$assignment['chair_id'];$chair=(string)$assignment['chair_name'];
    }
    $resourceConflicts=appointment_resource_conflicts($branch,$patientId,$doctorId,$chair,$start,$end,0,(int)$service['preparation_minutes'],(int)$service['cleanup_minutes'],$inhouseProviderId,$chairId);
    if($resourceConflicts['has_conflict'])throw new RuntimeException(appointment_conflict_message($resourceConflicts));

    $reason=$sourceType==='follow_up'?(string)$source['purpose']:(string)$source['recall_type'];
    $origin=$sourceType==='follow_up'?'clinical_followup':'recall';
    $followId=$sourceType==='follow_up'?(int)$source['id']:null;
    $appointmentNotes='Scheduled from '.str_replace('_',' ',$sourceType).' #'.$source['id'].($rescheduleNote!==''?"\nReschedule note: ".$rescheduleNote:'').($bypassRequested?"\nSchedule bypass: ".$bypassReason:'');
    $startedTransaction=!$pdo->inTransaction();
    if($startedTransaction)$pdo->beginTransaction();
    try{
        $pdo->prepare('INSERT INTO `' . table('appointments') . '` (branch_id,patient_id,service_id,dentist_user_id,dentist_name,inhouse_provider_user_id,inhouse_provider_name,service_name,provider_type,start_at,end_at,chair_id,chair_name,status,appointment_origin,follow_up_id,reason_for_visit,confirmation_status,schedule_bypass,schedule_bypass_reason,schedule_bypass_by,schedule_bypass_at,notes,created_by,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,"confirmed",?,?,?,?,?,?,?,?,?,?,NOW())')->execute([
            $branch,$patientId,$serviceId,$providerType==='dentist'?$doctorId:null,$providerType==='dentist'?$providerName:null,$inhouseProviderId?:null,$providerType==='inhouse'?$providerName:null,$service['name'],$providerType,$start,$end,$chairId?:null,$chair?:null,$origin,$followId,$reason,'confirmed',$bypassRequested?1:0,$bypassReason?:null,$bypassRequested?(int)(current_user()['id']??0):null,$bypassRequested?date('Y-m-d H:i:s'):null,$appointmentNotes,current_user()['id']??null
        ]);
        $appointmentId=(int)$pdo->lastInsertId();
        if($providerType==='dentist')commit_patient_dentist_continuity($patientId,$doctorId,$transferRequested,$transferReason);
        $pdo->prepare('INSERT INTO `' . table('appointment_status_history') . '` (appointment_id,previous_status,new_status,reason,changed_by,created_at) VALUES (?,NULL,"confirmed",?,?,NOW())')->execute([$appointmentId,'Scheduled from '.str_replace('_',' ',$sourceType),current_user()['id']??null]);
        if($sourceType==='follow_up')$pdo->prepare('UPDATE `' . table('follow_ups') . '` SET branch_id=?,service_id=?,dentist_user_id=?,recommended_date=?,preferred_time=?,duration_minutes=?,status="scheduled",appointment_id=?,scheduled_at=NOW(),scheduled_by=? WHERE id=?')->execute([$branch,$serviceId,$providerType==='dentist'?$doctorId:null,$date,$time,(int)$service['duration_minutes'],$appointmentId,current_user()['id']??null,(int)$source['id']]);
        else $pdo->prepare('UPDATE `' . table('recalls') . '` SET status="scheduled",appointment_id=? WHERE id=?')->execute([$appointmentId,(int)$source['id']]);
        workflow_event($patientId,'follow_up_scheduled',$sourceType==='follow_up'?'Follow-up appointment scheduled':'Recall appointment scheduled',['appointment_id'=>$appointmentId,'follow_up_id'=>$followId],date('M j, Y g:i A',strtotime($start)).' · '.$service['name'].' · '.$providerName);
        if($startedTransaction)$pdo->commit();
    }catch(Throwable $transactionError){if($startedTransaction&&$pdo->inTransaction())$pdo->rollBack();throw $transactionError;}
    $delivery=['id'=>0,'status'=>'not_available','error'=>'No valid patient email address.','attempts'=>0];
    if(filter_var((string)($source['email']??''),FILTER_VALIDATE_EMAIL)){
        $calendarUrl=app_absolute_url(appointment_calendar_relative_url(['id'=>$appointmentId,'patient_id'=>$patientId,'start_at'=>$start],true));
        try{
            $delivery=queue_template_email('appointment_confirmed',(string)$source['email'],[
                'recipient_name'=>(string)$source['first_name'],'appointment_datetime'=>date('F j, Y g:i A',strtotime($start)),
                'service_name'=>(string)$service['name'],'provider_name'=>$providerName,
                'branch_name'=>(string)(public_clinic_profile()['name']??'Dental clinic'),'calendar_url'=>$calendarUrl,
            ],['type'=>'follow_up_confirmed','appointment_id'=>$appointmentId,'source_type'=>$sourceType],true);
        }catch(Throwable $mailError){$delivery=['id'=>0,'status'=>'failed','error'=>$mailError->getMessage(),'attempts'=>0];error_log('Follow-up confirmation email could not be queued: '.$mailError->getMessage());}
    }
    return ['appointment_id'=>$appointmentId,'delivery'=>$delivery];
}


if($_SERVER['REQUEST_METHOD']==='POST'){
    try{
        verify_csrf();
        $action=(string)($_POST['action']??'');
        if($action==='schedule_followup'&&!$canSchedule)throw new RuntimeException('Only the Receptionist, Cashier, or Administrator can book a Dentist-recommended follow-up.');
        if($action==='schedule_recall'&&!$canCreateRecall)throw new RuntimeException('Only the Receptionist or Administrator can schedule a recall appointment.');
        if($action==='followup_status'&&!$canManageFollowupStatus)throw new RuntimeException('Only the Receptionist or Administrator can update follow-up status manually.');
        if($action==='followup_defer'&&!$canDeferFollowup)throw new RuntimeException('You cannot defer follow-up booking in this workspace.');
        if($action==='recall_status'&&!$canManageRecallStatus)throw new RuntimeException('Only the Receptionist or Administrator can update recall status.');
        if($action==='create_followup'){
            if(!$canRecommend)throw new RuntimeException('Only the assigned authenticated Dentist can create a clinical follow-up recommendation.');
            $patientId=(int)($_POST['patient_id']??0);require_patient($patientId);
            if(!task_patient_scope_allowed($patientId))throw new RuntimeException('This patient is outside the active task workspace.');
            $dentistId=(int)(current_user()['id']??0);
            $purpose=trim((string)($_POST['purpose']??''));$recommended=trim((string)($_POST['recommended_date']??''));
            $maximumDate=trim((string)($_POST['maximum_date']??''));
            if($purpose===''||$recommended==='')throw new RuntimeException('Enter the clinical purpose and recommended date.');
            if($recommended<date('Y-m-d'))throw new RuntimeException('The recommended follow-up date cannot be in the past.');
            if($maximumDate!==''&&$maximumDate<$recommended)throw new RuntimeException('The maximum acceptable date cannot be earlier than the recommended date.');
            $encounterId=followup_validate_patient_relation($pdo,table('clinical_encounters'),(int)($_POST['encounter_id']??0),$patientId,'clinical encounter');
            $pdo->prepare('INSERT INTO `' . table('follow_ups') . '` (patient_id,branch_id,service_id,encounter_id,dentist_user_id,purpose,recommended_date,preferred_time,duration_minutes,maximum_date,urgency,status,instructions,appointment_id,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,"pending",?,?,NOW())')->execute([
                $patientId,(int)($_POST['branch_id']??$branchId)?:null,(int)($_POST['service_id']??0)?:null,$encounterId,$dentistId,$purpose,$recommended,($_POST['preferred_time']??'')?:null,max(15,(int)($_POST['duration_minutes']??30)),$maximumDate?:null,in_array(($_POST['urgency']??''),['routine','important','urgent'],true)?$_POST['urgency']:'routine',trim((string)($_POST['instructions']??''))?:null,null
            ]);
            $id=(int)$pdo->lastInsertId();
            workflow_event($patientId,'follow_up_created','Dentist follow-up recommendation',['follow_up_id'=>$id],$purpose.' · '.$recommended.' · awaiting Receptionist/Cashier booking');
            audit('follow_up_created','follow_up',$id,['patient_id'=>$patientId,'dentist_user_id'=>$dentistId]);
            flash('success','Follow-up recommendation saved. Receptionist or Cashier can now book the appointment.');
            header('Location: followups.php?view=followups');exit;
        }
        if($action==='create_recall'){
            if(!$canCreateRecall)throw new RuntimeException('Only the Receptionist or Administrator can create a recall schedule.');
            $patientId=(int)($_POST['patient_id']??0);require_patient($patientId);
            if(!task_patient_scope_allowed($patientId))throw new RuntimeException('This patient is outside the active task workspace.');
            $due=trim((string)($_POST['due_date']??''));if($due==='')throw new RuntimeException('Enter the recall due date.');
            $status=$due<date('Y-m-d')?'overdue':($due<=date('Y-m-d',strtotime('+30 days'))?'due_soon':'future');
            $pdo->prepare('INSERT INTO `' . table('recalls') . '` (patient_id,recall_type,due_date,status,notes,created_at) VALUES (?,?,?,?,?,NOW())')->execute([$patientId,trim((string)($_POST['recall_type']??'Dental recall')),$due,$status,trim((string)($_POST['notes']??''))?:null]);
            $id=(int)$pdo->lastInsertId();workflow_event($patientId,'recall_created','Recall scheduled for future contact',[],trim((string)($_POST['recall_type']??'Dental recall')).' · '.$due);audit('recall_created','recall',$id,['patient_id'=>$patientId]);flash('success','Recall created.');
        }
        elseif($action==='schedule_followup'){$source=followup_record($pdo,(int)$_POST['id']);$scheduled=followup_create_appointment($pdo,$source,'follow_up',$dentists,$services,$branchId);$appointmentId=(int)$scheduled['appointment_id'];audit('follow_up_appointment_scheduled','follow_up',(int)$source['id'],['appointment_id'=>$appointmentId,'scheduled_by_role'=>$role]);$feedback=email_delivery_feedback((array)$scheduled['delivery'],'Follow-up confirmation');flash($feedback['type'],'Follow-up appointment confirmed. '.$feedback['message']);}
        elseif($action==='schedule_recall'){$source=recall_record($pdo,(int)$_POST['id']);$scheduled=followup_create_appointment($pdo,$source,'recall',$dentists,$services,$branchId);$appointmentId=(int)$scheduled['appointment_id'];audit('recall_appointment_scheduled','recall',(int)$source['id'],['appointment_id'=>$appointmentId,'scheduled_by_role'=>$role]);$feedback=email_delivery_feedback((array)$scheduled['delivery'],'Recall confirmation');flash($feedback['type'],'Recall appointment confirmed. '.$feedback['message']);}
        elseif($action==='followup_status'){$id=(int)$_POST['id'];followup_record($pdo,$id);$status=(string)($_POST['status']??'pending');if(!in_array($status,['pending','scheduled','completed','deferred','cancelled'],true))throw new RuntimeException('Invalid follow-up status.');$pdo->prepare('UPDATE `' . table('follow_ups') . '` SET status=?,completion_notes=? WHERE id=?')->execute([$status,trim((string)($_POST['completion_notes']??''))?:null,$id]);audit('follow_up_status_updated','follow_up',$id,['status'=>$status]);flash('success','Follow-up updated.');}
        elseif($action==='followup_defer'){$id=(int)$_POST['id'];$row=followup_record($pdo,$id);if(!empty($row['appointment_id'])||(string)$row['status']!=='pending')throw new RuntimeException('Only an unscheduled pending follow-up can be deferred.');$note=trim((string)($_POST['completion_notes']??''));if(mb_strlen($note)<5)throw new RuntimeException('Document why the appointment will be scheduled later.');$pdo->prepare('UPDATE `' . table('follow_ups') . '` SET status="deferred",completion_notes=? WHERE id=? AND status="pending" AND appointment_id IS NULL')->execute([$note,$id]);audit('follow_up_booking_deferred','follow_up',$id,['reason'=>$note,'deferred_by_role'=>$role]);flash('success','Follow-up booking deferred with a documented reason. The visit may continue, and the recommendation remains in the booking list.');}
        elseif($action==='recall_status'){$id=(int)$_POST['id'];recall_record($pdo,$id);$status=(string)($_POST['status']??'future');$pdo->prepare('UPDATE `' . table('recalls') . '` SET status=?,contacted_at=IF(?="contacted",NOW(),contacted_at) WHERE id=?')->execute([$status,$status,$id]);audit('recall_status_updated','recall',$id,['status'=>$status]);flash('success','Recall updated.');}
        elseif($action==='create_diagnostic_review'){
            if(!$canRequestDiagnostic)throw new RuntimeException('You cannot create a Dentist diagnostic-review task in this workspace.');
            if(!$diagnosticReady)throw new RuntimeException('Apply Version 0.5.9.8.5 before creating diagnostic-review tasks.');
            $patientId=(int)($_POST['patient_id']??0);$patient=require_patient($patientId);
            if(!task_patient_scope_allowed($patientId))throw new RuntimeException('This patient is outside the active task workspace.');
            $patientBranch=(int)($patient['branch_id']??0)?:$branchId;
            $primary=patient_primary_dentist($patientId);$dentistId=(int)($_POST['assigned_dentist_user_id']??0)?:((int)($primary['id']??0));
            $eligibleDentists=dentist_options($patientBranch?:null);$validDentist=false;foreach($eligibleDentists as $dentist){if((int)$dentist['id']===$dentistId){$validDentist=true;break;}}
            if(!$validDentist)throw new RuntimeException('Assign an active Dentist authorized for this patient’s branch before sending the diagnostic-review reason.');
            $reason=trim((string)($_POST['reason']??''));if(mb_strlen($reason)<5)throw new RuntimeException('Describe the patient-reported concern or observation for Dentist review.');
            $priority=in_array(($_POST['priority']??''),['routine','important','urgent'],true)?$_POST['priority']:'routine';
            $encounterId=followup_validate_patient_relation($pdo,table('clinical_encounters'),(int)($_POST['encounter_id']??0),$patientId,'clinical encounter');
            $appointmentId=followup_validate_patient_relation($pdo,table('appointments'),(int)($_POST['appointment_id']??0),$patientId,'appointment');
            $pdo->prepare('INSERT INTO `' . table('diagnostic_review_tasks') . '` (patient_id,branch_id,encounter_id,appointment_id,assigned_dentist_user_id,reason,priority,status,source_role,created_by,created_at,updated_at) VALUES (?,?,?,?,?,?,?,"pending",?,?,NOW(),NOW())')->execute([$patientId,$patientBranch?:null,$encounterId,$appointmentId,$dentistId,$reason,$priority,$role,(int)(current_user()['id']??0)?:null]);
            $id=(int)$pdo->lastInsertId();workflow_event($patientId,'diagnostic_review_requested','Dentist diagnostic review requested',[],$reason);audit('diagnostic_review_requested','diagnostic_review_task',$id,['patient_id'=>$patientId,'assigned_dentist_user_id'=>$dentistId,'priority'=>$priority]);flash('success','Diagnostic-review reason sent to the assigned Dentist’s To-Do Tasks.');
        }
        elseif($action==='diagnostic_review_status'){
            if(!$canValidateDiagnostic)throw new RuntimeException('Only the assigned authenticated Dentist can validate this diagnostic-review task.');
            if(!$diagnosticReady)throw new RuntimeException('Apply Version 0.5.9.8.5 first.');
            $id=(int)($_POST['id']??0);$stmt=$pdo->prepare('SELECT * FROM `' . table('diagnostic_review_tasks') . '` WHERE id=? LIMIT 1');$stmt->execute([$id]);$task=$stmt->fetch();if(!$task)throw new RuntimeException('Diagnostic-review task not found.');
            if((int)$task['assigned_dentist_user_id']!==(int)(current_user()['id']??0))throw new RuntimeException('This task is assigned to another Dentist.');
            if(!task_patient_scope_allowed((int)$task['patient_id']))throw new RuntimeException('This patient is outside your assigned diagnostic-review scope.');
            if(!in_array((string)$task['status'],['pending','in_review'],true))throw new RuntimeException('This diagnostic-review task is already complete.');
            $status=(string)($_POST['status']??'in_review');if(!in_array($status,['in_review','validated','closed'],true))throw new RuntimeException('Invalid diagnostic-review status.');
            $notes=trim((string)($_POST['dentist_notes']??''));if(in_array($status,['validated','closed'],true)&&mb_strlen($notes)<3)throw new RuntimeException('Enter the Dentist validation or closure note.');
            $update=$pdo->prepare('UPDATE `' . table('diagnostic_review_tasks') . '` SET status=?,dentist_notes=?,validated_by=IF(? IN ("validated","closed"),?,validated_by),validated_at=IF(? IN ("validated","closed"),NOW(),validated_at),closed_at=IF(?="closed",NOW(),closed_at),updated_at=NOW() WHERE id=? AND assigned_dentist_user_id=? AND status IN ("pending","in_review")');
            $update->execute([$status,$notes?:null,$status,(int)current_user()['id'],$status,$status,$id,(int)current_user()['id']]);
            if($update->rowCount()!==1)throw new RuntimeException('The diagnostic-review task changed before your update was saved.');
            workflow_event((int)$task['patient_id'],'diagnostic_review_'.$status,'Dentist diagnostic review '.str_replace('_',' ',$status),[],$notes?:$task['reason']);audit('diagnostic_review_updated','diagnostic_review_task',$id,['status'=>$status]);flash('success','Diagnostic-review task updated.');
        }
        elseif(!in_array($action,['create_followup','create_recall'],true))throw new RuntimeException('Unsupported follow-up action.');
        header('Location: followups.php?view='.urlencode($view));exit;
    }catch(Throwable $exception){$error=$exception->getMessage();}
}

$q=trim((string)($_GET['q']??''));$statusFilter=trim((string)($_GET['status']??''));$page=module_page_number();$perPage=module_page_size('per_page',25);$rowsTotal=0;$followups=[];$recalls=[];$diagnosticTasks=[];
if($view==='recalls'){
    [$scope,$scopeParams]=task_patient_scope_condition('r.patient_id');$where=' WHERE 1=1'.$scope;$args=$scopeParams;
    if($patientId){$where.=' AND r.patient_id=?';$args[]=$patientId;}if($statusFilter!==''){$where.=' AND r.status=?';$args[]=$statusFilter;}if($q!==''){$like='%'.$q.'%';$where.=' AND (p.patient_no LIKE ? OR p.first_name LIKE ? OR p.last_name LIKE ? OR r.recall_type LIKE ? OR r.notes LIKE ?)';array_push($args,$like,$like,$like,$like,$like);}
    $from=' FROM `' . table('recalls') . '` r JOIN `' . table('patients') . '` p ON p.id=r.patient_id LEFT JOIN `' . table('appointments') . '` a ON a.id=r.appointment_id';$countStmt=$pdo->prepare('SELECT COUNT(*)'.$from.$where);$countStmt->execute($args);$rowsTotal=(int)$countStmt->fetchColumn();$offset=(max(1,$page)-1)*$perPage;$stmt=$pdo->prepare('SELECT r.*,CONCAT(p.first_name," ",p.last_name) patient,p.patient_no,a.start_at appointment_start'.$from.$where.' ORDER BY r.due_date,r.id DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($args);$recalls=$stmt->fetchAll();
}elseif($view==='diagnostic'&&$diagnosticReady){
    [$scope,$scopeParams]=task_patient_scope_condition('d.patient_id');$where=' WHERE 1=1'.$scope;$args=$scopeParams;
    if(active_role_is('dentist')){$where.=' AND d.assigned_dentist_user_id=?';$args[]=(int)(current_user()['id']??0);}if($patientId){$where.=' AND d.patient_id=?';$args[]=$patientId;}if($statusFilter!==''){$where.=' AND d.status=?';$args[]=$statusFilter;}if($q!==''){$like='%'.$q.'%';$where.=' AND (p.patient_no LIKE ? OR p.first_name LIKE ? OR p.last_name LIKE ? OR d.reason LIKE ? OR u.name LIKE ?)';array_push($args,$like,$like,$like,$like,$like);}
    $from=' FROM `' . table('diagnostic_review_tasks') . '` d JOIN `' . table('patients') . '` p ON p.id=d.patient_id LEFT JOIN `' . table('users') . '` u ON u.id=d.assigned_dentist_user_id LEFT JOIN `' . table('users') . '` c ON c.id=d.created_by';$countStmt=$pdo->prepare('SELECT COUNT(*)'.$from.$where);$countStmt->execute($args);$rowsTotal=(int)$countStmt->fetchColumn();$offset=(max(1,$page)-1)*$perPage;$stmt=$pdo->prepare('SELECT d.*,CONCAT(p.first_name," ",p.last_name) patient,p.patient_no,u.name dentist,c.name created_by_name'.$from.$where.' ORDER BY FIELD(d.status,"pending","in_review","validated","closed","cancelled"),FIELD(d.priority,"urgent","important","routine"),d.created_at DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($args);$diagnosticTasks=$stmt->fetchAll();
}else{
    [$scope,$scopeParams]=task_patient_scope_condition('f.patient_id');$where=' WHERE 1=1'.$scope;$args=$scopeParams;
    if($patientId){$where.=' AND f.patient_id=?';$args[]=$patientId;}if($statusFilter!==''){$where.=' AND f.status=?';$args[]=$statusFilter;}if($q!==''){$like='%'.$q.'%';$where.=' AND (p.patient_no LIKE ? OR p.first_name LIKE ? OR p.last_name LIKE ? OR f.purpose LIKE ? OR f.instructions LIKE ? OR u.name LIKE ? OR s.name LIKE ?)';array_push($args,$like,$like,$like,$like,$like,$like,$like);}
    $from=' FROM `' . table('follow_ups') . '` f JOIN `' . table('patients') . '` p ON p.id=f.patient_id LEFT JOIN `' . table('users') . '` u ON u.id=f.dentist_user_id LEFT JOIN `' . table('appointments') . '` a ON a.id=f.appointment_id LEFT JOIN `' . table('service_catalog') . '` s ON s.id=f.service_id';$countStmt=$pdo->prepare('SELECT COUNT(*)'.$from.$where);$countStmt->execute($args);$rowsTotal=(int)$countStmt->fetchColumn();$offset=(max(1,$page)-1)*$perPage;$stmt=$pdo->prepare('SELECT f.*,CONCAT(p.first_name," ",p.last_name) patient,p.patient_no,u.name dentist,a.start_at appointment_start,a.status appointment_status,s.name service_name'.$from.$where.' ORDER BY FIELD(f.status,"pending","scheduled","deferred","completed","cancelled"),f.recommended_date,f.id DESC LIMIT '.$perPage.' OFFSET '.$offset);$stmt->execute($args);$followups=$stmt->fetchAll();
}
$manageId=max(0,(int)($_GET['manage_id']??0));
$managedRow=null;
$managedRows=$view==='diagnostic'?$diagnosticTasks:($view==='recalls'?$recalls:$followups);
foreach($managedRows as $candidate){
    if((int)$candidate['id']===$manageId){$managedRow=$candidate;break;}
}
$scheduleSource=null;$scheduleType=null;$scheduleContinuityDoctor=null;
try{if($scheduleId){$scheduleSource=followup_record($pdo,$scheduleId);$scheduleType='follow_up';}elseif($scheduleRecallId){$scheduleSource=recall_record($pdo,$scheduleRecallId);$scheduleType='recall';}if($scheduleSource){$scheduleContinuityDoctor=patient_primary_dentist((int)$scheduleSource['patient_id']);if(empty($scheduleSource['dentist_user_id'])&&$scheduleContinuityDoctor)$scheduleSource['dentist_user_id']=(int)$scheduleContinuityDoctor['id'];}}catch(Throwable $e){$error=$e->getMessage();}
render_header('Follow-Ups, Recalls & Dentist Review');
?>
<?php if($error):?><div class="flash error"><?=e($error)?></div><?php endif;?>
<div class="toolbar"><div class="tabs"><a class="<?=$view==='followups'?'active':''?>" href="followups.php?view=followups">Follow-Ups</a><?php if($canViewRecalls):?><a class="<?=$view==='recalls'?'active':''?>" href="followups.php?view=recalls">Recalls</a><?php endif;?><?php if($canViewDiagnostic):?><a class="<?=$view==='diagnostic'?'active':''?>" href="followups.php?view=diagnostic">Dentist Review Tasks</a><?php endif;?></div><div class="status-actions"><?php if($canRecommend):?><button class="btn primary" data-open="followup-form">Recommend follow-up</button><?php endif;?><?php if($canCreateRecall):?><button class="btn ghost" data-open="recall-form">New recall</button><?php endif;?><?php if($canRequestDiagnostic):?><button class="btn ghost" data-open="diagnostic-form">Send reason to Dentist</button><?php endif;?></div></div>
<form method="get" class="card compact-filter" data-auto-submit><input type="hidden" name="view" value="<?=e($view)?>"><label>Search<input type="search" name="q" value="<?=e($q)?>" placeholder="Patient, reason, service, or Dentist" data-auto-search data-min-length="2"></label><label>Status<select name="status"><option value="">All statuses</option><?php $filterStatuses=$view==='recalls'?['future','due_soon','due','overdue','contacted','scheduled','completed','deferred','declined','unable_to_contact']:($view==='diagnostic'?['pending','in_review','validated','closed','cancelled']:['pending','scheduled','completed','deferred','cancelled']);foreach($filterStatuses as $filterStatus):?><option value="<?=e($filterStatus)?>" <?=$statusFilter===$filterStatus?'selected':''?>><?=e(module_status($filterStatus))?></option><?php endforeach;?></select></label></form>
<?php if($scheduleSource&&(($scheduleType==='recall'&&$canCreateRecall)||($scheduleType==='follow_up'&&$canSchedule))):?><section class="card"><div class="card-head"><div><span class="eyebrow"><?=e(ucwords(str_replace('_',' ',$scheduleType)))?> scheduler</span><h2><?=e((string)$scheduleSource['first_name'].' '.(string)$scheduleSource['last_name'])?></h2><p><?=e((string)($scheduleSource['purpose']??$scheduleSource['recall_type']))?> · Recommended <?=e(date('M j, Y',strtotime((string)($scheduleSource['recommended_date']??$scheduleSource['due_date']))))?></p></div><a href="followups.php?view=<?=$scheduleType==='recall'?'recalls':'followups'?>">Close</a></div><form method="post" class="form-grid" data-slot-picker data-schedule-first data-branch-id="<?=$branchId?>" data-preferred-date="<?=e((string)($scheduleSource['recommended_date']??$scheduleSource['due_date']))?>" data-preferred-time="<?=e(substr((string)($scheduleSource['preferred_time']??''),0,5))?>" data-preferred-doctor="<?=e((string)($scheduleSource['dentist_user_id']??''))?>"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="<?=$scheduleType==='recall'?'schedule_recall':'schedule_followup'?>"><input type="hidden" name="id" value="<?=(int)$scheduleSource['id']?>"><input type="hidden" name="branch_id" value="<?=$branchId?>"><label>Service<select name="service_id" data-slot-service required><option value="">Choose service</option><?php foreach($services as $service):?><option value="<?=(int)$service['id']?>" data-provider-type="<?=e(service_provider_type($service))?>" data-requires-chair="<?=(int)($service['requires_chair']??1)?>" <?=(int)($scheduleSource['service_id']??0)===(int)$service['id']?'selected':''?>><?=e($service['name'].' · '.$service['duration_minutes'].' min')?></option><?php endforeach;?></select></label><label>Date<input type="date" name="date" data-slot-date min="<?=date('Y-m-d')?>" value="<?=e((string)($scheduleSource['recommended_date']??$scheduleSource['due_date']))?>" required></label><div class="available-dates-panel" data-available-dates>Choose the service to see the next available dates.</div><label>Available time<select name="time" data-slot-time required><option value="">Choose service and date</option></select></label><label>Available dentist<select name="dentist_user_id" data-slot-doctor required><option value="">Choose a time first</option></select></label><label>Assigned chair<input type="hidden" name="inhouse_provider_user_id" data-inhouse-provider><input type="hidden" name="chair_id" data-slot-chair-id><input type="hidden" name="chair_name" data-slot-chair><input data-slot-chair-display value="Assigned from dentist" readonly></label><label class="wide" data-reschedule-note hidden>Reschedule note<textarea name="reschedule_note" placeholder="Explain a change from the recommended date, time, or dentist."></textarea></label><div class="wide continuity-care-note">This booking follows the patient’s continuity dentist<?= $scheduleContinuityDoctor ? ": " . e((string)$scheduleContinuityDoctor['name']) : ". The selected dentist will become the continuity dentist." ?></div><?php if(patient_dentist_transfer_allowed()):?><label class="wide check-label"><input type="checkbox" name="change_continuity_dentist" value="1" data-continuity-transfer><span>Transfer future care to another dentist</span></label><label class="wide" data-continuity-reason-wrap hidden>Care-transfer reason<textarea name="continuity_change_reason" data-continuity-reason></textarea></label><?php endif;?><?php if(schedule_bypass_allowed()&&can('appointments.schedule_bypass',['super_admin','clinic_admin'])):?><label class="wide check-label"><input type="checkbox" name="schedule_bypass" value="1" data-bypass-toggle="#followup-bypass"><span>Use Schedule Bypass only for an authorized time outside published availability. Resource conflicts remain blocked.</span></label><div class="wide form-grid" id="followup-bypass" hidden><label>Bypass dentist<select name="bypass_dentist_user_id"><option value="">Choose dentist</option><?php foreach($dentists as $doctor):?><option value="<?=(int)$doctor['id']?>"><?=e((string)($doctor['display_name']??$doctor['name']))?></option><?php endforeach;?></select></label><label>Manual time<input type="time" name="bypass_time"></label><label class="wide">Bypass reason<textarea name="schedule_bypass_reason"></textarea></label></div><?php endif;?><button class="btn primary wide">Confirm follow-up appointment</button></form></section><?php endif;?>

<?php if($view==='diagnostic'):?>
<?php if(!$diagnosticReady):?>
<div class="alert warning">Apply Version 0.5.9.8.5 to enable Dentist diagnostic-review To-Do tasks.</div>
<?php else:?>
<div class="card server-list-card">
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','diagnostic reviews');?>
<div class="table-wrap"><table>
<thead><tr><th>Priority</th><th>Patient</th><th>Reason for Dentist review</th><th>Assigned Dentist</th><th>Status</th><th>Action</th></tr></thead>
<tbody>
<?php foreach($diagnosticTasks as $row):?>
<tr>
<td><span class="badge"><?=e(module_status((string)$row['priority']))?></span><small><?=e(date('M j, Y g:i A',strtotime((string)$row['created_at'])))?></small></td>
<td><strong><?=e((string)$row['patient'])?></strong><small><?=e((string)$row['patient_no'])?></small><div class="inline-actions"><a href="patients.php?action=view&amp;id=<?=(int)$row['patient_id']?>">Open patient</a><?php if(!empty($row['encounter_id'])&&task_context_page_allowed('clinical_workspace')):?><a href="clinical_workspace.php?encounter_id=<?=(int)$row['encounter_id']?>">Open encounter</a><?php endif;?></div></td>
<td><?=nl2br(e((string)$row['reason']))?><small>Reported by <?=e((string)($row['created_by_name']?:module_status((string)$row['source_role'])))?>. This is not a diagnosis until validated by the Dentist.</small><?php if($row['dentist_notes']):?><div class="notice success"><strong>Dentist note:</strong> <?=nl2br(e((string)$row['dentist_notes']))?></div><?php endif;?></td>
<td><?=e((string)($row['dentist']?:'Unassigned'))?></td>
<td><span class="badge"><?=e(module_status((string)$row['status']))?></span></td>
<td><a class="btn ghost compact" href="<?=e(module_management_url(['manage_id'=>(int)$row['id']]))?>"><?=$canValidateDiagnostic?'Manage':'View'?></a></td>
</tr>
<?php endforeach;?>
<?php if(!$diagnosticTasks):?><tr><td colspan="6" class="muted">No diagnostic-review tasks found.</td></tr><?php endif;?>
</tbody></table></div>
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','diagnostic reviews');?>
</div>
<?php endif;?>
<?php elseif($view==='recalls'):?>
<div class="card server-list-card">
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','recalls');?>
<div class="table-wrap"><table>
<thead><tr><th>Due</th><th>Patient</th><th>Recall</th><th>Appointment</th><th>Status</th><th>Action</th></tr></thead>
<tbody>
<?php foreach($recalls as $row):?>
<tr>
<td><?=e(date('M j, Y',strtotime((string)$row['due_date'])))?></td>
<td><strong><?=e((string)$row['patient'])?></strong><small><?=e((string)$row['patient_no'])?></small></td>
<td><?=e((string)$row['recall_type'])?><small><?=e((string)$row['notes'])?></small></td>
<td><?=$row['appointment_start']?e(date('M j, Y g:i A',strtotime((string)$row['appointment_start']))):'Not scheduled'?></td>
<td><span class="badge"><?=e(module_status((string)$row['status']))?></span></td>
<td><div class="inline-actions"><?php if($canCreateRecall&&!$row['appointment_id']):?><a href="followups.php?view=recalls&amp;schedule_recall_id=<?=(int)$row['id']?>">Schedule</a><?php endif;?><a class="btn ghost compact" href="<?=e(module_management_url(['manage_id'=>(int)$row['id']]))?>"><?=$canManageRecallStatus?'Manage':'View'?></a></div></td>
</tr>
<?php endforeach;?>
<?php if(!$recalls):?><tr><td colspan="6" class="muted">No recalls found.</td></tr><?php endif;?>
</tbody></table></div>
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','recalls');?>
</div>
<?php else:?>
<div class="alert success"><strong>Clinical recommendation and booking are separated.</strong> The Dentist records the reason, date range, and instructions. Receptionist or Cashier chooses the actual available appointment.</div>
<div class="card server-list-card">
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','follow-ups');?>
<div class="table-wrap"><table>
<thead><tr><th>Recommended</th><th>Patient</th><th>Dentist recommendation</th><th>Appointment</th><th>Status</th><th>Action</th></tr></thead>
<tbody>
<?php foreach($followups as $row):?>
<tr>
<td><?=e(date('M j, Y',strtotime((string)$row['recommended_date'])))?><?php if($row['preferred_time']):?><small><?=e(date('g:i A',strtotime((string)$row['preferred_time'])))?> preferred</small><?php endif;?></td>
<td><strong><?=e((string)$row['patient'])?></strong><small><?=e((string)$row['patient_no'])?></small></td>
<td><strong><?=e((string)$row['purpose'])?></strong><small><?=nl2br(e((string)$row['instructions']))?></small><small><?=e((string)($row['dentist']?:'Dentist not recorded'))?> · <?=e((string)($row['service_name']?:'Service selected during booking'))?></small></td>
<td><?=$row['appointment_start']?e(date('M j, Y g:i A',strtotime((string)$row['appointment_start']))):'Waiting for Receptionist/Cashier'?></td>
<td><span class="badge"><?=e(module_status((string)$row['status']))?></span></td>
<td><div class="inline-actions"><?php if($canSchedule&&!$row['appointment_id']&&in_array($row['status'],['pending','deferred'],true)):?><a class="btn primary compact" href="followups.php?view=followups&amp;schedule_id=<?=(int)$row['id']?>">Book</a><?php endif;?><a class="btn ghost compact" href="<?=e(module_management_url(['manage_id'=>(int)$row['id']]))?>"><?=($canDeferFollowup||$canManageFollowupStatus)?'Manage':'View'?></a></div></td>
</tr>
<?php endforeach;?>
<?php if(!$followups):?><tr><td colspan="6" class="muted">No follow-up recommendations found.</td></tr><?php endif;?>
</tbody></table></div>
<?php module_render_server_pagination($rowsTotal,$page,$perPage,'page','per_page','follow-ups');?>
</div>
<?php endif;?>

<?php if($managedRow):?>
<dialog id="followup-manage" class="wide-dialog" data-auto-open-dialog>
<form method="dialog"><button type="submit" class="dialog-close">×</button></form>
<div class="dialog-head"><div><span class="eyebrow"><?=e($view==='diagnostic'?'Dentist review task':($view==='recalls'?'Recall':'Follow-up'))?></span><h2><?=e((string)$managedRow['patient'])?></h2><p><?=e((string)$managedRow['patient_no'])?> · <?=e(module_status((string)$managedRow['status']))?></p></div><a class="btn ghost compact" href="<?=e(module_management_url(['manage_id'=>null]))?>">Close</a></div>
<?php if($view==='diagnostic'):?>
<div class="notice info"><strong>Reason for review</strong><br><?=nl2br(e((string)$managedRow['reason']))?></div>
<dl class="detail-grid"><div><dt>Priority</dt><dd><?=e(module_status((string)$managedRow['priority']))?></dd></div><div><dt>Assigned Dentist</dt><dd><?=e((string)($managedRow['dentist']?:'Unassigned'))?></dd></div></dl>
<?php if(!empty($managedRow['dentist_notes'])):?><div class="notice success"><strong>Dentist note</strong><br><?=nl2br(e((string)$managedRow['dentist_notes']))?></div><?php endif;?>
<?php if($canValidateDiagnostic&&(int)$managedRow['assigned_dentist_user_id']===(int)(current_user()['id']??0)&&in_array($managedRow['status'],['pending','in_review'],true)):?>
<form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="diagnostic_review_status"><input type="hidden" name="id" value="<?=(int)$managedRow['id']?>"><label>Status<select name="status"><option value="in_review">Mark in review</option><option value="validated">Validate</option><option value="closed">Validate and close</option></select></label><label class="wide">Dentist validation or closure note<textarea name="dentist_notes" required></textarea></label><button class="btn primary wide">Save Dentist review</button></form>
<?php endif;?>
<?php elseif($view==='recalls'):?>
<dl class="detail-grid"><div><dt>Recall</dt><dd><?=e((string)$managedRow['recall_type'])?></dd></div><div><dt>Due date</dt><dd><?=e(date('M j, Y',strtotime((string)$managedRow['due_date'])))?></dd></div><div><dt>Appointment</dt><dd><?=$managedRow['appointment_start']?e(date('M j, Y g:i A',strtotime((string)$managedRow['appointment_start']))):'Not scheduled'?></dd></div></dl>
<?php if(!empty($managedRow['notes'])):?><div class="notice info"><?=nl2br(e((string)$managedRow['notes']))?></div><?php endif;?>
<div class="inline-actions"><?php if($canCreateRecall&&!$managedRow['appointment_id']):?><a class="btn primary" href="followups.php?view=recalls&amp;schedule_recall_id=<?=(int)$managedRow['id']?>">Schedule appointment</a><?php endif;?></div>
<?php if($canManageRecallStatus):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="recall_status"><input type="hidden" name="id" value="<?=(int)$managedRow['id']?>"><label>Status<select name="status"><?php foreach(['future','due_soon','due','overdue','contacted','scheduled','completed','deferred','declined','unable_to_contact'] as $optionStatus):?><option value="<?=e($optionStatus)?>" <?=$managedRow['status']===$optionStatus?'selected':''?>><?=e(module_status($optionStatus))?></option><?php endforeach;?></select></label><button class="btn primary">Save recall status</button></form><?php endif;?>
<?php else:?>
<dl class="detail-grid"><div><dt>Purpose</dt><dd><?=e((string)$managedRow['purpose'])?></dd></div><div><dt>Recommended</dt><dd><?=e(date('M j, Y',strtotime((string)$managedRow['recommended_date'])))?></dd></div><div><dt>Dentist</dt><dd><?=e((string)($managedRow['dentist']?:'Not recorded'))?></dd></div><div><dt>Appointment</dt><dd><?=$managedRow['appointment_start']?e(date('M j, Y g:i A',strtotime((string)$managedRow['appointment_start']))):'Not scheduled'?></dd></div></dl>
<?php if(!empty($managedRow['instructions'])):?><div class="notice info"><?=nl2br(e((string)$managedRow['instructions']))?></div><?php endif;?>
<div class="inline-actions"><?php if($canSchedule&&!$managedRow['appointment_id']&&in_array($managedRow['status'],['pending','deferred'],true)):?><a class="btn primary" href="followups.php?view=followups&amp;schedule_id=<?=(int)$managedRow['id']?>">Book appointment</a><?php endif;?></div>
<?php if($canDeferFollowup&&!$managedRow['appointment_id']&&$managedRow['status']==='pending'):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="followup_defer"><input type="hidden" name="id" value="<?=(int)$managedRow['id']?>"><label class="wide">Reason to schedule later<textarea name="completion_notes" required minlength="5"></textarea></label><button class="btn ghost wide">Defer with reason</button></form><?php endif;?>
<?php if($canManageFollowupStatus):?><form method="post" class="form-grid"><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="followup_status"><input type="hidden" name="id" value="<?=(int)$managedRow['id']?>"><label>Status<select name="status"><?php foreach(['pending','scheduled','completed','deferred','cancelled'] as $optionStatus):?><option value="<?=e($optionStatus)?>" <?=$managedRow['status']===$optionStatus?'selected':''?>><?=e(module_status($optionStatus))?></option><?php endforeach;?></select></label><label class="wide">Completion or status note<textarea name="completion_notes"><?=e((string)($managedRow['completion_notes']??''))?></textarea></label><button class="btn primary wide">Save follow-up status</button></form><?php endif;?>
<?php endif;?>
</dialog>
<?php endif;?>
<?php if($canRecommend):?><dialog id="followup-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Recommend follow-up</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="create_followup"><input type="hidden" name="branch_id" value="<?=$branchId?>"><label class="wide">Patient<?php module_remote_patient_picker('patient_id',$selectedPatient,true);?></label><label>Suggested service<select name="service_id"><option value="">Choose during staff booking</option><?php foreach($services as $service):?><option value="<?=(int)$service['id']?>"><?=e((string)$service['name'])?></option><?php endforeach;?></select></label><input type="hidden" name="encounter_id" value="<?=$contextEncounterId?>"><?php if($contextEncounterId):?><div class="notice info wide">Linked to clinical encounter #<?=$contextEncounterId?>.</div><?php endif;?><label class="wide">Clinical purpose / diagnostic reason<input name="purpose" required></label><label>Recommended date<input type="date" name="recommended_date" min="<?=date('Y-m-d')?>" required></label><label>Preferred time, optional<input type="time" name="preferred_time"></label><label>Duration<input type="number" name="duration_minutes" min="15" step="5" value="30"></label><label>Maximum acceptable date<input type="date" name="maximum_date"></label><label>Urgency<select name="urgency"><option value="routine">Routine</option><option value="important">Important</option><option value="urgent">Urgent</option></select></label><label class="wide">Instructions for the booking staff and next Dentist visit<textarea name="instructions"></textarea></label><button class="btn primary wide">Save recommendation for Receptionist/Cashier</button></form></dialog><?php endif;?>
<?php if($canCreateRecall):?><dialog id="recall-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">New recall</h2><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="create_recall"><label class="wide">Patient<?php module_remote_patient_picker('patient_id',$selectedPatient,true);?></label><label>Recall type<input name="recall_type" value="Six-month Dental Examination" required></label><label>Due date<input type="date" name="due_date" required></label><label class="wide">Notes<textarea name="notes"></textarea></label><button class="btn primary wide">Create recall</button></form></dialog><?php endif;?>
<?php if($canRequestDiagnostic):?><dialog id="diagnostic-form" class="wide-dialog"><form method="post" class="form-grid"><button type="button" class="dialog-close" data-close>×</button><h2 class="wide">Send reason for Dentist diagnostic review</h2><div class="alert warning wide"><strong>This is not a diagnosis.</strong> Record the patient-reported concern, observation, or unresolved reason. The assigned Dentist must validate it from the To-Do Tasks list.</div><input type="hidden" name="csrf" value="<?=e(csrf_token())?>"><input type="hidden" name="action" value="create_diagnostic_review"><label class="wide">Patient<?php module_remote_patient_picker('patient_id',$selectedPatient,true);?></label><label>Assigned Dentist<select name="assigned_dentist_user_id"><option value="">Use patient continuity Dentist</option><?php foreach($dentists as $dentist):?><option value="<?=(int)$dentist['id']?>"><?=e((string)($dentist['display_name']??$dentist['name']))?></option><?php endforeach;?></select></label><label>Priority<select name="priority"><option value="routine">Routine</option><option value="important">Important</option><option value="urgent">Urgent</option></select></label><input type="hidden" name="encounter_id" value="<?=$contextEncounterId?>"><input type="hidden" name="appointment_id" value="<?=$contextAppointmentId?>"><?php if($contextEncounterId||$contextAppointmentId):?><div class="notice info wide">This task will be linked to<?=$contextEncounterId?' clinical encounter #'.(int)$contextEncounterId:''?><?=$contextAppointmentId?($contextEncounterId?' and':'').' appointment #'.(int)$contextAppointmentId:''?>.</div><?php endif;?><label class="wide">Reason requiring Dentist review<textarea name="reason" required placeholder="Example: Patient reports persistent swelling after treatment and asks whether another examination is required."></textarea></label><button class="btn primary wide">Send to Dentist To-Do Tasks</button></form></dialog><?php endif;?>
<?php render_footer(); ?>
