<?php
/**
 * Facility Access Scan API
 * Records access attempts from facility access control devices
 * 
 * Method: POST
 * Parameters:
 * - device_id: Device identifier (SN)
 * - CodeVal: QR/NFC code value (decoded to FIN)
 * - CodeType: Scan method (QR, NFC, Face)
 */

// Set Singapore timezone
date_default_timezone_set('Asia/Singapore');

// ============================================
// LOGGING CONFIGURATION
// ============================================
// Set to true to enable file logging, false to disable
define('ENABLE_FILE_LOGGING', true);  // Change to false to disable file logging
define('ENABLE_DB_LOGGING', true);    // Change to false to disable database logging
// ============================================

header('Content-Type: application/json');
require_once 'config/config.php';

$start_time = microtime(true);
$db = getDbInstance();
$log_file = 'logs/checkcode1_requests.log';

// Ensure logs directory exists
if (ENABLE_FILE_LOGGING && !is_dir('logs')) {
    mkdir('logs', 0777, true);
}

// Helper function for file logging
function writeLog($message) {
    global $log_file;
    if (ENABLE_FILE_LOGGING) {
        $entry = date('Y-m-d H:i:s') . " | " . $message . "\n";
        file_put_contents($log_file, $entry, FILE_APPEND);
    }
}

// Function to decode CodeVal to FIN number
function decodeCodeValToFIN($codeVal) {
    if (strlen($codeVal) < 8) return $codeVal;
    
    $firstCharMap = array('7'=>'S', '5'=>'T', '9'=>'F', '1'=>'G', '3'=>'M');
    $lastCharMap = array('16'=>'A','13'=>'B','11'=>'C','14'=>'D','20'=>'E','23'=>'F','21'=>'G','25'=>'H',
                         '34'=>'I','28'=>'J','32'=>'K','38'=>'L','39'=>'M','41'=>'N','43'=>'O','55'=>'P',
                         '52'=>'Q','57'=>'R','59'=>'S','51'=>'T','61'=>'U','63'=>'V','66'=>'W','69'=>'X',
                         '71'=>'Y','75'=>'Z');
    
    $middleDigits = substr($codeVal, 0, 7);
    // Position 7-14: ddhhmmss (skipped)
    $firstDigit = substr($codeVal, 15, 1);
    $lastTwoDigits = substr($codeVal, 16, 2);
    
    $firstChar = isset($firstCharMap[$firstDigit]) ? $firstCharMap[$firstDigit] : '';
    $lastChar = isset($lastCharMap[$lastTwoDigits]) ? $lastCharMap[$lastTwoDigits] : '';
    
    return $firstChar . $middleDigits . $lastChar;
}

// Get POST data
$raw_input = file_get_contents('php://input');
$input = json_decode($raw_input, true);

$remote_addr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'UNKNOWN';
$request_method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'UNKNOWN';
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'UNKNOWN';

// ============================================
// LOG: Raw request received
// ============================================
writeLog("REQUEST_RECEIVED | IP: " . $remote_addr . " | Method: " . $request_method . " | RAW: " . ($raw_input ? $raw_input : 'EMPTY') . " | USER_AGENT: " . $user_agent);

// Map device parameters to system parameters
$device_id = isset($input['SN']) ? trim($input['SN']) : '';
$codeVal = isset($input['CodeVal']) ? trim($input['CodeVal']) : '';
$fin_no = decodeCodeValToFIN($codeVal);
$access_type = 'entry';
$scan_method = isset($input['CodeType']) ? trim($input['CodeType']) : 'QR';
$temperature = null;

// ============================================
// LOG: Parsed input details
// ============================================
$parsed_keys = is_array($input) ? implode(',', array_keys($input)) : 'NONE';
writeLog("INPUT_PARSED | SN: " . ($device_id ? $device_id : 'EMPTY') . " | CodeVal: " . ($codeVal ? $codeVal : 'EMPTY') . " | DecodedFIN: " . $fin_no . " | ScanMethod: " . $scan_method . " | InputKeys: " . $parsed_keys);

