<?php
// classes/JobQueueProcessor.php
// Processes queued jobs without cron jobs using on-demand execution

class JobQueueProcessor {
    private $db;
    private $max_execution_time = 30; // seconds
    private $batch_size = 10;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    /**
     * Process pending jobs (called on page loads or AJAX requests)
     * This runs opportunistically without cron
     */
    public function processPendingJobs() {
        $start_time = time();
        $processed = 0;
        
        try {
            // Get pending jobs ordered by priority and scheduled time
            $stmt = $this->db->prepare("
                SELECT * FROM job_queue
                WHERE status = 'pending'
                AND (scheduled_at IS NULL OR scheduled_at <= NOW())
                AND attempts < max_attempts
                ORDER BY priority DESC, scheduled_at ASC
                LIMIT ?
            ");
            
            $stmt->bind_param("i", $this->batch_size);
            $stmt->execute();
            $result = $stmt->get_result();
            
            while ($job = $result->fetch_assoc()) {
                // Check if we've exceeded max execution time
                if ((time() - $start_time) >= $this->max_execution_time) {
                    break;
                }
                
                $this->processJob($job);
                $processed++;
            }
            
            return [
                'success' => true,
                'processed' => $processed
            ];
            
        } catch (Exception $e) {
            error_log("Job Queue Error: " . $e->getMessage());
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * Process individual job
     */
    private function processJob($job) {
        // Mark as processing
        $this->updateJobStatus($job['id'], 'processing');
        
        try {
            $payload = json_decode($job['payload'], true);
            
            switch ($job['job_type']) {
                case 'whatsapp':
                    $this->sendWhatsAppMessage($payload);
                    break;
                    
                case 'sms':
                    $this->sendSMS($payload);
                    break;
                    
                case 'email':
                    $this->sendEmail($payload);
                    break;
                    
                case 'notification':
                    $this->sendNotification($payload);
                    break;
                    
                case 'report':
                    $this->generateReport($payload);
                    break;
                    
                default:
                    throw new Exception("Unknown job type: {$job['job_type']}");
            }
            
            // Mark as completed
            $this->updateJobStatus($job['id'], 'completed', null, true);
            
        } catch (Exception $e) {
            // Mark as failed and increment attempts
            $this->updateJobStatus($job['id'], 'failed', $e->getMessage(), false, $job['attempts'] + 1);
        }
    }
    
    /**
     * Send WhatsApp message via AfricasTalking
     */
    private function sendWhatsAppMessage($payload) {
        $phone = $this->formatPhoneNumber($payload['phone']);
        $message = $payload['message'];
        
        $url = ChatbotConfig::AT_WHATSAPP_URL;
        
        $data = [
            'username' => ChatbotConfig::AT_USERNAME,
            'phoneNumbers' => [$phone],
            'message' => $message
        ];
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Accept: application/json',
            'Content-Type: application/json',
            'apiKey: ' . ChatbotConfig::AT_API_KEY
        ]);
        
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($http_code !== 200 && $http_code !== 201) {
            throw new Exception("WhatsApp API returned status code: {$http_code}. Response: {$response}");
        }
        
        // Log the communication
        $this->logCommunication('whatsapp', $phone, $message, $response);
        
        return true;
    }
    
    /**
     * Send SMS via AfricasTalking
     */
    private function sendSMS($payload) {
        $phone = $this->formatPhoneNumber($payload['phone']);
        $message = $payload['message'];
        
        $url = 'https://api.africastalking.com/version1/messaging';
        
        $data = [
            'username' => ChatbotConfig::AT_USERNAME,
            'to' => $phone,
            'message' => $message
        ];
        
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Accept: application/json',
            'Content-Type: application/x-www-form-urlencoded',
            'apiKey: ' . ChatbotConfig::AT_API_KEY
        ]);
        
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($http_code !== 200 && $http_code !== 201) {
            throw new Exception("SMS API returned status code: {$http_code}");
        }
        
        $this->logCommunication('sms', $phone, $message, $response);
        
