<?php
/**
 * API Helper/Router
 * SSCI Office Platform
 */

// Start output buffering to catch any unexpected output
ob_start();

require_once __DIR__ . '/../config/bootstrap.php';

$auth = $GLOBALS['auth'];
$db = $GLOBALS['db'];
$notification = $GLOBALS['notification'];

// Clear any buffered output before sending JSON
ob_clean();

// Get API endpoint
$endpoint = $_GET['endpoint'] ?? '';
$method = $_SERVER['REQUEST_METHOD'];
$id = $_GET['id'] ?? $_POST['id'] ?? null;

// Set JSON header
header('Content-Type: application/json');

// Route API calls
switch ($endpoint) {
    // Notifications API
    case 'notifications/get':
        handleGetNotifications();
        break;
    case 'notifications/mark-read':
        handleMarkNotificationAsRead($id);
        break;
    case 'notifications/mark-all-read':
        handleMarkAllNotificationsAsRead();
        break;
    
    // Push Notifications API
    case 'push/subscribe':
        handleSavePushSubscription();
        break;
    case 'push/vapid-key':
        handleGetVapidKey();
        break;

    // Language API
    case 'language/set':
        handleSetLanguage();
        break;

    // Draft API
    case 'draft/save':
        handleSaveDraft();
        break;
    case 'draft/get':
        handleGetDraft($id);
        break;
    case 'draft/delete':
        handleDeleteDraft($id);
        break;

    // Health check
    case 'health':
        http_response_code(200);
        echo json_encode(['status' => 'ok']);
        break;

    // Sync queue
    case 'sync':
        handleSync();
        break;

    // Users API
    case 'users/list':
        handleGetUsers();
        break;
    case 'users/get':
        handleGetUser($id);
        break;
    case 'users/create':
        handleCreateUser();
        break;
    case 'users/update':
        handleUpdateUser($id);
        break;
    case 'users/delete':
        handleDeleteUser($id);
        break;

    // Clients API
    case 'clients/list':
        handleGetClients();
        break;
    case 'clients/create':
        handleCreateClient();
        break;
    case 'clients/update':
        handleUpdateClient($id);
        break;
    case 'clients/delete':
        handleDeleteClient($id);
        break;

    // Posts API
    case 'posts/list':
        handleGetPosts();
        break;
    case 'posts/get':
        handleGetPost($id);
        break;
    case 'posts/create':
        handleCreatePost();
        break;
    case 'posts/update':
        handleUpdatePost($id);
        break;
    case 'posts/delete':
        handleDeletePost($id);
        break;

    // Guards API - Post Assignments
    case 'guards/assign-to-post':
        handleAssignGuardToPost();
        break;
    case 'guards/remove-assignment':
        handleRemoveGuardAssignment($id);
        break;
    case 'guards/get-post-guards':
        handleGetPostGuards($id);
        break;
    case 'guards/get-by-post':
        handleGetGuardsByPost($_GET['post_id'] ?? null);
        break;
    case 'guards/record-control':
        handleRecordGuardControl();
        break;
    case 'guards/get-control':
        handleGetGuardControl($id);
        break;
    case 'guards/update-control':
        handleUpdateGuardControl();
        break;
    case 'guards/delete-control':
        handleDeleteGuardControl($id);
        break;
    case 'guards/generate-report':
        handleGenerateControlReport();
        break;
    case 'guards/report-pdf':
        handleGenerateControlReportPDF();
        break;
    case 'guards/get-controls':
        handleGetGuardControls($id);
        break;

    // Patrol API (QR Code Scanning & Hourly Tracking)
    case 'patrol/dashboard':
        handlePatrolDashboard($_GET['post_id'] ?? null);
        break;
    case 'patrol/scan':
        handlePatrolScan();
        break;
    case 'patrol/status':
        handleGetPatrolStatus($_GET['post_id'] ?? null);
        break;
    case 'patrol/history':
        handleGetPatrolHistory($_GET['post_id'] ?? null, $_GET['days'] ?? 7);
        break;
    case 'patrol/today':
        handleGetTodayPatrolSummary($_GET['post_id'] ?? null);
        break;
    case 'patrol/countdown':
        handleGetCountdown();
        break;
    case 'patrol/last-scan':
        handleGetLastScan($_GET['post_id'] ?? null);
        break;
    case 'patrol/qr-details':
        handleGetQRDetails($_GET['qr_code'] ?? null);
        break;

    // Patrol Management API (Setup & Configuration)
    case 'patrol/points/list':
        handleGetPatrolPoints($_GET['post_id'] ?? null);
        break;
    case 'patrol/points/create':
        handleCreatePatrolPoint();
        break;
    case 'patrol/points/update':
        handleUpdatePatrolPoint($_GET['id'] ?? null);
        break;
    case 'patrol/points/delete':
        handleDeletePatrolPoint($_GET['id'] ?? null);
        break;
    case 'patrol/points/generate-qr':
        handleGenerateQRCode($_GET['point_id'] ?? null);
        break;

    // Invoices API
    case 'invoices/list':
        handleGetInvoices();
        break;
    case 'invoices/create':
        handleCreateInvoice();
        break;
    case 'invoices/update':
        handleUpdateInvoice($id);
        break;
    case 'invoices/delete':
        handleDeleteInvoice($id);
        break;
    case 'invoices/send':
        handleSendInvoice($id);
        break;
    case 'invoices/get':
        handleGetInvoice($id);
        break;
    case 'invoices/generate':
        handleGenerateInvoices();
        break;
    case 'invoices/stats':
        handleGetInvoiceStats();
        break;
    case 'invoices/settings':
        handleInvoiceSettings();
        break;
    case 'invoices/pdf':
        handleGenerateInvoicePDF($_GET['id'] ?? null);
        break;

    // Payment API
    case 'payments/record':
        handleRecordPayment();
        break;
    case 'payments/request-mobile-money':
        handleRequestMobileMoneyPayment();
        break;
    case 'payments/check-mobile-money-status':
        handleCheckMobileMoneyStatus();
        break;
    case 'payments/list':
        handleGetPayments();
        break;
    case 'payments/receipt':
        generatePaymentReceipt($id);
        break;
    case 'payments/update':
        handleUpdatePayment();
        break;
    case 'payments/update-status':
        handleUpdatePaymentStatus();
        break;
    case 'payments/delete':
        handleDeletePayment();
        break;

    // Salaries API
    case 'salaries/list':
        handleGetSalaries();
        break;
    case 'salaries/create':
        handleCreateSalary();
        break;
    case 'salaries/update':
        handleUpdateSalary();
        break;
    case 'salaries/delete':
        handleDeleteSalary();
        break;
    case 'salaries/bulk-update':
        handleBulkUpdateSalaries();
        break;
    case 'salaries/bulk-pay':
        handleBulkPaySalaries();
        break;
    case 'salaries/payslip':
        generatePayslip($_GET['id'] ?? null);
        break;

    // Staff API
    case 'staff/list':
        handleGetStaff();
        break;
    case 'staff/create':
        handleCreateStaff();
        break;
    case 'staff/update':
        handleUpdateStaff($id);
        break;
    case 'staff/delete':
        handleDeleteStaff($id);
        break;

    // Bulk Import API
    case 'import/users':
        handleBulkImportUsers();
        break;
    case 'import/validate':
        handleValidateImportFile();
        break;

    // RBAC API
    case 'roles/list':
        handleGetRoles();
        break;
    case 'permissions/list':
        handleGetPermissions();
        break;
    case 'permissions/assign':
        handleAssignPermission();
        break;
    case 'permissions/revoke':
        handleRevokePermission();
        break;

    // Audit Log API
    case 'audit/logs':
        handleGetAuditLogs();
        break;
    case 'audit/login-attempts':
        handleGetLoginAttempts();
        break;

    // Client API
    case 'clients/add-payment':
        handleClientAddPayment();
        break;
    case 'clients/add-feedback':
        handleClientAddFeedback();
        break;
    case 'clients/delete-feedback':
        handleClientDeleteFeedback($id);
        break;
    case 'clients/send-message':
        handleClientSendMessage();
        break;
    case 'clients/send-reply':
        handleClientSendReply();
        break;
    case 'guards/send-message':
        handleClientSendMessage(); // Use same handler as clients
        break;
    case 'guards/send-reply':
        handleClientSendReply(); // Use same handler as clients
        break;
    case 'guards/payslip':
        handleGuardsPayslip($_GET['id'] ?? null);
        break;
    case 'guards/contract-pdf':
        handleGuardsContractPdf($_GET['user_id'] ?? null);
        break;
    case 'guards/badge-image':
        handleGuardsBadgeImage($_GET['user_id'] ?? null);
        break;
    case 'controllers/send-message':
        handleClientSendMessage(); // Use same handler as clients
        break;
    case 'controllers/send-reply':
        handleClientSendReply(); // Use same handler as clients
        break;
    case 'admin/send-message':
        handleAdminSendMessage();
        break;
    case 'admin/send-reply':
        handleAdminSendReply();
        break;
    case 'admin/mark-message-read':
        handleMarkMessageRead();
        break;
    case 'admin/message-thread':
        handleGetMessageThread($id);
        break;
    case 'clients/mark-message-read':
        handleMarkMessageRead();
        break;
    case 'clients/message-thread':
        handleGetMessageThread($id);
        break;
    case 'guards/mark-message-read':
        handleMarkMessageRead();
        break;
    case 'guards/message-thread':
        handleGetMessageThread($id);
        break;
    case 'controllers/mark-message-read':
        handleMarkMessageRead();
        break;
    case 'controllers/message-thread':
        handleGetMessageThread($id);
        break;
    case 'clients/my-invoices':
        handleClientMyInvoices();
        break;
    case 'clients/my-invoice':
        handleGetInvoice2($id);
        break;
    case 'clients/invoice-pdf':
        handleClientInvoicePdf($id);
        break;
    case 'clients/payments-list':
        handleClientPaymentsList();
        break;
    case 'clients/record-payment':
        handleClientRecordPayment();
        break;
    case 'clients/request-mobile-money':
        handleRequestMobileMoneyPayment();
        break;
    case 'clients/payment-detail':
        handleClientPaymentDetail($id);
        break;
    case 'clients/payment-receipt':
        handleClientPaymentReceipt($id);
        break;
    case 'clients/check-mobile-money-status':
        handleClientCheckMobileMoneyStatus();
        break;
    case 'clients/check-payment-status':
        handleClientCheckPaymentStatus();
        break;
    case 'clients/contract-pdf':
        handleClientContractPdf($id);

        break;
    case 'clients/assessment-pdf':
        handleClientAssessmentPdf($id);
        break;

    // Guards Downloads API
    case 'guards/payslip':
        handleGuardsPayslip($_GET['salary_id'] ?? null);
        break;
    case 'guards/contract-pdf':
        handleGuardsContractPdf($_SESSION['user_id'] ?? null);
        break;
    case 'guards/badge-image':
        handleGuardsBadgeImage($_SESSION['user_id'] ?? null);
        break;

    // Messages API
    case 'messages/download-attachment':
        handleDownloadMessageAttachment();
        break;

    default:
        http_response_code(404);
        echo json_encode(['error' => 'Endpoint not found']);
        break;
}

// ==================== Notification Handlers ====================

function handleGetNotifications() {
    global $auth, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    // Get all notifications (both read and unread), ordered by most recent first
    $notifications = $notification->getUserNotifications($_SESSION['user_id'], 20, false);
    echo json_encode(['notifications' => $notifications]);
}

function handleMarkNotificationAsRead($id) {
    global $auth, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $notification->markAsRead($id);
    echo json_encode(['success' => true]);
}

function handleMarkAllNotificationsAsRead() {
    global $auth, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $notification->markAllAsRead($_SESSION['user_id']);
    echo json_encode(['success' => true]);
}

/**
 * Handle Get VAPID Public Key
 */
function handleGetVapidKey() {
    global $db;
    
    try {
        $settings = $db->fetch("SELECT vapid_public_key FROM settings LIMIT 1");
        
        if ($settings && !empty($settings['vapid_public_key'])) {
            http_response_code(200);
            echo json_encode(['vapidKey' => $settings['vapid_public_key']]);
        } else {
            error_log("VAPID key not configured in settings");
            http_response_code(400);
            echo json_encode(['error' => 'VAPID key not configured']);
        }
    } catch (Exception $e) {
        error_log("Error getting VAPID key: " . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => 'Failed to get VAPID key']);
    }
}

/**
 * Handle Save Push Subscription
 */
function handleSavePushSubscription() {
    global $auth, $db;
    
    try {
        if (!$auth->isAuthenticated()) {
            http_response_code(401);
            echo json_encode(['error' => 'Unauthorized']);
            return;
        }
        
        $userId = $_SESSION['user_id'];
        $data = json_decode(file_get_contents('php://input'), true);
        
        if (!isset($data['endpoint']) || !isset($data['keys'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Missing endpoint or keys']);
            return;
        }
        
        // Get keys (they should already be base64 encoded from client)
        $p256dh = $data['keys']['p256dh'];
        $auth_key = $data['keys']['auth'];
        $endpoint = $data['endpoint'];
        
        // Check if subscription already exists
        $existing = $db->fetch(
            "SELECT id FROM user_push_subscriptions WHERE user_id = ? AND endpoint = ?",
            [$userId, $endpoint]
        );
        
        if ($existing) {
            // Update existing subscription
            $db->query(
                "UPDATE user_push_subscriptions SET p256dh = ?, auth = ?, updated_at = NOW() WHERE id = ?",
                [$p256dh, $auth_key, $existing['id']]
            );
            error_log("Push subscription updated for user $userId");
        } else {
            // Create new subscription
            $db->query(
                "INSERT INTO user_push_subscriptions (user_id, endpoint, p256dh, auth, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())",
                [$userId, $endpoint, $p256dh, $auth_key]
            );
            error_log("Push subscription created for user $userId");
        }
        
        http_response_code(200);
        echo json_encode(['success' => true, 'message' => 'Push subscription saved']);
    } catch (Exception $e) {
        error_log("Error saving push subscription: " . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => 'Failed to save subscription', 'message' => $e->getMessage()]);
    }
}

// ==================== Language Handlers ====================

function handleSetLanguage() {
    $input = json_decode(file_get_contents('php://input'), true);
    $language = $input['language'] ?? 'en';

    if (!in_array($language, ['en', 'ar'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid language']);
        return;
    }

    $_SESSION['language'] = $language;
    setcookie('language', $language, time() + (365 * 24 * 60 * 60), '/');
    echo json_encode(['success' => true, 'language' => $language]);
}

// ==================== Sync Handler ====================

function handleSync() {
    // Process offline sync queue
    echo json_encode(['success' => true, 'synced' => 0]);
}

// ==================== User Handlers ====================

function handleGetUsers() {
    global $auth, $db;

    if (!$auth->hasPermission('view_staff') && !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $pagination = isset($_GET['pagination']) ? (int)$_GET['pagination'] : 1;
    $limit = 15;
    $offset = (int)(($pagination - 1) * $limit);
    
    // Optional role filter
    $role = $_GET['role'] ?? null;
    $whereClause = '';
    
    if ($role) {
        $role = preg_replace('/[^a-zA-Z0-9_-]/', '', $role);
        $whereClause = " WHERE role = '$role'";
    }

    // Note: LIMIT requires integer values, not parameters, so we use sprintf
    $users = $db->fetchAll(
        "SELECT id, name, email, phone, whatsapp, address, role, active, created_at FROM users" . $whereClause . " LIMIT " . (int)$offset . ", " . (int)$limit
    );

    $total = $db->fetch("SELECT COUNT(*) as count FROM users" . $whereClause)['count'];

    echo json_encode([
        'success' => true,
        'users' => $users,
        'total' => $total,
        'page' => $pagination,
        'per_page' => $limit
    ]);
}

function handleGetUser($id) {
    global $auth, $db;

    if (!$auth->hasPermission('view_staff') && !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'User ID required']);
        return;
    }

    $user = $db->fetch(
        "SELECT id, name, email, phone, whatsapp, address, role, active, created_at, photo FROM users WHERE id = ?",
        [$id]
    );

    if (!$user) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'User not found']);
        return;
    }

    echo json_encode(['success' => true, 'user' => $user]);
}