// Validate required fields
if (empty($device_id) || empty($codeVal)) {
    writeLog("VALIDATION_FAILED | SN: " . ($device_id ? $device_id : 'EMPTY') . " | CodeVal: " . ($codeVal ? $codeVal : 'EMPTY') . " | Reason: Missing required fields");
    
    $response = array(
        'UID' => 5657,
        'Status' => 1,
        'StatusDesc' => 'Invalid request',
        'Relay1Time' => 0,
        'BeepType' => 0,
        'BeepTime' => 0
    );
    
    // LOG: Response sent
    $execution_time = microtime(true) - $start_time;
    writeLog("RESPONSE_SENT | Status: INVALID_REQUEST | Time: " . round($execution_time, 4) . "s");
    writeLog(str_repeat('-', 80));
    
    // DB logging
    if (ENABLE_DB_LOGGING) {
        try {
            $db->insert('api_request_logs', array(
                'endpoint' => 'CheckCode',
                'device_id' => $device_id,
                'ip_address' => $remote_addr,
                'request_method' => $request_method,
                'request_data' => $raw_input ? $raw_input : '',
                'response_data' => json_encode($response),
                'response_code' => 200,
                'execution_time' => $execution_time
            ));
        } catch (Exception $e) {
            writeLog("DB_LOG_ERROR | " . $e->getMessage());
        }
    }
    
    echo json_encode($response);
    exit;
}

// Get device information
$db->where('device_id', $device_id);
$db->where('status', 'active');
$device = $db->getOne('facility_access_devices');

if (!$device) {
    writeLog("DEVICE_NOT_FOUND | SN: " . $device_id . " | Reason: Device not found or inactive");
    
    $response = array(
        'UID' => 5657,
        'Status' => 1,
        'StatusDesc' => 'Device not found',
        'Relay1Time' => 0,
        'BeepType' => 0,
        'BeepTime' => 0
    );
    
    $execution_time = microtime(true) - $start_time;
    writeLog("RESPONSE_SENT | Status: DEVICE_NOT_FOUND | Device: " . $device_id . " | Time: " . round($execution_time, 4) . "s");
    writeLog(str_repeat('-', 80));
    
    if (ENABLE_DB_LOGGING) {
        try {
            $db->insert('api_request_logs', array(
                'endpoint' => 'CheckCode',
                'device_id' => $device_id,
                'ip_address' => $remote_addr,
                'request_method' => $request_method,
                'request_data' => $raw_input ? $raw_input : '',
                'response_data' => json_encode($response),
                'response_code' => 200,
                'execution_time' => $execution_time
            ));
        } catch (Exception $e) {
            writeLog("DB_LOG_ERROR | " . $e->getMessage());
        }
    }
    
    echo json_encode($response);
    exit;
} else {
    writeLog("DEVICE_FOUND | SN: " . $device_id . " | DeviceID(DB): " . $device['id'] . " | Room: " . (isset($device['facility_room_id']) ? $device['facility_room_id'] : 'N/A') . " | Dormitory: " . (isset($device['dormitory_id']) ? $device['dormitory_id'] : 'N/A'));
}

// Set access type based on device type (entry or exit)
$access_type = (isset($device['device_type']) && $device['device_type'] == 'exit') ? 'exit' : 'entry';
writeLog("ACCESS_TYPE | Device: " . $device_id . " | DeviceType: " . (isset($device['device_type']) ? $device['device_type'] : 'not set') . " | AccessType: " . $access_type);

// Get resident information by FIN number (must match device's dormitory_id)
$dormitory_id = isset($device['dormitory_id']) ? $device['dormitory_id'] : '';
$lookup_method = 'fin_no';
$db->where('fin_no', $fin_no);
$db->where('status', 1); // Only consider active residents for lookup
$db->where('dormitory_id', $dormitory_id);
$resident = $db->getOne('employee_info');

if (!$resident) {
    $lookup_method = 'resident_id';
    $db->where('resident_id', $codeVal);
    $db->where('dormitory_id', $dormitory_id);
    $resident = $db->getOne('employee_info');
}