        return true;
    }
    
    /**
     * Send email via PHPMailer
     */
    private function sendEmail($payload) {
        require_once 'vendor/phpmailer/phpmailer/src/PHPMailer.php';
        require_once 'vendor/phpmailer/phpmailer/src/SMTP.php';
        require_once 'vendor/phpmailer/phpmailer/src/Exception.php';
        
        $mail = new PHPMailer\PHPMailer\PHPMailer(true);
        
        try {
            // Server settings
            $mail->isSMTP();
            $mail->Host = $_ENV['SMTP_HOST'] ?? 'smtp.gmail.com';
            $mail->SMTPAuth = true;
            $mail->Username = $_ENV['SMTP_USERNAME'];
            $mail->Password = $_ENV['SMTP_PASSWORD'];
            $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
            $mail->Port = $_ENV['SMTP_PORT'] ?? 587;
            
            // Recipients
            $mail->setFrom($_ENV['SMTP_FROM_EMAIL'], ChatbotConfig::HOSPITAL_NAME);
            $mail->addAddress($payload['to'], $payload['to_name'] ?? '');
            
            // Content
            $mail->isHTML(true);
            $mail->Subject = $payload['subject'];
            $mail->Body = $payload['body'];
            $mail->AltBody = strip_tags($payload['body']);
            
            // Attachments
            if (isset($payload['attachments'])) {
                foreach ($payload['attachments'] as $attachment) {
                    $mail->addAttachment($attachment['path'], $attachment['name']);
                }
            }
            
            $mail->send();
            
            $this->logCommunication('email', $payload['to'], $payload['subject'], 'Sent successfully');
            
            return true;
            
        } catch (Exception $e) {
            throw new Exception("Email send failed: {$mail->ErrorInfo}");
        }
    }
    
    /**
     * Send in-app notification
     */
    private function sendNotification($payload) {
        $stmt = $this->db->prepare("
            INSERT INTO notifications (
                user_id, title, message, type, 
                priority, is_read, created_at
            ) VALUES (?, ?, ?, ?, ?, 0, NOW())
        ");
        
        $user_id = $payload['user_id'] ?? null;
        $title = $payload['title'];
        $message = $payload['message'];
        $type = $payload['type'] ?? 'general';
        $priority = $payload['priority'] ?? 'medium';
        
        $stmt->bind_param("issss", $user_id, $title, $message, $type, $priority);
        $stmt->execute();
        
        return true;
    }
    
    /**
     * Generate report
     */
    private function generateReport($payload) {
        // Placeholder for report generation
        // This would integrate with your FPDF implementation
        return true;
    }
    
    /**
     * Format phone number for API
     */
    private function formatPhoneNumber($phone) {
        // Remove all non-numeric characters
        $phone = preg_replace('/[^0-9]/', '', $phone);
        
        // If starts with 0, replace with country code
        if (substr($phone, 0, 1) === '0') {
            $phone = '254' . substr($phone, 1);
        }
        
        // If doesn't start with +, add it
        if (substr($phone, 0, 1) !== '+') {
            $phone = '+' . $phone;
        }
        
        return $phone;
    }
    
    /**
     * Update job status
     */
    private function updateJobStatus($job_id, $status, $error_message = null, $completed = false, $attempts = null) {
        if ($completed) {
            $stmt = $this->db->prepare("
                UPDATE job_queue 
                SET status = ?, 
                    completed_at = NOW(),
                    error_message = ?
                WHERE id = ?
            ");
            $stmt->bind_param("ssi", $status, $error_message, $job_id);
        } else {
            if ($status === 'processing') {
                $stmt = $this->db->prepare("
                    UPDATE job_queue 
                    SET status = ?, 
                        started_at = NOW()
                    WHERE id = ?
                ");
                $stmt->bind_param("si", $status, $job_id);
            } else {
                $stmt = $this->db->prepare("
                    UPDATE job_queue 
                    SET status = ?, 
                        error_message = ?,
                        attempts = ?
                    WHERE id = ?
                ");
                $stmt->bind_param("ssii", $status, $error_message, $attempts, $job_id);
            }
        }
        
        $stmt->execute();
    }
    
    /**
     * Log communication
     */
    private function logCommunication($type, $recipient, $message, $response) {
        $stmt = $this->db->prepare("
            INSERT INTO communication_log (
                communication_type, recipient, message, 
                response, status, created_at
            ) VALUES (?, ?, ?, ?, 'sent', NOW())
        ");
        
        $stmt->bind_param("ssss", $type, $recipient, $message, $response);
        $stmt->execute();
    }
    
    /**
     * Get queue statistics
     */
    public function getQueueStats() {
        $stmt = $this->db->query("
            SELECT 
                status,
                COUNT(*) as count,
                MIN(created_at) as oldest,
                MAX(created_at) as newest
            FROM job_queue
            GROUP BY status
        ");
        
        $stats = [];
        while ($row = $stmt->fetch_assoc()) {
            $stats[$row['status']] = [
                'count' => $row['count'],
                'oldest' => $row['oldest'],
                'newest' => $row['newest']
            ];
        }
        
        return $stats;
    }
    
    /**
     * Retry failed jobs
     */
    public function retryFailedJobs($max_age_hours = 24) {
        $stmt = $this->db->prepare("
            UPDATE job_queue
            SET status = 'pending',
                attempts = 0,
                error_message = NULL,
                scheduled_at = NOW()
            WHERE status = 'failed'
            AND attempts < max_attempts
            AND created_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
        ");
        
        $stmt->bind_param("i", $max_age_hours);
        $stmt->execute();
        
        return $stmt->affected_rows;
    }
    
    /**
     * Clean old completed jobs
     */
    public function cleanOldJobs($days_old = 30) {
        $stmt = $this->db->prepare("
            DELETE FROM job_queue
            WHERE status = 'completed'
            AND completed_at < DATE_SUB(NOW(), INTERVAL ? DAY)
        ");
        
        $stmt->bind_param("i", $days_old);
        $stmt->execute();
        
        return $stmt->affected_rows;
    }
}

// Auto-processor that runs opportunistically on page loads
// Add this to your main pages or create an AJAX endpoint

function triggerJobProcessing() {
    global $conn; // Your database connection
    
    // Only process jobs occasionally (e.g., 20% of page loads)
    if (rand(1, 100) <= 20) {
        $processor = new JobQueueProcessor($conn);
        $processor->processPendingJobs();
    }
}

// Add this line to pages that load frequently (dashboard, patient list, etc.)
// triggerJobProcessing();