function handleCreateUser() {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('create_staff') && !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    // Handle multipart form data (with file upload) or JSON
    if (isset($_FILES['photo'])) {
        $input = $_POST;
    } else {
        $input = json_decode(file_get_contents('php://input'), true);
    }

    // Validation
    if (empty($input['name']) || empty($input['email'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields: name and email']);
        return;
    }

    // Check if email already exists
    $existing = $db->fetch("SELECT id FROM users WHERE email = ?", [$input['email']]);
    if ($existing) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Email already exists']);
        return;
    }

    // Generate temporary password if not provided
    $password = $input['password'] ?? bin2hex(random_bytes(8));
    $password_hash = password_hash($password, PASSWORD_BCRYPT);

    // Generate email verification token
    $verification_token = bin2hex(random_bytes(32));

    // Handle photo upload
    $photoPath = null;
    if (isset($_FILES['photo']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
        $photoPath = handleUserPhotoUpload($_FILES['photo']);
    }

    $db->query(
        "INSERT INTO users (name, email, password, role, phone, whatsapp, address, photo, active, email_verified_at, verification_token, created_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, NOW())",
        [
            $input['name'],
            $input['email'],
            $password_hash,
            $input['role'] ?? 'guard',
            $input['phone'] ?? null,
            $input['whatsapp'] ?? null,
            $input['address'] ?? null,
            $photoPath,
            $input['active'] ? 1 : 0,
            $verification_token
        ]
    );

    $userId = $db->lastInsertId();

    // Send account creation notification to new user
    if ($notification) {
        $role = $input['role'] ?? 'guard';
        $notification
            ->reset()
            ->forUser($userId)
            ->setTitle('Account Created Successfully ✓')
            ->setMessage("Welcome! Your account has been created as a {$role}. Your temporary password: {$password}. Please log in and change your password.")
            ->setType('success')
            ->addCallToAction('Login', "/login?email=" . urlencode($input['email']))
            ->via(['email', 'sms', 'push', 'in-app'])
            ->send();
    }

    // Send notification to admins about new user creation
    $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin') LIMIT 10");
    if ($notification && !empty($adminUsers)) {
        foreach ($adminUsers as $admin) {
            $notification
                ->reset()
                ->forUser($admin['id'])
                ->setTitle('New User Account Created')
                ->setMessage($input['name'] . " ({$input['email']}) has been added as a " . ($input['role'] ?? 'guard'))
                ->setType('info')
                ->addCallToAction('View User', "/dashboard?page=users&id={$userId}")
                ->via(['push', 'in-app'])
                ->send();
        }
    }

    echo json_encode([
        'success' => true,
        'id' => $userId,
        'message' => 'User created successfully'
    ]);
}

function handleUpdateUser($id) {
    global $auth, $db;

    if (!$auth->hasPermission('edit_staff') && !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    // Extract ID from parameters or JSON payload
    if (!$id && !isset($_POST['id'])) {
        $input = json_decode(file_get_contents('php://input'), true);
        $id = $input['id'] ?? null;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'User ID is required']);
        return;
    }

    // Handle multipart form data (with file upload) or JSON
    if (isset($_FILES['photo']) || isset($_POST['name'])) {
        $input = $_POST;
    } else {
        $input = json_decode(file_get_contents('php://input'), true);
    }

    if (!$input || empty($input['name']) || empty($input['email'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields: name and email']);
        return;
    }

    // Handle photo upload
    $photoPath = null;
    if (isset($_FILES['photo']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
        $photoPath = handleUserPhotoUpload($_FILES['photo']);
    }

    if ($photoPath) {
        $db->query(
            "UPDATE users SET name = ?, email = ?, role = ?, phone = ?, whatsapp = ?, address = ?, photo = ?, active = ? WHERE id = ?",
            [
                $input['name'],
                $input['email'],
                $input['role'],
                $input['phone'] ?? null,
                $input['whatsapp'] ?? null,
                $input['address'] ?? null,
                $photoPath,
                $input['active'] ? 1 : 0,
                $id
            ]
        );
    } else {
        $db->query(
            "UPDATE users SET name = ?, email = ?, role = ?, phone = ?, whatsapp = ?, address = ?, active = ? WHERE id = ?",
            [
                $input['name'],
                $input['email'],
                $input['role'],
                $input['phone'] ?? null,
                $input['whatsapp'] ?? null,
                $input['address'] ?? null,
                $input['active'] ? 1 : 0,
                $id
            ]
        );
    }

    echo json_encode(['success' => true, 'message' => 'User updated successfully']);
}

function handleDeleteUser($id) {
    global $auth, $db;

    if (!$auth->hasPermission('delete_staff') && !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $db->query("DELETE FROM users WHERE id = ?", [$id]);
    echo json_encode(['success' => true, 'message' => 'User deleted successfully']);
}

// ==================== Client Handlers ====================

function handleGetClients() {
    global $auth, $db;

    if (!$auth->hasPermission('view_clients')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $pagination = isset($_GET['pagination']) ? (int)$_GET['pagination'] : 1;
    $limit = 15;
    $offset = (int)(($pagination - 1) * $limit);

    $clients = $db->fetchAll(
        "SELECT c.*, u.name as created_by_name FROM clients c 
         LEFT JOIN users u ON c.created_by = u.id 
         WHERE c.active = 1 ORDER BY c.name ASC LIMIT " . (int)$offset . ", " . (int)$limit
    );

    $total = $db->fetch("SELECT COUNT(*) as count FROM clients WHERE active = 1")['count'];

    echo json_encode([
        'success' => true,
        'clients' => $clients,
        'total' => $total,
        'page' => $pagination,
        'per_page' => $limit
    ]);
}

function handleCreateClient() {
    global $auth, $db, $notification;

    try {
        if (!$auth->hasPermission('create_clients')) {
            http_response_code(403);
            echo json_encode(['error' => 'Forbidden: You do not have permission to create clients']);
            return;
        }

        $input = json_decode(file_get_contents('php://input'), true);

        if (empty($input['name'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Name is required']);
            return;
        }

        $db->query(
            "INSERT INTO clients (name, email, phone, whatsapp, address, contact_person, company_type, created_by, created_at)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())",
            [
                $input['name'],
                $input['email'] ?? null,
                $input['phone'] ?? null,
                $input['whatsapp'] ?? null,
                $input['address'] ?? null,
                $input['contact_person'] ?? null,
                $input['company_type'] ?? null,
                $_SESSION['user_id'] ?? 1
            ]
        );

        $clientId = $db->lastInsertId();

        // Send welcome notification if client has email
        if ($notification && !empty($input['email'])) {
            // Try to find if client has a user account
            $clientUser = $db->fetch("SELECT id FROM users WHERE email = ?", [$input['email']]);
            if ($clientUser) {
                $notification
                    ->reset()
                    ->forUser($clientUser['id'])
                    ->setTitle('Account Registered as Client')
                    ->setMessage("Your account has been registered in our system as a client. You can now access your invoices and payments.")
                    ->setType('success')
                    ->addCallToAction('View Dashboard', "/dashboard")
                    ->via(['email', 'push', 'in-app'])
                    ->send();
            }
        }

        // Send notification to sales/admin staff
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'sales') LIMIT 10");
        if ($notification && !empty($adminUsers)) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('New Client Registered')
                    ->setMessage($input['name'] . " (" . ($input['company_type'] ?? 'Other') . ") - " . ($input['contact_person'] ?? 'No contact person'))
                    ->setType('info')
                    ->addCallToAction('View Client', "/dashboard?page=clients&id={$clientId}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode(['success' => true, 'id' => $clientId]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
    }
}

function handleUpdateClient($id) {
    global $auth, $db;

    try {
        if (!$auth->hasPermission('edit_clients')) {
            http_response_code(403);
            echo json_encode(['error' => 'Forbidden: You do not have permission to edit clients']);
            return;
        }

        $input = json_decode(file_get_contents('php://input'), true);

        $db->query(
            "UPDATE clients SET name = ?, email = ?, phone = ?, whatsapp = ?, address = ?, contact_person = ?, company_type = ?, updated_at = NOW() WHERE id = ?",
            [
                $input['name'],
                $input['email'] ?? null,
                $input['phone'] ?? null,
                $input['whatsapp'] ?? null,
                $input['address'] ?? null,
                $input['contact_person'] ?? null,
                $input['company_type'] ?? null,
                $id
            ]
        );

        echo json_encode(['success' => true]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
    }
}

function handleDeleteClient($id) {
    global $auth, $db;

    try {
        if (!$auth->hasPermission('delete_clients')) {
            http_response_code(403);
            echo json_encode(['error' => 'Forbidden: You do not have permission to delete clients']);
            return;
        }

        $db->query("UPDATE clients SET active = 0, updated_at = NOW() WHERE id = ?", [$id]);
        echo json_encode(['success' => true]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
    }
}

// ==================== Post Handlers ====================

function handleGetPosts() {
    global $auth, $db;

    if (!$auth->hasPermission('view_posts')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $pagination = isset($_GET['pagination']) ? (int)$_GET['pagination'] : 1;
    $limit = 15;
    $offset = (int)(($pagination - 1) * $limit);

    $posts = $db->fetchAll(
        "SELECT p.*, u.name as owner_name, creator.name as created_by_name FROM posts p 
         LEFT JOIN users u ON p.owner_id = u.id 
         LEFT JOIN users creator ON p.created_by = creator.id 
         WHERE p.status = 'Active' ORDER BY p.name ASC LIMIT " . (int)$offset . ", " . (int)$limit
    );

    $total = $db->fetch("SELECT COUNT(*) as count FROM posts WHERE status = 'Active'")['count'];

    echo json_encode([
        'success' => true,
        'posts' => $posts,
        'total' => $total,
        'page' => $pagination,
        'per_page' => $limit
    ]);
}

function handleGetPost($id) {
    global $auth, $db;

    if (!$auth->hasPermission('view_posts')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Post ID required']);
        return;
    }

    $post = $db->fetch(
        "SELECT p.*, u.name as owner_name, creator.name as created_by_name FROM posts p 
         LEFT JOIN users u ON p.owner_id = u.id 
         LEFT JOIN users creator ON p.created_by = creator.id 
         WHERE p.id = ?",
        [$id]
    );

    if (!$post) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Post not found']);
        return;
    }

    echo json_encode([
        'success' => true,
        'post' => $post
    ]);
}

function handleCreatePost() {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('create_posts')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['name'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Facility name is required']);
        return;
    }

    try {
        error_log('handleCreatePost payload: ' . json_encode($input));
        
        // Get owner/client details for notification
        $client = $input['owner_id'] ? $db->fetch("SELECT id, name, email FROM users WHERE id = ?", [$input['owner_id']]) : null;
        
        $result = $db->query(
            "INSERT INTO posts (
                name, facility_type, zone, owner_id, location, owner_phone, owner_address, latitude, longitude,
                assessment_security_post, assessment_fence, assessment_cctv_alarms, assessment_fire_extinguishers, 
                assessment_lighting, assessment_toilet, assessment_accessibility,
                assessment_crime_rate, assessment_public_security_proximity, assessment_living_standards, assessment_labour_availability,
                observations, recommendations, surface_area, access_points, num_buildings, content_value, grand_total, services,
                contract_date, contract_end, status, created_by, created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())",
            [
                $input['name'] ?? null,
                $input['facility_type'] ?? null,
                $input['zone'] ?? null,
                $input['owner_id'] ?? null,
                $input['location'] ?? null,
                $input['owner_phone'] ?? null,
                $input['owner_address'] ?? null,
                $input['latitude'] ?? 0,
                $input['longitude'] ?? 0,
                $input['assessment_security_post'] ?? null,
                $input['assessment_fence'] ?? null,
                $input['assessment_cctv_alarms'] ?? null,
                $input['assessment_fire_extinguishers'] ?? null,
                $input['assessment_lighting'] ?? null,
                $input['assessment_toilet'] ?? null,
                $input['assessment_accessibility'] ?? null,
                $input['assessment_crime_rate'] ?? null,
                $input['assessment_public_security_proximity'] ?? null,
                $input['assessment_living_standards'] ?? null,
                $input['assessment_labour_availability'] ?? null,
                $input['observations'] ?? null,
                $input['recommendations'] ?? null,
                $input['surface_area'] ?? null,
                $input['access_points'] ?? null,
                $input['num_buildings'] ?? null,
                $input['content_value'] ?? null,
                $input['grand_total'] ?? 0,
                $input['services'] ?? null,
                $input['contract_date'] ?? null,
                $input['contract_end'] ?? null,
                'Active',
                $_SESSION['user_id'] ?? 1
            ]
        );

        $postId = $db->lastInsertId();
        error_log('Post created successfully with ID: ' . $postId);

        // Send post creation notification to client
        if ($notification && $client) {
            $notification
                ->reset()
                ->forUser($client['id'])
                ->setTitle('Facility/Post Created ✓')
                ->setMessage("Facility \"" . $input['name'] . "\" at " . ($input['location'] ?? 'TBD') . " has been registered in the system.")
                ->setType('success')
                ->addCallToAction('View Details', "/dashboard?page=posts&id={$postId}")
                ->via(['email', 'push', 'in-app'])
                ->send();
        }

        // Send notification to relevant staff
        $staffUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'supervisor', 'operations') LIMIT 15");
        if ($notification && !empty($staffUsers)) {
            foreach ($staffUsers as $staff) {
                $notification
                    ->reset()
                    ->forUser($staff['id'])
                    ->setTitle('New Facility/Post Created')
                    ->setMessage($input['name'] . " - " . ($input['location'] ?? 'Location TBD') . " - Total Value: " . number_format($input['grand_total'] ?? 0, 2))
                    ->setType('info')
                    ->addCallToAction('View', "/dashboard?page=posts&id={$postId}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode(['success' => true, 'id' => $postId]);
    } catch (Exception $e) {
        error_log('handleCreatePost error: ' . $e->getMessage());
        error_log('Stack trace: ' . $e->getTraceAsString());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false, 'trace' => $e->getTraceAsString()]);
    }
}

function handleUpdatePost($id) {
    global $auth, $db;

    if (!$auth->hasPermission('edit_posts')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    try {
        error_log('handleUpdatePost payload: ' . json_encode($input));
        error_log('Updating post ID: ' . $id);
        
        $db->query(
            "UPDATE posts SET 
                name = ?, facility_type = ?, zone = ?, owner_id = ?, location = ?, owner_phone = ?, owner_address = ?, latitude = ?, longitude = ?,
                assessment_security_post = ?, assessment_fence = ?, assessment_cctv_alarms = ?, 
                assessment_fire_extinguishers = ?, assessment_lighting = ?, assessment_toilet = ?, assessment_accessibility = ?,
                assessment_crime_rate = ?, assessment_public_security_proximity = ?, assessment_living_standards = ?, assessment_labour_availability = ?,
                observations = ?, recommendations = ?, surface_area = ?, access_points = ?, num_buildings = ?, content_value = ?, grand_total = ?,
                services = ?, contract_date = ?, contract_end = ?,
                updated_at = NOW()
            WHERE id = ?",
            [
                $input['name'] ?? null,
                $input['facility_type'] ?? null,
                $input['zone'] ?? null,
                $input['owner_id'] ?? null,
                $input['location'] ?? null,
                $input['owner_phone'] ?? null,
                $input['owner_address'] ?? null,
                $input['latitude'] ?? 0,
                $input['longitude'] ?? 0,
                $input['assessment_security_post'] ?? null,
                $input['assessment_fence'] ?? null,
                $input['assessment_cctv_alarms'] ?? null,
                $input['assessment_fire_extinguishers'] ?? null,
                $input['assessment_lighting'] ?? null,
                $input['assessment_toilet'] ?? null,
                $input['assessment_accessibility'] ?? null,
                $input['assessment_crime_rate'] ?? null,
                $input['assessment_public_security_proximity'] ?? null,
                $input['assessment_living_standards'] ?? null,
                $input['assessment_labour_availability'] ?? null,
                $input['observations'] ?? null,
                $input['recommendations'] ?? null,
                $input['surface_area'] ?? null,
                $input['access_points'] ?? null,
                $input['num_buildings'] ?? null,
                $input['content_value'] ?? null,
                $input['grand_total'] ?? 0,
                $input['services'] ?? null,
                $input['contract_date'] ?? null,
                $input['contract_end'] ?? null,
                $id
            ]
        );

        error_log('Post updated successfully');
        echo json_encode(['success' => true]);
    } catch (Exception $e) {
        error_log('handleUpdatePost error: ' . $e->getMessage());
        error_log('Stack trace: ' . $e->getTraceAsString());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false, 'trace' => $e->getTraceAsString()]);
    }
}

function handleDeletePost($id) {
    global $auth, $db;

    if (!$auth->hasPermission('delete_posts')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        $db->query("UPDATE posts SET status = 'Closed', updated_at = NOW() WHERE id = ?", [$id]);
        echo json_encode(['success' => true]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}





// ==================== Draft Handlers ====================

function handleSaveDraft() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    
    if (empty($input['formId']) || empty($input['data'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing required fields']);
        return;
    }

    $userId = $_SESSION['user_id'] ?? null;
    $formId = $input['formId'];
    $data = json_encode($input['data']);
    
    if (!$userId) {
        http_response_code(401);
        echo json_encode(['error' => 'User not authenticated']);
        return;
    }

    // Delete old draft for this form and user (keep only latest)
    $db->query(
        "DELETE FROM drafts WHERE user_id = ? AND form_id = ?",
        [$userId, $formId]
    );

    // Insert new draft
    $db->query(
        "INSERT INTO drafts (user_id, form_id, data, created_at) VALUES (?, ?, ?, NOW())",
        [$userId, $formId, $data]
    );

    echo json_encode(['success' => true, 'message' => 'Draft saved']);
}

function handleGetDraft($formId) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $userId = $_SESSION['user_id'] ?? null;
    
    if (!$userId || !$formId) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing parameters']);
        return;
    }

    $draft = $db->query(
        "SELECT * FROM drafts WHERE user_id = ? AND form_id = ? ORDER BY created_at DESC LIMIT 1",
        [$userId, $formId]
    )->fetch();

    if (!$draft) {
        http_response_code(404);
        echo json_encode(['error' => 'No draft found']);
        return;
    }

    echo json_encode([
        'success' => true,
        'draft' => [
            'id' => $draft['id'],
            'data' => json_decode($draft['data'], true),
            'created_at' => $draft['created_at']
        ]
    ]);
}

function handleDeleteDraft($formId) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $userId = $_SESSION['user_id'] ?? null;
    
    if (!$userId || !$formId) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing parameters']);
        return;
    }

    $db->query(
        "DELETE FROM drafts WHERE user_id = ? AND form_id = ?",
        [$userId, $formId]
    );

    echo json_encode(['success' => true]);
}

/**
 * Get all staff members
 */
function handleGetStaff() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    try {
        $staff = $db->fetchAll(
            "SELECT s.*, u.name as employee_name, u.email 
             FROM staff s 
             JOIN users u ON s.user_id = u.id 
             ORDER BY s.created_at DESC"
        );

        echo json_encode([
            'success' => true,
            'data' => $staff
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Failed to fetch staff',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Create new staff member
 */
function handleCreateStaff() {
    global $auth, $db, $notification;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_staff')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        $data = json_decode(file_get_contents('php://input'), true);

        // Validate required fields
        if (empty($data['user_id']) || empty($data['staff_id']) || empty($data['position'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Missing required fields']);
            return;
        }

        // Check if user already has staff record
        $existing = $db->fetch("SELECT id FROM staff WHERE user_id = ?", [$data['user_id']]);
        if ($existing) {
            http_response_code(409);
            echo json_encode(['error' => 'User already has a staff record']);
            return;
        }

        // Get user details for notification
        $user = $db->fetch("SELECT id, name, email FROM users WHERE id = ?", [$data['user_id']]);

        // Prepare remunerations
        $remunerations = !empty($data['remunerations']) ? $data['remunerations'] : null;

        // Insert staff record
        $db->query(
            "INSERT INTO staff (user_id, staff_id, position, zone, remunerations, total_monthly_pay, contract_date, contract_end, status, notes)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [
                $data['user_id'],
                $data['staff_id'],
                $data['position'],
                $data['zone'] ?? null,
                $remunerations,
                (float)($data['total_monthly_pay'] ?? 0),
                !empty($data['contract_date']) ? $data['contract_date'] : date('Y-m-d'),
                $data['contract_end'] ?? null,
                $data['status'] ?? 'Probation',
                $data['notes'] ?? null
            ]
        );

        $staffId = $db->lastInsertId();

        // Send staff assignment notification to employee
        if ($notification && $user) {
            $notification
                ->reset()
                ->forUser($user['id'])
                ->setTitle('Staff Position Assigned ✓')
                ->setMessage("Congratulations! You have been assigned as " . ($data['position'] ?? 'Staff') . " starting from " . (!empty($data['contract_date']) ? $data['contract_date'] : date('Y-m-d')) . ". Monthly pay: " . number_format($data['total_monthly_pay'] ?? 0, 2))
                ->setType('success')
                ->addCallToAction('View Details', "/dashboard?page=profile&staff_id={$staffId}")
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }

        // Send notification to HR/admin about new staff assignment
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'hr') LIMIT 10");
        if ($notification && !empty($adminUsers)) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('New Staff Member Assigned')
                    ->setMessage(($user['name'] ?? 'Unknown') . " assigned as " . ($data['position'] ?? 'Staff') . ". ID: " . ($data['staff_id'] ?? 'N/A'))
                    ->setType('info')
                    ->addCallToAction('View Staff', "/dashboard?page=staff&id={$staffId}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true,
            'message' => 'Staff member created successfully',
            'id' => $staffId
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        error_log("Staff creation error: " . $e->getMessage() . "\n" . $e->getTraceAsString());
        echo json_encode([
            'error' => 'Failed to create staff member',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Update staff member
 */
function handleUpdateStaff($id) {
    global $auth, $db, $notification;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_staff')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['error' => 'Staff ID required']);
        return;
    }

    try {
        $data = json_decode(file_get_contents('php://input'), true);

        // Get original staff data to detect changes
        $originalStaff = $db->fetch("SELECT s.*, u.name, u.role FROM staff s JOIN users u ON s.user_id = u.id WHERE s.id = ?", [$id]);
        $user = $originalStaff ? $db->fetch("SELECT id, name, email FROM users WHERE id = ?", [$originalStaff['user_id']]) : null;

        // Update users table with basic info (name, email, phone, etc.)
        if (!empty($data['name']) || !empty($data['email'])) {
            $db->query(
                "UPDATE users SET 
                 name = ?, email = ?, phone = ?, whatsapp = ?, address = ?, role = ?, active = ?
                 WHERE id = ?",
                [
                    $data['name'] ?? null,
                    $data['email'] ?? null,
                    $data['phone'] ?? null,
                    $data['whatsapp'] ?? null,
                    $data['address'] ?? null,
                    $data['role'] ?? 'guard',
                    isset($data['active']) ? (int)$data['active'] : 1,
                    $id
                ]
            );
        }

        // Prepare remunerations
        $remunerations = !empty($data['remunerations']) ? $data['remunerations'] : null;

        // Check if salary changed
        $salaryChanged = isset($data['total_monthly_pay']) && $data['total_monthly_pay'] != ($originalStaff['total_monthly_pay'] ?? 0);
        $statusChanged = isset($data['status']) && $data['status'] != ($originalStaff['status'] ?? 'Probation');

        // Update staff record
        $db->query(
            "UPDATE staff SET 
             staff_id = ?, position = ?, zone = ?, remunerations = ?, 
             total_monthly_pay = ?, contract_date = ?, contract_end = ?, 
             status = ?, notes = ?
             WHERE id = ?",
            [
                $data['staff_id'] ?? '',
                $data['position'] ?? '',
                $data['zone'] ?? null,
                $remunerations,
                (float)($data['total_monthly_pay'] ?? 0),
                !empty($data['contract_date']) ? $data['contract_date'] : date('Y-m-d'),
                $data['contract_end'] ?? null,
                $data['status'] ?? 'Probation',
                $data['notes'] ?? null,
                $id
            ]
        );

        // Send notification if salary changed
        if ($salaryChanged && $notification && $user) {
            $notification
                ->reset()
                ->forUser($user['id'])
                ->setTitle('Salary Updated ✓')
                ->setMessage("Your monthly salary has been updated to " . number_format($data['total_monthly_pay'], 2) . ". Check your latest payslip for details.")
                ->setType('success')
                ->addCallToAction('View Payslip', "/dashboard?page=payroll")
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }

        // Send notification if status changed (e.g., from Probation to Confirmed)
        if ($statusChanged && $notification && $user) {
            $notification
                ->reset()
                ->forUser($user['id'])
                ->setTitle('Contract Status Updated')
                ->setMessage("Your employment status has been updated to: " . $data['status'])
                ->setType('info')
                ->addCallToAction('View Details', "/dashboard?page=profile")
                ->via(['email', 'push', 'in-app'])
                ->send();
        }

        // Notify HR/Admin about staff updates
        $hrUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'hr') LIMIT 10");
        if ($notification && !empty($hrUsers) && ($salaryChanged || $statusChanged)) {
            foreach ($hrUsers as $hr) {
                $changeMsg = [];
                if ($salaryChanged) $changeMsg[] = "Salary updated to " . number_format($data['total_monthly_pay'], 2);
                if ($statusChanged) $changeMsg[] = "Status changed to " . $data['status'];
                
                $notification
                    ->reset()
                    ->forUser($hr['id'])
                    ->setTitle('Staff Information Updated')
                    ->setMessage(($user['name'] ?? 'Unknown') . " - " . implode(", ", $changeMsg))
                    ->setType('info')
                    ->addCallToAction('View', "/dashboard?page=staff&id={$id}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true,
            'message' => 'Staff member updated successfully'
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        error_log("Staff update error: " . $e->getMessage() . "\n" . $e->getTraceAsString());
        echo json_encode([
            'error' => 'Failed to update staff member',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Delete staff member
 */
function handleDeleteStaff($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_staff')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['error' => 'Staff ID required']);
        return;
    }

    try {
        $db->query("DELETE FROM staff WHERE id = ?", [$id]);

        echo json_encode([
            'success' => true,
            'message' => 'Staff member deleted successfully'
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        error_log("Staff deletion error: " . $e->getMessage());
        echo json_encode([
            'error' => 'Failed to delete staff member',
            'message' => $e->getMessage()
        ]);
    }
}

// ==================== RBAC Handlers ====================

/**
 * Get all roles with permissions
 */
function handleGetRoles() {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        $rolesConfig = require config_path('roles.php');
        
        $roles = [];
        foreach ($rolesConfig as $roleName => $config) {
            $roles[] = [
                'name' => $roleName,
                'description' => $config['description'] ?? '',
                'permissions' => $config['permissions'] ?? [],
                'permission_count' => count($config['permissions'] ?? [])
            ];
        }

        echo json_encode([
            'success' => true,
            'roles' => $roles
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Failed to fetch roles',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Get all available permissions
 */
function handleGetPermissions() {
    global $auth;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    try {
        $rolesConfig = require config_path('roles.php');
        
        $allPermissions = [];
        foreach ($rolesConfig as $config) {
            if (isset($config['permissions'])) {
                $allPermissions = array_unique(array_merge($allPermissions, $config['permissions']));
            }
        }
        sort($allPermissions);

        echo json_encode([
            'success' => true,
            'permissions' => $allPermissions,
            'count' => count($allPermissions)
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Failed to fetch permissions',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Assign permission to role (updates config)
 * Note: This is a simplified version. In production, you'd want to persist this to database
 */
function handleAssignPermission() {
    global $auth;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['role']) || empty($input['permission'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing role or permission']);
        return;
    }

    // Note: In a production system, you would update a permissions table instead
    echo json_encode([
        'success' => true,
        'message' => 'Permission assigned successfully',
        'role' => $input['role'],
        'permission' => $input['permission']
    ]);
}

/**
 * Revoke permission from role
 */
function handleRevokePermission() {
    global $auth;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('manage_roles')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['role']) || empty($input['permission'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing role or permission']);
        return;
    }

    echo json_encode([
        'success' => true,
        'message' => 'Permission revoked successfully',
        'role' => $input['role'],
        'permission' => $input['permission']
    ]);
}

// ==================== Audit Log Handlers ====================

/**
 * Get audit logs
 */
function handleGetAuditLogs() {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('view_audit_logs')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        // Check if table exists
        $tables = $db->fetchAll("SHOW TABLES LIKE 'audit_logs'");
        if (empty($tables)) {
            echo json_encode([
                'logs' => [],
                'total' => 0,
                'message' => 'Audit logs table not yet created'
            ]);
            return;
        }

        $page = $_GET['page'] ?? 1;
        $limit = 50;
        $offset = ($page - 1) * $limit;

        $logs = $db->fetchAll(
            "SELECT al.*, u.name as user_name FROM audit_logs al 
             LEFT JOIN users u ON al.user_id = u.id 
             ORDER BY al.created_at DESC LIMIT ?, ?",
            [$offset, $limit]
        );

        $total = $db->fetch("SELECT COUNT(*) as count FROM audit_logs")['count'];

        echo json_encode([
            'success' => true,
            'logs' => $logs,
            'total' => $total,
            'page' => $page,
            'per_page' => $limit
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Failed to fetch audit logs',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Get login attempts
 */
function handleGetLoginAttempts() {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('view_audit_logs')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        // Check if table exists
        $tables = $db->fetchAll("SHOW TABLES LIKE 'login_attempts'");
        if (empty($tables)) {
            echo json_encode([
                'attempts' => [],
                'total' => 0,
                'message' => 'Login attempts table not yet created'
            ]);
            return;
        }

        $page = $_GET['page'] ?? 1;
        $limit = 50;
        $offset = ($page - 1) * $limit;
        $days = $_GET['days'] ?? 7; // Last 7 days by default

        $attempts = $db->fetchAll(
            "SELECT * FROM login_attempts 
             WHERE created_at > DATE_SUB(NOW(), INTERVAL ? DAY) 
             ORDER BY created_at DESC LIMIT ?, ?",
            [$days, $offset, $limit]
        );

        $total = $db->fetch(
            "SELECT COUNT(*) as count FROM login_attempts 
             WHERE created_at > DATE_SUB(NOW(), INTERVAL ? DAY)",
            [$days]
        )['count'];

        echo json_encode([
            'success' => true,
            'attempts' => $attempts,
            'total' => $total,
            'page' => $page,
            'per_page' => $limit,
            'days' => $days
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Failed to fetch login attempts',
            'message' => $e->getMessage()
        ]);
    }
}

// ==================== Bulk Import Handlers ====================

/**
 * Validate import file
 */
function handleValidateImportFile() {
    global $auth;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('create_staff')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    if (!isset($_FILES['file'])) {
        http_response_code(400);
        echo json_encode(['error' => 'No file provided']);
        return;
    }

    $file = $_FILES['file'];
    $filePath = $file['tmp_name'];

    try {
        // Check file type
        if (!in_array($file['type'], ['text/csv', 'application/vnd.ms-excel'])) {
            http_response_code(400);
            echo json_encode(['error' => 'Invalid file type. Only CSV files are accepted.']);
            return;
        }

        // Read and validate CSV
        $errors = [];
        $rows = [];
        $rowNumber = 0;
        $requiredFields = ['name', 'email', 'role'];

        if (($handle = fopen($filePath, 'r')) !== false) {
            $headers = null;
            
            while (($data = fgetcsv($handle, 1000, ',')) !== false) {
                $rowNumber++;
                
                // First row is header
                if ($rowNumber === 1) {
                    $headers = array_map('trim', $data);
                    
                    // Validate headers
                    foreach ($requiredFields as $field) {
                        if (!in_array($field, $headers)) {
                            $errors[] = "Missing required column: $field";
                        }
                    }
                    continue;
                }

                // Validate each row
                $row = array_combine($headers, $data);
                
                if (empty($row['name'])) {
                    $errors[] = "Row $rowNumber: Name is required";
                }
                
                if (empty($row['email']) || !filter_var($row['email'], FILTER_VALIDATE_EMAIL)) {
                    $errors[] = "Row $rowNumber: Valid email is required";
                }
                
                if (empty($row['role']) || !in_array($row['role'], ['admin', 'manager', 'controller', 'guard', 'client'])) {
                    $errors[] = "Row $rowNumber: Invalid role. Must be one of: admin, manager, controller, guard, client";
                }
                
                $rows[] = $row;
            }
            fclose($handle);
        }

        echo json_encode([
            'success' => empty($errors),
            'errors' => $errors,
            'row_count' => count($rows),
            'preview' => array_slice($rows, 0, 5)
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'File validation failed',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Bulk import users from CSV
 */
function handleBulkImportUsers() {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$auth->hasPermission('create_staff')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    if (!isset($_FILES['file'])) {
        http_response_code(400);
        echo json_encode(['error' => 'No file provided']);
        return;
    }

    $file = $_FILES['file'];
    $filePath = $file['tmp_name'];
    $imported = 0;
    $failed = 0;
    $errors = [];
    $rows = [];

    try {
        if (($handle = fopen($filePath, 'r')) !== false) {
            $headers = null;
            $rowNumber = 0;

            while (($data = fgetcsv($handle, 1000, ',')) !== false) {
                $rowNumber++;
                
                if ($rowNumber === 1) {
                    $headers = array_map('trim', $data);
                    continue;
                }

                try {
                    $row = array_combine($headers, $data);
                    
                    // Validation
                    if (empty($row['name']) || empty($row['email'])) {
                        $failed++;
                        $errors[] = "Row $rowNumber: Missing required fields";
                        continue;
                    }

                    // Check if email already exists
                    if ($db->fetch("SELECT id FROM users WHERE email = ?", [$row['email']])) {
                        $failed++;
                        $errors[] = "Row $rowNumber: Email {$row['email']} already exists";
                        continue;
                    }

                    // Generate temporary password
                    $tempPassword = bin2hex(random_bytes(8));
                    $passwordHash = password_hash($tempPassword, PASSWORD_BCRYPT);
                    $verificationToken = bin2hex(random_bytes(32));

                    // Insert user
                    $db->query(
                        "INSERT INTO users (name, email, password, role, phone, address, verification_token, active, created_at) 
                         VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())",
                        [
                            $row['name'],
                            $row['email'],
                            $passwordHash,
                            $row['role'] ?? 'guard',
                            $row['phone'] ?? null,
                            $row['address'] ?? null,
                            $verificationToken,
                            1
                        ]
                    );

                    $rows[] = [
                        'name' => $row['name'],
                        'email' => $row['email'],
                        'role' => $row['role'] ?? 'guard',
                        'temp_password' => $tempPassword,
                        'verification_token' => $verificationToken
                    ];

                    $imported++;
                } catch (Exception $e) {
                    $failed++;
                    $errors[] = "Row $rowNumber: " . $e->getMessage();
                }
            }
            fclose($handle);
        }

        echo json_encode([
            'success' => true,
            'imported' => $imported,
            'failed' => $failed,
            'errors' => $errors,
            'users' => $rows
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode([
            'error' => 'Bulk import failed',
            'message' => $e->getMessage()
        ]);
    }
}

/**
 * Handle user photo upload
 */
function handleUserPhotoUpload($file) {
    // Validate file
    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!in_array($file['type'], $allowedTypes)) {
        return null;
    }

    // Check file size (5MB max)
    if ($file['size'] > 5 * 1024 * 1024) {
        return null;
    }

    // Create upload directory if it doesn't exist
    $uploadDir = __DIR__ . '/../uploads/profiles/';
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    // Generate unique filename
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    $filename = 'user_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
    $filepath = $uploadDir . $filename;

    // Move uploaded file
    if (move_uploaded_file($file['tmp_name'], $filepath)) {
        // Return relative path from web root
        return '/public/uploads/profiles/' . $filename;
    }

    return null;
}

// ==================== Invoice Handlers ====================

function handleGetInvoices() {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $pagination = isset($_GET['pagination']) ? (int)$_GET['pagination'] : 1;
    $limit = 15;
    $offset = (int)(($pagination - 1) * $limit);

    // Get filter parameters
    $status = isset($_GET['status']) ? trim($_GET['status']) : '';
    $month = isset($_GET['month']) ? trim($_GET['month']) : '';
    $search = isset($_GET['search']) ? trim($_GET['search']) : '';

    // Build WHERE clause
    $where = '1=1';
    $params = [];

    if (!empty($status)) {
        $where .= ' AND i.status = ?';
        $params[] = $status;
    }

    if (!empty($month)) {
        $where .= ' AND i.invoice_month LIKE ?';
        $params[] = $month . '%';
    }

    if (!empty($search)) {
        $where .= ' AND (i.invoice_number LIKE ? OR u.name LIKE ?)';
        $params[] = '%' . $search . '%';
        $params[] = '%' . $search . '%';
    }

    // Build query with filters
    $query = "SELECT i.*, u.name as owner_name, p.name as post_name FROM invoices i 
              LEFT JOIN users u ON i.owner_id = u.id
              LEFT JOIN posts p ON i.post_id = p.id
              WHERE " . $where . "
              ORDER BY i.invoice_date DESC LIMIT " . (int)$offset . ", " . (int)$limit;

    // Execute with parameters
    $invoices = empty($params) ? $db->fetchAll($query) : $db->fetchAll($query, $params);
    
    // Ensure total_amount is set for each invoice
    foreach ($invoices as &$inv) {
        if (empty($inv['total_amount']) && !empty($inv['subtotal'])) {
            $inv['total_amount'] = $inv['subtotal'] + ($inv['tax_amount'] ?? 0);
        }
        if (empty($inv['total_amount'])) {
            $inv['total_amount'] = 0;
        }
    }
    unset($inv);

    // Get total count with filters
    $countQuery = "SELECT COUNT(*) as count FROM invoices i 
                   LEFT JOIN users u ON i.owner_id = u.id
                   WHERE " . $where;
    $total = empty($params) ? $db->fetch($countQuery)['count'] : $db->fetch($countQuery, $params)['count'];

    echo json_encode([
        'success' => true,
        'invoices' => $invoices,
        'total' => $total,
        'page' => $pagination,
        'per_page' => $limit,
        'filters' => [
            'status' => $status,
            'month' => $month,
            'search' => $search
        ]
    ]);
}

function handleGetInvoice($id) {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Invoice ID required']);
        return;
    }

    $invoice = $db->fetch(
        "SELECT i.*, u.name as owner_name, u.email as owner_email 
         FROM invoices i
         LEFT JOIN users u ON i.owner_id = u.id
         WHERE i.id = ?",
        [$id]
    );

    if (!$invoice) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invoice not found']);
        return;
    }

    // Parse services from invoice or get from post if not set
    $services = null;
    if (!empty($invoice['services'])) {
        $services = is_string($invoice['services']) ? json_decode($invoice['services'], true) : $invoice['services'];
    } elseif (!empty($invoice['post_id'])) {
        $post = $db->fetch("SELECT services FROM posts WHERE id = ?", [$invoice['post_id']]);
        if ($post && !empty($post['services'])) {
            $services = is_string($post['services']) ? json_decode($post['services'], true) : $post['services'];
        }
    }

    // Get invoice items
    $items = $db->fetchAll("SELECT * FROM invoice_items WHERE invoice_id = ?", [$id]);

    // Calculate totals if not already set
    if (empty($invoice['total_amount']) || empty($invoice['subtotal'])) {
        $subtotal = 0;
        foreach ($items as $item) {
            $subtotal += $item['line_total'] ?? 0;
        }
        
        // Use stored values or calculated ones
        $invoice['subtotal'] = $invoice['subtotal'] ?? $subtotal;
        $invoice['tax_amount'] = $invoice['tax_amount'] ?? 0;
        $invoice['total_amount'] = $invoice['subtotal'] + $invoice['tax_amount'];
    }

    // Get letterhead and company info from settings
    $settings = $db->fetch("SELECT site_name, site_acronym, logo, letterhead FROM settings LIMIT 1");

    // Format company response
    $company = $settings ? [
        'site_name' => $settings['site_name'] ?? 'SSCI Office',
        'site_acronym' => $settings['site_acronym'] ?? 'SSCI',
        'logo' => $settings['logo'] ?? null,
        'letterhead' => $settings['letterhead'] ?? null
    ] : [
        'site_name' => 'SSCI Office',
        'site_acronym' => 'SSCI',
        'logo' => null,
        'letterhead' => null
    ];

    echo json_encode([
        'success' => true,
        'invoice' => $invoice,
        'items' => $items,
        'services' => $services,
        'company' => $company
    ]);
}

function handleCreateInvoice() {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

    // Validation
    if (empty($input['owner_id']) || empty($input['invoice_date'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    // Generate invoice number
    $invoiceNumber = generateInvoiceNumber($db);
    $userId = $auth->getCurrentUser()['id'];

    // Get client details for notification
    $client = $db->fetch("SELECT id, name, email, phone FROM users WHERE id = ?", [$input['owner_id']]);

    // Get services and calculate totals from the post if post_id is provided
    $services = null;
    $servicesJson = null;
    $subtotal = 0;
    $taxAmount = 0;
    $totalAmount = 0;

    if (!empty($input['post_id'])) {
        $post = $db->fetch("SELECT services, grand_total FROM posts WHERE id = ?", [$input['post_id']]);
        if ($post && !empty($post['services'])) {
            $services = $post['services'];
            $servicesJson = is_string($services) ? $services : json_encode($services);
            $subtotal = floatval($post['grand_total'] ?? 0);
            // TODO: Get tax rate from settings if needed
            $totalAmount = $subtotal + $taxAmount;
        }
    }
    
    // Allow custom services if provided in input
    if (!empty($input['services'])) {
        $customServices = $input['services'];
        if (is_string($customServices)) {
            $customServices = json_decode($customServices, true);
        }
        if (is_array($customServices)) {
            $servicesJson = json_encode($customServices);
            // Recalculate totals from custom services
            $subtotal = 0;
            foreach ($customServices as $service) {
                $subtotal += floatval($service['total'] ?? 0);
            }
            $totalAmount = $subtotal + $taxAmount;
        }
    }

    // Set invoice_month from invoice_date (YYYY-MM format) or use provided value
    $invoiceMonth = !empty($input['invoice_month']) ? $input['invoice_month'] : substr($input['invoice_date'], 0, 7);

    $db->query(
        "INSERT INTO invoices (invoice_number, owner_id, post_id, invoice_date, invoice_month, due_date, currency, services, subtotal, tax_amount, total_amount, status, notes, created_by, created_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())",
        [
            $invoiceNumber,
            $input['owner_id'],
            $input['post_id'] ?? null,
            $input['invoice_date'],
            $invoiceMonth,
            $input['due_date'] ?? null,
            $input['currency'] ?? 'FCFA',
            $servicesJson,
            $subtotal,
            $taxAmount,
            $totalAmount,
            'Draft',
            $input['notes'] ?? null,
            $userId
        ]
    );

$invoiceId = $db->lastInsertId();

// Send invoice creation notification to client
if ($notification && $client) {
    $dueDate = $input['due_date'] ?? 'Not specified';
    $notification
        ->reset()
        ->forUser($client['id'])
        ->setTitle('New Invoice Created')
        ->setMessage("Invoice #{$invoiceNumber} has been created for " . number_format($totalAmount, 2) . ". Due date: {$dueDate}")
        ->setType('info')
        ->addCallToAction('View Invoice', "/dashboard?page=invoices&id={$invoiceId}")
        ->via(['email', 'sms', 'push', 'in-app'])
        ->send();
}

// Send notification to finance staff
$financeUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'finance') LIMIT 10");
if ($notification && !empty($financeUsers)) {
    foreach ($financeUsers as $finance) {
        $clientname = $client['name'] ?? 'Unknown';
        $notification
            ->reset()
            ->forUser($finance['id'])
            ->setTitle('Invoice Created')
            ->setMessage("Invoice #{$invoiceNumber} created for {$clientname} - Amount: " . number_format($totalAmount, 2))
            ->setType('info')
            ->addCallToAction('Review', "/dashboard?page=invoices&id={$invoiceId}")
            ->via(['push', 'in-app'])
            ->send();
    }
}
    echo json_encode([
        'success' => true,
        'id' => $invoiceId,
        'invoice_number' => $invoiceNumber,
        'message' => 'Invoice created successfully'
    ]);
}

function handleUpdateInvoice($id) {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

    // Get services and calculate totals if post_id changed
    $subtotal = null;
    $taxAmount = null;
    $totalAmount = null;
    $invoiceMonth = null;

    if (!empty($input['post_id'])) {
        $post = $db->fetch("SELECT services, grand_total FROM posts WHERE id = ?", [$input['post_id']]);
        if ($post && !empty($post['services'])) {
            $subtotal = floatval($post['grand_total'] ?? 0);
            $taxAmount = 0; // TODO: Get from settings
            $totalAmount = $subtotal + $taxAmount;
        }
    }

    // Set invoice_month from invoice_date
    if (!empty($input['invoice_date'])) {
        $invoiceMonth = substr($input['invoice_date'], 0, 7);
    }

    // Build update query dynamically to handle optional updates
    $updates = [];
    $params = [];

    if (isset($input['owner_id'])) {
        $updates[] = "owner_id = ?";
        $params[] = $input['owner_id'];
    }
    if (isset($input['post_id'])) {
        $updates[] = "post_id = ?";
        $params[] = $input['post_id'];
    }
    if (isset($input['invoice_date'])) {
        $updates[] = "invoice_date = ?";
        $params[] = $input['invoice_date'];
    }
    if (isset($input['due_date'])) {
        $updates[] = "due_date = ?";
        $params[] = $input['due_date'];
    }
    if (isset($input['status'])) {
        $updates[] = "status = ?";
        $params[] = $input['status'];
    }
    if (isset($input['notes'])) {
        $updates[] = "notes = ?";
        $params[] = $input['notes'];
    }
    if ($subtotal !== null) {
        $updates[] = "subtotal = ?";
        $params[] = $subtotal;
    }
    if ($taxAmount !== null) {
        $updates[] = "tax_amount = ?";
        $params[] = $taxAmount;
    }
    if ($totalAmount !== null) {
        $updates[] = "total_amount = ?";
        $params[] = $totalAmount;
    }
    if ($invoiceMonth !== null) {
        $updates[] = "invoice_month = ?";
        $params[] = $invoiceMonth;
    }
    if (isset($input['currency'])) {
        $updates[] = "currency = ?";
        $params[] = $input['currency'];
    }
    
    // Handle custom services updates
    if (!empty($input['services'])) {
        $customServices = $input['services'];
        if (is_string($customServices)) {
            $customServices = json_decode($customServices, true);
        }
        if (is_array($customServices)) {
            $updates[] = "services = ?";
            $params[] = json_encode($customServices);
            // Recalculate totals from custom services
            $newSubtotal = 0;
            foreach ($customServices as $service) {
                $newSubtotal += floatval($service['total'] ?? 0);
            }
            $updates[] = "subtotal = ?";
            $params[] = $newSubtotal;
            $updates[] = "total_amount = ?";
            $params[] = $newSubtotal; // TODO: Add tax if needed
        }
    }

    $updates[] = "updated_at = NOW()";
    $params[] = $id;

    if (!empty($updates)) {
        $query = "UPDATE invoices SET " . implode(", ", $updates) . " WHERE id = ?";
        $db->query($query, $params);
    }

    echo json_encode(['success' => true, 'message' => 'Invoice updated successfully']);
}

function handleDeleteInvoice($id) {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    // Get invoice details before deleting
    $invoice = $db->fetch("SELECT id, invoice_number, owner_id, total_amount FROM invoices WHERE id = ?", [$id]);
    
    if ($invoice) {
        // Get client details for notification
        $client = $db->fetch("SELECT id, name, email FROM users WHERE id = ?", [$invoice['owner_id']]);

        $db->query("DELETE FROM invoices WHERE id = ?", [$id]);

        // Send cancellation notification to client
        if ($notification && $client) {
            $notification
                ->reset()
                ->forUser($client['id'])
                ->setTitle('Invoice Cancelled')
                ->setMessage("Invoice #{$invoice['invoice_number']} (Amount: " . number_format($invoice['total_amount'], 2) . ") has been cancelled.")
                ->setType('warning')
                ->addCallToAction('View Dashboard', "/dashboard?page=invoices")
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }

        // Send notification to finance staff
        $financeUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'finance') LIMIT 10");
        if ($notification && !empty($financeUsers)) {
            foreach ($financeUsers as $finance) {
                $notification
                    ->reset()
                    ->forUser($finance['id'])
                    ->setTitle('Invoice Deleted')
                    ->setMessage("Invoice #{$invoice['invoice_number']} from " . ($client['name'] ?? 'Unknown') . " has been deleted.")
                    ->setType('warning')
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode(['success' => true, 'message' => 'Invoice deleted successfully']);
    } else {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invoice not found']);
    }
}

function handleGenerateInvoices() {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['billing_month']) || empty($input['post_ids'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    $billingMonth = $input['billing_month'];
    $postIds = $input['post_ids'];
    $invoiceNote = $input['invoice_note'] ?? '';
    $userId = $auth->getCurrentUser()['id'];
    $generatedCount = 0;
    
    // Get currency from config
    $appConfig = require __DIR__ . '/../config/app.php';
    $currency = $appConfig['currency']['code'] ?? 'FCFA';

    // Get billing month dates
    list($year, $month) = explode('-', $billingMonth);
    $invoiceDate = "$year-$month-01";
    $dueDate = date('Y-m-d', strtotime("$invoiceDate +30 days"));

    foreach ($postIds as $postId) {
        // Get post details including client
        $post = $db->fetch(
            "SELECT * FROM posts WHERE id = ? AND status = 'Active'",
            [$postId]
        );

        if (!$post) {
            error_log("Post ID $postId not found or inactive");
            continue;
        }

        // Get owner_id from post - handle if null
        $clientId = $post['owner_id'] ?? null;
        
        if (!$clientId) {
            error_log("Post ID $postId has no owner assigned");
            continue;
        }

        // Check if invoice already exists for this month
        $existing = $db->fetch(
            "SELECT id FROM invoices WHERE owner_id = ? AND post_id = ? AND DATE_FORMAT(invoice_date, '%Y-%m') = ?",
            [$clientId, $postId, $billingMonth]
        );

        if ($existing) continue;

        // Generate invoice
        $invoiceNumber = generateInvoiceNumber($db);
        
        // Prepare services JSON
        $servicesJson = null;
        if (!empty($post['services'])) {
            $servicesJson = is_string($post['services']) ? $post['services'] : json_encode($post['services']);
        }
        
        $db->query(
            "INSERT INTO invoices (invoice_number, owner_id, post_id, invoice_date, invoice_month, due_date, currency, services, status, notes, created_by, created_at)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())",
            [
                $invoiceNumber,
                $clientId,
                $postId,
                $invoiceDate,
                $billingMonth,
                $dueDate,
                $currency,
                $servicesJson,
                'Draft',
                $invoiceNote,
                $userId
            ]
        );

        $invoiceId = $db->lastInsertId();

        // Add line items from post services (JSON)
        $services = [];
        if (!empty($post['services'])) {
            $services = json_decode($post['services'], true) ?: [];
        }

        if (!empty($services) && is_array($services)) {
            // Add each service as a line item
            foreach ($services as $service) {
                $description = $service['name'] ?? 'Service';
                $quantity = (float)($service['quantity'] ?? 1);
                $unitPrice = (float)($service['price'] ?? 0);
                $lineTotal = $quantity * $unitPrice;

                $db->query(
                    "INSERT INTO invoice_items (invoice_id, description, quantity, unit_price, line_total)
                     VALUES (?, ?, ?, ?, ?)",
                    [
                        $invoiceId,
                        $description,
                        $quantity,
                        $unitPrice,
                        $lineTotal
                    ]
                );
            }
        } else {
            // Fallback: Add default line item if no services defined
            $monthName = date('F', strtotime($invoiceDate));
            $db->query(
                "INSERT INTO invoice_items (invoice_id, description, quantity, unit_price, line_total)
                 VALUES (?, ?, ?, ?, ?)",
                [
                    $invoiceId,
                    "Security Services - {$monthName}",
                    1,
                    5000.00,
                    5000.00
                ]
            );
        }

        // Update invoice totals
        updateInvoiceTotals($db, $invoiceId);

        // Send invoice creation notification to client
        global $notification;
        if ($notification) {
            $client = $db->fetch("SELECT id, name FROM users WHERE id = ?", [$clientId]);
            if ($client) {
                $notification
                    ->reset()
                    ->forUser($client['id'])
                    ->setTitle('Invoice Generated')
                    ->setMessage("Invoice #{$invoiceNumber} for {$post['post_name']} has been generated for " . date('F Y', strtotime($invoiceDate)))
                    ->setType('info')
                    ->addCallToAction('View Invoice', "/dashboard?page=invoices&id={$invoiceId}")
                    ->via(['email', 'in-app'])
                    ->send();
            }
        }

        $generatedCount++;
    }

    echo json_encode([
        'success' => true,
        'generated_count' => $generatedCount,
        'message' => "Generated $generatedCount invoices"
    ]);
}

function handleGetInvoiceStats() {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    $activeClients = $db->fetch("SELECT COUNT(*) as count FROM clients WHERE status = 'Active'")['count'];
    $expectedRevenue = $db->fetch("SELECT SUM(total_amount) as total FROM invoices WHERE status IN ('Draft', 'Sent', 'Partial')")['total'] ?? 0;
    $unpaidInvoices = $db->fetch("SELECT COUNT(*) as count FROM invoices WHERE status IN ('Sent', 'Overdue', 'Partial')")['count'];

    echo json_encode([
        'success' => true,
        'active_clients' => $activeClients,
        'expected_revenue' => $expectedRevenue,
        'unpaid_invoices' => $unpaidInvoices
    ]);
}

function handleInvoiceSettings() {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    // Handle GET request to retrieve settings
    if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'get') {
        $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
        echo json_encode([
            'success' => true,
            'settings' => $settings
        ]);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    // Save billing settings
    if (isset($input['invoice_prefix'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['invoice_prefix', $input['invoice_prefix']]
        );
    }

    if (isset($input['tax_rate'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['tax_rate', $input['tax_rate']]
        );
    }

    if (isset($input['due_days'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['invoice_due_days', $input['due_days']]
        );
    }

    if (isset($input['auto_send_email'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['auto_send_invoice_email', $input['auto_send_email'] ? '1' : '0']
        );
    }

    // Save cron/scheduler settings
    if (isset($input['enable_auto_generate'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['auto_generate_invoices', $input['enable_auto_generate'] ? '1' : '0']
        );
    }

    if (isset($input['auto_generate_day'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['auto_generate_day', $input['auto_generate_day']]
        );
    }

    if (isset($input['auto_generate_time'])) {
        $db->query(
            "INSERT INTO settings (key, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)",
            ['auto_generate_time', $input['auto_generate_time']]
        );
    }

    // Save Campay API token
    if (isset($input['campay_api_token'])) {
        $db->query(
            "UPDATE settings SET campay_api_token = ? LIMIT 1",
            [$input['campay_api_token']]
        );
    }

    echo json_encode([
        'success' => true,
        'message' => 'Settings saved successfully'
    ]);
}

function handleGenerateInvoicePDF($invoiceId) {
    global $auth, $db;

    if (!$auth->hasPermission('view_finances')) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    if (!$invoiceId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Invoice ID required']);
        return;
    }

    // Get invoice data
    $invoice = $db->fetch(
        "SELECT i.*, u.name as owner_name, u.email as owner_email, p.name as post_name
         FROM invoices i
         LEFT JOIN users u ON i.owner_id = u.id
         LEFT JOIN posts p ON i.post_id = p.id
         WHERE i.id = ?",
        [$invoiceId]
    );

    if (!$invoice) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invoice not found']);
        return;
    }

    // Get services
    $services = null;
    if (!empty($invoice['services'])) {
        $services = is_string($invoice['services']) ? json_decode($invoice['services'], true) : $invoice['services'];
    } elseif (!empty($invoice['post_id'])) {
        $post = $db->fetch("SELECT services FROM posts WHERE id = ?", [$invoice['post_id']]);
        if ($post && !empty($post['services'])) {
            $services = is_string($post['services']) ? json_decode($post['services'], true) : $post['services'];
        }
    }

    // Get company info
    $settings = $db->fetch("SELECT site_name, site_acronym, logo, letterhead FROM settings LIMIT 1");

    // Include TCPDF library
    require_once __DIR__ . '/../lib/tcpdf.php';

    // Custom TCPDF class with footer
    class TCPDF_WithFooter extends TCPDF {
        public function Footer() {
            $this->SetY(-15);
            $this->SetFont('ptsansnarrow', '', 8);
            $this->SetTextColor(128, 128, 128);
            $this->Ln();
            $this->Cell(0, 10, '© ' . date('Y') . ' SHEPHERD SECURITY AND CONSULTANCY INTERNATIONAL (SSCI). Generated on ' . date('d M Y H:i:s'), 0, false, 'C', false);
        }
    }

    // Create PDF with full width letterhead capability
    $pdf = new TCPDF_WithFooter(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
    $pdf->SetMargins(10, 10, 10);
    $pdf->SetAutoPageBreak(true, 20);
    $pdf->AddPage();

    // Use ptsansnarrow font (custom font from project)
    $pdf->SetFont('ptsansnarrow', '', 11);

    // Add letterhead with full width
    if (!empty($settings['letterhead'])) {
        $letterheadPath = SSCI_PUBLIC . $settings['letterhead'];
        if (file_exists($letterheadPath)) {
            // Full width letterhead (190 for A4 with 10px margins on each side)
            // Height of 0 maintains aspect ratio
            $pdf->Image($letterheadPath, 10, 10, 190, 0, '', '', 'T', false, 300);
            $pdf->Ln(50); // Space after letterhead
        }
    } else if (!empty($settings['logo'])) {
        $logoPath = SSCI_PUBLIC . $settings['logo'];
        if (file_exists($logoPath)) {
            $pdf->Image($logoPath, 10, 10, 30, 0, '', '', 'T', false, 150);
        }
        $pdf->SetFont('ptsansnarrow', 'B', 16);
        $pdf->Ln(25);
        $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
        $pdf->Ln(5);
    } else {
        $pdf->SetFont('ptsansnarrow', 'B', 18);
        $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
        $pdf->SetFont('ptsansnarrow', '', 11);
        $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
        $pdf->Ln(10);
    }

    // Invoice header
    $pdf->SetFont('ptsansnarrow', 'B', 14);
    $pdf->Cell(0, 8, 'INVOICE', 0, 1, 'C');
    $pdf->Ln(5);

    // Invoice details with QR code on the right (75% / 25% split)
    $pdf->SetFont('ptsansnarrow', '', 10);
    $pdf->SetFillColor(240, 240, 240);

    $invoiceDate = date('d M Y', strtotime($invoice['invoice_date']));
    $dueDate = !empty($invoice['due_date']) ? date('d M Y', strtotime($invoice['due_date'])) : 'Upon receipt';

    // Get current Y position for QR code placement
    $invoiceDetailsY = $pdf->GetY();
    
    // Create 75% width table for invoice details (0-150mm), QR code in 25% (150-190mm)
    // Invoice number row
    $pdf->Cell(40, 6, 'Invoice Number:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $invoice['invoice_number'], 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false); // 25% space for QR
    
    // Invoice Date row
    $pdf->Cell(40, 6, 'Invoice Date:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $invoiceDate, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Due Date row
    $pdf->Cell(40, 6, 'Due Date:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $dueDate, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Status row
    $pdf->Cell(40, 6, 'Status:', 0, 0, 'L', true);
    $statusText = ($invoice['status'] === 'Draft') ? 'Unpaid' : $invoice['status'];
    $pdf->Cell(110, 6, $statusText, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Add QR code on the right side (positioned at the start of invoice details)
    $qrData = $invoice['invoice_number'] ?? 'INV-0001';
    try {
        $pdf->write2DBarcode($qrData, 'QRCODE,M', 155, $invoiceDetailsY, 35, 35, array(), 'N');
    } catch (Exception $e) {
        // QR code generation failed, continue without it
    }

    $pdf->Ln(5);

    // Bill to
    $pdf->SetFont('ptsansnarrow', 'B', 10);
    $pdf->Cell(0, 6, 'BILL TO:', 0, 1);
    $pdf->SetFont('ptsansnarrow', '', 10);
    $pdf->Cell(0, 5, $invoice['owner_name'] ?? 'N/A', 0, 1);
    $pdf->Cell(0, 5, $invoice['owner_email'] ?? '', 0, 1);
    $pdf->Ln(5);

    // Line items table
    $pdf->SetFont('ptsansnarrow', 'B', 10);
    $pdf->SetFillColor(200, 200, 200);
    $pdf->Cell(80, 6, 'Description', 0, 0, 'L', true);
    $pdf->Cell(30, 6, 'Qty', 0, 0, 'R', true);
    $pdf->Cell(40, 6, 'Unit Price', 0, 0, 'R', true);
    $pdf->Cell(40, 6, 'Total', 0, 1, 'R', true);

    $pdf->SetFont('ptsansnarrow', '', 9);
    $pdf->SetFillColor(255, 255, 255);

    if ($services && is_array($services) && count($services) > 0) {
        foreach ($services as $service) {
            $qty = $service['qty'] ?? 1;
            $unitCost = $service['unit_cost'] ?? 0;
            $total = $qty * $unitCost;

            $pdf->Cell(80, 5, $service['name'] ?? 'Service', 0, 0, 'L');
            $pdf->Cell(30, 5, number_format($qty, 2), 0, 0, 'R');
            $pdf->Cell(40, 5, number_format($unitCost, 2), 0, 0, 'R');
            $pdf->Cell(40, 5, number_format($total, 2), 0, 1, 'R');
        }
    } else {
        $pdf->Cell(80, 5, 'Services', 0, 0, 'L');
        $pdf->Cell(30, 5, '1', 0, 0, 'R');
        $pdf->Cell(40, 5, number_format($invoice['subtotal'] ?? 0, 2), 0, 0, 'R');
        $pdf->Cell(40, 5, number_format($invoice['subtotal'] ?? 0, 2), 0, 1, 'R');
    }

    $pdf->Ln(5);

    // Totals
    $pdf->SetFont('ptsansnarrow', '', 10);
    $subtotal = floatval($invoice['subtotal'] ?? 0);
    $tax = floatval($invoice['tax_amount'] ?? 0);
    $total = floatval($invoice['total_amount'] ?? 0);

    $pdf->Cell(150, 6, 'Subtotal:', 0, 0, 'R');
    $pdf->Cell(40, 6, number_format($subtotal, 2), 0, 1, 'R');

    $pdf->Cell(150, 6, 'Tax:', 0, 0, 'R');
    $pdf->Cell(40, 6, number_format($tax, 2), 0, 1, 'R');

    $pdf->SetFont('ptsansnarrow', 'B', 11);
    $pdf->SetFillColor(220, 220, 220);
    $pdf->Cell(150, 7, 'TOTAL:', 0, 0, 'R', true);
    $pdf->Cell(40, 7, number_format($total, 2) . ' ' . ($invoice['currency'] ?? 'FCFA'), 0, 1, 'R', true);

    // Notes
    if (!empty($invoice['notes'])) {
        $pdf->Ln(10);
        $pdf->SetFont('ptsansnarrow', 'B', 10);
        $pdf->Cell(0, 6, 'Notes:', 0, 1);
        $pdf->SetFont('ptsansnarrow', '', 9);
        $pdf->MultiCell(0, 5, $invoice['notes'], 0, 'L');
    }

    // Output PDF
    $filename = 'Invoice-' . $invoice['invoice_number'] . '.pdf';
    $pdf->Output($filename, 'D'); // Download the PDF
}

function generateInvoiceNumber($db) {
    $prefix = 'INV-';
    $year = date('Y');
    $month = date('m');
    
    $lastInvoice = $db->fetch(
        "SELECT invoice_number FROM invoices WHERE invoice_number LIKE ? ORDER BY id DESC LIMIT 1",
        ["$prefix$year$month%"]
    );

    $sequence = 1;
    if ($lastInvoice) {
        $matches = [];
        if (preg_match('/INV-(\d{6})(\d+)/', $lastInvoice['invoice_number'], $matches)) {
            $sequence = (int)$matches[2] + 1;
        }
    }

    return $prefix . $year . $month . str_pad($sequence, 4, '0', STR_PAD_LEFT);
}

function updateInvoiceTotals($db, $invoiceId) {
    // Calculate totals from line items
    $items = $db->fetchAll("SELECT * FROM invoice_items WHERE invoice_id = ?", [$invoiceId]);
    
    $subtotal = 0;
    foreach ($items as $item) {
        $subtotal += $item['line_total'];
    }

    // Get tax rate (default 0)
    $taxRate = 0; // TODO: Get from settings
    $taxAmount = $subtotal * ($taxRate / 100);
    $totalAmount = $subtotal + $taxAmount;

    $db->query(
        "UPDATE invoices SET subtotal = ?, tax_amount = ?, total_amount = ? WHERE id = ?",
        [$subtotal, $taxAmount, $totalAmount, $invoiceId]
    );
}

/**
 * PAYMENT FUNCTIONS
 */

function handleRecordPayment() {
    global $db, $auth, $notification;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $data = json_decode(file_get_contents('php://input'), true);
    $invoiceId = $data['invoice_id'] ?? null;
    $amount = floatval($data['amount'] ?? 0);
    $paymentMethod = $data['payment_method'] ?? 'Cash';
    $description = $data['description'] ?? '';
    $phonenumber = $data['phone_number'] ?? null;

    if (!$invoiceId || $amount <= 0) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Invalid invoice or amount']);
        return;
    }

    try {
        // Get invoice to verify it exists
        $invoice = $db->fetch("SELECT * FROM invoices WHERE id = ?", [$invoiceId]);
        if (!$invoice) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Invoice not found']);
            return;
        }

        // Get client details for notification
        $client = $db->fetch("SELECT id, name, email, phone FROM users WHERE id = ?", [$invoice['owner_id']]);

        // Generate reference number
        $referenceNumber = 'PAY-' . date('YmdHis') . '-' . uniqid();

        // Create payment record
        $paymentId = $db->query(
            "INSERT INTO payments (invoice_id, reference_number, payment_type, related_id, amount, currency, payment_method, status, phone_number, description, created_by, payment_date)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [$invoiceId, $referenceNumber, 'Client_Income', $invoiceId, $amount, $invoice['currency'] ?? 'XAF', $paymentMethod, 'Confirmed', $phonenumber, $description, ($auth->getCurrentUser()['id'] ?? null), date('Y-m-d')]
        );

        // Update invoice status if all paid
        updateInvoiceStatus($db, $invoiceId);

        // Send payment confirmation notification to client
        if ($notification && $client) {
            $notification
                ->reset()
                ->forUser($client['id'])
                ->setTitle('Payment Received ✓')
                ->setMessage("Thank you! Payment of " . number_format($amount, 2) . " received for Invoice #{$invoice['invoice_number']}. Reference: {$referenceNumber}")
                ->setType('success')
                ->addCallToAction('View Invoice', "/dashboard?page=invoices&id={$invoiceId}")
                ->via(['email', 'sms', 'whatsapp', 'push', 'in-app'])
                ->send();
        }

        // Send payment notification to admin/finance staff
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'finance') LIMIT 10");
        if ($notification && !empty($adminUsers)) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('New Payment Recorded')
                    ->setMessage("Payment of " . number_format($amount, 2) . " from " . ($client['name'] ?? 'Unknown') . " for Invoice #{$invoice['invoice_number']}. Method: {$paymentMethod}")
                    ->setType('info')
                    ->addCallToAction('View Payment', "/dashboard?page=payments&id={$paymentId}")
                    ->via(['email', 'push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true,
            'message' => 'Payment recorded successfully',
            'payment_id' => $paymentId,
            'reference' => $referenceNumber
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error recording payment: ' . $e->getMessage()]);
    }
}

function handleRequestMobileMoneyPayment() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $data = json_decode(file_get_contents('php://input'), true);
    $invoiceId = $data['invoice_id'] ?? null;
    $phoneNumber = $data['phone'] ?? $data['phone_number'] ?? null;
    $amount = floatval($data['amount'] ?? 0);

    if (!$invoiceId || !$phoneNumber || $amount <= 0) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Invalid parameters']);
        return;
    }

    try {
        // Get invoice
        $invoice = $db->fetch("SELECT * FROM invoices WHERE id = ?", [$invoiceId]);
        if (!$invoice) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Invoice not found']);
            return;
        }

        // Get Campay API token from settings
        $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
        $campayToken = $settings['campay_api_token'] ?? null;

        if (!$campayToken) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Campay API not configured']);
            return;
        }

        // Calculate amount with 3% Campay fee
        $campayFee = $amount * 0.03;
        $totalWithFee = $amount + $campayFee;

        // Request payment from Campay
        $campayResponse = requestCampayPayment($phoneNumber, $totalWithFee, "Invoice #" . $invoice['invoice_number'], $campayToken);

        if (!$campayResponse['success']) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Campay request failed: ' . $campayResponse['message']]);
            return;
        }

        // Create pending payment record
        $referenceNumber = 'CAMPAY-' . $campayResponse['reference'];
        $db->query(
            "INSERT INTO payments (invoice_id, reference_number, payment_type, related_id, amount, currency, payment_method, status, phone_number, campay_reference, operator, description, created_by, payment_date)
             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [$invoiceId, $referenceNumber, 'Client_Income', $invoiceId, $amount, $invoice['currency'] ?? 'XAF', 'Mobile Money', 'Pending', $phoneNumber, $campayResponse['reference'], $campayResponse['operator'] ?? 'unknown', "Mobile Money payment", ($auth->getCurrentUser()['id'] ?? null), date('Y-m-d')]
        );
        $paymentId = $db->lastInsertId();

        echo json_encode([
            'success' => true,
            'message' => 'Payment request sent',
            'payment_id' => $paymentId,
            'reference' => $campayResponse['reference'],
            'ussd_code' => $campayResponse['ussd_code'],
            'operator' => $campayResponse['operator'],
            'amount_with_fee' => $totalWithFee,
            'campay_fee' => $campayFee
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
    }
}

function handleCheckMobileMoneyStatus() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $data = json_decode(file_get_contents('php://input'), true);
    $paymentId = $data['payment_id'] ?? null;

    if (!$paymentId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID required']);
        return;
    }

    try {
        // Get payment record
        $payment = $db->fetch("SELECT * FROM payments WHERE id = ?", [$paymentId]);
        if (!$payment || !$payment['campay_reference']) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Payment not found']);
            return;
        }

        // Get Campay API token
        $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
        $campayToken = $settings['campay_api_token'] ?? null;

        if (!$campayToken) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Campay API not configured']);
            return;
        }

        // Check status with Campay
        $statusResponse = checkCampayStatus($payment['campay_reference'], $campayToken);

        if (!$statusResponse['success']) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Status check failed']);
            return;
        }

        $status = $statusResponse['status'];

        // Update payment status if confirmed
        if ($status === 'SUCCESS' || $status === 'COMPLETED') {
            $db->query(
                "UPDATE payments SET status = ? WHERE id = ?",
                ['Confirmed', $paymentId]
            );
            // Update invoice status
            updateInvoiceStatus($db, $payment['invoice_id']);
        } elseif ($status === 'FAILED' || $status === 'CANCELLED') {
            $db->query(
                "UPDATE payments SET status = ? WHERE id = ?",
                ['Cancelled', $paymentId]
            );
        }

        echo json_encode([
            'success' => true,
            'status' => $status,
            'payment' => $statusResponse
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
    }
}

function handleGetPayments() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $invoiceId = $_GET['invoice_id'] ?? null;
    $limit = max(1, intval($_GET['limit'] ?? 50));
    $offset = max(0, intval($_GET['offset'] ?? 0));

    try {
        if ($invoiceId) {
            $sql = "SELECT p.*, 
                    i.invoice_number,
                    po.name as post_name, 
                    u.name as owner_name
                    FROM payments p 
                    LEFT JOIN invoices i ON p.invoice_id = i.id
                    LEFT JOIN posts po ON i.post_id = po.id
                    LEFT JOIN users u ON i.owner_id = u.id
                    WHERE p.invoice_id = ? 
                    ORDER BY p.payment_date DESC, p.created_at DESC 
                    LIMIT " . $limit . " OFFSET " . $offset;
            $payments = $db->fetchAll($sql, [$invoiceId]);
        } else {
            $sql = "SELECT p.*, 
                    i.invoice_number,
                    po.name as post_name, 
                    u.name as owner_name
                    FROM payments p 
                    LEFT JOIN invoices i ON p.invoice_id = i.id
                    LEFT JOIN posts po ON i.post_id = po.id
                    LEFT JOIN users u ON i.owner_id = u.id
                    WHERE p.payment_type = 'Client_Income' 
                    ORDER BY p.payment_date DESC, p.created_at DESC 
                    LIMIT " . $limit . " OFFSET " . $offset;
            $payments = $db->fetchAll($sql, []);
        }

        echo json_encode([
            'success' => true,
            'payments' => $payments,
            'count' => count($payments)
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
    }
}

/**
 * Campay API Helper Functions
 */

function requestCampayPayment($phoneNumber, $amount, $description, $apiToken) {
    $curl = curl_init();
    curl_setopt_array($curl, array(
        CURLOPT_URL => 'https://www.campay.net/api/collect/',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => 'POST',
        CURLOPT_POSTFIELDS => json_encode([
            'amount' => $amount,
            'from' => $phoneNumber,
            'description' => $description,
            'external_reference' => ''
        ]),
        CURLOPT_HTTPHEADER => array(
            'Authorization: Token ' . $apiToken,
            'Content-Type: application/json'
        ),
    ));

    $response = curl_exec($curl);
    $curlError = curl_error($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);

    // Log the response for debugging
    error_log("Campay Response Code: " . $httpCode);
    error_log("Campay Response: " . $response);

    if ($curlError) {
        error_log("Campay Curl Error: " . $curlError);
        return ['success' => false, 'message' => 'Curl error: ' . $curlError];
    }

    $data = json_decode($response, true);

    if (!$data) {
        error_log("Campay JSON Decode Error: Invalid JSON response");
        return ['success' => false, 'message' => 'Invalid JSON response from Campay'];
    }

    if (isset($data['error'])) {
        error_log("Campay Error: " . $data['error']);
        return ['success' => false, 'message' => $data['error']];
    }

    if ($httpCode !== 200) {
        error_log("Campay HTTP Error: " . $httpCode . " - " . json_encode($data));
        return ['success' => false, 'message' => 'Campay API error: HTTP ' . $httpCode];
    }

    return [
        'success' => true,
        'reference' => $data['reference'] ?? null,
        'ussd_code' => $data['ussd_code'] ?? null,
        'operator' => $data['operator'] ?? null
    ];
}

function checkCampayStatus($reference, $apiToken) {
    $curl = curl_init();
    curl_setopt_array($curl, array(
        CURLOPT_URL => 'https://www.campay.net/api/transaction/' . $reference . '/',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => 'GET',
        CURLOPT_HTTPHEADER => array(
            'Authorization: Token ' . $apiToken,
            'Content-Type: application/json'
        ),
    ));

    $response = curl_exec($curl);
    $curlError = curl_error($curl);
    curl_close($curl);

    if ($curlError) {
        return ['success' => false, 'message' => 'Curl error: ' . $curlError];
    }

    $data = json_decode($response, true);

    if (!$data || isset($data['error'])) {
        return ['success' => false, 'message' => $data['error'] ?? 'Unknown error'];
    }

    return [
        'success' => true,
        'status' => $data['status'] ?? 'PENDING',
        'data' => $data
    ];
}

function updateInvoiceStatus($db, $invoiceId) {
    // Get invoice total
    $invoice = $db->fetch("SELECT total_amount FROM invoices WHERE id = ?", [$invoiceId]);
    
    // Get total paid amount (confirmed payments only)
    $payments = $db->fetch(
        "SELECT SUM(amount) as total_paid FROM payments WHERE related_id = ? AND status = 'Confirmed'",
        [$invoiceId]
    );
    
    $totalPaid = floatval($payments['total_paid'] ?? 0);
    $totalAmount = floatval($invoice['total_amount'] ?? 0);
    
    if ($totalPaid >= $totalAmount) {
        $newStatus = 'Paid';
    } elseif ($totalPaid > 0) {
        $newStatus = 'Partially Paid';
    } else {
        $newStatus = 'Unpaid';
    }
    
    $db->query(
        "UPDATE invoices SET status = ? WHERE id = ?",
        [$newStatus, $invoiceId]
    );
}

function handleUpdatePayment() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }
    
    $input = json_decode(file_get_contents('php://input'), true);
    
    $paymentId = $input['payment_id'] ?? null;
    $status = $input['status'] ?? null;
    $reference = $input['reference_number'] ?? null;
    $description = $input['description'] ?? null;
    $paymentDate = $input['payment_date'] ?? null;
    
    if (!$paymentId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID is required']);
        return;
    }
    
    // Verify payment exists
    $payment = $db->fetch("SELECT * FROM payments WHERE id = ?", [$paymentId]);
    if (!$payment) {
        http_response_code(404);
        echo json_encode(['success' => false, 'message' => 'Payment not found']);
        return;
    }
    
    // Update payment
    $updates = [];
    $params = [];
    
    if ($status) {
        $updates[] = "status = ?";
        $params[] = $status;
    }
    if ($reference !== null) {
        $updates[] = "reference_number = ?";
        $params[] = $reference;
    }
    if ($description !== null) {
        $updates[] = "description = ?";
        $params[] = $description;
    }
    if ($paymentDate) {
        $updates[] = "payment_date = ?";
        $params[] = $paymentDate;
    }
    
    if (empty($updates)) {
        echo json_encode(['success' => false, 'message' => 'No fields to update']);
        return;
    }
    
    $updates[] = "updated_at = NOW()";
    $params[] = $paymentId;
    
    $query = "UPDATE payments SET " . implode(", ", $updates) . " WHERE id = ?";
    
    try {
        $db->query($query, $params);
        
        // Update invoice status if payment status changed
        if ($status) {
            updateInvoiceStatus($db, $payment['invoice_id']);
        }
        
        echo json_encode(['success' => true, 'message' => 'Payment updated successfully']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to update payment']);
    }
}

function handleUpdatePaymentStatus() {
    global $db, $auth, $notification;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }
    
    $input = json_decode(file_get_contents('php://input'), true);
    
    $paymentId = $input['payment_id'] ?? null;
    $status = $input['status'] ?? null;
    
    if (!$paymentId || !$status) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID and status are required']);
        return;
    }
    
    // Verify payment exists
    $payment = $db->fetch("SELECT * FROM payments WHERE id = ?", [$paymentId]);
    if (!$payment) {
        http_response_code(404);
        echo json_encode(['success' => false, 'message' => 'Payment not found']);
        return;
    }
    
    // Get invoice and client info for notifications
    $invoice = $db->fetch("SELECT id, invoice_number, owner_id FROM invoices WHERE id = ?", [$payment['invoice_id']]);
    $client = $invoice ? $db->fetch("SELECT id, name, email FROM users WHERE id = ?", [$invoice['owner_id']]) : null;

    $previousStatus = $payment['status'];
    
    try {
        $db->query("UPDATE payments SET status = ?, updated_at = NOW() WHERE id = ?", [$status, $paymentId]);
        
        // Update invoice status
        updateInvoiceStatus($db, $payment['invoice_id']);
        
        // Send payment status update notification to client if status changed to Confirmed
        if ($previousStatus !== 'Confirmed' && $status === 'Confirmed' && $notification && $client && $invoice) {
            $notification
                ->reset()
                ->forUser($client['id'])
                ->setTitle('Payment Confirmed ✓')
                ->setMessage("Your payment of " . number_format($payment['amount'], 2) . " for Invoice #{$invoice['invoice_number']} has been confirmed.")
                ->setType('success')
                ->addCallToAction('View Invoice', "/dashboard?page=invoices&id=" . $invoice['id'])
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }
        
        // Send rejection notification if marked as Failed/Rejected
        if ($status === 'Failed' || $status === 'Rejected') {
            if ($notification && $client && $invoice) {
                $notification
                    ->reset()
                    ->forUser($client['id'])
                    ->setTitle('Payment ' . $status)
                    ->setMessage("Payment of " . number_format($payment['amount'], 2) . " for Invoice #{$invoice['invoice_number']} has been marked as {$status}. Please resubmit or contact support.")
                    ->setType('error')
                    ->addCallToAction('Submit Payment', "/dashboard?page=invoices&id=" . $invoice['id'])
                    ->via(['email', 'sms', 'push', 'in-app'])
                    ->send();
            }
        }

        // Notify finance staff about status change
        $financeUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'finance') LIMIT 10");
        if ($notification && !empty($financeUsers) && $invoice && $client) {
            foreach ($financeUsers as $finance) {
                $notification
                    ->reset()
                    ->forUser($finance['id'])
                    ->setTitle('Payment Status Updated')
                    ->setMessage("Payment from " . $client['name'] . " for Invoice #{$invoice['invoice_number']} updated to: {$status}")
                    ->setType('info')
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }
        
        echo json_encode(['success' => true, 'message' => 'Payment status updated']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to update payment status']);
    }
}

function handleDeletePayment() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }
    
    $input = json_decode(file_get_contents('php://input'), true);
    $paymentId = $input['payment_id'] ?? null;
    
    if (!$paymentId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID is required']);
        return;
    }
    
    // Verify payment exists
    $payment = $db->fetch("SELECT * FROM payments WHERE id = ?", [$paymentId]);
    if (!$payment) {
        http_response_code(404);
        echo json_encode(['success' => false, 'message' => 'Payment not found']);
        return;
    }
    
    try {
        $db->query("DELETE FROM payments WHERE id = ?", [$paymentId]);
        
        // Recalculate invoice status after deletion
        if ($payment['invoice_id']) {
            updateInvoiceStatus($db, $payment['invoice_id']);
        }
        
        echo json_encode(['success' => true, 'message' => 'Payment deleted successfully']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to delete payment']);
    }
}

/**
 * Convert number to words (English)
 */
function numberToWords($number) {
    $ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
    $teens = ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];
    $tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
    $scales = ['', 'thousand', 'million', 'billion', 'trillion'];
    
    if ($number == 0) return 'zero';
    
    if ($number < 0) {
        return 'negative ' . numberToWords(-$number);
    }
    
    $number = intval($number);
    $groups = [];
    $groupIndex = 0;
    
    while ($number > 0) {
        $groups[$groupIndex] = $number % 1000;
        $number = intval($number / 1000);
        $groupIndex++;
    }
    
    $words = [];
    for ($i = count($groups) - 1; $i >= 0; $i--) {
        if ($groups[$i] == 0) continue;
        
        $groupWords = '';
        $group = $groups[$i];
        
        // Hundreds place
        $hundreds = intval($group / 100);
        if ($hundreds > 0) {
            $groupWords .= $ones[$hundreds] . ' hundred ';
        }
        
        // Tens and ones
        $remainder = $group % 100;
        if ($remainder >= 20) {
            $tensDigit = intval($remainder / 10);
            $onesDigit = $remainder % 10;
            $groupWords .= $tens[$tensDigit];
            if ($onesDigit > 0) {
                $groupWords .= '-' . $ones[$onesDigit];
            }
        } elseif ($remainder >= 10) {
            $groupWords .= $teens[$remainder - 10];
        } elseif ($remainder > 0) {
            $groupWords .= $ones[$remainder];
        }
        
        $groupWords = trim($groupWords);
        if (!empty($groupWords) && $i > 0) {
            $groupWords .= ' ' . $scales[$i];
        }
        
        $words[] = trim($groupWords);
    }
    
    return ucfirst(implode(' ', $words));
}

function generatePaymentReceipt($paymentId)
{
    global $db, $settings;

    if (ob_get_level()) {
        ob_end_clean();
    }
    ob_start();

    $payment = $db->fetch(
        "SELECT * FROM payments WHERE id = ?",
        [$paymentId]
    );

    if (!$payment) {
        http_response_code(404);
        ob_end_clean();
        die('Payment not found');
    }

    $invoice = $db->fetch(
        "SELECT 
            i.*, 
            p.name AS post_name, 
            u.name AS owner_name
         FROM invoices i
         LEFT JOIN posts p ON i.post_id = p.id
         LEFT JOIN users u ON i.owner_id = u.id
         WHERE i.id = ?",
        [$payment['invoice_id']]
    );

    if (!$invoice) {
        http_response_code(404);
        ob_end_clean();
        die('Invoice not found');
    }

    $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
    ob_end_clean();

    require_once __DIR__ . '/../lib/tcpdf.php';

    class TCPDF_SimpleReceipt extends TCPDF
    {
        public function Header() {}
        public function Footer() {}
    }

    $pdf = new TCPDF_SimpleReceipt(
        PDF_PAGE_ORIENTATION,
        PDF_UNIT,
        PDF_PAGE_FORMAT,
        true,
        'UTF-8',
        false
    );

    $pdf->SetMargins(12, 8, 12);
    $pdf->SetAutoPageBreak(false);
    $pdf->AddPage();

    $drawReceipt = function ($startY, $isDuplicate = false) use ($pdf, $payment, $invoice, $settings) {

        $boxX = 10;
        $boxY = $startY;
        $boxW = 195;
        $boxH = 110;

        $pdf->SetLineWidth(0.4);
        $pdf->Rect($boxX, $boxY, $boxW, $boxH);

        if ($isDuplicate) {
            $pdf->SetFont('ptsansnarrow', 'B', 42);
            $pdf->SetTextColor(230, 230, 230);
            $pdf->SetAlpha(0.15);
            $pdf->SetXY($boxX + 35, $boxY + 45);
            $pdf->Write(0, 'DUPLICATA');
            $pdf->SetAlpha(1);
            $pdf->SetTextColor(0);
        }

        $pX = $boxX + 6;
        $pY = $boxY + 6;
        $pW = $boxW - 12;

        /* ---------------- Header ---------------- */

        $logoExists = false;
        if (!empty($settings['logo'])) {
            $logoPath = SSCI_PUBLIC . $settings['logo'];
            if (file_exists($logoPath)) {
                $pdf->Image($logoPath, $pX, $pY, 18);
                $logoExists = true;
            }
        }

        $pdf->SetFont('ptsansnarrow', 'B', 13);
        $pdf->SetXY($logoExists ? $pX + 22 : $pX, $pY);
        $pdf->Cell($pW - 30, 7, $settings['site_name'] ?? 'SSCI', 0, 1);

        $qrData = $invoice['invoice_number'] . '|RCP:' . $payment['id'];
        try {
            $pdf->write2DBarcode(
                $qrData,
                'QRCODE,M',
                $pX + $pW - 22,
                $pY,
                20,
                20
            );
        } catch (Exception $e) {}

        $pdf->Ln(10);
        $pdf->SetFont('ptsansnarrowb', 'B', 14);
        $pdf->Cell($pW, 8, 'PAYMENT RECEIPT / REÇU DE PAIEMENT', 0, 1, 'C');

        /* ---------------- Meta ---------------- */

        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->Cell(
            $pW / 2,
            20,
            'Receipt No: ' . str_pad($payment['id'], 5, '0', STR_PAD_LEFT),
            0,
            0
        );
        $pdf->Cell(
            $pW / 2,
            20,
            'Date: ' . date('d/m/Y', strtotime($payment['payment_date'])),
            0,
            1,
            'R'
        );

        $pdf->Ln(2);
        $pdf->SetDrawColor(180);
        $pdf->Line($pX, $pdf->GetY(), $pX + $pW, $pdf->GetY());
        $pdf->Ln(4);

        /* ---------------- Client ---------------- */

        $pdf->SetFont('ptsansnarrow', 'B', 10);
        $pdf->Cell(
            $pW,
            6,
            ($invoice['post_name'] ?? 'N/A') . ' (' . ($invoice['owner_name'] ?? 'N/A') . ')',
            0,
            1
        );

        $pdf->Ln(2);

        /* ---------------- Table ---------------- */

        $col1 = 55;
        $col2 = 70;
        $col3 = 40;

        $pdf->SetFont('ptsansnarrow', 'B', 10);
        $pdf->SetFillColor(235, 240, 250);

        $pdf->Cell($col1, 6, 'Invoice', 1, 0, 'C', true);
        $pdf->Cell($col2, 6, 'Payment Method', 1, 0, 'C', true);
        $pdf->Cell($col3, 6, 'Amount', 1, 1, 'R', true);

        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->SetFillColor(250, 250, 250);

        $pdf->Cell(
            $col1,
            6,
            '#' . ($invoice['invoice_number'] ?? $invoice['id']),
            1
        );
        $pdf->Cell(
            $col2,
            6,
            substr($payment['payment_method'], 0, 30),
            1
        );
        // Normalize currency
        $currencyCode = ($payment['currency'] ?? 'FCFA');
        if ($currencyCode === 'FCF') $currencyCode = 'FCFA';
        $pdf->Cell(
            $col3,
            6,
            number_format($payment['amount'], 2) . ' ' . $currencyCode,
            1,
            1,
            'R'
        );

        // Amount in words
        $amountInWords = strtoupper(numberToWords(intval($payment['amount']))) . ' ' . $currencyCode;
        $pdf->Ln(4);
        $pdf->SetFont('ptsansnarrow', 'I', 8);
        $pdf->SetFillColor(250, 250, 250);
        $pdf->MultiCell($pW, 20, 'Amount: ' . $amountInWords, 0, 'L');

        /* ---------------- Status ---------------- */

        $pdf->SetFont('ptsansnarrow', '', 9);
        $pdf->Cell($pW / 2, 5, 'Status: ' . strtoupper($payment['status']), 0);
        $pdf->Cell(
            $pW / 2,
            5,
            'Reference: ' . ($payment['reference_number'] ?? '-'),
            0,
            1,
            'R'
        );

        /* ---------------- Footer ---------------- */

        $pdf->SetFont('ptsansnarrow', 'I', 9);
        $pdf->SetTextColor(120);
        $pdf->SetXY($pX, $boxY + $boxH - 8);
        $pdf->Cell($pW, 6, 'Thank you for your payment', 0, 1, 'C');
        $pdf->SetTextColor(0);
    };

    $drawReceipt(10, false);
    $drawReceipt(10 + 110 + 15, true);

    $filename =
        'Receipt_' .
        str_pad($paymentId, 5, '0', STR_PAD_LEFT) .
        '_' . date('Ymd_His') .
        '.pdf';

    $pdf->Output($filename, 'D');
}

/**
 * =====================================================
 * STAFF SALARIES API HANDLERS
 * =====================================================
 */

function handleGetSalaries() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    try {
        // Note: staff_salaries.staff_id references users.id, not staff.id
        // So we join users first, then staff through user_id
        $sql = "SELECT s.*, u.name as staff_name, st.position 
                FROM staff_salaries s 
                LEFT JOIN users u ON s.staff_id = u.id
                LEFT JOIN staff st ON st.user_id = u.id
                ORDER BY s.salary_month DESC, s.created_at DESC";
        $salaries = $db->fetchAll($sql, []);
        
        echo json_encode([
            'success' => true,
            'salaries' => $salaries,
            'count' => count($salaries)
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
    }
}

function handleCreateSalary() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    
    $staffId = $input['staff_id'] ?? null;
    $month = $input['salary_month'] ?? null;
    $status = $input['status'] ?? 'Draft';
    $method = $input['payment_method'] ?? 'Bank Transfer';
    $notes = $input['notes'] ?? '';
    
    // New fields - remunerations and deductions as JSON
    $remunerations = $input['remunerations'] ?? [];
    $deductions = $input['deductions'] ?? [];
    $gross = $input['gross_amount'] ?? 0;
    $net = $input['net_amount'] ?? 0;
    $totalDed = $input['total_deductions'] ?? 0;
    
    // Fallback to old fields if new ones not provided
    $basic = $input['basic_salary'] ?? 0;
    $allowances = $input['allowances'] ?? 0;

    if (!$staffId || !$month) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Staff ID and month are required']);
        return;
    }

    try {
        $userId = $_SESSION['user_id'] ?? null;
        
        // Check if salary already exists for this staff/month
        $existing = $db->fetch(
            "SELECT id FROM staff_salaries WHERE staff_id = ? AND salary_month = ?",
            [$staffId, $month]
        );
        
        // If remunerations not provided but basic_salary is, create remunerations array from it
        if (empty($remunerations) && ($basic > 0 || $allowances > 0)) {
            $remunerations = [];
            if ($basic > 0) {
                $remunerations[] = ['item' => 'Basic Salary', 'amount' => $basic];
            }
            if ($allowances > 0) {
                $remunerations[] = ['item' => 'Allowances', 'amount' => $allowances];
            }
        }
        
        if ($existing) {
            // Update existing salary instead of creating
            $db->query(
                "UPDATE staff_salaries 
                 SET remunerations = ?, deductions = ?, gross_amount = ?, net_amount = ?, total_deductions = ?, status = ?, payment_method = ?, notes = ?, updated_at = NOW()
                 WHERE id = ?",
                [json_encode($remunerations), json_encode($deductions), $gross, $net, $totalDed, $status, $method, $notes, $existing['id']]
            );
            echo json_encode(['success' => true, 'message' => 'Salary updated successfully', 'id' => $existing['id']]);
        } else {
            // Create new salary
            $db->query(
                "INSERT INTO staff_salaries (staff_id, salary_month, remunerations, deductions, gross_amount, net_amount, total_deductions, status, payment_method, notes, created_by) 
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                [$staffId, $month, json_encode($remunerations), json_encode($deductions), $gross, $net, $totalDed, $status, $method, $notes, $userId]
            );
            echo json_encode(['success' => true, 'message' => 'Salary created successfully']);
        }
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to create salary: ' . $e->getMessage()]);
    }
}

function handleUpdateSalary() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    
    $id = $input['id'] ?? null;
    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Salary ID is required']);
        return;
    }

    try {
        $updates = [];
        $params = [];

        // Handle new remunerations field
        if (isset($input['remunerations'])) {
            $updates[] = "remunerations = ?";
            $params[] = json_encode($input['remunerations']);
        }
        
        // Handle new deductions field
        if (isset($input['deductions'])) {
            $updates[] = "deductions = ?";
            $params[] = json_encode($input['deductions']);
        }
        
        // Handle old fields for backwards compatibility
        if (isset($input['basic_salary'])) {
            $updates[] = "basic_salary = ?";
            $params[] = $input['basic_salary'];
        }
        if (isset($input['allowances'])) {
            $updates[] = "allowances = ?";
            $params[] = $input['allowances'];
        }
        if (isset($input['total_deductions'])) {
            $updates[] = "total_deductions = ?";
            $params[] = $input['total_deductions'];
        }
        if (isset($input['gross_amount'])) {
            $updates[] = "gross_amount = ?";
            $params[] = $input['gross_amount'];
        }
        if (isset($input['net_amount'])) {
            $updates[] = "net_amount = ?";
            $params[] = $input['net_amount'];
        }
        if (isset($input['status'])) {
            $updates[] = "status = ?";
            $params[] = $input['status'];
        }
        if (isset($input['payment_method'])) {
            $updates[] = "payment_method = ?";
            $params[] = $input['payment_method'];
        }
        if (isset($input['notes'])) {
            $updates[] = "notes = ?";
            $params[] = $input['notes'];
        }

        if (empty($updates)) {
            echo json_encode(['success' => false, 'message' => 'No fields to update']);
            return;
        }

        $updates[] = "updated_at = NOW()";
        $params[] = $id;

        $query = "UPDATE staff_salaries SET " . implode(", ", $updates) . " WHERE id = ?";
        $db->query($query, $params);

        // Send notification if status changed to 'Paid'
        if ($input['status'] === 'Paid') {
            global $notification;
            $salary = $db->fetch("SELECT staff_id, gross_amount, salary_month FROM staff_salaries WHERE id = ?", [$id]);
            if ($salary) {
                $user = $db->fetch("SELECT id, name FROM users WHERE id = ?", [$salary['staff_id']]);
                if ($user && $notification) {
                    $monthDisplay = date('F Y', strtotime($salary['salary_month'] . '-01'));
                    $notification
                        ->reset()
                        ->forUser($user['id'])
                        ->setTitle('Salary Payment Processed ✓')
                        ->setMessage("Your salary for {$monthDisplay} has been paid. Gross: FCFA " . number_format($salary['gross_amount'], 2) . ".")
                        ->setType('success')
                        ->addCallToAction('View Payslip', "?page=guards-payments")
                        ->via(['email', 'push', 'in-app'])
                        ->send();
                }
            }
        }

        echo json_encode(['success' => true, 'message' => 'Salary updated successfully']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to update salary: ' . $e->getMessage()]);
    }
}

function handleDeleteSalary() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    // Support both JSON body and query parameter for ID
    $id = $input['id'] ?? $_GET['id'] ?? null;

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Salary ID is required']);
        return;
    }

    try {
        $db->query("DELETE FROM staff_salaries WHERE id = ?", [$id]);
        echo json_encode(['success' => true, 'message' => 'Salary deleted successfully']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to delete salary: ' . $e->getMessage()]);
    }
}

function handleBulkUpdateSalaries() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    $ids = $input['ids'] ?? [];
    $status = $input['status'] ?? null;

    if (empty($ids) || !$status) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'IDs and status are required']);
        return;
    }

    try {
        $placeholders = implode(',', array_fill(0, count($ids), '?'));
        $params = array_merge($ids, [$status]);
        $db->query("UPDATE staff_salaries SET status = ? WHERE id IN ($placeholders)", array_merge([$status], $ids));
        echo json_encode(['success' => true, 'message' => 'Salaries updated']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to update salaries']);
    }
}

function handleBulkPaySalaries() {
    global $db, $auth, $notification;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    
    // Get salary record IDs (the 'ids' are staff_salaries.id from the checkbox)
    $salaryIds = $input['ids'] ?? [];
    $status = $input['status'] ?? 'Paid';

    if (empty($salaryIds)) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Salary IDs are required']);
        return;
    }

    try {
        $placeholders = implode(',', array_fill(0, count($salaryIds), '?'));
        
        // Fetch all selected salary records
        $salaries = $db->fetchAll(
            "SELECT id, staff_id, gross_amount, salary_month FROM staff_salaries WHERE id IN ($placeholders)",
            $salaryIds
        );

        if (empty($salaries)) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'No salaries found for selected records']);
            return;
        }

        // Fetch staff details for notifications
        $userIds = array_unique(array_map(function($s) { return $s['staff_id']; }, $salaries));
        $users = $db->fetchAll(
            "SELECT id, name, email FROM users WHERE id IN (" . implode(',', array_fill(0, count($userIds), '?')) . ")",
            $userIds
        );
        $userMap = [];
        foreach ($users as $user) {
            $userMap[$user['id']] = $user;
        }

        // Update all selected salary records to mark them as paid
        $updatePlaceholders = implode(',', array_fill(0, count($salaryIds), '?'));
        $db->query(
            "UPDATE staff_salaries SET status = ?, payment_date = NOW() WHERE id IN ($updatePlaceholders)",
            array_merge([$status], $salaryIds)
        );

        // Send salary payment confirmation to each staff member
        $totalGross = 0;
        if ($notification) {
            foreach ($salaries as $salary) {
                $totalGross += $salary['gross_amount'];
                $staffId = $salary['staff_id'];
                if (isset($userMap[$staffId])) {
                    $user = $userMap[$staffId];
                    $monthDisplay = date('F Y', strtotime($salary['salary_month'] . '-01'));
                    $notification
                        ->reset()
                        ->forUser($user['id'])
                        ->setTitle('Salary Payment Processed ✓')
                        ->setMessage("Your salary for {$monthDisplay} has been paid. Gross: FCFA " . number_format($salary['gross_amount'], 2) . ". Check payslip for details.")
                        ->setType('success')
                        ->addCallToAction('View Payslip', "?page=guards-payments")
                        ->via(['email', 'sms', 'push', 'in-app'])
                        ->send();
                }
            }
        }

        // Send bulk payment notification to admin/finance
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'finance') LIMIT 10");
        if ($notification && !empty($adminUsers)) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('Bulk Salary Payment Marked Complete')
                    ->setMessage(count($salaries) . " salary records marked as paid. Total Gross: FCFA " . number_format($totalGross, 2))
                    ->setType('info')
                    ->addCallToAction('View Salaries', "?page=admin-salaries")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true, 
            'message' => 'Marked ' . count($salaries) . ' salaries as ' . $status,
            'count' => count($salaries),
            'total_gross' => $totalGross
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Failed to create bulk payment: ' . $e->getMessage()]);
    }
}

function generatePayslip($salaryId)
{
    global $db, $settings;

    if (!$salaryId) {
        http_response_code(400);
        die('Salary ID required');
    }

    /* ---------------------------------------------
     | Fetch Salary + Staff Info
     * --------------------------------------------- */
    $salary = $db->fetch(
        "SELECT s.*, 
                u.name AS staff_name, 
                u.email, 
                u.phone, 
                st.staff_id AS staff_number, 
                st.zone
         FROM staff_salaries s
         LEFT JOIN users u ON s.staff_id = u.id
         LEFT JOIN staff st ON s.staff_id = st.user_id
         WHERE s.id = ?",
        [$salaryId]
    );

    if (!$salary) {
        http_response_code(404);
        die('Salary not found');
    }

    $settings = $db->fetch("SELECT * FROM settings LIMIT 1");

    if (ob_get_level()) {
        ob_end_clean();
    }
    ob_start();
    ob_end_clean();

    require_once __DIR__ . '/../lib/tcpdf.php';

    class TCPDF_Payslip extends TCPDF
    {
        public function Header() {}
        public function Footer() {}
    }

    $pdf = new TCPDF_Payslip(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    $pdf->SetMargins(12, 8, 12);
    $pdf->SetAutoPageBreak(false);
    $pdf->AddPage();

    /* ---------------------------------------------
     | Payslip Renderer
     * --------------------------------------------- */
    $drawPayslip = function ($startY, $isDuplicate = false) use ($pdf, $salary, $settings) {

        $boxX = 10;
        $boxY = $startY;
        $boxW = 195;
        $boxH = 130;

        /* Border */
        $pdf->SetLineWidth(0.4);
        $pdf->Rect($boxX, $boxY, $boxW, $boxH);

        /* Duplicate watermark */
        if ($isDuplicate) {
            $pdf->SetFont('ptsansnarrow', 'B', 65);
            $pdf->SetTextColor(220);
            $pdf->SetAlpha(0.6);
            $pdf->SetXY($boxX + 35, $boxY + 50);
            $pdf->Write(0, 'DUPLICATA');
            $pdf->SetAlpha(1);
            $pdf->SetTextColor(0);
        }

        $pX = $boxX + 6;
        $pY = $boxY + 6;
        $pW = $boxW - 12;

        /* ---------------------------------------------
         | Header
         * --------------------------------------------- */
        $logoExists = false;
        if (!empty($settings['logo'])) {
            $logoPath = SSCI_PUBLIC . $settings['logo'];
            if (file_exists($logoPath)) {
                $pdf->Image($logoPath, $pX, $pY, 18);
                $logoExists = true;
            }
        }

        $pdf->SetFont('ptsansnarrow', 'B', 14);
        $pdf->SetXY($logoExists ? $pX + 22 : $pX, $pY);
        $pdf->Cell($pW - 30, 7, $settings['site_name'] ?? 'SSCI', 0, 1);

        $qrData = 'PAYSLIP|' . $salary['id'] . '|' . ($salary['staff_number'] ?? '');
        try {
            $pdf->write2DBarcode($qrData, 'QRCODE,M', $pX + $pW - 22, $pY, 20, 20);
        } catch (Exception $e) {}

        $pdf->Ln(2);
        $pdf->SetFont('ptsansnarrowb', 'B', 15);
        $pdf->Cell($pW, 8, 'PAYSLIP / BULLETIN DE PAIE', 0, 1, 'C');

        /* ---------------------------------------------
         | Employee Meta
         * --------------------------------------------- */
        $pdf->SetFont('ptsansnarrowb', '', 12);
        $pdf->Cell($pW / 2, 6, 'Month: ' . $salary['salary_month'], 0, 1);
        $pdf->Cell($pW / 2, 6, 'Employee: ' . $salary['staff_name'], 0, 1, 'L');

        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->Cell(
            $pW,
            5,
            'Staff #: ' . ($salary['staff_number'] ?? '-') .
            ' | Zone: ' . ($salary['zone'] ?? '-') .
            ' | Method: ' . ($salary['payment_method'] ?? '-') .
            ' | Date: ' . date('d/m/Y', strtotime($salary['created_at'])),
            0,
            1,
            'L'
        );

        $pdf->Ln(2);
        $pdf->SetDrawColor(180);
        $pdf->Line($pX, $pdf->GetY(), $pX + $pW, $pdf->GetY());
        $pdf->Ln(4);

        /* ---------------------------------------------
         | Table Setup
         * --------------------------------------------- */
        $col1 = $pW * 0.65;
        $col2 = $pW * 0.35;

        /* ========== INCOME ========== */
        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(235, 240, 250);
        $pdf->Cell($col1, 7, 'INCOME / REVENUS', 1, 0, 'L', true);
        $pdf->Cell($col2, 7, 'Amount', 1, 1, 'R', true);

        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->SetFillColor(255);

        $remunerations = is_string($salary['remunerations'] ?? null)
            ? json_decode($salary['remunerations'], true)
            : ($salary['remunerations'] ?? []);

        if (!empty($remunerations)) {
            foreach ($remunerations as $r) {
                $pdf->Cell($col1, 6, '  ' . ($r['item'] ?? 'Item'), 1);
                $pdf->Cell($col2, 6, number_format($r['amount'] ?? 0, 2), 1, 1, 'R');
            }
        } else {
            $pdf->Cell($col1, 6, '  Basic Salary', 1);
            $pdf->Cell($col2, 6, number_format($salary['basic_salary'] ?? 0, 2), 1, 1, 'R');
        }

        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(245);
        $pdf->Cell($col1, 7, 'TOTAL INCOME', 1, 0, 'L', true);
        $pdf->Cell($col2, 7, number_format($salary['gross_amount'], 2), 1, 1, 'R', true);

        $pdf->Ln(2);

        /* ========== DEDUCTIONS ========== */
        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(235, 240, 250);
        $pdf->Cell($col1, 7, 'DEDUCTIONS / RETENUES', 1, 0, 'L', true);
        $pdf->Cell($col2, 7, 'Amount', 1, 1, 'R', true);

        $pdf->SetFont('ptsansnarrow', '', 10);

        $deductions = is_string($salary['deductions'] ?? null)
            ? json_decode($salary['deductions'], true)
            : ($salary['deductions'] ?? []);

        $totalDeductions = 0;
        if (!empty($deductions)) {
            foreach ($deductions as $d) {
                $amount = $d['amount'] ?? 0;
                $totalDeductions += $amount;
                $pdf->Cell($col1, 6, '  ' . ($d['item'] ?? 'Deduction'), 1);
                $pdf->Cell($col2, 6, number_format($amount, 2), 1, 1, 'R');
            }
        } else {
            $pdf->Cell($col1, 6, '  (No deductions)', 1);
            $pdf->Cell($col2, 6, '0.00', 1, 1, 'R');
        }

        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(245);
        $pdf->Cell($col1, 7, 'TOTAL DEDUCTIONS', 1, 0, 'L', true);
        $pdf->Cell($col2, 7, number_format($totalDeductions, 2), 1, 1, 'R', true);

        $pdf->Ln(2);

        /* ========== NET PAY ========== */
        $pdf->SetFont('ptsansnarrow', 'B', 13);
        $pdf->SetFillColor(230, 245, 230);
        $pdf->Cell($col1, 8, 'NET PAY', 1, 0, 'L', true);
        $pdf->Cell(
            $col2,
            8,
            number_format($salary['net_amount'], 2) . ' ' . ($salary['currency'] ?? 'XAF'),
            1,
            1,
            'R',
            true
        );

        $pdf->SetFont('ptsansnarrow', 'I', 10);
        $pdf->Ln(6);
        $pdf->Cell(
            $pW,
            5,
            'Amount in words: ' .
            numberToWords((int)$salary['net_amount']) .
            ' ' . ($salary['currency'] ?? 'XAF'), 0, 1, 'L'
        );

        /* Footer */
        $pdf->SetFont('ptsansnarrow', 'I', 10);
        $pdf->SetTextColor(120);
        $pdf->SetXY($pX, $boxY + $boxH - 6);
        $pdf->Cell(
            $pW,
            5,
            'Status: ' . strtoupper($salary['status']) . ' | Generated on ' . date('d/m/Y'),
            0,
            1,
            'C'
        );
        $pdf->SetTextColor(0);
    };

    /* Render Original + Duplicate */
    $drawPayslip(5, false);
    $drawPayslip(10 + 130 + 10, true);

    $filename = 'Payslip_' . str_pad($salaryId, 5, '0', STR_PAD_LEFT) . '_' . date('Ymd_His') . '.pdf';
    $pdf->Output($filename, 'D');
}

// ============================================
// GUARD MANAGEMENT & CONTROLS API HANDLERS
// ============================================

function handleAssignGuardToPost() {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('manage_guards')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['post_id']) || empty($input['guard_id']) || empty($input['assignment_start_date'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing required fields: post_id, guard_id, assignment_start_date']);
        return;
    }

    try {
        // Check if assignment already exists
        $existing = $db->fetch(
            "SELECT id FROM guard_post_assignments WHERE post_id = ? AND guard_id = ? AND status = 'Active'",
            [$input['post_id'], $input['guard_id']]
        );

        if ($existing) {
            http_response_code(409);
            echo json_encode(['error' => 'Guard already assigned to this post']);
            return;
        }

        // Get guard and post details for notification
        $guard = $db->fetch("SELECT u.id, u.name, u.email FROM users u WHERE u.id = ?", [$input['guard_id']]);
        $post = $db->fetch("SELECT id, post_name, location FROM posts WHERE id = ?", [$input['post_id']]);

        $result = $db->query(
            "INSERT INTO guard_post_assignments (
                post_id, guard_id, assigned_by, assignment_start_date, assignment_end_date, status, notes, created_at
            ) VALUES (?, ?, ?, ?, ?, 'Active', ?, NOW())",
            [
                $input['post_id'],
                $input['guard_id'],
                $_SESSION['user_id'] ?? 1,
                $input['assignment_start_date'],
                $input['assignment_end_date'] ?? null,
                $input['notes'] ?? null
            ]
        );

        $assignmentId = $db->lastInsertId();

        // Send assignment notification to guard
        if ($notification && $guard) {
            $startDate = date('F j, Y', strtotime($input['assignment_start_date']));
            $endDate = !empty($input['assignment_end_date']) ? date('F j, Y', strtotime($input['assignment_end_date'])) : 'Open-ended';
            $notification
                ->reset()
                ->forUser($guard['id'])
                ->setTitle('New Post Assignment ✓')
                ->setMessage("You have been assigned to " . ($post['post_name'] ?? 'Post') . " at " . ($post['location'] ?? 'TBD') . ". Start: {$startDate}, End: {$endDate}")
                ->setType('success')
                ->addCallToAction('View Assignment', "/dashboard?page=assignments&id={$assignmentId}")
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }

        // Send notification to supervisors/admin about assignment
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'supervisor') LIMIT 10");
        if ($notification && !empty($adminUsers)) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('Guard Post Assignment')
                    ->setMessage(($guard['name'] ?? 'Unknown') . " assigned to " . ($post['post_name'] ?? 'Post') . " starting " . $input['assignment_start_date'])
                    ->setType('info')
                    ->addCallToAction('View', "/dashboard?page=assignments&id={$assignmentId}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode(['success' => true, 'id' => $assignmentId]);
    } catch (Exception $e) {
        error_log('handleAssignGuardToPost error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handleRemoveGuardAssignment($id) {
    global $auth, $db, $notification;

    if (!$auth->hasPermission('manage_guards')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        // Get assignment details before updating
        $assignment = $db->fetch(
            "SELECT gpa.*, s.user_id, u.name, p.post_name, p.location FROM guard_post_assignments gpa
             JOIN staff s ON gpa.guard_id = s.id
             JOIN users u ON s.user_id = u.id
             JOIN posts p ON gpa.post_id = p.id
             WHERE gpa.id = ?",
            [$id]
        );

        $db->query(
            "UPDATE guard_post_assignments SET status = 'Completed', updated_at = NOW() WHERE id = ?",
            [$id]
        );

        // Send removal notification to guard
        if ($notification && $assignment) {
            $notification
                ->reset()
                ->forUser($assignment['user_id'])
                ->setTitle('Post Assignment Ended')
                ->setMessage("Your assignment to " . $assignment['post_name'] . " at " . $assignment['location'] . " has been ended.")
                ->setType('info')
                ->addCallToAction('View Dashboard', "/dashboard?page=assignments")
                ->via(['email', 'sms', 'push', 'in-app'])
                ->send();
        }

        // Send notification to supervisors/admin
        $adminUsers = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'supervisor') LIMIT 10");
        if ($notification && !empty($adminUsers) && $assignment) {
            foreach ($adminUsers as $admin) {
                $notification
                    ->reset()
                    ->forUser($admin['id'])
                    ->setTitle('Guard Assignment Removed')
                    ->setMessage($assignment['name'] . " removed from " . $assignment['post_name'])
                    ->setType('info')
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode(['success' => true]);
    } catch (Exception $e) {
        error_log('handleRemoveGuardAssignment error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handleGetPostGuards($postId) {
    global $db;

    try {
        $guards = $db->fetchAll(
            "SELECT gpa.*, u.name, u.email, u.phone, s.staff_id, s.zone
             FROM guard_post_assignments gpa
             JOIN users u ON gpa.guard_id = u.id
             LEFT JOIN staff s ON u.id = s.user_id
             WHERE gpa.post_id = ? AND gpa.status = 'Active'
             ORDER BY u.name",
            [$postId]
        );

        echo json_encode(['success' => true, 'data' => $guards]);
    } catch (Exception $e) {
        error_log('handleGetPostGuards error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handleGetGuardsByPost($postId) {
    global $db;

    if (!$postId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Post ID required']);
        return;
    }

    try {
        $guards = $db->fetchAll(
            "SELECT u.id, u.name, s.staff_id
             FROM users u
             LEFT JOIN staff s ON u.id = s.user_id
             JOIN guard_post_assignments gpa ON u.id = gpa.guard_id
             WHERE gpa.post_id = ? AND gpa.status = 'Active' AND u.role = 'guard'
             ORDER BY u.name",
            [$postId]
        );

        echo json_encode(['success' => true, 'guards' => $guards]);
    } catch (Exception $e) {
        error_log('handleGetGuardsByPost error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleRecordGuardControl() {
    global $auth, $db;

    // Controllers, managers, and admins can record controls
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id'] ?? 0]);
    if (!$user || !in_array($user['role'], ['controller', 'manager', 'admin'])) {
        http_response_code(403);
        echo json_encode(['error' => 'Only controllers, managers, or admins can record controls']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['post_id']) || empty($input['guard_id'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing required fields: post_id, guard_id']);
        return;
    }

    try {
        // Verify guard is assigned to post
        $assignment = $db->fetch(
            "SELECT id FROM guard_post_assignments WHERE post_id = ? AND guard_id = ? AND status = 'Active'",
            [$input['post_id'], $input['guard_id']]
        );

        if (!$assignment) {
            http_response_code(400);
            echo json_encode(['error' => 'Guard is not assigned to this post']);
            return;
        }

        // Handle photo upload if present
        $photoPath = null;
        if (!empty($input['photo_base64'])) {
            $photoPath = handlePhotoUpload($input['photo_base64'], $input['post_id']);
        }

        $controlDatetime = !empty($input['control_datetime']) ? $input['control_datetime'] : date('Y-m-d H:i:s');

        $result = $db->query(
            "INSERT INTO guards_controls (
                post_id, guard_id, controller_id, control_datetime, latitude, longitude,
                attendance_punctuality, uniform_presentation, patrol_reporting_discipline,
                client_feedback, controller_general_evaluation, notes, photo_path, total_score, status, created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'Pending', NOW())",
            [
                $input['post_id'],
                $input['guard_id'],
                $_SESSION['user_id'] ?? 1,
                $controlDatetime,
                $input['latitude'] ?? null,
                $input['longitude'] ?? null,
                $input['attendance_punctuality'] ?? 3,
                $input['uniform_presentation'] ?? 3,
                $input['patrol_reporting_discipline'] ?? 3,
                $input['client_feedback'] ?? 3,
                $input['controller_general_evaluation'] ?? 3,
                $input['notes'] ?? null,
                $photoPath,
                null,
            ]
        );

        $controlId = $db->lastInsertId();

        // Send notification to guard about control recording
        global $notification;
        if ($notification) {
            $guard = $db->fetch("SELECT name FROM users WHERE id = ?", [$input['guard_id']]);
            $post = $db->fetch("SELECT post_name, location FROM posts WHERE id = ?", [$input['post_id']]);
            $controller = $db->fetch("SELECT name FROM users WHERE id = ?", [$_SESSION['user_id']]);
            
            if ($guard && $post && $controller) {
                $notification
                    ->reset()
                    ->forUser($input['guard_id'])
                    ->setTitle('Control Assessment Completed ✓')
                    ->setMessage("Control assessment completed by {$controller['name']} at {$post['post_name']}. Review your score for feedback.")
                    ->setType('info')
                    ->addCallToAction('View Control', "?page=guards-controls&id={$controlId}")
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true,
            'id' => $controlId,
            'message' => 'Control recorded successfully'
        ]);
    } catch (Exception $e) {
        error_log('handleRecordGuardControl error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handleGetGuardControls($guardId) {
    global $db;

    try {
        $controls = $db->fetchAll(
            "SELECT gc.*, p.name as post_name, u.name as controller_name
             FROM guards_controls gc
             JOIN posts p ON gc.post_id = p.id
             JOIN users u ON gc.controller_id = u.id
             WHERE gc.guard_id = ?
             ORDER BY gc.control_datetime DESC
             LIMIT 50",
            [$guardId]
        );

        echo json_encode(['success' => true, 'data' => $controls]);
    } catch (Exception $e) {
        error_log('handleGetGuardControls error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handlePhotoUpload($base64Data, $postId) {
    try {
        // Create uploads directory if it doesn't exist
        $uploadDir = __DIR__ . '/uploads/controls/' . date('Y/m');
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        // Generate unique filename
        $filename = 'control_' . $postId . '_' . time() . '_' . uniqid() . '.jpg';
        $filepath = $uploadDir . '/' . $filename;

        // Decode base64 image
        if (strpos($base64Data, 'data:image') === 0) {
            // Remove data:image/png;base64, prefix
            $base64Data = substr($base64Data, strpos($base64Data, ',') + 1);
        }

        $imageData = base64_decode($base64Data, true);
        if ($imageData === false) {
            throw new Exception('Invalid base64 image data');
        }

        // Try using GD library if available for compression
        if (extension_loaded('gd') && function_exists('imagecreatefromstring')) {
            try {
                // Create image from string
                $image = imagecreatefromstring($imageData);
                if ($image !== false) {
                    // Get current dimensions
                    $originalWidth = imagesx($image);
                    $originalHeight = imagesy($image);

                    // Calculate new dimensions (max width 320px to keep file size low)
                    $maxWidth = 320;
                    $maxHeight = 240;
                    $newWidth = $originalWidth;
                    $newHeight = $originalHeight;

                    // Scale down if needed
                    if ($originalWidth > $maxWidth) {
                        $ratio = $maxWidth / $originalWidth;
                        $newWidth = $maxWidth;
                        $newHeight = (int)($originalHeight * $ratio);
                    }
                    if ($newHeight > $maxHeight) {
                        $ratio = $maxHeight / $newHeight;
                        $newHeight = $maxHeight;
                        $newWidth = (int)($newWidth * $ratio);
                    }

                    // Create resized image
                    $resized = imagecreatetruecolor($newWidth, $newHeight);
                    if ($resized !== false) {
                        imagecopyresampled($resized, $image, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);
                        
                        // Save with progressive quality reduction until under 30KB
                        $quality = 85;
                        $maxAttempts = 10;
                        $attempt = 0;

                        while ($attempt < $maxAttempts) {
                            ob_start();
                            imagejpeg($resized, null, $quality);
                            $compressed = ob_get_clean();

                            $fileSize = strlen($compressed);

                            // If under 30KB, save and exit
                            if ($fileSize <= 30720) { // 30KB in bytes
                                file_put_contents($filepath, $compressed);
                                imagedestroy($resized);
                                imagedestroy($image);
                                return '/public/uploads/controls/' . date('Y/m') . '/' . $filename;
                            }

                            // Reduce quality and try again
                            $quality -= 5;
                            if ($quality < 30) {
                                // Force save even if slightly over
                                file_put_contents($filepath, $compressed);
                                imagedestroy($resized);
                                imagedestroy($image);
                                return '/public/uploads/controls/' . date('Y/m') . '/' . $filename;
                            }
                            $attempt++;
                        }

                        imagedestroy($resized);
                    }
                    imagedestroy($image);
                }
            } catch (Exception $gdError) {
                error_log('GD compression failed: ' . $gdError->getMessage() . ' - falling back to raw save');
            }
        }

        // Fallback: Save image as-is if GD not available or failed
        // Mobile camera images are usually already compressed
        $fileSize = strlen($imageData);
        if ($fileSize <= 102400) { // 100KB max if GD not available
            file_put_contents($filepath, $imageData);
            return '/public/uploads/controls/' . date('Y/m') . '/' . $filename;
        } else {
            throw new Exception('Image too large (' . round($fileSize / 1024) . 'KB) and GD compression unavailable');
        }
    } catch (Exception $e) {
        error_log('handlePhotoUpload error: ' . $e->getMessage());
        return null;
    }
}

function handleGetGuardControl($controlId) {
    global $db;

    if (!$controlId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Control ID required']);
        return;
    }

    try {
        $control = $db->fetch(
            "SELECT gc.*, p.name as post_name, u.name as guard_name 
             FROM guards_controls gc
             JOIN posts p ON gc.post_id = p.id
             JOIN users u ON gc.guard_id = u.id
             WHERE gc.id = ?",
            [$controlId]
        );

        if (!$control) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'Control not found']);
            return;
        }

        echo json_encode(['success' => true, 'control' => $control]);
    } catch (Exception $e) {
        error_log('handleGetGuardControl error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleUpdateGuardControl() {
    global $auth, $db;

    // Controllers, managers, and admins can update controls
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id'] ?? 0]);
    if (!$user || !in_array($user['role'], ['controller', 'manager', 'admin'])) {
        http_response_code(403);
        echo json_encode(['error' => 'Only controllers, managers, or admins can update controls']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['id'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing control ID']);
        return;
    }

    try {
        // Check if control exists
        $control = $db->fetch("SELECT * FROM guards_controls WHERE id = ?", [$input['id']]);
        if (!$control) {
            http_response_code(404);
            echo json_encode(['error' => 'Control not found']);
            return;
        }

        // Permission check: controller can only update their own controls
        if ($user['role'] === 'controller' && $control['controller_id'] != $_SESSION['user_id']) {
            http_response_code(403);
            echo json_encode(['error' => 'You can only update your own records']);
            return;
        }

        // Handle photo update if present
        $photoPath = $control['photo_path'];
        if (!empty($input['photo_base64']) && $input['photo_base64'] !== 'unchanged') {
            // Delete old photo if exists
            if ($control['photo_path']) {
                $oldPhotoPath = __DIR__ . $control['photo_path'];
                if (file_exists($oldPhotoPath)) {
                    unlink($oldPhotoPath);
                }
            }
            // Upload new photo
            $photoPath = handlePhotoUpload($input['photo_base64'], $control['post_id']);
        }

        // Update control
        $db->query(
            "UPDATE guards_controls SET
                attendance_punctuality = ?,
                uniform_presentation = ?,
                patrol_reporting_discipline = ?,
                client_feedback = ?,
                controller_general_evaluation = ?,
                notes = ?,
                photo_path = ?,
                latitude = ?,
                longitude = ?,
                updated_at = NOW()
            WHERE id = ?",
            [
                $input['attendance_punctuality'] ?? 3,
                $input['uniform_presentation'] ?? 3,
                $input['patrol_reporting_discipline'] ?? 3,
                $input['client_feedback'] ?? 3,
                $input['controller_general_evaluation'] ?? 3,
                $input['notes'] ?? null,
                $photoPath,
                $input['latitude'] ?? null,
                $input['longitude'] ?? null,
                $input['id']
            ]
        );

        echo json_encode([
            'success' => true,
            'id' => $input['id'],
            'message' => 'Control updated successfully'
        ]);
    } catch (Exception $e) {
        error_log('handleUpdateGuardControl error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage(), 'success' => false]);
    }
}

function handleDeleteGuardControl($controlId) {
    global $auth, $db;

    if (!$controlId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Control ID required']);
        return;
    }

    try {
        // Check if control exists
        $control = $db->fetch("SELECT * FROM guards_controls WHERE id = ?", [$controlId]);
        if (!$control) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'Control not found']);
            return;
        }

        // Permission check: controller can only delete their own controls, managers/admins can delete any
        $currentUser = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id'] ?? 0]);
        
        if ($currentUser['role'] === 'controller' && $control['controller_id'] != $_SESSION['user_id']) {
            http_response_code(403);
            echo json_encode(['success' => false, 'error' => 'You can only delete your own records']);
            return;
        }

        // Delete the control record using query() method instead of execute()
        $db->query("DELETE FROM guards_controls WHERE id = ?", [$controlId]);

        // If there's a photo, delete it
        if ($control['photo_path']) {
            $photoPath = __DIR__ . $control['photo_path'];
            if (file_exists($photoPath)) {
                unlink($photoPath);
            }
        }

        echo json_encode(['success' => true, 'message' => 'Control record deleted successfully']);
    } catch (Exception $e) {
        error_log('handleDeleteGuardControl error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleGenerateControlReport() {
    global $db;

    try {
        $fromDate = $_GET['from'] ?? null;
        $toDate = $_GET['to'] ?? null;

        if (!$fromDate || !$toDate) {
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'From and To dates required']);
            return;
        }

        // Get controls within date range
        $controls = $db->fetchAll(
            "SELECT gc.*, p.id as post_id, p.zone, u.name as guard_name, u.id as guard_id
             FROM guards_controls gc
             JOIN posts p ON gc.post_id = p.id
             JOIN users u ON gc.guard_id = u.id
             WHERE DATE(gc.control_datetime) >= ? AND DATE(gc.control_datetime) <= ?
             ORDER BY gc.control_datetime ASC",
            [$fromDate, $toDate]
        );

        if (empty($controls)) {
            echo json_encode(['success' => true, 'report' => [
                'zone' => 'N/A',
                'postCount' => 0,
                'guardCount' => 0,
                'totalControls' => 0,
                'avgControlsPerPost' => 0,
                'avgGapHours' => 0,
                'bestScore' => 0,
                'worstScore' => 0,
                'strongestAspect' => ['name' => 'N/A', 'score' => 0],
                'weakestAspect' => ['name' => 'N/A', 'score' => 0],
                'bestGuard' => ['name' => 'N/A', 'score' => 0],
                'worstGuard' => ['name' => 'N/A', 'score' => 0],
                'guardPerformance' => []
            ]]);
            return;
        }

        // Calculate report data
        $postIds = array_unique(array_column($controls, 'post_id'));
        $guardIds = array_unique(array_column($controls, 'guard_id'));
        
        $totalControls = count($controls);
        $postCount = count($postIds);
        $guardCount = count($guardIds);
        
        $avgControlsPerPost = $postCount > 0 ? round($totalControls / $postCount, 2) : 0;

        // Calculate average gap between controls
        $gaps = [];
        for ($i = 1; $i < count($controls); $i++) {
            $prev = strtotime($controls[$i-1]['control_datetime']);
            $curr = strtotime($controls[$i]['control_datetime']);
            $gaps[] = ($curr - $prev) / 3600; // Convert to hours
        }
        $avgGapHours = !empty($gaps) ? round(array_sum($gaps) / count($gaps), 2) : 0;

        // Calculate scores and find best/worst
        $scores = array_map(function($c) {
            return (($c['attendance_punctuality'] + $c['uniform_presentation'] + $c['patrol_reporting_discipline'] + $c['client_feedback'] + $c['controller_general_evaluation']) / 5 / 5) * 100;
        }, $controls);

        $bestScore = !empty($scores) ? max($scores) : 0;
        $worstScore = !empty($scores) ? min($scores) : 0;

        // Find best and worst guard
        $bestControlIdx = array_key_first(array_flip(array_keys($scores, $bestScore)));
        $worstControlIdx = array_key_first(array_flip(array_keys($scores, $worstScore)));

        $bestGuard = [
            'name' => $controls[$bestControlIdx]['guard_name'],
            'score' => $bestScore
        ];
        $worstGuard = [
            'name' => $controls[$worstControlIdx]['guard_name'],
            'score' => $worstScore
        ];

        // Calculate average by aspect
        $avgByAspect = [
            'attendance' => array_sum(array_column($controls, 'attendance_punctuality')) / $totalControls,
            'uniform' => array_sum(array_column($controls, 'uniform_presentation')) / $totalControls,
            'patrol' => array_sum(array_column($controls, 'patrol_reporting_discipline')) / $totalControls,
            'client' => array_sum(array_column($controls, 'client_feedback')) / $totalControls,
            'general' => array_sum(array_column($controls, 'controller_general_evaluation')) / $totalControls
        ];

        arsort($avgByAspect);
        $strongest = array_key_first($avgByAspect);
        $weakest = array_key_last($avgByAspect);

        $aspectNames = [
            'attendance' => 'Attendance & Punctuality',
            'uniform' => 'Uniform & Presentation',
            'patrol' => 'Patrol & Reporting Discipline',
            'client' => 'Client Feedback',
            'general' => 'General Evaluation'
        ];

        $strongestAspect = [
            'name' => $aspectNames[$strongest],
            'score' => $avgByAspect[$strongest]
        ];
        $weakestAspect = [
            'name' => $aspectNames[$weakest],
            'score' => $avgByAspect[$weakest]
        ];

        // Guard performance summary
        $guardPerformance = [];
        $guardStats = [];

        foreach ($controls as $control) {
            $guardId = $control['guard_id'];
            if (!isset($guardStats[$guardId])) {
                $guardStats[$guardId] = [
                    'name' => $control['guard_name'],
                    'controls' => [],
                    'controlCount' => 0,
                    'attendance' => 0,
                    'uniform' => 0,
                    'patrol' => 0,
                    'client' => 0,
                    'general' => 0
                ];
            }
            $guardStats[$guardId]['controls'][] = $control;
            $guardStats[$guardId]['controlCount']++;
            $guardStats[$guardId]['attendance'] += $control['attendance_punctuality'];
            $guardStats[$guardId]['uniform'] += $control['uniform_presentation'];
            $guardStats[$guardId]['patrol'] += $control['patrol_reporting_discipline'];
            $guardStats[$guardId]['client'] += $control['client_feedback'];
            $guardStats[$guardId]['general'] += $control['controller_general_evaluation'];
        }

        foreach ($guardStats as $stats) {
            $avg_attendance = $stats['attendance'] / $stats['controlCount'];
            $avg_uniform = $stats['uniform'] / $stats['controlCount'];
            $avg_patrol = $stats['patrol'] / $stats['controlCount'];
            $avg_client = $stats['client'] / $stats['controlCount'];
            $avg_general = $stats['general'] / $stats['controlCount'];
            $overallScore = (($avg_attendance + $avg_uniform + $avg_patrol + $avg_client + $avg_general) / 5 / 5) * 100;

            $guardPerformance[] = [
                'name' => $stats['name'],
                'controlCount' => $stats['controlCount'],
                'attendance' => $avg_attendance,
                'uniform' => $avg_uniform,
                'patrol' => $avg_patrol,
                'client' => $avg_client,
                'general' => $avg_general,
                'overallScore' => $overallScore
            ];
        }

        // Sort by overall score descending
        usort($guardPerformance, function($a, $b) {
            return $b['overallScore'] <=> $a['overallScore'];
        });

        $zone = !empty($controls) ? $controls[0]['zone'] : 'N/A';

        $report = [
            'zone' => $zone,
            'postCount' => $postCount,
            'guardCount' => $guardCount,
            'totalControls' => $totalControls,
            'avgControlsPerPost' => $avgControlsPerPost,
            'avgGapHours' => $avgGapHours,
            'bestScore' => $bestScore,
            'worstScore' => $worstScore,
            'strongestAspect' => $strongestAspect,
            'weakestAspect' => $weakestAspect,
            'bestGuard' => $bestGuard,
            'worstGuard' => $worstGuard,
            'guardPerformance' => $guardPerformance
        ];

        echo json_encode(['success' => true, 'report' => $report]);
    } catch (Exception $e) {
        error_log('handleGenerateControlReport error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleGenerateControlReportPDF() {
    global $db;

    try {
        $fromDate = $_GET['from'] ?? null;
        $toDate = $_GET['to'] ?? null;

        if (!$fromDate || !$toDate) {
            http_response_code(400);
            die('From and To dates required');
        }

        // Get controls within date range
        $controls = $db->fetchAll(
            "SELECT gc.*, p.id as post_id, p.name as post_name, p.zone, u.name as guard_name, u.id as guard_id
             FROM guards_controls gc
             JOIN posts p ON gc.post_id = p.id
             JOIN users u ON gc.guard_id = u.id
             WHERE DATE(gc.control_datetime) >= ? AND DATE(gc.control_datetime) <= ?
             ORDER BY gc.control_datetime ASC",
            [$fromDate, $toDate]
        );

        // Generate report data (reuse same logic as JSON endpoint)
        if (empty($controls)) {
            die('No controls found for the specified date range');
        }

        $postIds = array_unique(array_column($controls, 'post_id'));
        $guardIds = array_unique(array_column($controls, 'guard_id'));
        
        $totalControls = count($controls);
        $postCount = count($postIds);
        $guardCount = count($guardIds);
        $avgControlsPerPost = $postCount > 0 ? round($totalControls / $postCount, 2) : 0;

        // Calculate gaps
        $gaps = [];
        for ($i = 1; $i < count($controls); $i++) {
            $prev = strtotime($controls[$i-1]['control_datetime']);
            $curr = strtotime($controls[$i]['control_datetime']);
            $gaps[] = ($curr - $prev) / 3600;
        }
        $avgGapHours = !empty($gaps) ? round(array_sum($gaps) / count($gaps), 2) : 0;

        // Calculate scores
        $scores = array_map(function($c) {
            return (($c['attendance_punctuality'] + $c['uniform_presentation'] + $c['patrol_reporting_discipline'] + $c['client_feedback'] + $c['controller_general_evaluation']) / 5 / 5) * 100;
        }, $controls);

        $bestScore = !empty($scores) ? max($scores) : 0;
        $worstScore = !empty($scores) ? min($scores) : 0;

        $bestControlIdx = array_key_first(array_flip(array_keys($scores, $bestScore)));
        $worstControlIdx = array_key_first(array_flip(array_keys($scores, $worstScore)));

        $bestGuard = [
            'name' => $controls[$bestControlIdx]['guard_name'],
            'score' => $bestScore
        ];
        $worstGuard = [
            'name' => $controls[$worstControlIdx]['guard_name'],
            'score' => $worstScore
        ];

        // Calculate average by aspect
        $avgByAspect = [
            'attendance' => array_sum(array_column($controls, 'attendance_punctuality')) / $totalControls,
            'uniform' => array_sum(array_column($controls, 'uniform_presentation')) / $totalControls,
            'patrol' => array_sum(array_column($controls, 'patrol_reporting_discipline')) / $totalControls,
            'client' => array_sum(array_column($controls, 'client_feedback')) / $totalControls,
            'general' => array_sum(array_column($controls, 'controller_general_evaluation')) / $totalControls
        ];

        arsort($avgByAspect);
        $strongest = array_key_first($avgByAspect);
        $weakest = array_key_last($avgByAspect);

        $aspectNames = [
            'attendance' => 'Attendance & Punctuality',
            'uniform' => 'Uniform & Presentation',
            'patrol' => 'Patrol & Reporting Discipline',
            'client' => 'Client Feedback',
            'general' => 'General Evaluation'
        ];

        // Guard performance summary
        $guardPerformance = [];
        $guardStats = [];

        foreach ($controls as $control) {
            $guardId = $control['guard_id'];
            if (!isset($guardStats[$guardId])) {
                $guardStats[$guardId] = [
                    'name' => $control['guard_name'],
                    'controls' => [],
                    'controlCount' => 0,
                    'attendance' => 0,
                    'uniform' => 0,
                    'patrol' => 0,
                    'client' => 0,
                    'general' => 0
                ];
            }
            $guardStats[$guardId]['controls'][] = $control;
            $guardStats[$guardId]['controlCount']++;
            $guardStats[$guardId]['attendance'] += $control['attendance_punctuality'];
            $guardStats[$guardId]['uniform'] += $control['uniform_presentation'];
            $guardStats[$guardId]['patrol'] += $control['patrol_reporting_discipline'];
            $guardStats[$guardId]['client'] += $control['client_feedback'];
            $guardStats[$guardId]['general'] += $control['controller_general_evaluation'];
        }

        foreach ($guardStats as $stats) {
            $avg_attendance = $stats['attendance'] / $stats['controlCount'];
            $avg_uniform = $stats['uniform'] / $stats['controlCount'];
            $avg_patrol = $stats['patrol'] / $stats['controlCount'];
            $avg_client = $stats['client'] / $stats['controlCount'];
            $avg_general = $stats['general'] / $stats['controlCount'];
            $overallScore = (($avg_attendance + $avg_uniform + $avg_patrol + $avg_client + $avg_general) / 5 / 5) * 100;

            $guardPerformance[] = [
                'name' => $stats['name'],
                'controlCount' => $stats['controlCount'],
                'attendance' => $avg_attendance,
                'uniform' => $avg_uniform,
                'patrol' => $avg_patrol,
                'client' => $avg_client,
                'general' => $avg_general,
                'overallScore' => $overallScore
            ];
        }

        usort($guardPerformance, function($a, $b) {
            return $b['overallScore'] <=> $a['overallScore'];
        });

        $zone = !empty($controls) ? $controls[0]['zone'] : 'N/A';

        // Get company info
        $settings = $db->fetch("SELECT site_name, site_acronym, logo, letterhead FROM settings LIMIT 1");

        // Include TCPDF library
        require_once __DIR__ . '/../lib/tcpdf.php';

        // Custom TCPDF class with footer
        class TCPDF_Report extends TCPDF {
            public function Footer() {
                $this->SetY(-15);
                $this->SetFont('ptsansnarrow', '', 8);
                $this->SetTextColor(128, 128, 128);
                $this->Ln();
                $this->Cell(0, 10, '© ' . date('Y') . ' SHEPHERD SECURITY AND CONSULTANCY INTERNATIONAL (SSCI). Generated on ' . date('d M Y H:i:s'), 0, false, 'C', false);
            }
        }

        // Create PDF
        $pdf = new TCPDF_Report(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
        $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
        $pdf->SetMargins(10, 10, 10);
        $pdf->SetAutoPageBreak(true, 20);
        $pdf->AddPage();
        $pdf->SetFont('ptsansnarrow', '', 11);

        // Add letterhead
        if (!empty($settings['letterhead'])) {
            $letterheadPath = SSCI_PUBLIC . $settings['letterhead'];
            if (file_exists($letterheadPath)) {
                $pdf->Image($letterheadPath, 10, 10, 190, 0, '', '', 'T', false, 300);
                $pdf->Ln(50);
            }
        } else if (!empty($settings['logo'])) {
            $logoPath = SSCI_PUBLIC . $settings['logo'];
            if (file_exists($logoPath)) {
                $pdf->Image($logoPath, 10, 10, 30, 0, '', '', 'T', false, 150);
            }
            $pdf->SetFont('ptsansnarrow', 'B', 16);
            $pdf->Ln(25);
            $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
            $pdf->SetFont('ptsansnarrow', '', 10);
            $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
            $pdf->Ln(5);
        } else {
            $pdf->SetFont('ptsansnarrow', 'B', 18);
            $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
            $pdf->SetFont('ptsansnarrow', '', 11);
            $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
            $pdf->Ln(10);
        }

        // Report title
        $pdf->SetFont('ptsansnarrow', 'B', 14);
        $pdf->Cell(0, 8, 'GUARD CONTROL PERFORMANCE REPORT', 0, 1, 'C');
        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->Cell(0, 5, 'Period: ' . date('d M Y', strtotime($fromDate)) . ' to ' . date('d M Y', strtotime($toDate)), 0, 1, 'C');
        $pdf->Ln(8);

        // Summary section
        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(200, 200, 200);
        $pdf->Cell(0, 6, 'REPORT SUMMARY', 0, 1, 'L', true);
        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->SetFillColor(240, 240, 240);

        $summaryData = [
            ['Zone', $zone],
            ['Total Posts', $postCount],
            ['Total Guards', $guardCount],
            ['Total Controls Recorded', $totalControls],
            ['Average Controls per Post', $avgControlsPerPost],
            ['Average Gap between Controls', $avgGapHours . ' hours'],
            ['Best Control Score', round($bestScore, 2) . '%'],
            ['Worst Control Score', round($worstScore, 2) . '%']
        ];

        foreach ($summaryData as $row) {
            $pdf->Cell(100, 5, $row[0] . ':', 1, 0, 'L', true);
            $pdf->Cell(90, 5, $row[1], 1, 1, 'L', false);
        }
        $pdf->Ln(5);

        // Performance Analysis
        $pdf->SetFont('ptsansnarrow', 'B', 11);
        $pdf->SetFillColor(200, 200, 200);
        $pdf->Cell(0, 6, 'PERFORMANCE ANALYSIS', 0, 1, 'L', true);
        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->SetFillColor(240, 240, 240);

        $pdf->Cell(100, 5, 'Strongest Point:', 1, 0, 'L', true);
        $pdf->Cell(90, 5, $aspectNames[$strongest] . ' (' . round($avgByAspect[$strongest], 2) . '/5)', 1, 1, 'L', false);

        $pdf->Cell(100, 5, 'Weakest Point:', 1, 0, 'L', true);
        $pdf->Cell(90, 5, $aspectNames[$weakest] . ' (' . round($avgByAspect[$weakest], 2) . '/5)', 1, 1, 'L', false);

        $pdf->Cell(100, 5, 'Best Guard:', 1, 0, 'L', true);
        $pdf->Cell(90, 5, $bestGuard['name'] . ' (' . round($bestGuard['score'], 2) . '%)', 1, 1, 'L', false);

        $pdf->Cell(100, 5, 'Needs Improvement:', 1, 0, 'L', true);
        $pdf->Cell(90, 5, $worstGuard['name'] . ' (' . round($worstGuard['score'], 2) . '%)', 1, 1, 'L', false);
        $pdf->Ln(8);

        // Guard Performance Table
        $pdf->SetFont('ptsansnarrow', 'B', 10);
        $pdf->SetFillColor(200, 200, 200);
        $pdf->Cell(40, 6, 'Guard Name', 1, 0, 'L', true);
        $pdf->Cell(20, 6, 'Controls', 1, 0, 'C', true);
        $pdf->Cell(25, 6, 'Overall %', 1, 0, 'C', true);
        $pdf->Cell(18, 6, 'Attend.', 1, 0, 'C', true);
        $pdf->Cell(18, 6, 'Uniform', 1, 0, 'C', true);
        $pdf->Cell(18, 6, 'Patrol', 1, 0, 'C', true);
        $pdf->Cell(18, 6, 'Client', 1, 0, 'C', true);
        $pdf->Cell(18, 6, 'General', 1, 1, 'C', true);

        $pdf->SetFont('ptsansnarrow', '', 9);
        $pdf->SetFillColor(255, 255, 255);

        foreach ($guardPerformance as $guard) {
            $pdf->Cell(40, 5, substr($guard['name'], 0, 15), 1, 0, 'L', false);
            $pdf->Cell(20, 5, $guard['controlCount'], 1, 0, 'C', false);
            $pdf->Cell(25, 5, round($guard['overallScore'], 0), 1, 0, 'C', false);
            $pdf->Cell(18, 5, round($guard['attendance'], 1), 1, 0, 'C', false);
            $pdf->Cell(18, 5, round($guard['uniform'], 1), 1, 0, 'C', false);
            $pdf->Cell(18, 5, round($guard['patrol'], 1), 1, 0, 'C', false);
            $pdf->Cell(18, 5, round($guard['client'], 1), 1, 0, 'C', false);
            $pdf->Cell(18, 5, round($guard['general'], 1), 1, 1, 'C', false);
        }

        // Output PDF
        $filename = 'Guard_Control_Report_' . date('Y-m-d', strtotime($fromDate)) . '_to_' . date('Y-m-d', strtotime($toDate)) . '.pdf';
        $pdf->Output($filename, 'D');

    } catch (Exception $e) {
        error_log('handleGenerateControlReportPDF error: ' . $e->getMessage());
        http_response_code(500);
        die('Error generating PDF: ' . $e->getMessage());
    }
}

// ==================== Client API Handlers ====================

function handleClientAddPayment() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Only clients can add payments
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id']]);
    if (!$user || $user['role'] !== 'client') {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Only clients can add payments']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    // Validate input
    if (empty($input['invoice_id']) || empty($input['amount']) || empty($input['payment_date']) || empty($input['payment_method'])) {
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    // Verify invoice belongs to this user
    $invoice = $db->fetch(
        "SELECT id, owner_id FROM invoices WHERE id = ?",
        [$input['invoice_id']]
    );

    if (!$invoice || $invoice['owner_id'] != $_SESSION['user_id']) {
        echo json_encode(['success' => false, 'error' => 'Invalid invoice']);
        return;
    }

    try {
        // Generate unique reference number if not provided
        $reference = $input['reference'] ?? 'PAY-' . uniqid();
        
        $result = $db->query(
            "INSERT INTO payments (reference_number, payment_type, related_id, amount, payment_method, payment_date, status, description, created_by, created_at)
             VALUES (?, 'Client_Income', ?, ?, ?, ?, 'Confirmed', ?, ?, NOW())",
            [
                $reference,
                $input['invoice_id'],
                floatval($input['amount']),
                $input['payment_method'],
                $input['payment_date'],
                $input['notes'] ?? null,
                $_SESSION['user_id']
            ]
        );

        if ($result) {
            echo json_encode(['success' => true, 'message' => 'Payment recorded successfully']);
        } else {
            echo json_encode(['success' => false, 'error' => 'Failed to record payment']);
        }
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleClientAddFeedback() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Only clients can add feedback
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id']]);
    if (!$user || $user['role'] !== 'client') {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Only clients can add feedback']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    // Accept both lowercase and capitalized versions
    $date = $input['date'] ?? $input['Date'] ?? null;
    $subject = $input['subject'] ?? $input['Subject'] ?? null;

    // Validate input - at minimum need Date and Subject
    if (empty($date) || empty($subject)) {
        echo json_encode(['success' => false, 'error' => 'Missing required fields: Date and Subject']);
        return;
    }

    try {
        // Parse date to extract month and year
        $feedbackDate = new DateTime($date);
        $month = $feedbackDate->format('m');
        $year = $feedbackDate->format('Y');

        // Guard scoring fields - use provided values or 3 (neutral)
        $punctuality = intval($input['Punctuality'] ?? $input['punctuality'] ?? 3);
        $attendance = intval($input['Attendance'] ?? $input['attendance'] ?? 3);
        $neatness = intval($input['Neatness'] ?? $input['neatness'] ?? 3);
        $patrolling = intval($input['Patrolling'] ?? $input['patrolling'] ?? 3);
        $communication = intval($input['Communication'] ?? $input['communication'] ?? 3);
        $respect = intval($input['Respect'] ?? $input['respect'] ?? 3);
        $jobMastery = intval($input['Job_Mastery'] ?? $input['job_mastery'] ?? 3);
        $vigilance = intval($input['Vigilance'] ?? $input['vigilance'] ?? 3);

        // Calculate Guard Score (average of guard criteria)
        $guardScore = round(($punctuality + $attendance + $neatness + $patrolling + 
                            $communication + $respect + $jobMastery + $vigilance) / 8, 2);

        // Controller scoring fields
        $frequency = intval($input['Frequency'] ?? $input['frequency'] ?? 3);
        $response = intval($input['Response'] ?? $input['response'] ?? 3);
        $collaboration = intval($input['Collaboration'] ?? $input['collaboration'] ?? 3);

        // Calculate Controller Score
        $controllerScore = round(($frequency + $response + $collaboration) / 3, 2);

        // Management scoring fields
        $serviceDelivery = intval($input['Service_Delivery'] ?? $input['service_delivery'] ?? 3);
        $timeliness = intval($input['Timeliness'] ?? $input['timeliness'] ?? 3);
        $innovation = intval($input['Innovation'] ?? $input['innovation'] ?? 3);
        $documentation = intval($input['Documentation'] ?? $input['documentation'] ?? 3);

        // Calculate Management Score
        $managementScore = round(($serviceDelivery + $timeliness + $innovation + $documentation) / 4, 2);

        // Calculate Overall Score (weighted average: 50% Guard, 30% Controller, 20% Management)
        $overallScore = round(($guardScore * 0.5) + ($controllerScore * 0.3) + ($managementScore * 0.2), 2);

        // Additional field: Equipping if provided
        $equipping = intval($input['Equipping'] ?? $input['equipping'] ?? 3);

        // Remarks/notes field (accept both 'remarks' and 'Remarks')
        $remarks = $input['remarks'] ?? $input['Remarks'] ?? '';

        // Insert feedback into new feedback table
        $result = $db->query(
            "INSERT INTO feedback (
                Client, Date, Month, Year, Subject,
                Punctuality, Attendance, Neatness, Patrolling, Communication, Respect, Job_Mastery, Vigilance,
                Frequency, Response, Collaboration,
                Service_Delivery, Timeliness, Innovation, Documentation, Equipping,
                Guard_Score, Controller_Score, Management_Score, Overall_Score,
                Remarks
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [
                $_SESSION['user_id'],  // Client = current user
                $date,
                $month,
                $year,
                $subject,
                $punctuality, $attendance, $neatness, $patrolling, $communication, $respect, $jobMastery, $vigilance,
                $frequency, $response, $collaboration,
                $serviceDelivery, $timeliness, $innovation, $documentation, $equipping,
                $guardScore, $controllerScore, $managementScore, $overallScore,
                $remarks
            ]
        );

        if ($result) {
            echo json_encode([
                'success' => true,
                'message' => 'Feedback submitted successfully',
                'scores' => [
                    'guard_score' => $guardScore,
                    'controller_score' => $controllerScore,
                    'management_score' => $managementScore,
                    'overall_score' => $overallScore
                ]
            ]);
        } else {
            echo json_encode(['success' => false, 'error' => 'Failed to submit feedback']);
        }
    } catch (Exception $e) {
        error_log('handleClientAddFeedback error: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleClientDeleteFeedback($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Verify feedback belongs to user
    $feedback = $db->fetch(
        "SELECT FeedID FROM feedback WHERE FeedID = ? AND Client = ?",
        [$id, $_SESSION['user_id']]
    );

    if (!$feedback) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Feedback not found or does not belong to you']);
        return;
    }

    try {
        $result = $db->query(
            "DELETE FROM feedback WHERE FeedID = ?",
            [$id]
        );

        if ($result) {
            echo json_encode(['success' => true, 'message' => 'Feedback deleted successfully']);
        } else {
            echo json_encode(['success' => false, 'error' => 'Failed to delete feedback']);
        }
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleClientSendMessage() {
    global $auth, $db, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Clients and guards can send messages
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id']]);
    if (!$user || !in_array($user['role'], ['client', 'guard'])) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Only clients and guards can send messages']);
        return;
    }

    // Get POST data - handle both JSON and FormData
    $input = $_POST;
    
    // If no POST data, try JSON
    if (empty($input)) {
        $input = json_decode(file_get_contents('php://input'), true);
    }

    // Validate input
    if (empty($input['recipient_id']) || empty($input['subject']) || empty($input['message'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    // Verify recipient exists and is active
    $recipient = $db->fetch(
        "SELECT id FROM users WHERE id = ? AND active = 1",
        [$input['recipient_id']]
    );

    if (!$recipient) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Recipient not found or inactive']);
        return;
    }

    $attachments = [];
    $uploadDir = __DIR__ . '/../uploads/messages/';
    
    // Ensure upload directory exists
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    // Handle file uploads
    if (!empty($_FILES['attachments'])) {
        $allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', '7z', 'pptx', 'mp4', 'avi', 'mov'];
        $maxFileSize = 5 * 1024 * 1024; // 5MB

        foreach ($_FILES['attachments']['error'] as $key => $error) {
            if ($error === UPLOAD_ERR_NO_FILE) {
                continue; // Skip empty file inputs
            }

            if ($error !== UPLOAD_ERR_OK) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File upload error: ' . $error]);
                return;
            }

            $fileSize = $_FILES['attachments']['size'][$key];
            $fileName = basename($_FILES['attachments']['name'][$key]);
            $fileTmp = $_FILES['attachments']['tmp_name'][$key];

            // Validate file size
            if ($fileSize > $maxFileSize) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File ' . $fileName . ' exceeds 50MB limit']);
                return;
            }

            // Validate file extension
            $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
            if (!in_array($fileExt, $allowedExtensions)) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File type .' . $fileExt . ' not allowed']);
                return;
            }

            // Generate safe filename
            $uniqueName = uniqid('msg_') . '_' . time() . '.' . $fileExt;
            $filePath = $uploadDir . $uniqueName;

            // Move file to upload directory
            if (!move_uploaded_file($fileTmp, $filePath)) {
                http_response_code(500);
                echo json_encode(['success' => false, 'error' => 'Failed to save file: ' . $fileName]);
                return;
            }

            $attachments[] = [
                'original_name' => $fileName,
                'file_path' => 'uploads/messages/' . $uniqueName,
                'file_size' => $fileSize
            ];
        }
    }

    try {
        // Sanitize message content
        $messageContent = !empty($input['message']) ? strip_tags($input['message'], '<p><br><b><i><u><strong><em><a><blockquote><ol><li><ul>') : '';
        
        // Store attachments as JSON if present
        $attachmentsJson = !empty($attachments) ? json_encode($attachments) : null;

        $result = $db->query(
            "INSERT INTO messages (sender_id, recipient_id, subject, message, priority, attachments, is_read, created_at)
             VALUES (?, ?, ?, ?, ?, ?, 0, NOW())",
            [
                $_SESSION['user_id'],
                $input['recipient_id'],
                substr($input['subject'], 0, 255),
                $messageContent,
                $input['priority'] ?? 'normal',
                $attachmentsJson
            ]
        );

        if ($result) {
            // Send notification to recipient
            $messageId = $db->lastInsertId();
            $sender = $db->fetch("SELECT name FROM users WHERE id = ?", [$_SESSION['user_id']]);
            
            if ($notification && $sender) {
                $notification
                    ->reset()
                    ->forUser($input['recipient_id'])
                    ->setTitle('New Message from ' . $sender['name'])
                    ->setMessage(substr($messageContent, 0, 100) . (strlen($messageContent) > 100 ? '...' : ''))
                    ->setType('info')
                    ->addCallToAction('View Message', "/dashboard?page=messages&id={$messageId}")
                    ->via(['email', 'in-app'])
                    ->send();
            }
            
            echo json_encode(['success' => true, 'message' => 'Message sent successfully']);
        } else {
            // Clean up uploaded files on failure
            foreach ($attachments as $attachment) {
                $filePath = __DIR__ . '/../' . $attachment['file_path'];
                if (file_exists($filePath)) {
                    unlink($filePath);
                }
            }
            http_response_code(500);
            echo json_encode(['success' => false, 'error' => 'Failed to send message']);
        }
    } catch (Exception $e) {
        // Clean up uploaded files on error
        foreach ($attachments as $attachment) {
            $filePath = __DIR__ . '/../' . $attachment['file_path'];
            if (file_exists($filePath)) {
                unlink($filePath);
            }
        }
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

function handleAdminSendMessage() {
    global $auth, $db, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Only admins, managers, and controllers can send messages
    $user = $db->fetch("SELECT role FROM users WHERE id = ?", [$_SESSION['user_id']]);
    if (!$user || !in_array($user['role'], ['admin', 'manager', 'controller'])) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Insufficient permissions']);
        return;
    }

    // Get POST data - handle both JSON and FormData
    $input = $_POST;
    
    // If no POST data, try JSON
    if (empty($input)) {
        $input = json_decode(file_get_contents('php://input'), true);
    }

    // Validate input
    if (empty($input['recipient_id']) || empty($input['subject']) || empty($input['message'])) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    // Verify recipient exists
    $recipient = $db->fetch(
        "SELECT id, role FROM users WHERE id = ? AND active = 1",
        [$input['recipient_id']]
    );

    if (!$recipient) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invalid recipient']);
        return;
    }

    $attachments = [];
    $uploadDir = __DIR__ . '/../uploads/messages/';
    
    // Ensure upload directory exists
    if (!is_dir($uploadDir)) {
        mkdir($uploadDir, 0755, true);
    }

    // Handle file uploads
    if (!empty($_FILES['attachments'])) {
        $allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', '7z', 'pptx', 'mp4', 'avi', 'mov'];
        $maxFileSize = 50 * 1024 * 1024; // 50MB

        foreach ($_FILES['attachments']['error'] as $key => $error) {
            if ($error === UPLOAD_ERR_NO_FILE) {
                continue;
            }

            if ($error !== UPLOAD_ERR_OK) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File upload error: ' . $error]);
                return;
            }

            $fileSize = $_FILES['attachments']['size'][$key];
            $fileName = basename($_FILES['attachments']['name'][$key]);
            $fileTmp = $_FILES['attachments']['tmp_name'][$key];

            // Validate file size
            if ($fileSize > $maxFileSize) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File ' . $fileName . ' exceeds 50MB limit']);
                return;
            }

            // Validate file extension
            $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
            if (!in_array($fileExt, $allowedExtensions)) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'File type not allowed: ' . $fileExt]);
                return;
            }

            // Generate safe filename
            $uniqueName = uniqid('msg_') . '_' . time() . '.' . $fileExt;
            $filePath = $uploadDir . $uniqueName;

            // Move file to upload directory
            if (!move_uploaded_file($fileTmp, $filePath)) {
                http_response_code(500);
                echo json_encode(['success' => false, 'error' => 'Failed to save file: ' . $fileName]);
                return;
            }

            $attachments[] = [
                'original_name' => $fileName,
                'file_path' => 'uploads/messages/' . $uniqueName,
                'file_size' => $fileSize
            ];
        }
    }

    try {
        // Sanitize message content
        $messageContent = !empty($input['message']) ? strip_tags($input['message'], '<p><br><b><i><u><strong><em><a><blockquote><ol><li><ul>') : '';
        
        // Store attachments as JSON if present
        $attachmentsJson = !empty($attachments) ? json_encode($attachments) : null;

        $result = $db->query(
            "INSERT INTO messages (sender_id, recipient_id, subject, message, priority, attachments, is_read, created_at)
             VALUES (?, ?, ?, ?, ?, ?, 0, NOW())",
            [
                $_SESSION['user_id'],
                $input['recipient_id'],
                $input['subject'],
                $messageContent,
                $input['priority'] ?? 'normal',
                $attachmentsJson
            ]
        );

        if ($result) {
            // Send notification to recipient
            $messageId = $db->lastInsertId();
            $sender = $db->fetch("SELECT name FROM users WHERE id = ?", [$_SESSION['user_id']]);
            
            if ($notification && $sender) {
                $notification
                    ->reset()
                    ->forUser($input['recipient_id'])
                    ->setTitle('New Message from ' . $sender['name'])
                    ->setMessage(substr($messageContent, 0, 100) . (strlen($messageContent) > 100 ? '...' : ''))
                    ->setType('info')
                    ->addCallToAction('View Message', "/dashboard?page=messages&id={$messageId}")
                    ->via(['email', 'sms', 'push', 'in-app'])
                    ->send();
            }
            
            echo json_encode(['success' => true, 'message' => 'Message sent successfully']);
        } else {
            // Clean up uploaded files on failure
            foreach ($attachments as $attachment) {
                $filePath = __DIR__ . '/../' . $attachment['file_path'];
                if (file_exists($filePath)) {
                    unlink($filePath);
                }
            }
            http_response_code(500);
            echo json_encode(['success' => false, 'error' => 'Failed to send message']);
        }
    } catch (Exception $e) {
        // Clean up uploaded files on error
        foreach ($attachments as $attachment) {
            $filePath = __DIR__ . '/../' . $attachment['file_path'];
            if (file_exists($filePath)) {
                unlink($filePath);
            }
        }
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

function handleAdminSendReply() {
    global $auth, $db;

    // Check authentication and authorization
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $user = $auth->getCurrentUser();
    $allowedRoles = ['admin', 'manager', 'controller'];
    if (!in_array($user['role'], $allowedRoles)) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Forbidden']);
        return;
    }

    try {
        $parentMessageId = isset($_POST['parent_message_id']) ? (int)$_POST['parent_message_id'] : null;
        $subject = isset($_POST['subject']) ? trim($_POST['subject']) : '';
        $message = isset($_POST['message']) ? trim($_POST['message']) : '';

        // Validate required fields
        if (!$parentMessageId) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Parent message ID is required']);
            return;
        }

        if (empty($message) && empty($_FILES)) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Message or attachment is required']);
            return;
        }

        // Get the parent message to determine recipient
        $parentMessage = $db->fetch('SELECT sender_id, recipient_id FROM messages WHERE id = ?', [$parentMessageId]);

        if (!$parentMessage) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Parent message not found']);
            return;
        }

        // Determine recipient (sender of parent message)
        $recipientId = ($parentMessage['sender_id'] == $user['id']) ? $parentMessage['recipient_id'] : $parentMessage['sender_id'];

        // Check if recipient exists and is active
        $recipientCheck = $db->fetch('SELECT id FROM users WHERE id = ? AND active = 1', [$recipientId]);
        if (!$recipientCheck) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Recipient not found or inactive']);
            return;
        }

        // Handle file attachments
        $attachments = [];
        $uploadDir = __DIR__ . '/../uploads/messages/';
        if (!file_exists($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        if (!empty($_FILES['attachments'])) {
            $files = is_array($_FILES['attachments']['name']) ? $_FILES['attachments'] : [
                'name' => [$_FILES['attachments']['name']],
                'tmp_name' => [$_FILES['attachments']['tmp_name']],
                'size' => [$_FILES['attachments']['size']],
                'error' => [$_FILES['attachments']['error']]
            ];

            $allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', '7z', 'pptx', 'mp4', 'avi', 'mov'];
            $maxFileSize = 50 * 1024 * 1024; // 50MB

            foreach ($files['name'] as $i => $fileName) {
                if (empty($fileName)) continue;

                if ($files['error'][$i] !== UPLOAD_ERR_OK) {
                    throw new Exception('File upload error: ' . $files['error'][$i]);
                }

                if ($files['size'][$i] > $maxFileSize) {
                    throw new Exception("File $fileName exceeds 50MB limit");
                }

                $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
                if (!in_array($ext, $allowedExtensions)) {
                    throw new Exception("File type .$ext not allowed");
                }

                $newFileName = uniqid('msg_', true) . '.' . $ext;
                $filePath = $uploadDir . $newFileName;

                if (!move_uploaded_file($files['tmp_name'][$i], $filePath)) {
                    throw new Exception("Failed to upload file: $fileName");
                }

                $attachments[] = [
                    'original_name' => $fileName,
                    'file_path' => 'uploads/messages/' . $newFileName,
                    'file_size' => $files['size'][$i]
                ];
            }
        }

        // Insert reply message into database
        $attachmentsJson = !empty($attachments) ? json_encode($attachments) : null;
        $db->query('
            INSERT INTO messages (sender_id, recipient_id, subject, message, attachments, parent_message_id, created_at)
            VALUES (?, ?, ?, ?, ?, ?, NOW())
        ', [
            $user['id'],
            $recipientId,
            $subject,
            $message,
            $attachmentsJson,
            $parentMessageId
        ]);

        http_response_code(200);
        echo json_encode([
            'success' => true,
            'message' => 'Reply sent successfully',
            'message_id' => $db->lastInsertId()
        ]);
    } catch (Exception $e) {
        // Clean up uploaded files on error
        foreach ($attachments as $attachment) {
            $filePath = __DIR__ . '/../' . $attachment['file_path'];
            if (file_exists($filePath)) {
                unlink($filePath);
            }
        }
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

function handleClientSendReply() {
    global $auth, $db, $notification;

    // Check authentication and authorization
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $user = $auth->getCurrentUser();
    // Allow both clients and guards to send replies
    if (!in_array($user['role'], ['client', 'guard'])) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Only clients and guards can send replies']);
        return;
    }

    try {
        $parentMessageId = isset($_POST['parent_message_id']) ? (int)$_POST['parent_message_id'] : null;
        $subject = isset($_POST['subject']) ? trim($_POST['subject']) : '';
        $message = isset($_POST['message']) ? trim($_POST['message']) : '';

        // Validate required fields
        if (!$parentMessageId) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Parent message ID is required']);
            return;
        }

        if (empty($message) && empty($_FILES)) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Message or attachment is required']);
            return;
        }

        // Get the parent message to determine recipient
        $parentMessage = $db->fetch('SELECT sender_id, recipient_id FROM messages WHERE id = ?', [$parentMessageId]);

        if (!$parentMessage) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Parent message not found']);
            return;
        }

        // Determine recipient (sender of parent message)
        $recipientId = ($parentMessage['sender_id'] == $user['id']) ? $parentMessage['recipient_id'] : $parentMessage['sender_id'];

        // Check if recipient exists and is active
        $recipientCheck = $db->fetch('SELECT id FROM users WHERE id = ? AND active = 1', [$recipientId]);
        if (!$recipientCheck) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Recipient not found or inactive']);
            return;
        }

        // Handle file attachments
        $attachments = [];
        $uploadDir = __DIR__ . '/../uploads/messages/';
        if (!file_exists($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        if (!empty($_FILES['attachments'])) {
            $files = is_array($_FILES['attachments']['name']) ? $_FILES['attachments'] : [
                'name' => [$_FILES['attachments']['name']],
                'tmp_name' => [$_FILES['attachments']['tmp_name']],
                'size' => [$_FILES['attachments']['size']],
                'error' => [$_FILES['attachments']['error']]
            ];

            $allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar', '7z', 'pptx', 'mp4', 'avi', 'mov'];
            $maxFileSize = 50 * 1024 * 1024; // 50MB

            foreach ($files['name'] as $i => $fileName) {
                if (empty($fileName)) continue;

                if ($files['error'][$i] !== UPLOAD_ERR_OK) {
                    throw new Exception('File upload error: ' . $files['error'][$i]);
                }

                if ($files['size'][$i] > $maxFileSize) {
                    throw new Exception("File $fileName exceeds 50MB limit");
                }

                $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
                if (!in_array($ext, $allowedExtensions)) {
                    throw new Exception("File type .$ext not allowed");
                }

                $newFileName = uniqid('msg_', true) . '.' . $ext;
                $filePath = $uploadDir . $newFileName;

                if (!move_uploaded_file($files['tmp_name'][$i], $filePath)) {
                    throw new Exception("Failed to upload file: $fileName");
                }

                $attachments[] = [
                    'original_name' => $fileName,
                    'file_path' => 'uploads/messages/' . $newFileName,
                    'file_size' => $files['size'][$i]
                ];
            }
        }

        // Insert reply message into database
        $attachmentsJson = !empty($attachments) ? json_encode($attachments) : null;
        $db->query('
            INSERT INTO messages (sender_id, recipient_id, subject, message, attachments, parent_message_id, created_at)
            VALUES (?, ?, ?, ?, ?, ?, NOW())
        ', [
            $user['id'],
            $recipientId,
            $subject,
            $message,
            $attachmentsJson,
            $parentMessageId
        ]);

        $messageId = $db->lastInsertId();

        // Send notification to recipient about the reply
        if ($notification) {
            $notification
                ->reset()
                ->forUser($recipientId)
                ->setTitle('New Reply from ' . $user['name'])
                ->setMessage(substr($message, 0, 100) . (strlen($message) > 100 ? '...' : ''))
                ->setType('info')
                ->addCallToAction('View Reply', "/dashboard?page=messages&id={$messageId}")
                ->via(['email', 'in-app'])
                ->send();
        }

        http_response_code(200);
        echo json_encode([
            'success' => true,
            'message' => 'Reply sent successfully',
            'message_id' => $messageId
        ]);
    } catch (Exception $e) {
        // Clean up uploaded files on error
        foreach ($attachments as $attachment) {
            $filePath = __DIR__ . '/../' . $attachment['file_path'];
            if (file_exists($filePath)) {
                unlink($filePath);
            }
        }
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

function handleMarkMessageRead() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    try {
        $data = json_decode(file_get_contents('php://input'), true);
        $messageId = isset($data['message_id']) ? (int)$data['message_id'] : null;

        if (!$messageId) {
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'Message ID required']);
            return;
        }

        // Update message to mark as read
        $db->query('UPDATE messages SET is_read = 1 WHERE id = ? AND recipient_id = ?', [$messageId, $_SESSION['user_id']]);

        // Get updated unread message count
        $unreadCount = $db->fetch(
            'SELECT COUNT(*) as count FROM messages WHERE recipient_id = ? AND is_read = 0',
            [$_SESSION['user_id']]
        );

        http_response_code(200);
        echo json_encode([
            'success' => true, 
            'message' => 'Message marked as read',
            'unread_count' => $unreadCount['count'] ?? 0
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

function handleGetMessageThread($messageId) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    if (!$messageId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Message ID required']);
        return;
    }

    try {
        $userId = $_SESSION['user_id'];
        $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
        $offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;

        // Get the root message (find the original message in the thread)
        $rootMessage = $db->fetch(
            'SELECT id FROM messages WHERE (id = ? OR parent_message_id = ?) AND (sender_id = ? OR recipient_id = ?)',
            [$messageId, $messageId, $userId, $userId]
        );

        if (!$rootMessage) {
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'Message not found']);
            return;
        }

        // Find the root of the conversation thread
        $rootId = $messageId;
        $current = $db->fetch('SELECT parent_message_id FROM messages WHERE id = ?', [$rootId]);
        while ($current && $current['parent_message_id']) {
            $rootId = $current['parent_message_id'];
            $current = $db->fetch('SELECT parent_message_id FROM messages WHERE id = ?', [$rootId]);
        }

        // Get total count of messages in thread
        $countResult = $db->fetch(
            'SELECT COUNT(*) as total FROM messages WHERE id = ? OR parent_message_id IN (
                WITH RECURSIVE thread AS (
                    SELECT id, parent_message_id FROM messages WHERE id = ?
                    UNION ALL
                    SELECT m.id, m.parent_message_id FROM messages m
                    INNER JOIN thread t ON m.parent_message_id = t.id
                )
                SELECT id FROM thread
            )',
            [$rootId, $rootId]
        );

        // Simpler approach: get all messages in thread
        $allMessages = $db->fetchAll(
            'SELECT m.*, u.name as sender_name, u.role FROM messages m
             LEFT JOIN users u ON m.sender_id = u.id
             WHERE m.id = ? OR m.parent_message_id = ?
             ORDER BY m.created_at ASC',
            [$rootId, $rootId]
        );

        $total = count($allMessages);
        $thread = array_slice($allMessages, $offset, $limit);

        // Add reply count to each message
        foreach ($thread as &$msg) {
            $replyCount = $db->fetch(
                'SELECT COUNT(*) as count FROM messages WHERE parent_message_id = ?',
                [$msg['id']]
            );
            $msg['reply_count'] = $replyCount['count'] ?? 0;
            
            // Parse attachments if JSON
            if ($msg['attachments']) {
                $msg['attachments'] = json_decode($msg['attachments'], true);
            }
        }

        http_response_code(200);
        echo json_encode([
            'success' => true,
            'thread' => $thread,
            'total' => $total,
            'limit' => $limit,
            'offset' => $offset,
            'has_more' => ($offset + $limit) < $total
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Error: ' . $e->getMessage()]);
    }
}

// ==================== Client Invoices Handlers ====================

function handleGetInvoice2($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    if (!$id) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Invoice ID required']);
        return;
    }

    $invoice = $db->fetch(
        "SELECT i.*, u.name as owner_name, u.email as owner_email 
         FROM invoices i
         LEFT JOIN users u ON i.owner_id = u.id
         WHERE i.id = ?",
        [$id]
    );

    if (!$invoice) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invoice not found']);
        return;
    }

    // Parse services from invoice or get from post if not set
    $services = null;
    if (!empty($invoice['services'])) {
        $services = is_string($invoice['services']) ? json_decode($invoice['services'], true) : $invoice['services'];
    } elseif (!empty($invoice['post_id'])) {
        $post = $db->fetch("SELECT services FROM posts WHERE id = ?", [$invoice['post_id']]);
        if ($post && !empty($post['services'])) {
            $services = is_string($post['services']) ? json_decode($post['services'], true) : $post['services'];
        }
    }

    // Get invoice items
    $items = $db->fetchAll("SELECT * FROM invoice_items WHERE invoice_id = ?", [$id]);

    // Calculate totals if not already set
    if (empty($invoice['total_amount']) || empty($invoice['subtotal'])) {
        $subtotal = 0;
        foreach ($items as $item) {
            $subtotal += $item['line_total'] ?? 0;
        }
        
        // Use stored values or calculated ones
        $invoice['subtotal'] = $invoice['subtotal'] ?? $subtotal;
        $invoice['tax_amount'] = $invoice['tax_amount'] ?? 0;
        $invoice['total_amount'] = $invoice['subtotal'] + $invoice['tax_amount'];
    }

    // Get letterhead and company info from settings
    $settings = $db->fetch("SELECT site_name, site_acronym, logo, letterhead FROM settings LIMIT 1");

    // Format company response
    $company = $settings ? [
        'site_name' => $settings['site_name'] ?? 'SSCI Office',
        'site_acronym' => $settings['site_acronym'] ?? 'SSCI',
        'logo' => $settings['logo'] ?? null,
        'letterhead' => $settings['letterhead'] ?? null
    ] : [
        'site_name' => 'SSCI Office',
        'site_acronym' => 'SSCI',
        'logo' => null,
        'letterhead' => null
    ];

    echo json_encode([
        'success' => true,
        'invoice' => $invoice,
        'items' => $items,
        'services' => $services,
        'company' => $company
    ]);
}


function handleClientMyInvoices() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true) ?? [];
    $status = $input['status'] ?? '';
    $date = $input['date'] ?? '';
// get invoices for this user with post details
    $query = "SELECT i.*, i.invoice_month as billing_month, u.name as owner_name, p.name as post_name FROM invoices i 
              LEFT JOIN users u ON i.owner_id = u.id
              LEFT JOIN posts p ON i.post_id = p.id WHERE p.owner_id = ?";
    $params = [$_SESSION['user_id']];

    if (!empty($status)) {
        $query .= " AND status = ?";
        $params[] = $status;
    }

    if (!empty($date)) {
        $query .= " AND DATE(invoice_date) = ?";
        $params[] = $date;
    }

    $query .= " ORDER BY invoice_date DESC";

    $invoices = $db->fetchAll($query, $params);

    echo json_encode([
        'success' => true,
        'invoices' => $invoices ?? []
    ]);
}

function handleClientInvoicePdf($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Get invoice - verify ownership
    $invoice = $db->fetch(
        "SELECT i.*, u.name as owner_name, u.email as owner_email, p.name as post_name
         FROM invoices i
         LEFT JOIN users u ON i.owner_id = u.id
         LEFT JOIN posts p ON i.post_id = p.id
         WHERE i.id = ? AND i.owner_id = ?",
        [$id, $_SESSION['user_id']]
    );

    if (!$invoice) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Invoice not found']);
        return;
    }

    // Get services - from invoice services JSON field or from invoice_items table
    $services = null;
    if (!empty($invoice['services'])) {
        $services = is_string($invoice['services']) ? json_decode($invoice['services'], true) : $invoice['services'];
    }
    
    // If no services in invoice, try invoice_items table
    if (!$services || !is_array($services)) {
        $items = $db->fetchAll(
            "SELECT description, quantity, unit_price FROM invoice_items WHERE invoice_id = ?",
            [$id]
        );
        if ($items && is_array($items) && count($items) > 0) {
            $services = array_map(function($item) {
                return [
                    'name' => $item['description'],
                    'qty' => $item['quantity'],
                    'unit_cost' => $item['unit_price']
                ];
            }, $items);
        }
    }

    // Get company settings for letterhead
    $settings = $db->fetch("SELECT site_name, site_acronym, logo, letterhead FROM settings LIMIT 1");

    // Include TCPDF library
    require_once __DIR__ . '/../lib/tcpdf.php';

    // Custom TCPDF class with footer
    class TCPDF_ClientInvoice extends TCPDF {
        public function Footer() {
            $this->SetY(-15);
            $this->SetFont('ptsansnarrow', '', 8);
            $this->SetTextColor(128, 128, 128);
            $this->Ln();
            $this->Cell(0, 10, '© ' . date('Y') . ' SHEPHERD SECURITY AND CONSULTANCY INTERNATIONAL (SSCI). Generated on ' . date('d M Y H:i:s'), 0, false, 'C', false);
        }
    }

    // Create PDF with full width letterhead capability
    $pdf = new TCPDF_ClientInvoice(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
    $pdf->SetMargins(10, 10, 10);
    $pdf->SetAutoPageBreak(true, 20);
    $pdf->AddPage();

    // Use ptsansnarrow font (custom font from project)
    $pdf->SetFont('ptsansnarrow', '', 11);

    // Add letterhead with full width
    if (!empty($settings['letterhead'])) {
        $letterheadPath = SSCI_PUBLIC . $settings['letterhead'];
        if (file_exists($letterheadPath)) {
            // Full width letterhead (190 for A4 with 10px margins on each side)
            // Height of 0 maintains aspect ratio
            $pdf->Image($letterheadPath, 10, 10, 190, 0, '', '', 'T', false, 300);
            $pdf->Ln(50); // Space after letterhead
        }
    } else if (!empty($settings['logo'])) {
        $logoPath = SSCI_PUBLIC . $settings['logo'];
        if (file_exists($logoPath)) {
            $pdf->Image($logoPath, 10, 10, 30, 0, '', '', 'T', false, 150);
        }
        $pdf->SetFont('ptsansnarrow', 'B', 16);
        $pdf->Ln(25);
        $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
        $pdf->SetFont('ptsansnarrow', '', 10);
        $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
        $pdf->Ln(5);
    } else {
        $pdf->SetFont('ptsansnarrow', 'B', 18);
        $pdf->Cell(0, 10, $settings['site_name'] ?? 'SSCI Office', 0, 1, 'C');
        $pdf->SetFont('ptsansnarrow', '', 11);
        $pdf->Cell(0, 5, $settings['site_acronym'] ?? 'SSCI', 0, 1, 'C');
        $pdf->Ln(10);
    }

    // Invoice header
    $pdf->SetFont('ptsansnarrow', 'B', 14);
    $pdf->Cell(0, 8, 'INVOICE', 0, 1, 'C');
    $pdf->Ln(5);

    // Invoice details with QR code on the right (75% / 25% split)
    $pdf->SetFont('ptsansnarrow', '', 10);
    $pdf->SetFillColor(240, 240, 240);

    $invoiceDate = date('d M Y', strtotime($invoice['invoice_date']));
    $dueDate = !empty($invoice['due_date']) ? date('d M Y', strtotime($invoice['due_date'])) : 'Upon receipt';

    // Get current Y position for QR code placement
    $invoiceDetailsY = $pdf->GetY();
    
    // Create 75% width table for invoice details (0-150mm), QR code in 25% (150-190mm)
    // Invoice number row
    $pdf->Cell(40, 6, 'Invoice Number:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $invoice['invoice_number'], 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false); // 25% space for QR
    
    // Invoice Date row
    $pdf->Cell(40, 6, 'Invoice Date:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $invoiceDate, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Due Date row
    $pdf->Cell(40, 6, 'Due Date:', 0, 0, 'L', true);
    $pdf->Cell(110, 6, $dueDate, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Status row
    $pdf->Cell(40, 6, 'Status:', 0, 0, 'L', true);
    $statusText = ($invoice['status'] === 'Draft') ? 'Unpaid' : $invoice['status'];
    $pdf->Cell(110, 6, $statusText, 0, 0, 'L', false);
    $pdf->Cell(40, 6, '', 0, 1, 'L', false);
    
    // Add QR code on the right side (positioned at the start of invoice details)
    $qrData = $invoice['invoice_number'] ?? 'INV-0001';
    try {
        $pdf->write2DBarcode($qrData, 'QRCODE,M', 155, $invoiceDetailsY, 35, 35, array(), 'N');
    } catch (Exception $e) {
        // QR code generation failed, continue without it
    }

    $pdf->Ln(5);

    // Bill to
    $pdf->SetFont('ptsansnarrow', 'B', 10);
    $pdf->Cell(0, 6, 'BILL TO:', 0, 1);
    $pdf->SetFont('ptsansnarrow', '', 10);
    $pdf->Cell(0, 5, $invoice['owner_name'] ?? 'N/A', 0, 1);
    $pdf->Cell(0, 5, $invoice['owner_email'] ?? '', 0, 1);
    $pdf->Ln(5);

    // Line items table
    $pdf->SetFont('ptsansnarrow', 'B', 10);
    $pdf->SetFillColor(200, 200, 200);
    $pdf->Cell(80, 6, 'Description', 0, 0, 'L', true);
    $pdf->Cell(30, 6, 'Qty', 0, 0, 'R', true);
    $pdf->Cell(40, 6, 'Unit Price', 0, 0, 'R', true);
    $pdf->Cell(40, 6, 'Total', 0, 1, 'R', true);

    $pdf->SetFont('ptsansnarrow', '', 9);
    $pdf->SetFillColor(255, 255, 255);

    if ($services && is_array($services) && count($services) > 0) {
        foreach ($services as $service) {
            $qty = $service['qty'] ?? $service['quantity'] ?? 1;
            $unitCost = $service['unit_cost'] ?? $service['unit_price'] ?? 0;
            $total = $qty * $unitCost;

            $pdf->Cell(80, 5, $service['name'] ?? $service['description'] ?? 'Service', 0, 0, 'L');
            $pdf->Cell(30, 5, number_format($qty, 2), 0, 0, 'R');
            $pdf->Cell(40, 5, number_format($unitCost, 2), 0, 0, 'R');
            $pdf->Cell(40, 5, number_format($total, 2), 0, 1, 'R');
        }
    } else {
        $pdf->Cell(80, 5, 'Services', 0, 0, 'L');
        $pdf->Cell(30, 5, '1', 0, 0, 'R');
        $pdf->Cell(40, 5, number_format($invoice['subtotal'] ?? 0, 2), 0, 0, 'R');
        $pdf->Cell(40, 5, number_format($invoice['subtotal'] ?? 0, 2), 0, 1, 'R');
    }

    $pdf->Ln(5);

    // Totals
    $pdf->SetFont('ptsansnarrow', '', 10);
    $subtotal = floatval($invoice['subtotal'] ?? 0);
    $tax = floatval($invoice['tax_amount'] ?? 0);
    $total = floatval($invoice['total_amount'] ?? 0);

    $pdf->Cell(150, 6, 'Subtotal:', 0, 0, 'R');
    $pdf->Cell(40, 6, number_format($subtotal, 2), 0, 1, 'R');

    $pdf->Cell(150, 6, 'Tax:', 0, 0, 'R');
    $pdf->Cell(40, 6, number_format($tax, 2), 0, 1, 'R');

    $pdf->SetFont('ptsansnarrow', 'B', 11);
    $pdf->SetFillColor(220, 220, 220);
    $pdf->Cell(150, 7, 'TOTAL:', 0, 0, 'R', true);
    $pdf->Cell(40, 7, number_format($total, 2) . ' ' . ($invoice['currency'] ?? 'FCFA'), 0, 1, 'R', true);

    // Notes
    if (!empty($invoice['notes'])) {
        $pdf->Ln(10);
        $pdf->SetFont('ptsansnarrow', 'B', 10);
        $pdf->Cell(0, 6, 'Notes:', 0, 1);
        $pdf->SetFont('ptsansnarrow', '', 9);
        $pdf->MultiCell(0, 5, $invoice['notes'], 0, 'L');
    }

    // Output PDF
    $filename = 'Invoice-' . $invoice['invoice_number'] . '.pdf';
    $pdf->Output($filename, 'D'); // Download the PDF
}

// ==================== Client Payments Handlers ====================

function handleClientPaymentsList() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true) ?? [];
    $status = $input['status'] ?? '';
    $method = $input['method'] ?? '';
    $date = $input['date'] ?? '';

    $query = "SELECT p.id, p.amount, p.payment_method, p.status, p.payment_date, p.reference_number,
                     p.description, i.invoice_number, i.id as invoice_id
              FROM payments p
              LEFT JOIN invoices i ON p.related_id = i.id
              WHERE p.payment_type = 'Client_Income' AND i.owner_id = ?";
    $params = [$_SESSION['user_id']];

    if (!empty($status)) {
        $query .= " AND p.status = ?";
        $params[] = $status;
    }

    if (!empty($method)) {
        $query .= " AND p.payment_method = ?";
        $params[] = $method;
    }

    if (!empty($date)) {
        $query .= " AND DATE(p.payment_date) = ?";
        $params[] = $date;
    }

    $query .= " ORDER BY p.payment_date DESC LIMIT 100";

    $payments = $db->fetchAll($query, $params);

    echo json_encode([
        'success' => true,
        'payments' => $payments ?? []
    ]);
}

function handleClientRecordPayment() {
    global $auth, $db, $notification, $auditLog;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['invoice_id']) || empty($input['amount']) || empty($input['payment_method'])) {
        echo json_encode(['success' => false, 'error' => 'Missing required fields']);
        return;
    }

    // Verify invoice belongs to user
    $invoice = $db->fetch(
        "SELECT id, invoice_number, total_amount FROM invoices WHERE id = ? AND owner_id = ?",
        [$input['invoice_id'], $_SESSION['user_id']]
    );

    if (!$invoice) {
        echo json_encode(['success' => false, 'error' => 'Invalid invoice']);
        return;
    }

    try {
        // Generate reference number if not provided
        $reference = $input['reference_number'] ?? 'PAY-' . strtoupper(uniqid());
        
        $result = $db->query(
            "INSERT INTO payments (
                invoice_id, amount, payment_method, payment_date, 
                reference_number, phone_number, description, status, payment_type, related_id, created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, 'Pending', 'Client_Income', ?, NOW())",
            [
                $input['invoice_id'],
                $input['amount'],
                $input['payment_method'],
                $input['payment_date'] ?? date('Y-m-d'),
                $reference,
                $input['phone_number'] ?? null,
                $input['description'] ?? null,
                $input['invoice_id']
            ]
        );

        if ($result) {
            // Get the last inserted payment ID
            $paymentId = $db->lastInsertId();
            
            // Log audit event for payment recording
            try {
                if ($auditLog) {
                    $auditLog->log(
                        'PAYMENT',
                        'payments',
                        $paymentId,
                        "Payment recorded: {$input['payment_method']} - Amount: {$input['amount']} FCFA for invoice #{$invoice['invoice_number']}",
                        $_SESSION['user_id'],
                        ['invoice_id' => $input['invoice_id'], 'amount' => $input['amount'], 'method' => $input['payment_method']]
                    );
                }
            } catch (Exception $auditErr) {
                error_log("Audit log error: " . $auditErr->getMessage());
            }
            
            // Send notification to client about payment submission
            try {
                if ($notification) {
                    $notification
                        ->reset()  // Reset state in case notification object was reused
                        ->setTitle('Payment Submitted')
                        ->setMessage("Your payment of " . number_format($input['amount'], 0, ',', ' ') . " FCFA for invoice #{$invoice['invoice_number']} has been recorded. Status: Pending")
                        ->setType('success')
                        ->setData(['invoice_id' => $input['invoice_id'], 'payment_id' => $paymentId])
                        ->via(['email', 'sms', 'whatsapp', 'push', 'in-app'])
                        ->forUser($_SESSION['user_id'])
                        ->send();
                }
            } catch (Exception $notifyErr) {
                error_log("Notification error: " . $notifyErr->getMessage());
            }
            
            echo json_encode(['success' => true, 'message' => 'Payment recorded successfully', 'payment_id' => $paymentId]);
        } else {
            echo json_encode(['success' => false, 'error' => 'Failed to record payment']);
        }
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
}

function handleClientPaymentDetail($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $payment = $db->fetch(
        "SELECT p.* FROM payments p
         LEFT JOIN invoices i ON p.invoice_id = i.id OR p.related_id = i.id
         WHERE p.id = ? AND i.owner_id = ?",
        [$id, $_SESSION['user_id']]
    );

    if (!$payment) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Payment not found']);
        return;
    }

    echo json_encode([
        'success' => true,
        'payment' => $payment
    ]);
}

function handleClientPaymentReceipt($paymentId) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Get payment with invoice details, ensuring user owns the invoice
    $payment = $db->fetch(
        "SELECT p.*, i.invoice_number, i.total_amount, i.invoice_date, 
                u.name as client_name, u.email as client_email
         FROM payments p
         LEFT JOIN invoices i ON p.related_id = i.id
         LEFT JOIN users u ON i.owner_id = u.id
         WHERE p.id = ? AND i.owner_id = ?",
        [$paymentId, $_SESSION['user_id']]
    );

    if (!$payment) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Payment not found']);
        return;
    }

    if ($payment['status'] !== 'Confirmed') {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Receipt only available for confirmed payments']);
        return;
    }

    // Generate PDF receipt
    generatePaymentReceipt($paymentId);
}

function handleClientCheckMobileMoneyStatus() {
    global $db, $auth;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $data = json_decode(file_get_contents('php://input'), true);
    $paymentId = $data['payment_id'] ?? null;

    if (!$paymentId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID required']);
        return;
    }

    try {
        // Get payment record - ensure user owns it
        $payment = $db->fetch(
            "SELECT p.* FROM payments p
             LEFT JOIN invoices i ON p.related_id = i.id
             WHERE p.id = ? AND i.owner_id = ?",
            [$paymentId, $_SESSION['user_id']]
        );

        if (!$payment || !$payment['campay_reference']) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Payment not found']);
            return;
        }

        // Get Campay API token
        $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
        $campayToken = $settings['campay_api_token'] ?? null;

        if (!$campayToken) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Campay API not configured']);
            return;
        }

        // Check status with Campay
        $statusResponse = checkCampayStatus($payment['campay_reference'], $campayToken);

        if (!$statusResponse['success']) {
            http_response_code(400);
            echo json_encode(['success' => false, 'message' => 'Status check failed']);
            return;
        }

        $status = $statusResponse['status'];

        // Update payment status if confirmed
        if ($status === 'SUCCESSFUL' || $status === 'COMPLETED') {
            $db->query(
                "UPDATE payments SET status = ? WHERE id = ?",
                ['Confirmed', $paymentId]
            );
            // Update invoice status
            updateInvoiceStatus($db, $payment['invoice_id'] ?? $payment['related_id']);
        } elseif ($status === 'FAILED' || $status === 'CANCELLED') {
            $db->query(
                "UPDATE payments SET status = ? WHERE id = ?",
                ['Cancelled', $paymentId]
            );
        }

        echo json_encode([
            'success' => true,
            'status' => $status,
            'message' => 'Status updated'
        ]);

    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error checking status: ' . $e->getMessage()]);
    }
}

// Check general payment status (not just Campay mobile money)
function handleClientCheckPaymentStatus() {
    global $db, $auth, $notification;
    
    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'message' => 'Unauthorized']);
        return;
    }

    $data = json_decode(file_get_contents('php://input'), true);
    $paymentId = $data['payment_id'] ?? null;

    if (!$paymentId) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Payment ID required']);
        return;
    }

    try {
        // Get payment record - ensure user owns it
        $payment = $db->fetch(
            "SELECT p.*, i.invoice_number, i.total_amount FROM payments p
             LEFT JOIN invoices i ON p.invoice_id = i.id OR p.related_id = i.id
             WHERE p.id = ? AND i.owner_id = ?",
            [$paymentId, $_SESSION['user_id']]
        );

        if (!$payment) {
            http_response_code(404);
            echo json_encode(['success' => false, 'message' => 'Payment not found']);
            return;
        }

        $previousStatus = $payment['status'];
        $currentStatus = $previousStatus;

        // If mobile money payment, check with Campay for latest status
        if ($payment['method'] === 'mobile_money' && $payment['campay_reference']) {
            $settings = $db->fetch("SELECT * FROM settings LIMIT 1");
            $campayToken = $settings['campay_api_token'] ?? null;

            if ($campayToken) {
                $statusResponse = checkCampayStatus($payment['campay_reference'], $campayToken);
                
                if ($statusResponse['success']) {
                    $status = $statusResponse['status'];
                    
                    // Map Campay status to our status
                    if ($status === 'SUCCESSFUL' || $status === 'COMPLETED') {
                        $currentStatus = 'Confirmed';
                        $db->query(
                            "UPDATE payments SET status = ? WHERE id = ?",
                            ['Confirmed', $paymentId]
                        );
                        // Update invoice status
                        if ($payment['invoice_id'] || $payment['related_id']) {
                            updateInvoiceStatus($db, $payment['invoice_id'] ?? $payment['related_id']);
                        }
                    } elseif ($status === 'FAILED' || $status === 'CANCELLED') {
                        $currentStatus = 'Cancelled';
                        $db->query(
                            "UPDATE payments SET status = ? WHERE id = ?",
                            ['Cancelled', $paymentId]
                        );
                    }
                }
            }
        }

        // Send notification if status changed
        if ($currentStatus !== $previousStatus && $notification) {
            $statusMessages = [
                'Confirmed' => 'Your payment has been confirmed and processed successfully!',
                'Cancelled' => 'Your payment was cancelled or failed. Please try again.',
                'Pending' => 'Your payment is still being processed. Please check back shortly.'
            ];
            
            $notification
                ->reset()
                ->setTitle('Payment Status Update')
                ->setMessage($statusMessages[$currentStatus] ?? 'Payment status: ' . $currentStatus)
                ->setType($currentStatus === 'Confirmed' ? 'success' : ($currentStatus === 'Cancelled' ? 'error' : 'info'))
                ->setData(['invoice_id' => $payment['invoice_id'] ?? $payment['related_id'], 'payment_id' => $paymentId])
                ->via(['email', 'sms', 'whatsapp', 'push', 'in-app'])
                ->forUser($_SESSION['user_id'])
                ->send();
        }

        echo json_encode([
            'success' => true,
            'status' => $currentStatus,
            'message' => 'Payment status: ' . $currentStatus,
            'statusChanged' => ($currentStatus !== $previousStatus)
        ]);

    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'message' => 'Error checking status: ' . $e->getMessage()]);
    }
}

// ==================== Client Documents Handlers ====================

function handleClientContractPdf($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Get client info
    $client = $db->fetch("SELECT id, name FROM users WHERE id = ? AND role = 'client'", [$_SESSION['user_id']]);

    if (!$client) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Client not found']);
        return;
    }

    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="contract_' . $client['id'] . '.pdf"');
    
    // Placeholder - generate actual contract PDF
    echo "Contract PDF for " . $client['name'];
}

function handleClientAssessmentPdf($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    // Get client info
    $client = $db->fetch("SELECT id, name FROM users WHERE id = ? AND role = 'client'", [$_SESSION['user_id']]);

    if (!$client) {
        http_response_code(404);
        echo json_encode(['success' => false, 'error' => 'Client not found']);
        return;
    }

    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="assessment_' . $client['id'] . '.pdf"');
    
    // Placeholder - generate actual assessment PDF
    echo "Site Assessment PDF for " . $client['name'];
}

// ==================== Patrol System Handlers (QR Code Scanning) ====================

function handlePatrolScan() {
    global $auth, $db, $notification;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['success' => false, 'error' => 'Unauthorized']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);
    
    $postId = $input['post_id'] ?? null;
    $qrCode = $input['qr_code'] ?? null;
    $latitude = $input['latitude'] ?? null;
    $longitude = $input['longitude'] ?? null;
    $deviceInfo = $input['device_info'] ?? null;

    if (!$postId || !$qrCode) {
        http_response_code(400);
        echo json_encode(['success' => false, 'error' => 'Post ID and QR code are required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $result = $patrol->recordScan(
            $postId,
            $qrCode,
            $_SESSION['user_id'],
            $latitude,
            $longitude,
            $deviceInfo
        );

        if (!$result['success']) {
            http_response_code(400);
            echo json_encode($result);
            return;
        }

        // Get patrol details
        $patrolDetails = $result['patrol'];

        // Send notification on successful scan
        $notification
            ->reset()
            ->forUser($_SESSION['user_id'])
            ->setTitle('Scan Recorded ✓')
            ->setMessage("Patrol point \"" . $result['point']['point_name'] . "\" scanned. Patrol Score: " . $patrolDetails['score'] . "/8 (" . $patrolDetails['completion_percentage'] . "%)")
            ->setType('success')
            ->addCallToAction('View Status', "/dashboard?page=patrols&post_id={$postId}")
            ->via(['push', 'in-app'])
            ->send();

        // Send notification to supervisors about scan
        $supervisors = $db->fetchAll("SELECT id FROM users WHERE role IN ('admin', 'supervisor') LIMIT 10");
        if (!empty($supervisors)) {
            $guardUser = $db->fetch("SELECT name FROM users WHERE id = ?", [$_SESSION['user_id']]);
            foreach ($supervisors as $supervisor) {
                $notification
                    ->reset()
                    ->forUser($supervisor['id'])
                    ->setTitle('Patrol Point Scanned')
                    ->setMessage(($guardUser['name'] ?? 'Guard') . " scanned at post {$postId}: " . $result['point']['point_name'])
                    ->setType('info')
                    ->via(['push', 'in-app'])
                    ->send();
            }
        }

        echo json_encode([
            'success' => true,
            'message' => 'Scan recorded successfully',
            'point' => $result['point'],
            'patrol' => $patrolDetails,
            'countdown' => $patrol->getCountdownToNextHour()
        ]);

    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['success' => false, 'error' => 'Scan recording failed: ' . $e->getMessage()]);
    }
}

function handlePatrolDashboard($postId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        
        // Get current hour status
        $currentStatus = $patrol->getCurrentHourStatus($postId);
        
        // Get today's summary
        $todaySummary = $patrol->getTodaysSummary($postId);
        
        // Get recent scans (last 10)
        $recentScans = $db->fetchAll(
            "SELECT ps.*, pp.point_name, pp.location_description, u.name as guard_name
             FROM patrol_scans ps
             JOIN patrol_points pp ON ps.patrol_point_id = pp.id
             JOIN users u ON ps.guard_id = u.id
             WHERE ps.post_id = ? AND ps.scan_timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
             ORDER BY ps.scan_timestamp DESC
             LIMIT 10",
            [$postId]
        );
        
        // Get last scan
        $lastScan = $patrol->getLastScanInfo($postId);
        
        echo json_encode([
            'success' => true,
            'data' => [
                'current_status' => $currentStatus,
                'today_summary' => $todaySummary,
                'recent_scans' => $recentScans,
                'last_scan' => $lastScan,
                'countdown' => $patrol->getCountdownToNextHour()
            ]
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetPatrolStatus($postId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $status = $patrol->getCurrentHourStatus($postId);

        echo json_encode([
            'success' => true,
            'data' => $status,
            'countdown' => $patrol->getCountdownToNextHour()
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetPatrolHistory($postId, $days = 7) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $history = $patrol->getPatrolHistory($postId, $days);

        echo json_encode([
            'success' => true,
            'data' => $history,
            'days' => $days
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetTodayPatrolSummary($postId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $summary = $patrol->getTodaysSummary($postId);

        echo json_encode([
            'success' => true,
            'data' => $summary
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetCountdown() {
    global $auth;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol(null);
        $countdown = $patrol->getCountdownToNextHour();

        echo json_encode([
            'success' => true,
            'data' => $countdown
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetLastScan($postId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $lastScan = $patrol->getLastScanInfo($postId);

        echo json_encode([
            'success' => true,
            'data' => $lastScan
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleGetQRDetails($qrCode) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$qrCode) {
        http_response_code(400);
        echo json_encode(['error' => 'QR code required']);
        return;
    }

    try {
        $patrol = new \SSCI\Classes\Patrol($db);
        $qrDetails = $patrol->getQRCodeDetails($qrCode);

        if (!$qrDetails) {
            http_response_code(404);
            echo json_encode(['error' => 'QR code not found']);
            return;
        }

        echo json_encode([
            'success' => true,
            'data' => $qrDetails
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

// ==================== Patrol Management Handlers ====================

function handleGetPatrolPoints($postId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$postId) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID required']);
        return;
    }

    if (!$auth->hasPermission('manage_patrols') && !$auth->hasPermission('view_patrols')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        $points = $db->fetchAll(
            "SELECT * FROM patrol_points WHERE post_id = ? ORDER BY point_name ASC",
            [$postId]
        );

        echo json_encode([
            'success' => true,
            'data' => $points,
            'count' => count($points)
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => $e->getMessage()]);
    }
}

function handleCreatePatrolPoint() {
    global $auth, $db;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    if (!$auth->hasPermission('manage_patrols')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    if (empty($input['post_id']) || empty($input['point_name'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Post ID and point name required']);
        return;
    }

    try {
        // Generate unique QR code
        $qrCode = 'PATROL-' . $input['post_id'] . '-' . bin2hex(random_bytes(8));

        // Check if we have 4 patrol points already
        $count = $db->fetch(
            "SELECT COUNT(*) as cnt FROM patrol_points WHERE post_id = ?",
            [$input['post_id']]
        );

        if ($count['cnt'] >= 4) {
            http_response_code(400);
            echo json_encode(['error' => 'Maximum 4 patrol points per post']);
            return;
        }

        $db->query(
            "INSERT INTO patrol_points (post_id, point_name, qr_code, latitude, longitude, location_description, is_active)
             VALUES (?, ?, ?, ?, ?, ?, TRUE)",
            [
                $input['post_id'],
                $input['point_name'],
                $qrCode,
                $input['latitude'] ?? null,
                $input['longitude'] ?? null,
                $input['location_description'] ?? null
            ]
        );

        $pointId = $db->lastInsertId();

        echo json_encode([
            'success' => true,
            'id' => $pointId,
            'qr_code' => $qrCode,
            'message' => 'Patrol point created successfully'
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Failed to create patrol point: ' . $e->getMessage()]);
    }
}

function handleUpdatePatrolPoint($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$id) {
        http_response_code(400);
        echo json_encode(['error' => 'Point ID required']);
        return;
    }

    if (!$auth->hasPermission('manage_patrols')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    $input = json_decode(file_get_contents('php://input'), true);

    try {
        $updates = [];
        $params = [];

        if (isset($input['point_name'])) {
            $updates[] = "point_name = ?";
            $params[] = $input['point_name'];
        }

        if (isset($input['location_description'])) {
            $updates[] = "location_description = ?";
            $params[] = $input['location_description'];
        }

        if (isset($input['latitude'])) {
            $updates[] = "latitude = ?";
            $params[] = $input['latitude'];
        }

        if (isset($input['longitude'])) {
            $updates[] = "longitude = ?";
            $params[] = $input['longitude'];
        }

        if (isset($input['is_active'])) {
            $updates[] = "is_active = ?";
            $params[] = $input['is_active'] ? 1 : 0;
        }

        if (empty($updates)) {
            echo json_encode(['error' => 'No fields to update']);
            return;
        }

        $updates[] = "updated_at = NOW()";
        $params[] = $id;

        $db->query(
            "UPDATE patrol_points SET " . implode(", ", $updates) . " WHERE id = ?",
            $params
        );

        echo json_encode(['success' => true, 'message' => 'Patrol point updated']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Update failed: ' . $e->getMessage()]);
    }
}

function handleDeletePatrolPoint($id) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$id) {
        http_response_code(400);
        echo json_encode(['error' => 'Point ID required']);
        return;
    }

    if (!$auth->hasPermission('manage_patrols')) {
        http_response_code(403);
        echo json_encode(['error' => 'Forbidden']);
        return;
    }

    try {
        $db->query("DELETE FROM patrol_points WHERE id = ?", [$id]);

        echo json_encode(['success' => true, 'message' => 'Patrol point deleted']);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'Delete failed: ' . $e->getMessage()]);
    }
}

function handleGenerateQRCode($pointId) {
    global $auth, $db;

    if (!$auth->isAuthenticated() || !$pointId) {
        http_response_code(400);
        echo json_encode(['error' => 'Point ID required']);
        return;
    }

    try {
        $point = $db->fetch("SELECT * FROM patrol_points WHERE id = ?", [$pointId]);

        if (!$point) {
            http_response_code(404);
            echo json_encode(['error' => 'Patrol point not found']);
            return;
        }

        // Generate QR code image (use phpqrcode library or similar)
        // For now, return the QR code value
        echo json_encode([
            'success' => true,
            'qr_code' => $point['qr_code'],
            'point_name' => $point['point_name'],
            'post_id' => $point['post_id']
        ]);
    } catch (Exception $e) {
        http_response_code(500);
        echo json_encode(['error' => 'QR generation failed: ' . $e->getMessage()]);
    }
}

// ============================================
// GUARDS DOWNLOADS HANDLERS (Payslip, Contract, Badge)
// ============================================

/**
 * Generate Guards Payslip as PDF (TCPDF)
 * Uses same format as admin payslip
 */
function handleGuardsPayslip($salaryId)
{
    global $db;

    if (!isset($_SESSION['user_id']) || !$salaryId) {
        http_response_code(400);
        die('Salary ID required');
    }

    // Verify guard owns this salary record
    $salary = $db->fetch(
        "SELECT s.id FROM staff_salaries s WHERE s.id = ? AND s.staff_id = ?",
        [$salaryId, $_SESSION['user_id']]
    );

    if (!$salary) {
        http_response_code(403);
        die('Unauthorized: Salary record not found or does not belong to you');
    }

    // Call the existing generatePayslip function
    if (function_exists('generatePayslip')) {
        generatePayslip($salaryId);
    } else {
        http_response_code(500);
        die('Payslip generation function not found');
    }
}

/**
 * Generate Guards Contract as PDF (TCPDF with Letterhead)
 * Uses same letterhead design as invoices
 */
function handleGuardsContractPdf($userId = null)
{
    global $db;

    if (!$userId) {
        $userId = $_SESSION['user_id'] ?? null;
    }

    if (!$userId || !isset($_SESSION['user_id'])) {
        http_response_code(400);
        die('User ID required');
    }

    if ($_SESSION['user_id'] != $userId) {
        http_response_code(403);
        die('Unauthorized');
    }

    $user = $db->fetch("SELECT * FROM users WHERE id = ?", [$userId]);
    $staff = $db->fetch("SELECT * FROM staff WHERE user_id = ?", [$userId]);
    $settings = $db->fetch("SELECT * FROM settings LIMIT 1") ?: [];

    if (!$user || !$staff) {
        http_response_code(404);
        die('Record not found');
    }

    require_once __DIR__ . '/../lib/tcpdf.php';

    // ---------------------------------------------
    // PREP VARIABLES (NO PHP INSIDE HTML)
    // ---------------------------------------------
    $companyName    = $settings['site_name'] ?? 'SHEPHERD SECURITY AND CONSULTANCY INTERNATIONAL';
    $companyAddress = $settings['company_address'] ?? 'Opposite PCSS Buea Town, P.O.Box 554, Buea';
    $companyContact = $settings['company_contact'] ?? 'Tel: (237) 676037526';

    $contractNumber = $staff['staff_id'] ?? '';
    $contractDate   = date('Y-m-d');

    $employeeName       = $user['name'] ?? '';
    $employeePhone      = $user['phone'] ?? '';
    $employeeResidence  = $staff['residence'] ?? '';
    $employeeId         = $staff['national_id'] ?? '';
    $idIssuedAt         = $staff['id_issued_at'] ?? '';
    $idIssueDate        = $staff['id_issue_date'] ?? '';

    $jobTitle       = $staff['position'] ?? '';
    $salary         = number_format($staff['salary'] ?? 0, 0, ',', ' ') . ' FCFA';
    $allowances     = $staff['allowances'] ?? 'As determined by management';
    $contractStart  = $staff['contract_date'] ?? '';
    $contractEnd    = $staff['contract_end'] ?? '';

    /* -------------------------------------------------
     * REMUNERATION JSON
     * ------------------------------------------------- */
    $remunerations = json_decode($staff['remunerations'] ?? '[]', true);
    $totalPay = 0;
    $remunerationRows = '';

    if (is_array($remunerations)) {
        foreach ($remunerations as $row) {
            $item   = htmlspecialchars($row['item'] ?? 'Allowance');
            $amount = (float)($row['amount'] ?? 0);
            $totalPay += $amount;

            $remunerationRows .= "
            <tr>
                <td>{$item}</td>
                <td align=\"right\">" . number_format($amount, 0, ',', ' ') . "</td>
            </tr>";
        }
    }

    $totalPayFormatted = number_format($totalPay, 0, ',', ' ') . ' FCFA';
    $totalPayWords     = numberToWords($totalPay) . ' Francs CFA';


    // ---------------------------------------------
    // PDF SETUP
    // ---------------------------------------------
    $pdf = new TCPDF();
    $pdf->SetCreator('SSCI');
    $pdf->SetAuthor($companyName);
    $pdf->SetTitle('Employee Contract');
    $pdf->SetMargins(15, 18, 15);
    $pdf->SetAutoPageBreak(true, 20);
    $pdf->AddPage();

    $pdf->SetFont('ptsansnarrow', '', 11);

    // ---------------------------------------------
    // HTML CONTENT
    // ---------------------------------------------
    $html = <<<HTML
<style>
body { font-family: ptsansnarrow; font-size:11pt; color:#111827; line-height:1.35; }
h1 { font-family: ptsansnarrowb; font-size:18pt; margin:0; }
h3 { font-family: ptsansnarrowb; color:#D2691E; font-size:13pt; margin-bottom:3px; }
li {line-height:1.4; margin-bottom:4; }
.header { border-bottom:6px solid #D2691E; padding-bottom:10px; }
.meta { font-size:9pt; color:#6b7280; }

.table { width:100%; border-collapse:collapse; margin-top:10px; }
.table th {
    border:1px solid #e5e7eb;
    background-color:#D2691E;
    color:#fff;
    padding:8px;
    font-family: ptsansnarrowb;
}
.table td {
    border:1px solid #e5e7eb;
    padding:8px;
}

.section2 {border-bottom:1px dashed #e5e7eb; margin-bottom:0; padding-bottom:0; }
.small { font-size:9pt; color:#6b7280; }
.footer { border-top:1px dashed #e5e7eb; margin-top:20px; padding-top:8px; font-size:9pt; color:#6b7280; }
</style>

<table width="100%" class="header">
<tr>
<td width="70%">
    <h1>{$companyName}</h1>
    <div class="small">{$companyAddress}</div>
</td>
<td width="30%" align="right" class="meta">
    <div><b>Employee Contract</b></div>
    <div>Contract No: <b>{$contractNumber}</b></div>
    <div>Date: <b>{$contractDate}</b></div>
</td>
</tr>
</table>

<br>

<div align="center" style="font-family:ptsansnarrowb;font-size:16pt;">EMPLOYEE CONTRACT</div>
<div align="center" class="small">(In accordance with Labour Code No. 92/007 of 14 August 1992)</div>
<div align="center" style="line-height:1.0;">This contract is made between<b><h3>{$companyName}</h3></b><i>(The Employer)</i><br>and <b><h3>{$employeeName}</h3></b><i>(The Employee)</i></div>

<div class="section2">
<h3>1. Personal Information</h3>
<ul>
<li>Telephone Number: <b>{$employeePhone}</b></li>
<li>Current Residence: <b>{$employeeResidence}</b></li>
<li>National ID Card No.: <b>{$employeeId}</b></li>
<li>Issued at: <b>{$idIssuedAt}</b> on <b>{$idIssueDate}</b></li>
</ul>
</div>

<div class="section2">
<h3>2. Terms of Employment</h3>
<p>The Employee shall be recruited as <b>{$jobTitle}</b>.</p>

<table class="table">
<thead>
<tr>
<th>Remuneration Item</th>
<th align="right">Amount (FCFA)</th>
</tr>
</thead>
<tbody>
{$remunerationRows}
<tr>
<td><b>NET MONTHLY PAY</b></td>
<td align="right"><b>{$totalPayFormatted}</b></td>
</tr>
</tbody>
</table>

<p>
<b>Net Monthly Pay in words:</b><br>
<i>{$totalPayWords}</i>
</p>
</div>

<div class="section2">
<h3>3. Contract Duration</h3>
<p>
This contract runs from <b>{$contractStart}</b> to <b>{$contractEnd}</b>.
If not terminated, it shall renew monthly under the same terms.
</p>
</div>

<div class="section2">
<h3>4. Work Shifts</h3>
<table class="table">
<tr><th>Shift</th><th>Starting Time</th><th>Closing Time</th></tr>
<tr><td>Day Shift</td><td>06:00 AM</td><td>06:00 PM</td></tr>
<tr><td>Night Shift</td><td>06:00 PM</td><td>06:00 AM</td></tr>
</table>
</div>

<div class="section2">
<h3>5. Responsibilities of the Employee</h3>
<ul>
<li>Report to work on time</li>
<li>Maintain cleanliness of materials</li>
<li>Return company property after duty</li>
<li>Perform duties professionally</li>
<li>Respect hierarchy</li>
<li>Maintain public courtesy</li>
<li>Observe professional secrecy</li>
<li>Wear uniform at all times</li>
<li>Accept shift work</li>
<li>Maintain accurate log books</li>
</ul>
</div>

<div class="section2">
<h3>6. Prohibited Actions</h3>
<ul>
<li>Reporting drunk</li>
<li>Sleeping during work</li>
<li>Introducing alcohol or strangers</li>
<li>Misuse of equipment</li>
<li>Illegal activities</li>
<li>Work disruption</li>
<li>Leaving without handover</li>
<li>Arguing with clients</li>
<li>Unauthorized representation</li>
</ul>
</div>

<div class="section2">
<h3>7. Acceptance of Terms</h3>
<p>
I, <b>{$employeeName}</b>, confirm that I have read, understood,
and accepted this contract.
</p>
</div>

<div class="section">
<h3>8. Signatures</h3>
<table width="100%">
<tr style="height:280px;">
<td width="50%">
<b>Employer</b><br>
<span class="small">Signed on {$contractDate}</span>
</td>
<td width="50%">
<b>Employee</b><br>
<span class="small">Signed on {$contractDate}</span>
</td>
</tr>
</table>
</div>

<div class="footer">
{$companyName} • {$companyContact}
</div>
HTML;

    // ---------------------------------------------
    // RENDER PDF
    // ---------------------------------------------
    $pdf->writeHTML($html, true, false, true, false, '');
    $pdf->Output('Employee_Contract_' . $contractNumber . '.pdf', 'I');
}



/**
 * Generate Guards Badge as Image (PNG using html2canvas)
 * Returns HTML with embedded html2canvas library for client-side conversion
 */
function handleGuardsBadgeImage($userId = null)
{
    global $db;

    // Use session user ID if not provided
    if (!$userId) {
        $userId = $_SESSION['user_id'] ?? null;
    }

    if (!isset($_SESSION['user_id']) || !$userId) {
        http_response_code(400);
        die('User ID required');
    }

    // Verify it's the user's own badge
    if ($_SESSION['user_id'] != $userId) {
        http_response_code(403);
        die('Unauthorized: Cannot access other user\'s badge');
    }

    // Get user and staff information
    $user = $db->fetch("SELECT * FROM users WHERE id = ? AND role = 'guard'", [$userId]);
    if (!$user) {
        http_response_code(404);
        die('Guard not found');
    }

    $staff = $db->fetch("SELECT * FROM staff WHERE user_id = ?", [$userId]);
    if (!$staff) {
        http_response_code(404);
        die('Staff record not found');
    }

    // Return HTML page with badge template and html2canvas script
    header('Content-Type: text/html; charset=utf-8');
    
    // Generate QR code data
    $qrData = "Guard-" . ($staff['badge_number'] ?? 'N/A') . "-" . $user['name'];
    $qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' . urlencode($qrData);
    
    // Prepare staff data for template
    $staff_data = [
        'employee_name' => $user['name'],
        'staff_id' => $staff['staff_id'] ?? 'N/A',
        'position' => $staff['position'] ?? 'Security Guard',
        'zone' => $staff['zone'] ?? '',
        'email' => $user['email'] ?? '',
        'phone' => $user['phone'] ?? '',
        'photo_url' => $staff['photo_url'] ?? ''
    ];
    
    ?>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!--PT Sans Narrow Font-->
        <link href="https://fonts.googleapis.com/css2?family=PT+Sans+Narrow&display=swap" rel="stylesheet">
        <title><?php echo htmlspecialchars($staff_data['employee_name']); ?> - Employee Badge</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
        <style>
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }

            body {
                font-family: 'PT Sans Narrow', sans-serif;
                background: #f5f5f5;
                /* padding: 20px;
                display: flex; */
                justify-content: center;
                align-items: center;
                line-height: 1;
                min-height: 100vh;
                padding: 20px;
                display: flex;
                flex-direction: column;
            }

            .badge-container {
                width: 350px;
                background: white;
                border-radius: 8px;
                overflow: hidden;
                box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
                position: relative;
                max-height: 610px;
            }

            /* Header with company colors */
            .badge-header {
                background: linear-gradient(135deg, #D2691E 0%, #A0522D 100%);
                color: white;
                padding: 20px;
                text-align: center;
            }

            .company-name {
                font-size: 18px;
                font-weight: bold;
                letter-spacing: 2px;
                margin-bottom: 5px;
            }

            .badge-title {
                font-size: 12px;
                opacity: 0.9;
                font-weight: 500;
            }

            /* Photo section */
            .photo-section {
                padding: 15px;
                text-align: center;
                border-bottom: 2px solid #f0f0f0;
            }

            .employee-photo {
                width: 120px;
                height: 120px;
                border: 3px solid #c1c1c1ff;
                border-radius: 8px;
                object-fit: cover;
                margin: 0 auto 15px;
                display: block;
                background: #f5f5f5;
            }

            .no-photo {
                width: 120px;
                height: 120px;
                border: 3px solid #c1c1c1ff;
                border-radius: 8px;
                margin: 0 auto 15px;
                display: flex;
                align-items: center;
                justify-content: center;
                background: linear-gradient(135deg, #f5f5f5, #e0e0e0);
                font-size: 48px;
                color: #474b55ff;
            }

            /* Employee info */
            .info-section {
                padding: 10px;
                text-align: center;
            }

            .employee-name {
                font-size: 18px;
                font-weight: bold;
                color: #1f2937;
                margin-bottom: 5px;
                text-transform: uppercase;
                letter-spacing: 1px;
            }

            .employee-position {
                font-size: 14px;
                color: #2563eb;
                font-weight: 600;
                margin-bottom: 10px;
            }

            .employee-details {
                font-size: 12px;
                color: #374151;
                line-height: 1.6;
                margin-bottom: 15px;
            }

            .detail-row {
                display: flex;
                justify-content: center;
                align-items: center;
                gap: 8px;
                margin-bottom: 5px;
            }

            .detail-icon {
                color: #2563eb;
                width: 16px;
            }

            /* QR Code section */
            .qr-section {
                padding: 15px;
                text-align: center;
                border-top: 2px solid #f0f0f0;
                background: #fafafa;
            }

            .qr-label {
                font-size: 10px;
                color: #999;
                text-transform: uppercase;
                letter-spacing: 1px;
                margin-bottom: 10px;
            }

            .qr-code {
                display: inline-block;
                padding: 5px;
                background: white;
                border: 1px solid #ddd;
                border-radius: 4px;
            }

            .qr-code img {
                display: block;
                width: 80px;
                height: 80px;
            }

            /* Footer */
            .badge-footer {
                padding: 10px 20px;
                background: #f0f0f0;
                text-align: center;
                font-size: 10px;
                color: #999;
                border-top: 1px solid #ddd;
            }

            .staff-id-display {
                font-weight: bold;
                color: #2563eb;
                letter-spacing: 1px;
            }

            /* Controls */
            .controls {
                margin-top: 30px;
                text-align: center;
                display: flex;
                gap: 10px;
                justify-content: center;
            }

            button {
                padding: 12px 30px;
                font-size: 16px;
                font-weight: bold;
                border: none;
                border-radius: 6px;
                cursor: pointer;
                transition: all 0.3s ease;
            }

            .download-btn {
                background: #16a34a;
                color: white;
            }

            .download-btn:hover {
                background: #15803d;
            }

            .close-btn {
                background: #e5e7eb;
                color: #374151;
            }

            .close-btn:hover {
                background: #d1d5db;
            }

            /* Print styles */
            @media print {
                body {
                    background: white;
                    padding: 0;
                }

                .badge-container {
                    box-shadow: none;
                    border: 1px solid #ddd;
                    width: 100%;
                    margin: 0;
                }

                .controls {
                    display: none;
                }

                /* Print as A6 (105mm x 148mm) or smaller */
                @page {
                    size: A6;
                    margin: 0;
                }
            }

            /* Responsive */
            @media (max-width: 600px) {
                .badge-container {
                    width: 100%;
                    max-width: 350px;
                }
            }
        </style>
    </head>
    <body>
        <div class="badge_area">
            <div class="badge-container" id="badgeElement">
                <!-- Header -->
                <div class="badge-header">
                    <div class="company-name">SHEPHERD SECURITY AND CONSULTANCY INTERNATIONAL</div>
                    <div class="badge-title">EMPLOYEE IDENTIFICATION BADGE</div>
                </div>

                <!-- Photo Section -->
                <div class="photo-section">
                    <?php if (!empty($staff_data['photo_url'])): ?>
                        <img src="<?php echo htmlspecialchars($staff_data['photo_url']); ?>" alt="<?php echo htmlspecialchars($staff_data['employee_name']); ?>" class="employee-photo">
                    <?php else: ?>
                        <div class="no-photo">👤</div>
                    <?php endif; ?>
                </div>

                <!-- Employee Information -->
                <div class="info-section">
                    <div class="employee-name"><?php echo htmlspecialchars($staff_data['employee_name']); ?></div>
                    <div class="employee-position"><?php echo htmlspecialchars($staff_data['position']); ?></div>

                    <div class="employee-details">
                        <div class="detail-row">
                            <span class="detail-icon">ID:</span>
                            <span class="staff-id-display"><?php echo htmlspecialchars($staff_data['staff_id']); ?></span>
                        </div>
                        <?php if (!empty($staff_data['zone'])): ?>
                            <div class="detail-row">
                                <span class="detail-icon">📍</span>
                                <span><?php echo htmlspecialchars($staff_data['zone']); ?></span>
                            </div>
                        <?php endif; ?>
                        <div class="detail-row">
                            <span class="detail-icon">📧</span>
                            <span><?php echo htmlspecialchars($staff_data['email'] ?? '-'); ?></span>
                        </div>
                        <?php if (!empty($staff_data['phone'])): ?>
                            <div class="detail-row">
                                <span class="detail-icon">📱</span>
                                <span><?php echo htmlspecialchars($staff_data['phone']); ?></span>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>

                <!-- QR Code -->
                <div class="qr-section">
                    <div class="qr-label">Verification Code</div>
                    <div class="qr-code">
                    <img 
                        id="qrImage"
                        src="<?php echo htmlspecialchars($qrUrl); ?>" 
                        alt="QR Code"
                    />
                   </div>
                </div>

                <!-- Footer -->
                <div class="badge-footer">
                    <span class="staff-id-display"><?php echo htmlspecialchars($staff_data['staff_id']); ?></span>
                    <span> • </span>
                    <span><?php echo date('M d, Y'); ?></span>
                </div>
            </div>
        </div>

        <div class="controls">
            <button class="download-btn" onclick="downloadBadge()">
                📥 Download as PNG
            </button>
            <button class="close-btn" onclick="window.close()">
                ✕ Close
            </button>
        </div>

        <script>
   async function downloadBadge() {
    const badge = document.getElementById('badgeElement');
    const qrImg = document.getElementById('qrImage');
    const button = document.querySelector('.download-btn');

    button.disabled = true;
    button.textContent = '⏳ Generating...';

    try {
        // Convert QR image to base64 BEFORE capture
        if (qrImg && !qrImg.src.startsWith('data:')) {
            const base64 = await convertImageToBase64(qrImg);
            qrImg.src = base64;
        }

        // Ensure all images are loaded
        const images = badge.querySelectorAll('img');
        await Promise.all(
            Array.from(images).map(img => {
                if (img.complete) return Promise.resolve();
                return new Promise(resolve => {
                    img.onload = img.onerror = resolve;
                });
            })
        );

        const rect = badge.getBoundingClientRect();

        const canvas = await html2canvas(badge, {
            scale: 3,
            backgroundColor: '#ffffff',
            useCORS: false,        // no longer needed
            allowTaint: true,
            logging: false
        });

        const link = document.createElement('a');
        link.href = canvas.toDataURL('image/png', 1.0);
        link.download = 'Badge_<?php echo htmlspecialchars($staff_data['staff_id']); ?>.png';
        link.click();

    } catch (error) {
        console.error(error);
        alert('Failed to generate badge image.');
    }

    button.disabled = false;
    button.textContent = '📥 Download as PNG';
}

async function convertImageToBase64(img) {
    const response = await fetch(img.src);
    const blob = await response.blob();

    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(blob);
    });
}

        </script>
    </body>
    </html>
    <?php
    exit();
}

/**
 * Handle message attachment download
 */
function handleDownloadMessageAttachment() {
    global $db, $auth;

    if (!$auth->isAuthenticated()) {
        http_response_code(401);
        echo json_encode(['error' => 'Unauthorized']);
        return;
    }

    $messageId = $_GET['message_id'] ?? null;
    $attachmentIndex = $_GET['index'] ?? 0;

    if (!$messageId) {
        http_response_code(400);
        echo json_encode(['error' => 'Message ID required']);
        return;
    }

    try {
        // Verify user has access to this message
        $message = $db->fetch(
            "SELECT id, sender_id, recipient_id, attachments FROM messages WHERE id = ? AND (sender_id = ? OR recipient_id = ?)",
            [$messageId, $_SESSION['user_id'], $_SESSION['user_id']]
        );

        if (!$message) {
            http_response_code(403);
            echo json_encode(['error' => 'Access denied']);
            return;
        }

        // Parse attachments JSON
        $attachments = $message['attachments'] ? json_decode($message['attachments'], true) : [];

        if (!isset($attachments[$attachmentIndex])) {
            http_response_code(404);
            echo json_encode(['error' => 'Attachment not found']);
            return;
        }

        $attachment = $attachments[$attachmentIndex];
        $filePath = __DIR__ . '/../' . $attachment['file_path'];

        // Security check - prevent directory traversal
        if (strpos(realpath($filePath), realpath(__DIR__ . '/../uploads/messages/')) === false) {
            http_response_code(403);
            echo json_encode(['error' => 'Invalid file path']);
            return;
        }

        if (!file_exists($filePath)) {
            http_response_code(404);
            echo json_encode(['error' => 'File not found']);
            return;
        }

        // Set headers for download
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($attachment['original_name']) . '"');
        header('Content-Length: ' . filesize($filePath));
        header('Cache-Control: no-cache, no-store, must-revalidate');
        header('Pragma: no-cache');
        header('Expires: 0');

        // Output file
        readfile($filePath);
        exit();

    } catch (Exception $e) {
        error_log('Error downloading attachment: ' . $e->getMessage());
        http_response_code(500);
        echo json_encode(['error' => 'Server error']);
        exit();
    }
}
?>