// If not a resident, check if this is a facility security user
$is_security_user = false;
$security_user = null;

if (!$resident) {
    writeLog("RESIDENT_LOOKUP_FAILED | FIN: " . $fin_no . " | CodeVal: " . $codeVal . " | DormitoryID: " . $dormitory_id . " | Now checking facility_security_users");
    writeLog("SECURITY_USER_LOOKUP | Searching fin_no: " . $input['SN'] . " | DormitoryID: " . $device['dormitory_id'] . " | Status: active");
    
    $db = getDbInstance();
    $db->where('fin_no', $input['CodeVal']);
    $db->where('status', 'active');
    $db->where('dormitory_id', $device['dormitory_id']);
    $security_user = $db->getOne('facility_security_users');

    if ($security_user) {
        $is_security_user = true;
        writeLog("SECURITY_USER_MATCH | Found security user | ID: " . $security_user['id'] . " | FIN: " . (isset($security_user['fin_no']) ? $security_user['fin_no'] : 'N/A') . " | Name: " . (isset($security_user['name']) ? $security_user['name'] : 'N/A'));
    } else {
        writeLog("SECURITY_USER_NOT_FOUND | No match in facility_security_users for fin_no: " . $input['SN'] . " | DormitoryID: " . $device['dormitory_id']);
    }
}

if (!$resident && !$is_security_user) {
    writeLog("RESIDENT_NOT_FOUND | FIN: " . $fin_no . " | CodeVal: " . $codeVal . " | DormitoryID: " . $dormitory_id . " | Tried: fin_no and resident_id lookup, facility_security_users (with dormitory_id match)");
    
    $response = array(
        'UID' => 5657,
        'Status' => 0,
        'StatusDesc' => 'Resident not found',
        'Relay1Time' => 0,
        'BeepType' => 2,
        'BeepTime' => 2000
    );
    
    $execution_time = microtime(true) - $start_time;
    writeLog("RESPONSE_SENT | Status: RESIDENT_NOT_FOUND | Device: " . $device_id . " | FIN: " . $fin_no . " | Time: " . round($execution_time, 4) . "s");
    writeLog(str_repeat('-', 80));
    
    if (ENABLE_DB_LOGGING) {
        try {
            $db->insert('api_request_logs', array(
                'endpoint' => 'CheckCode',
                'device_id' => $device_id,
                'ip_address' => $remote_addr,
                'request_method' => $request_method,
                'request_data' => $raw_input ? $raw_input : '',
                'response_data' => json_encode($response),
                'response_code' => 200,
                'execution_time' => $execution_time
            ));
        } catch (Exception $e) {
            writeLog("DB_LOG_ERROR | " . $e->getMessage());
        }
    }
    
    echo json_encode($response);
    exit;
} elseif ($is_security_user) {
    $lookup_method = 'security_user';
    $sec_name = '[Security] ' . (isset($security_user['name']) ? $security_user['name'] : 'N/A');
    writeLog("SECURITY_USER_FOUND | SecurityID: SEC-" . $security_user['id'] . " | Name: " . $sec_name . " | FIN: " . (isset($security_user['fin_no']) ? $security_user['fin_no'] : 'N/A') . " | DormitoryID: " . $dormitory_id);
} else {
    $resident_name = isset($resident['name']) ? $resident['name'] : 'N/A';
    $resident_status = ($resident['status'] == 1) ? 'Active' : 'Inactive';
    writeLog("RESIDENT_FOUND | LookupBy: " . $lookup_method . " | ResidentID: " . $resident['resident_id'] . " | Name: " . $resident_name . " | FIN: " . (isset($resident['fin_no']) ? $resident['fin_no'] : 'N/A') . " | Status: " . $resident_status . " | Type: " . (isset($resident['resident_type']) ? $resident['resident_type'] : 'N/A'));
}

// Check if resident is active (skip for security users - already verified active)
if (!$is_security_user && $resident['status'] != 1) {
    writeLog("ACCESS_DENIED | ResidentID: " . $resident['resident_id'] . " | Name: " . (isset($resident['name']) ? $resident['name'] : 'N/A') . " | FIN: " . (isset($resident['fin_no']) ? $resident['fin_no'] : 'N/A') . " | Reason: Resident inactive (status=" . $resident['status'] . ")");
    
    $response = array(
        'UID' => 5657,
        'Status' => 0,
        'StatusDesc' => 'Access denied',
        'Relay1Time' => 0,
        'BeepType' => 2,
        'BeepTime' => 2000
    );
    
    echo json_encode($response);
    
    // Log denied access to facility_access_logs
    try {
        $log_data = array(
            'resident_id' => $resident['resident_id'],
            'fin_no' => isset($resident['fin_no']) ? $resident['fin_no'] : '',
            'name' => isset($resident['name']) ? $resident['name'] : '',
            'facility_room_id' => isset($device['facility_room_id']) ? $device['facility_room_id'] : '',
            'device_id' => $device_id,
            'dormitory_id' => isset($device['dormitory_id']) ? $device['dormitory_id'] : '',
            'access_type' => $access_type,
            'scan_method' => $scan_method,
            'access_status' => 'denied',
            'temperature' => $temperature,
            'scanned_at' => date('Y-m-d H:i:s')
        );
        $db->insert('facility_access_logs', $log_data);
        writeLog("ACCESS_LOG_SAVED | Status: denied | ResidentID: " . $resident['resident_id']);
    } catch (Exception $e) {
        writeLog("DB_ERROR | Failed to insert access log (denied): " . $e->getMessage());
    }
    
    $execution_time = microtime(true) - $start_time;
    writeLog("RESPONSE_SENT | Status: ACCESS_DENIED | Device: " . $device_id . " | Resident: " . $resident['resident_id'] . " | Time: " . round($execution_time, 4) . "s");
    writeLog(str_repeat('-', 80));
    
    // DB logging
    if (ENABLE_DB_LOGGING) {
        try {
            $db->insert('api_request_logs', array(
                'endpoint' => 'CheckCode',
                'device_id' => $device_id,
                'ip_address' => $remote_addr,
                'request_method' => $request_method,
                'request_data' => $raw_input ? $raw_input : '',
                'response_data' => json_encode($response),
                'response_code' => 200,
                'execution_time' => $execution_time
            ));
        } catch (Exception $e) {
            writeLog("DB_LOG_ERROR | " . $e->getMessage());
        }
    }
    
    exit;
}

// ============================================
// OCCUPANCY CHECK - Only for entry devices, non-security users, rooms with occupancy enabled
// ============================================
if ($access_type == 'entry' && !$is_security_user && !empty($device['facility_room_id'])) {
    $db = getDbInstance();
    $db->where('id', $device['facility_room_id']);
    $room = $db->getOne('facility_rooms');
    
    // Resident ID for occupancy checks
    $occ_resident_id = $resident['resident_id'];
    
    if ($room && isset($room['occupancy_enabled']) && $room['occupancy_enabled'] == 1 && !empty($room['max_occupancy'])) {
        $current_occ = isset($room['current_occupancy']) ? intval($room['current_occupancy']) : 0;
        $max_occ = intval($room['max_occupancy']);
        
        writeLog("OCCUPANCY_CHECK | Room: " . $device['facility_room_id'] . " | Current: " . $current_occ . " | Max: " . $max_occ);
        
        // ============================================
        // RE-ENTRY VALIDATION (15 min rule)
        // If resident's last scan was entry (no exit) and > 15 min ago → deny
        // If < 15 min ago → allow (they probably didn't actually enter)
        // ============================================
        $db = getDbInstance();
        $last_entry = $db->rawQuery("SELECT scanned_at, access_type FROM facility_access_logs 
            WHERE resident_id = ? AND facility_room_id = ? AND access_status = 'granted'
            ORDER BY id DESC LIMIT 1", array($occ_resident_id, $device['facility_room_id']));
        
        if (count($last_entry) > 0 && $last_entry[0]['access_type'] == 'entry') {
            $last_entry_time = strtotime($last_entry[0]['scanned_at']);
            $minutes_since = (time() - $last_entry_time) / 60;
            
            writeLog("REENTRY_CHECK | ResidentID: " . $occ_resident_id . " | LastEntry: " . $last_entry[0]['scanned_at'] . " | MinutesSince: " . round($minutes_since, 1));
            
            if ($minutes_since > 15) {
                // More than 15 min without exit → deny, must go to security
                writeLog("REENTRY_DENIED | ResidentID: " . $occ_resident_id . " | MinutesSince: " . round($minutes_since, 1) . " | Reason: No exit scan after 15 min, contact security");
                
                $response = array(
                    'UID' => 5657,
                    'Status' => 0,
                    'StatusDesc' => 'Contact security',
                    'Relay1Time' => 0,
                    'BeepType' => 2,
                    'BeepTime' => 2000
                );
                
                // Log denied access
                try {
                    $db = getDbInstance();
                    $db->insert('facility_access_logs', array(
                        'resident_id' => $occ_resident_id,
                        'fin_no' => isset($resident['fin_no']) ? $resident['fin_no'] : '',
                        'name' => isset($resident['name']) ? $resident['name'] : '',
                        'facility_room_id' => $device['facility_room_id'],
                        'device_id' => $device_id,
                        'dormitory_id' => isset($device['dormitory_id']) ? $device['dormitory_id'] : '',
                        'access_type' => 'entry',
                        'scan_method' => $scan_method,
                        'access_status' => 'denied',
                        'temperature' => $temperature,
                        'scanned_at' => date('Y-m-d H:i:s')
                    ));
                } catch (Exception $e) {
                    writeLog("DB_ERROR | Failed to insert reentry denied log: " . $e->getMessage());
                }
                
                $execution_time = microtime(true) - $start_time;
                writeLog("RESPONSE_SENT | Status: REENTRY_DENIED | Device: " . $device_id . " | Resident: " . $occ_resident_id . " | Time: " . round($execution_time, 4) . "s");
                writeLog(str_repeat('-', 80));
                
                if (ENABLE_DB_LOGGING) {
                    try {
                        $db = getDbInstance();
                        $db->insert('api_request_logs', array(
                            'endpoint' => 'CheckCode',
                            'device_id' => $device_id,
                            'ip_address' => $remote_addr,
                            'request_method' => $request_method,
                            'request_data' => $raw_input ? $raw_input : '',
                            'response_data' => json_encode($response),
                            'response_code' => 200,
                            'execution_time' => $execution_time
                        ));
                    } catch (Exception $e) {
                        writeLog("DB_LOG_ERROR | " . $e->getMessage());
                    }
                }
                
                echo json_encode($response);
                exit;
            } else {
                // Less than 15 min → allow re-entry (they probably didn't enter the first time)
                writeLog("REENTRY_ALLOWED | ResidentID: " . $occ_resident_id . " | MinutesSince: " . round($minutes_since, 1) . " | Reason: Within 15 min grace period");
            }
        }
        
        if ($current_occ >= $max_occ) {
            writeLog("OCCUPANCY_FULL | Room: " . $device['facility_room_id'] . " | Current: " . $current_occ . "/" . $max_occ . " | ResidentID: " . $log_resident_id . " | ACCESS DENIED");
            
            $response = array(
                'UID' => 5657,
                'Status' => 0,
                'StatusDesc' => 'Room full',
                'Relay1Time' => 0,
                'BeepType' => 2,
                'BeepTime' => 2000
            );
            
            // Log denied access
            try {
                $db = getDbInstance();
                $db->insert('facility_access_logs', array(
                    'resident_id' => $log_resident_id,
                    'fin_no' => $log_fin_no,
                    'name' => $log_name,
                    'facility_room_id' => $device['facility_room_id'],
                    'device_id' => $device_id,
                    'dormitory_id' => isset($device['dormitory_id']) ? $device['dormitory_id'] : '',
                    'access_type' => 'entry',
                    'scan_method' => $scan_method,
                    'access_status' => 'denied',
                    'temperature' => $temperature,
                    'scanned_at' => date('Y-m-d H:i:s')
                ));
            } catch (Exception $e) {
                writeLog("DB_ERROR | Failed to insert occupancy denied log: " . $e->getMessage());
            }
            
            $execution_time = microtime(true) - $start_time;
            writeLog("RESPONSE_SENT | Status: ROOM_FULL | Device: " . $device_id . " | Resident: " . $log_resident_id . " | Time: " . round($execution_time, 4) . "s");
            writeLog(str_repeat('-', 80));
            
            if (ENABLE_DB_LOGGING) {
                try {
                    $db = getDbInstance();
                    $db->insert('api_request_logs', array(
                        'endpoint' => 'CheckCode',
                        'device_id' => $device_id,
                        'ip_address' => $remote_addr,
                        'request_method' => $request_method,
                        'request_data' => $raw_input ? $raw_input : '',
                        'response_data' => json_encode($response),
                        'response_code' => 200,
                        'execution_time' => $execution_time
                    ));
                } catch (Exception $e) {
                    writeLog("DB_LOG_ERROR | " . $e->getMessage());
                }
            }
            
            echo json_encode($response);
            exit;
        }
    }
}

// ============================================
// ACCESS GRANTED - Log and respond
// ============================================
if ($is_security_user) {
    $log_resident_id = 'SEC-' . $security_user['id'];
    $log_fin_no = isset($security_user['fin_no']) ? $security_user['fin_no'] : '';
    $log_name = '[Security] ' . (isset($security_user['name']) ? $security_user['name'] : '');
} else {
    $log_resident_id = $resident['resident_id'];
    $log_fin_no = isset($resident['fin_no']) ? $resident['fin_no'] : '';
    $log_name = isset($resident['name']) ? $resident['name'] : '';
}

writeLog("ACCESS_GRANTED | ResidentID: " . $log_resident_id . " | Name: " . $log_name . " | FIN: " . $log_fin_no . " | Device: " . $device_id . " | ScanMethod: " . $scan_method);

$log_data = array(
    'resident_id' => $log_resident_id,
    'fin_no' => $log_fin_no,
    'name' => $log_name,
    'facility_room_id' => isset($device['facility_room_id']) ? $device['facility_room_id'] : '',
    'device_id' => $device_id,
    'dormitory_id' => isset($device['dormitory_id']) ? $device['dormitory_id'] : '',
    'access_type' => $access_type,
    'scan_method' => $scan_method,
    'access_status' => 'granted',
    'temperature' => $temperature,
    'scanned_at' => date('Y-m-d H:i:s')
);

$log_id = false;
try {
    $log_id = $db->insert('facility_access_logs', $log_data);
    writeLog("ACCESS_LOG_SAVED | Status: granted | LogID: " . ($log_id ? $log_id : 'FAILED') . " | ResidentID: " . $log_resident_id);
} catch (Exception $e) {
    writeLog("DB_ERROR | Failed to insert access log (granted): " . $e->getMessage());
}

// Update room occupancy counter (skip for security users)
if ($log_id && !$is_security_user && !empty($device['facility_room_id'])) {
    try {
        if ($access_type == 'entry') {
            // Check if this person is already inside
            // Find their last granted log for this room BEFORE the one we just inserted
            $db = getDbInstance();
            $prev_entry = $db->rawQuery("SELECT id, access_type FROM facility_access_logs 
                WHERE resident_id = ? AND facility_room_id = ? AND access_status = 'granted' AND id < ?
                ORDER BY id DESC LIMIT 1", array($log_resident_id, $device['facility_room_id'], $log_id));
            
            // Person is already inside if: their previous log exists AND was an entry
            // If no previous log exists, they are entering for the first time = not inside
            $already_inside = false;
            if (count($prev_entry) > 0) {
                writeLog("OCCUPANCY_PREV_LOG | id: " . $prev_entry[0]['id'] . " | access_type: " . $prev_entry[0]['access_type'] . " | ResidentID: " . $log_resident_id);
                if ($prev_entry[0]['access_type'] == 'entry') {
                    $already_inside = true;
                }
            } else {
                writeLog("OCCUPANCY_NO_PREV_LOG | ResidentID: " . $log_resident_id . " | First time entry");
            }
            
            if (!$already_inside) {
                $db = getDbInstance();
                $db->rawQuery("UPDATE facility_rooms SET current_occupancy = current_occupancy + 1 WHERE id = ? AND occupancy_enabled = 1", array($device['facility_room_id']));
                writeLog("OCCUPANCY_INCREMENT | Room: " . $device['facility_room_id'] . " | ResidentID: " . $log_resident_id);
            } else {
                writeLog("OCCUPANCY_SKIP_INCREMENT | Room: " . $device['facility_room_id'] . " | ResidentID: " . $log_resident_id . " | Reason: Already inside (duplicate entry scan)");
            }
        } elseif ($access_type == 'exit') {
            $db = getDbInstance();
            $prev_log = $db->rawQuery("SELECT access_type FROM facility_access_logs 
                WHERE resident_id = ? AND facility_room_id = ? AND access_status = 'granted' AND id < ?
                ORDER BY id DESC LIMIT 1", array($log_resident_id, $device['facility_room_id'], $log_id));
            
            if (count($prev_log) > 0 && $prev_log[0]['access_type'] == 'entry') {
                $db = getDbInstance();
                $db->rawQuery("UPDATE facility_rooms SET current_occupancy = GREATEST(current_occupancy - 1, 0) WHERE id = ? AND occupancy_enabled = 1", array($device['facility_room_id']));
                writeLog("OCCUPANCY_DECREMENT | Room: " . $device['facility_room_id'] . " | ResidentID: " . $log_resident_id);
            } else {
                writeLog("OCCUPANCY_SKIP_DECREMENT | Room: " . $device['facility_room_id'] . " | ResidentID: " . $log_resident_id . " | Reason: No matching entry found (duplicate exit scan)");
            }
        }
    } catch (Exception $e) {
        writeLog("DB_ERROR | Failed to update occupancy: " . $e->getMessage());
    }
}

// Update device last sync
try {
    $db->where('id', $device['id']);
    $db->update('facility_access_devices', array('last_sync' => date('Y-m-d H:i:s')));
    writeLog("DEVICE_SYNC_UPDATED | DeviceID(DB): " . $device['id'] . " | SN: " . $device_id);
} catch (Exception $e) {
    writeLog("DB_ERROR | Failed to update device last_sync: " . $e->getMessage());
}

if ($log_id) {
    $response = array(
        'UID' => 5657,
        'Status' => 1,
        'StatusDesc' => 'Gate Opening',
        'Relay1Time' => 5000,
        'BeepType' => 1,
        'BeepTime' => 1000
    );
} else {
    $response = array(
        'UID' => 5657,
        'Status' => 0,
        'StatusDesc' => 'error',
        'Relay1Time' => 0,
        'BeepType' => 2,
        'BeepTime' => 2000
    );
    writeLog("RESPONSE_ERROR | LogID insert failed | Device: " . $device_id . " | Resident: " . $log_resident_id);
}

// ============================================
// FINAL LOGGING
// ============================================
$execution_time = microtime(true) - $start_time;
$status_desc = isset($response['StatusDesc']) ? $response['StatusDesc'] : 'unknown';
writeLog("RESPONSE_SENT | Status: " . $status_desc . " | Device: " . $device_id . " | Resident: " . $log_resident_id . " | LogID: " . ($log_id ? $log_id : 'NONE') . " | Time: " . round($execution_time, 4) . "s");
writeLog(str_repeat('-', 80));

// DB logging
if (ENABLE_DB_LOGGING) {
    try {
        $db->insert('api_request_logs', array(
            'endpoint' => 'CheckCode',
            'device_id' => $device_id,
            'ip_address' => $remote_addr,
            'request_method' => $request_method,
            'request_data' => $raw_input ? $raw_input : '',
            'response_data' => json_encode($response),
            'response_code' => 200,
            'execution_time' => $execution_time
        ));
    } catch (Exception $e) {
        writeLog("DB_LOG_ERROR | " . $e->getMessage());
    }
}

echo json_encode($response);
