<?php
require_once './includes/logging_helper.php';

/**
 * Normalizes Kenyan phone numbers to international format (+2547XXXXXXXX)
 */
function normalizeKenyanPhoneNumber(string $phoneNumber): string
{
    $cleaned = preg_replace('/[^0-9]/', '', $phoneNumber);
    
    if (preg_match('/^2547[0-9]{8}$/', $cleaned)) {
        return '+' . $cleaned;
    }
    
    if (preg_match('/^0?7[0-9]{8}$/', $cleaned)) {
        return '+254' . substr($cleaned, -9);
    }
    
    if (preg_match('/^254[0-9]{9}$/', $cleaned)) {
        return '+' . $cleaned;
    }
    
    return $phoneNumber;
}

/**
 * Validates sender ID format based on gateway requirements
 */
function validateSenderId(?string $senderId, array $gateway): ?string
{
    if ($senderId === null) {
        return $gateway['default_sender_id'] ?? null;
    }
    
    $maxLength = $gateway['sender_id_max_length'] ?? 11;
    if (strlen($senderId) > $maxLength) {
        $senderId = substr($senderId, 0, $maxLength);
    }
    
    // Alphanumeric only for most gateways
    if (!preg_match('/^[a-zA-Z0-9]+$/', $senderId)) {
        return $gateway['default_sender_id'] ?? null;
    }
    
    return $senderId;
}

/**
 * Checks rate limits for gateway
 */
function checkRateLimit(PDO $pdo, int $gatewayId): bool
{
    try {
        $stmt = $pdo->prepare("
            SELECT COUNT(*) as count 
            FROM communication_logs 
            WHERE gateway_id = :gateway_id 
            AND status = 'success'
            AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
        ");
        $stmt->execute([':gateway_id' => $gatewayId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        // Default limit: 1000 per hour (adjust based on gateway)
        $stmt = $pdo->prepare("SELECT rate_limit FROM sms_gateways WHERE id = :gateway_id");
        $stmt->execute([':gateway_id' => $gatewayId]);
        $gateway = $stmt->fetch(PDO::FETCH_ASSOC);
        
        $limit = $gateway['rate_limit'] ?? 1000;
        
        return $result['count'] < $limit;
    } catch (PDOException $e) {
        error_log("Rate limit check failed: " . $e->getMessage());
        return true; // Fail open
    }
}

/**
 * Generate unique request ID for tracing
 */
function generateRequestId(): string
{
    return uniqid('sms_', true);
}

/**
 * Enhanced SMS sending function with multi-gateway support
 */
function sendSms(PDO $pdo, string $recipient, string $message, ?string $senderId = null, bool $enqueueOnFailure = true): array
{
    $requestId = generateRequestId();
    $startTime = microtime(true);
    
    // Normalize Kenyan numbers automatically
    $originalRecipient = $recipient;
    $recipient = normalizeKenyanPhoneNumber($recipient);
    
    // Validate international number format
    if (!preg_match('/^\+[1-9]\d{1,14}$/', $recipient)) {
        $errorMessage = "Invalid phone number format";
        logCommunication('sms', $originalRecipient, $senderId, $message, 'failed', $errorMessage);
        return ["type" => "error", "message" => $errorMessage, "request_id" => $requestId];
    }

    // Message handling
    $maxLength = 600;
    if (strlen($message) > $maxLength) {
        $message = substr($message, 0, $maxLength);
        logCommunication('sms', $recipient, $senderId, $message, 'warning', 'Message truncated');
    }

    $gateway = null;
    $transactionActive = false;
    
    try {
        $transactionActive = !$pdo->inTransaction();
        if ($transactionActive) $pdo->beginTransaction();

        // Gateway selection with better error handling
        $gateway = selectSmsGateway($pdo);
        if ($gateway === null) {
            if ($transactionActive) $pdo->rollBack();
            $errorMessage = "No active SMS gateway found or gateway configuration error";
            logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage);
            
            if ($enqueueOnFailure) {
                enqueueFailedMessage($pdo, $recipient, $message, $senderId);
            }
            
            return ["type" => "error", "message" => $errorMessage, "request_id" => $requestId];
        }

        // Check rate limit
        if (!checkRateLimit($pdo, $gateway['id'])) {
            if ($transactionActive) $pdo->rollBack();
            $errorMessage = "Rate limit exceeded for gateway: " . $gateway['provider_name'];
            logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage, null, null, $gateway['id']);
            
            if ($enqueueOnFailure) {
                enqueueFailedMessage($pdo, $recipient, $message, $senderId);
            }
            
            return ["type" => "error", "message" => $errorMessage, "request_id" => $requestId];
        }

        // Validate sender ID
        $senderId = validateSenderId($senderId, $gateway);

        // Update gateway usage
        updateGatewayUsage($pdo, $gateway['id']);
        if ($transactionActive) $pdo->commit();

        // Route to appropriate gateway handler
        $responseTime = round((microtime(true) - $startTime) * 1000, 2);
        
        $result = sendViaGateway($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        $result['request_id'] = $requestId;
        $result['response_time'] = $responseTime;
        
        return $result;

    } catch (Exception $e) {
        if ($transactionActive && $pdo->inTransaction()) {
            $pdo->rollBack();
        }
        
        $errorMessage = "System Error: " . $e->getMessage();
        logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage, null, null, $gateway['id'] ?? null);
        
        if ($enqueueOnFailure) {
            enqueueFailedMessage($pdo, $recipient, $message, $senderId);
        }
        
        return ["type" => "error", "message" => $errorMessage, "request_id" => $requestId];
    }
}

/**
 * Selects an appropriate SMS gateway based on priority and availability
 */
function selectSmsGateway(PDO $pdo): ?array
{
    static $gatewayCache = null;
    static $cacheTime = 0;
    
    // Cache for 60 seconds
    if ($gatewayCache !== null && (time() - $cacheTime) < 60) {
        return $gatewayCache;
    }
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM sms_gateways 
            WHERE is_active = 1 
            ORDER BY priority DESC, last_used ASC 
            LIMIT 1
        ");
        $stmt->execute();
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        $gatewayCache = $result ?: null;
        $cacheTime = time();
        
        return $gatewayCache;
    } catch (PDOException $e) {
        error_log("Database error in selectSmsGateway: " . $e->getMessage());
        return null;
    }
}

/**
 * Updates gateway usage statistics
 */
function updateGatewayUsage(PDO $pdo, int $gatewayId): void
{
    try {
        $stmt = $pdo->prepare("
            UPDATE sms_gateways 
            SET last_used = NOW(), 
                usage_count = usage_count + 1 
            WHERE id = :gateway_id
        ");
        $stmt->execute([':gateway_id' => $gatewayId]);
    } catch (PDOException $e) {
        error_log("Failed to update gateway usage: " . $e->getMessage());
    }
}

/**
 * Routes SMS sending to the appropriate gateway handler
 */
function sendViaGateway(
    array $gateway, 
    string $recipient, 
    string $message, 
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    switch ($gateway['provider_name']) {
        case 'AfricasTalking':
            return sendViaAfricaTalking($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'Twilio':
            return sendViaTwilio($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'Nexmo':
        case 'Vonage':
            return sendViaNexmo($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'Plivo':
            return sendViaPlivo($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'Codic':
        case 'CustomGateway':
            return sendViaCustomGateway($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'OnfonMedia':
            return sendViaOnfonMedia($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        case 'InfoBip':
            return sendViaInfoBip($gateway, $recipient, $message, $senderId, $enqueueOnFailure, $pdo);
        default:
            $errorMessage = "Unsupported gateway provider: " . $gateway['provider_name'];
            logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage, null, null, $gateway['id']);
            
            if ($enqueueOnFailure) {
                enqueueFailedMessage($pdo, $recipient, $message, $senderId);
            }
            
            return [
                "type" => "error", 
                "message" => $errorMessage,
                "success" => false,
                "gateway" => $gateway['provider_name']
            ];
    }
}

/**
 * Africa's Talking Gateway Implementation
 */
function sendViaAfricaTalking(
    array $gateway, 
    string $recipient, 
    string $message, 
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    $apiKey = $gateway['api_key'];
    $username = $gateway['username'];
    $senderId = $senderId ?? $gateway['default_sender_id'] ?? null;

    $postData = [
        'username' => $username,
        'to' => $recipient,
        'message' => $message
    ];

    if ($senderId && ($gateway['supports_sender_id'] ?? false)) {
        $postData['from'] = $senderId;
    }

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => "https://api.africastalking.com/version1/messaging",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($postData),
        CURLOPT_HTTPHEADER => [
            "Accept: application/json",
            "Content-Type: application/x-www-form-urlencoded",
            "apikey: $apiKey"
        ],
        CURLOPT_TIMEOUT => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT => 'SMS-Gateway/1.0'
    ]);

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

    return processGatewayResponse(
        $response,
        $curlError,
        $httpCode,
        $gateway,
        $recipient,
        $message,
        $senderId,
        $enqueueOnFailure,
        $pdo,
        function($responseData) {
            // Africa's Talking success detection
            if (isset($responseData['SMSMessageData']['Recipients'][0]['status'])) {
                $status = $responseData['SMSMessageData']['Recipients'][0]['status'];
                $messageId = $responseData['SMSMessageData']['Recipients'][0]['messageId'] ?? null;
                $cost = $responseData['SMSMessageData']['Recipients'][0]['cost'] ?? null;
                
                if (strtolower($status) === 'success') {
                    return [
                        'success' => true,
                        'cost' => $cost,
                        'message_id' => $messageId,
                        'error' => null
                    ];
                }
                
                return [
                    'success' => false,
                    'error' => "API Error: " . ($responseData['SMSMessageData']['Message'] ?? 'Unknown error')
                ];
            }
            
            // Check alternative success indicators
            if (isset($responseData['SMSMessageData']['Message']) && 
                stripos($responseData['SMSMessageData']['Message'], 'Success') !== false) {
                return [
                    'success' => true,
                    'cost' => null,
                    'message_id' => null,
                    'error' => null
                ];
            }
            
            return ['success' => false, 'error' => "Malformed API response"];
        }
    );
}

/**
 * Twilio Gateway Implementation
 */
function sendViaTwilio(
    array $gateway, 
    string $recipient, 
    string $message, 
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    $accountSid = $gateway['api_key'];
    $authToken = $gateway['auth_token'] ?? $gateway['api_secret'];
    $senderId = $senderId ?? $gateway['default_sender_id'] ?? null;

    $url = "https://api.twilio.com/2010-04-01/Accounts/{$accountSid}/Messages.json";

    $postData = [
        'To' => $recipient,
        'Body' => $message
    ];

    // Validate sender ID format (must be E.164 for Twilio numbers)
    $isSenderValid = $senderId && preg_match('/^\+\d{10,15}$/', $senderId);

    if ($isSenderValid && ($gateway['supports_sender_id'] ?? false)) {
        $postData['From'] = $senderId;
    }

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($postData),
        CURLOPT_USERPWD => "{$accountSid}:{$authToken}",
        CURLOPT_HTTPHEADER => [
            "Accept: application/json",
            "Content-Type: application/x-www-form-urlencoded"
        ],
        CURLOPT_TIMEOUT => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2
    ]);

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

    return processGatewayResponse(
        $response,
        $curlError,
        $httpCode,
        $gateway,
        $recipient,
        $message,
        $senderId,
        $enqueueOnFailure,
        $pdo,
        function($responseData) use ($httpCode) {
            // Twilio success detection
            if ($httpCode >= 200 && $httpCode < 300) {
                if (isset($responseData['sid'])) {
                    return [
                        'success' => true,
                        'cost' => $responseData['price'] ?? null,
                        'message_id' => $responseData['sid'],
                        'error' => null
                    ];
                }
                
                if (isset($responseData['status'])) {
                    return [
                        'success' => true,
                        'cost' => null,
                        'message_id' => null,
                        'error' => null
                    ];
                }
            }

            $errorMsg = "API Error (HTTP $httpCode): " . ($responseData['message'] ?? $responseData['error_message'] ?? 'Unknown error');
            return [
                'success' => false,
                'error' => $errorMsg
            ];
        }
    );
}

/**
 * Nexmo (Vonage) Gateway Implementation
 */
function sendViaNexmo(
    array $gateway, 
    string $recipient, 
    string $message, 
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    $apiKey = $gateway['api_key'];
    $apiSecret = $gateway['api_secret'] ?? $gateway['auth_token'];
    $senderId = $senderId ?? $gateway['default_sender_id'] ?? null;

    $url = "https://rest.nexmo.com/sms/json";

    $postData = [
        'api_key' => $apiKey,
        'api_secret' => $apiSecret,
        'to' => $recipient,
        'text' => $message
    ];

    if ($senderId && ($gateway['supports_sender_id'] ?? false)) {
        $postData['from'] = $senderId;
    }

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($postData),
        CURLOPT_HTTPHEADER => [
            "Accept: application/json",
            "Content-Type: application/x-www-form-urlencoded"
        ],
        CURLOPT_TIMEOUT => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2
    ]);

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

    return processGatewayResponse(
        $response,
        $curlError,
        $httpCode,
        $gateway,
        $recipient,
        $message,
        $senderId,
        $enqueueOnFailure,
        $pdo,
        function($responseData) {
            // Nexmo success detection
            if (isset($responseData['messages'][0]['status'])) {
                $status = $responseData['messages'][0]['status'];
                
                if ($status == '0') {
                    return [
                        'success' => true,
                        'cost' => $responseData['messages'][0]['message-price'] ?? null,
                        'message_id' => $responseData['messages'][0]['message-id'] ?? null,
                        'error' => null
                    ];
                }
                
                return [
                    'success' => false,
                    'error' => "API Error: " . ($responseData['messages'][0]['error-text'] ?? 'Unknown error')
                ];
            }
            
            if (isset($responseData['message-count']) && isset($responseData['messages'])) {
                foreach ($responseData['messages'] as $msg) {
                    if ($msg['status'] == '0') {
                        return [
                            'success' => true,
                            'cost' => $msg['message-price'] ?? null,
                            'message_id' => $msg['message-id'] ?? null,
                            'error' => null
                        ];
                    }
                }
            }
            
            return ['success' => false, 'error' => "Malformed API response"];
        }
    );
}

/**
 * Onfon Media SMS Gateway
 */
function sendViaOnfonMedia(
    array $gateway,
    string $recipient,
    string $message,
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    try {
        // Validate configuration
        $baseUrl  = $gateway['base_url'] ?? null;
        $apiKey   = $gateway['api_key'] ?? null;
        $clientId = $gateway['username'] ?? null;
        $accessKey = $gateway['access_key'] ?? $gateway['auth_token'] ?? null;

        if (!$baseUrl || !$apiKey || !$clientId || !$accessKey) {
            throw new Exception("OnfonMedia gateway configuration incomplete");
        }

        // Normalize phone number
        $recipient = preg_replace('/^\+/', '', $recipient);
        if (preg_match('/^07\d{8}$/', $recipient)) {
            $recipient = '254' . substr($recipient, 1);
        }

        $senderId = $senderId ?? $gateway['default_sender_id'] ?? 'ONFON';

        // Build payload
        $payload = [
            'SenderId' => $senderId,
            'MessageParameters' => [
                [
                    'Number' => $recipient,
                    'Text'   => $message
                ]
            ],
            'ApiKey'   => $apiKey,
            'ClientId' => $clientId
        ];

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => rtrim($baseUrl, '/'),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'AccessKey: ' . $accessKey,
                'Accept: application/json'
            ],
            CURLOPT_TIMEOUT        => 20,
            CURLOPT_CONNECTTIMEOUT => 8,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2
        ]);

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

        return processGatewayResponse(
            $response,
            $curlError,
            $httpCode,
            $gateway,
            $recipient,
            $message,
            $senderId,
            $enqueueOnFailure,
            $pdo,
            function ($responseData) use ($httpCode) {
                // OnfonMedia success detection
                if ($httpCode === 200 || $httpCode === 201 || $httpCode === 202) {
                    if (
                        (isset($responseData['Success']) && $responseData['Success'] === true) ||
                        (isset($responseData['success']) && $responseData['success'] === true) ||
                        (isset($responseData['status']) && strtolower($responseData['status']) === 'success') ||
                        (isset($responseData['responseCode']) && $responseData['responseCode'] === '00') ||
                        (isset($responseData['ResponseCode']) && $responseData['ResponseCode'] === '00')
                    ) {
                        return [
                            'success'    => true,
                            'message_id' => $responseData['MessageId'] ?? $responseData['messageId'] ?? $responseData['id'] ?? null,
                            'cost'       => $responseData['Cost'] ?? $responseData['cost'] ?? null,
                            'error'      => null
                        ];
                    }
                }
                
                $errorMsg = 'OnfonMedia API error';
                if (isset($responseData['Message'])) {
                    $errorMsg = $responseData['Message'];
                } elseif (isset($responseData['message'])) {
                    $errorMsg = $responseData['message'];
                } elseif (isset($responseData['error'])) {
                    $errorMsg = $responseData['error'];
                } elseif (isset($responseData['ErrorMessage'])) {
                    $errorMsg = $responseData['ErrorMessage'];
                } elseif (isset($responseData['description'])) {
                    $errorMsg = $responseData['description'];
                }
                
                return [
                    'success' => false,
                    'error'   => $errorMsg
                ];
            }
        );

    } catch (Throwable $e) {
        error_log("OnfonMedia FAILED: " . $e->getMessage());

        if ($enqueueOnFailure) {
            enqueueFailedMessage($pdo, $recipient, $message, $senderId);
        }

        return [
            'success' => false,
            'error'   => $e->getMessage(),
            "type" => "error",
            "message" => $e->getMessage()
        ];
    }
}

/**
 * Codic SMS Gateway with mTLS
 */
function sendViaCustomGateway(
    array $gateway,
    string $recipient,
    string $message,
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    try {
        // Load Codic certs
        if (file_exists(__DIR__ . '/codic_api_certs.php')) {
            require_once __DIR__ . '/codic_api_certs.php';
        } else {
            throw new Exception("Codic certificates file not found");
        }

        if (empty($gateway['base_url'])) {
            throw new Exception("Missing Codic base_url in gateway config");
        }

        $endpoint     = rtrim($gateway['base_url'], '/');
        $username     = $gateway['username'] ?? '';
        $account      = $gateway['api_key'] ?? '';
        $sacco        = $gateway['sacco_id'] ?? '2';
        $alphanumeric = $senderId ?? ($gateway['default_sender_id'] ?? 'QueueFree');
        $trxid        = ($gateway['trx_prefix'] ?? 'TR') . uniqid();
        
        // Get password from gateway config
        $password = $gateway['password'] ?? getenv('CODIC_PASSWORD');
        if (!$password) {
            throw new Exception("Codic password not configured");
        }

        if (!$username || !$account) {
            throw new Exception("Missing Codic credentials: username or account");
        }

        if (!openssl_public_encrypt($password, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            throw new Exception("reg_key encryption failed");
        }
        $regKey = base64_encode($encrypted);

        // Normalize recipient
        $recipient = preg_replace('/^\+/', '', $recipient);

        // Build query
        $query = http_build_query([
            'trx_type'     => 'sms',
            'Sacco'        => $sacco,
            'phone'        => $recipient,
            'message'      => $message,
            'alphanumeric' => $alphanumeric,
            'username'     => $username,
            'account'      => $account,
            'trxid'        => $trxid,
            'reg_key'      => $regKey
        ]);

        $url = $endpoint . "/?" . $query;

        // mTLS using in-memory certs
        $ch = curl_init($url);
        
        $caFile = tmpfile();
        $clientFile = tmpfile();
        $keyFile = tmpfile();
        
        try {
            fwrite($caFile, $ca_cert);
            fwrite($clientFile, $client_pem);
            fwrite($keyFile, $client_key);

            $caPath     = stream_get_meta_data($caFile)['uri'];
            $clientPath = stream_get_meta_data($clientFile)['uri'];
            $keyPath    = stream_get_meta_data($keyFile)['uri'];

            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => 20,
                CURLOPT_CONNECTTIMEOUT => 10,
                CURLOPT_SSL_VERIFYPEER => true,
                CURLOPT_SSL_VERIFYHOST => 2,
                CURLOPT_CAINFO         => $caPath,
                CURLOPT_SSLCERT        => $clientPath,
                CURLOPT_SSLKEY         => $keyPath,
                CURLOPT_SSLKEYPASSWD   => $gateway['ssl_key_password'] ?? 'mvpnaz@1',
                CURLOPT_HTTPGET        => true,
                CURLOPT_VERBOSE        => false
            ]);

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

        } finally {
            curl_close($ch);
            fclose($caFile);
            fclose($clientFile);
            fclose($keyFile);
        }

        return processGatewayResponse(
            $response,
            $curlError,
            $httpCode,
            $gateway,
            $recipient,
            $message,
            $senderId,
            $enqueueOnFailure,
            $pdo,
            function($responseData) use ($httpCode, $trxid, $curlError) {
                // Codic success detection
                if ($httpCode === 200) {
                    if (isset($responseData['result'])) {
                        if (strtolower($responseData['result']) === 'success') {
                            return [
                                'success'    => true,
                                'cost'       => null,
                                'message_id' => $trxid,
                                'error'      => null
                            ];
                        }
                        $errMsg = $responseData['details'] ?? 'Unknown error';
                        return [
                            'success' => false,
                            'error'   => $errMsg
                        ];
                    }
                    
                    if (is_string($responseData)) {
                        if (stripos($responseData, 'success') !== false || 
                            stripos($responseData, 'ok') !== false || 
                            $responseData === '1' || 
                            $responseData === 'true') {
                            return [
                                'success'    => true,
                                'cost'       => null,
                                'message_id' => $trxid,
                                'error'      => null
                            ];
                        }
                    }
                }

                return [
                    'success' => false,
                    'error'   => $curlError ?: "Malformed API response"
                ];
            }
        );

    } catch (Throwable $e) {
        error_log("Codic EXCEPTION: " . $e->getMessage());

        if ($enqueueOnFailure) {
            enqueueFailedMessage($pdo, $recipient, $message, $senderId);
        }

        return [
            'success' => false,
            'error'   => $e->getMessage(),
            "type" => "error",
            "message" => $e->getMessage()
        ];
    }
}

/**
 * Plivo Gateway Implementation
 */
function sendViaPlivo(
    array $gateway, 
    string $recipient, 
    string $message, 
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    $authId = $gateway['api_key'] ?? $gateway['auth_id'];
    $authToken = $gateway['auth_token'] ?? $gateway['api_secret'];
    $senderId = $senderId ?? $gateway['default_sender_id'] ?? null;

    $url = "https://api.plivo.com/v1/Account/{$authId}/Message/";

    $postData = [
        'dst' => $recipient,
        'text' => $message
    ];

    if ($senderId && ($gateway['supports_sender_id'] ?? false)) {
        $postData['src'] = $senderId;
    }

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($postData),
        CURLOPT_USERPWD => "{$authId}:{$authToken}",
        CURLOPT_HTTPHEADER => [
            "Accept: application/json",
            "Content-Type: application/json"
        ],
        CURLOPT_TIMEOUT => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2
    ]);

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

    return processGatewayResponse(
        $response,
        $curlError,
        $httpCode,
        $gateway,
        $recipient,
        $message,
        $senderId,
        $enqueueOnFailure,
        $pdo,
        function($responseData) use ($httpCode) {
            // Plivo success detection
            if ($httpCode >= 200 && $httpCode < 300) {
                if (isset($responseData['message_uuid'][0])) {
                    return [
                        'success' => true,
                        'cost' => $responseData['total_amount'] ?? null,
                        'message_id' => $responseData['message_uuid'][0],
                        'error' => null
                    ];
                }
                
                if (isset($responseData['message_id'])) {
                    return [
                        'success' => true,
                        'cost' => null,
                        'message_id' => $responseData['message_id'],
                        'error' => null
                    ];
                }
            }
            
            return [
                'success' => false,
                'error' => "API Error: " . ($responseData['error'] ?? $responseData['message'] ?? 'Unknown error')
            ];
        }
    );
}

/**
 * InfoBip Gateway Implementation
 */
function sendViaInfoBip(
    array $gateway,
    string $recipient,
    string $message,
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo
): array {
    $apiKey = $gateway['api_key'];
    $baseUrl = $gateway['base_url'] ?? 'https://api.infobip.com';
    $senderId = $senderId ?? $gateway['default_sender_id'] ?? 'InfoSMS';

    $url = rtrim($baseUrl, '/') . '/sms/2/text/advanced';

    $payload = [
        'messages' => [
            [
                'from' => $senderId,
                'destinations' => [
                    ['to' => preg_replace('/^\+/', '', $recipient)]
                ],
                'text' => $message
            ]
        ]
    ];

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload),
        CURLOPT_HTTPHEADER => [
            "Authorization: App $apiKey",
            "Content-Type: application/json",
            "Accept: application/json"
        ],
        CURLOPT_TIMEOUT => 15,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2
    ]);

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

    return processGatewayResponse(
        $response,
        $curlError,
        $httpCode,
        $gateway,
        $recipient,
        $message,
        $senderId,
        $enqueueOnFailure,
        $pdo,
        function($responseData) use ($httpCode) {
            // InfoBip success detection
            if ($httpCode === 200) {
                if (isset($responseData['messages'][0]['status']['groupId']) && 
                    $responseData['messages'][0]['status']['groupId'] === 1) {
                    return [
                        'success' => true,
                        'cost' => $responseData['messages'][0]['price']['pricePerMessage'] ?? null,
                        'message_id' => $responseData['messages'][0]['messageId'] ?? null,
                        'error' => null
                    ];
                }
            }
            
            $errorMsg = 'InfoBip API error';
            if (isset($responseData['requestError']['serviceException']['text'])) {
                $errorMsg = $responseData['requestError']['serviceException']['text'];
            } elseif (isset($responseData['messages'][0]['status']['description'])) {
                $errorMsg = $responseData['messages'][0]['status']['description'];
            }
            
            return [
                'success' => false,
                'error' => $errorMsg
            ];
        }
    );
}

/**
 * Processes gateway responses consistently - FIXED VERSION with proper enqueueing
 */
function processGatewayResponse(
    $response,
    $curlError,
    int $httpCode,
    array $gateway,
    string $recipient,
    string $message,
    ?string $senderId,
    bool $enqueueOnFailure,
    PDO $pdo,
    callable $responseParser
): array {
    $gatewayId = $gateway['id'] ?? null;
    $providerName = $gateway['provider_name'] ?? 'Unknown';
    
    // Check for cURL errors first
    if ($response === false) {
        $errorMessage = "cURL Error: " . $curlError;
        error_log("SMS cURL Error [$providerName]: " . $errorMessage);
        logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage, null, null, $gatewayId);
        
        if ($enqueueOnFailure) {
            enqueueFailedMessage($pdo, $recipient, $message, $senderId);
        }
        
        return [
            "type" => "error", 
            "message" => $errorMessage,
            "success" => false,
            "gateway" => $providerName
        ];
    }

    // Handle non-JSON success responses
    if ($httpCode >= 200 && $httpCode < 300) {
        $responseText = is_string($response) ? $response : '';
        $isValidJson = json_decode($response, true) !== null;
        
        if (!$isValidJson) {
            if (stripos($responseText, 'sent') !== false || 
                stripos($responseText, 'ok') !== false || 
                stripos($responseText, 'success') !== false ||
                stripos($responseText, 'accepted') !== false ||
                stripos($responseText, 'queued') !== false) {
                
                error_log("SMS sent SUCCESSFULLY (non-JSON) to $recipient via $providerName");
                
                logCommunication(
                    'sms', 
                    $recipient, 
                    $senderId, 
                    $message, 
                    'success',
                    null,
                    null,
                    null,
                    $gatewayId,
                    ['http_code' => $httpCode, 'raw_response' => substr($responseText, 0, 200)]
                );
                
                return [
                    "type" => "success", 
                    "message" => "SMS sent successfully!",
                    "success" => true,
                    "cost" => null,
                    "message_id" => null,
                    "gateway" => $providerName
                ];
            }
        }
    }

    // Parse JSON response
    $responseData = json_decode($response, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        $errorMessage = "Invalid JSON response from API: " . substr($response, 0, 200);
        error_log($errorMessage);
        
        // If HTTP code indicates success but JSON is invalid, still consider it a success
        if ($httpCode >= 200 && $httpCode < 300) {
            logCommunication(
                'sms', 
                $recipient, 
                $senderId, 
                $message, 
                'success',
                "Non-JSON response but HTTP $httpCode",
                null,
                null,
                $gatewayId,
                ['http_code' => $httpCode, 'raw_response' => substr($response, 0, 200)]
            );
            
            return [
                "type" => "success", 
                "message" => "SMS sent successfully!",
                "success" => true,
                "cost" => null,
                "message_id" => null,
                "gateway" => $providerName
            ];
        }
        
        logCommunication('sms', $recipient, $senderId, $message, 'failed', $errorMessage, null, null, $gatewayId);
        
        if ($enqueueOnFailure) {
            enqueueFailedMessage($pdo, $recipient, $message, $senderId);
        }
        
        return [
            "type" => "error", 
            "message" => $errorMessage,
            "success" => false,
            "gateway" => $providerName
        ];
    }

    // Parse the response using gateway-specific parser
    $result = $responseParser($responseData);
    
    // Check success status properly
    if (isset($result['success']) && $result['success'] === true) {
        error_log("SMS sent SUCCESSFULLY to $recipient via $providerName, MessageID: " . ($result['message_id'] ?? 'N/A'));
        
        logCommunication(
            'sms', 
            $recipient, 
            $senderId, 
            $message, 
            'success',
            null,
            $result['cost'] ?? null, 
            $result['message_id'] ?? null,
            $gatewayId,
            ['response' => $responseData, 'http_code' => $httpCode]
        );
        
        return [
            "type" => "success", 
            "message" => "SMS sent successfully!",
            "success" => true,
            "cost" => $result['cost'] ?? null,
            "message_id" => $result['message_id'] ?? null,
            "gateway" => $providerName
        ];
    }
    
    // Check HTTP code as final fallback for success
    if ($httpCode >= 200 && $httpCode < 300) {
        error_log("SMS sent SUCCESSFULLY (HTTP $httpCode) to $recipient via $providerName");
        
        logCommunication(
            'sms', 
            $recipient, 
            $senderId, 
            $message, 
            'success',
            null,
            null,
            null,
            $gatewayId,
            ['response' => $responseData, 'http_code' => $httpCode]
        );
        
        return [
            "type" => "success", 
            "message" => "SMS sent successfully!",
            "success" => true,
            "cost" => null,
            "message_id" => null,
            "gateway" => $providerName
        ];
    }
    
    // FAILURE - Log as failed
    $errorMsg = $result['error'] ?? 'Unknown API error';
    error_log("SMS sending FAILED to $recipient via $providerName: " . $errorMsg);
    
    logCommunication(
        'sms', 
        $recipient, 
        $senderId, 
        $message, 
        'failed', 
        $errorMsg,
        null, 
        null,
        $gatewayId,
        ['response' => $responseData, 'http_code' => $httpCode, 'api_response' => $responseData]
    );
    
    if ($enqueueOnFailure) {
        enqueueFailedMessage($pdo, $recipient, $message, $senderId);
    }
    
    return [
        "type" => "error", 
        "message" => $errorMsg,
        "success" => false,
        "gateway" => $providerName,
        "error_details" => $errorMsg
    ];
}

/**
 * Enqueues failed SMS for later retry - ENHANCED VERSION
 */
function enqueueFailedMessage(PDO $pdo, string $recipient, string $message, ?string $senderId): void
{
    try {
        // Check if table exists
        $stmt = $pdo->query("SHOW TABLES LIKE 'sms_queue'");
        if ($stmt->rowCount() === 0) {
            error_log("sms_queue table does not exist, skipping enqueue");
            return;
        }
        
        // Start transaction
        if (!$pdo->inTransaction()) {
            $pdo->beginTransaction();
            $transactionStarted = true;
        } else {
            $transactionStarted = false;
        }
        
        // Check if message already exists in queue (avoid duplicates)
        $checkStmt = $pdo->prepare("
            SELECT id, attempt_count, status FROM sms_queue 
            WHERE recipient = :recipient 
            AND message = :message 
            AND (sender_id = :sender_id OR (sender_id IS NULL AND :sender_id IS NULL))
            AND status IN ('pending', 'failed', 'processing')
            AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)
            LIMIT 1
        ");
        $checkStmt->execute([
            ':recipient' => $recipient,
            ':message' => $message,
            ':sender_id' => $senderId
        ]);
        
        $existing = $checkStmt->fetch(PDO::FETCH_ASSOC);
        
        if ($existing) {
            // Update existing record instead of creating duplicate
            $nextAttemptInterval = pow(2, min($existing['attempt_count'] + 1, 5));
            
            $updateStmt = $pdo->prepare("
                UPDATE sms_queue 
                SET attempt_count = attempt_count + 1,
                    last_attempt = NOW(),
                    next_attempt = DATE_ADD(NOW(), INTERVAL :interval MINUTE),
                    status = 'pending',
                    error_message = 'Retry scheduled',
                    updated_at = NOW()
                WHERE id = :id
            ");
            $updateStmt->execute([
                ':id' => $existing['id'],
                ':interval' => $nextAttemptInterval
            ]);
            error_log("Updated existing queue entry for $recipient, attempt: " . ($existing['attempt_count'] + 1) . ", next attempt in $nextAttemptInterval minutes");
        } else {
            // Insert new record
            $insertStmt = $pdo->prepare("
                INSERT INTO sms_queue 
                (recipient, message, sender_id, attempt_count, last_attempt, next_attempt, status, created_at, updated_at) 
                VALUES 
                (:recipient, :message, :sender_id, 0, NULL, DATE_ADD(NOW(), INTERVAL 5 MINUTE), 'pending', NOW(), NOW())
            ");
            $insertStmt->execute([
                ':recipient' => $recipient,
                ':message' => $message,
                ':sender_id' => $senderId
            ]);
            error_log("New SMS enqueued for retry: $recipient, ID: " . $pdo->lastInsertId());
        }
        
        if ($transactionStarted) {
            $pdo->commit();
        }
        
    } catch (Exception $e) {
        if (isset($transactionStarted) && $transactionStarted && $pdo->inTransaction()) {
            $pdo->rollBack();
        }
        error_log("Failed to enqueue SMS for $recipient: " . $e->getMessage());
        // Log the error but don't throw - we don't want to break the main flow
    }
}

/**
 * Process queued SMS messages (call from cron job)
 */
function processSmsQueue(PDO $pdo, int $limit = 50): array
{
    $results = [
        'processed' => 0,
        'success' => 0,
        'failed' => 0,
        'errors' => []
    ];
    
    try {
        $pdo->beginTransaction();
        
        // Select messages ready to send
        $selectStmt = $pdo->prepare("
            SELECT * FROM sms_queue 
            WHERE status = 'pending' 
            AND (next_attempt IS NULL OR next_attempt <= NOW())
            AND attempt_count < 5
            ORDER BY next_attempt ASC 
            LIMIT :limit
            FOR UPDATE
        ");
        $selectStmt->execute([':limit' => $limit]);
        $messages = $selectStmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Mark as processing
        foreach ($messages as $message) {
            $updateStmt = $pdo->prepare("
                UPDATE sms_queue 
                SET status = 'processing', updated_at = NOW() 
                WHERE id = :id
            ");
            $updateStmt->execute([':id' => $message['id']]);
        }
        
        $pdo->commit();
        
        // Process each message
        foreach ($messages as $message) {
            try {
                $result = sendSms(
                    $pdo, 
                    $message['recipient'], 
                    $message['message'], 
                    $message['sender_id'],
                    false // Don't re-enqueue on failure to avoid loops
                );
                
                $pdo->beginTransaction();
                
                if ($result['type'] === 'success') {
                    // Message sent successfully
                    $deleteStmt = $pdo->prepare("DELETE FROM sms_queue WHERE id = :id");
                    $deleteStmt->execute([':id' => $message['id']]);
                    $results['success']++;
                    error_log("Queue message {$message['id']} sent successfully");
                } else {
                    // Failed, update attempt count
                    $nextAttemptInterval = pow(2, min($message['attempt_count'] + 1, 5));
                    
                    $updateStmt = $pdo->prepare("
                        UPDATE sms_queue 
                        SET status = 'failed',
                            attempt_count = attempt_count + 1,
                            last_attempt = NOW(),
                            next_attempt = DATE_ADD(NOW(), INTERVAL :interval MINUTE),
                            error_message = :error,
                            updated_at = NOW()
                        WHERE id = :id
                    ");
                    $updateStmt->execute([
                        ':id' => $message['id'],
                        ':interval' => $nextAttemptInterval,
                        ':error' => $result['message'] ?? 'Unknown error'
                    ]);
                    $results['failed']++;
                    $results['errors'][] = "Message {$message['id']}: {$result['message']}";
                    error_log("Queue message {$message['id']} failed, next attempt in $nextAttemptInterval minutes");
                }
                
                $pdo->commit();
                $results['processed']++;
                
            } catch (Exception $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                error_log("Queue processing error for message {$message['id']}: " . $e->getMessage());
                $results['errors'][] = "Message {$message['id']}: " . $e->getMessage();
            }
        }
        
    } catch (Exception $e) {
        if ($pdo->inTransaction()) {
            $pdo->rollBack();
        }
        error_log("SMS queue processing failed: " . $e->getMessage());
        $results['errors'][] = $e->getMessage();
    }
    
    return $results;
}

/**
 * Debug function to check recent logs
 */
function debugSmsLogs(PDO $pdo, int $limit = 10): array
{
    try {
        $stmt = $pdo->prepare("
            SELECT id, created_at, recipient, subject as sender_id, status, message_id, error_message, gateway_id
            FROM communication_logs 
            WHERE type = 'sms'
            ORDER BY id DESC 
            LIMIT :limit
        ");
        $stmt->execute([':limit' => $limit]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        error_log("Failed to debug logs: " . $e->getMessage());
        return [];
    }
}

/**
 * Debug function to check queue status
 */
function debugQueueStatus(PDO $pdo): array
{
    try {
        // Check if table exists
        $stmt = $pdo->query("SHOW TABLES LIKE 'sms_queue'");
        if ($stmt->rowCount() === 0) {
            return ['error' => 'sms_queue table does not exist'];
        }
        
        $stmt = $pdo->query("
            SELECT status, COUNT(*) as count 
            FROM sms_queue 
            GROUP BY status
        ");
        $stats = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        $stmt = $pdo->prepare("
            SELECT id, recipient, attempt_count, status, error_message, created_at, next_attempt 
            FROM sms_queue 
            WHERE status != 'success' 
            ORDER BY created_at DESC 
            LIMIT 10
        ");
        $stmt->execute();
        $recent = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        return [
            'statistics' => $stats,
            'recent_pending' => $recent,
            'total_pending' => array_sum(array_column($stats, 'count'))
        ];
    } catch (PDOException $e) {
        return ['error' => $e->getMessage()];
    }
}

/**
 * Get gateway statistics
 */
function getGatewayStats(PDO $pdo): array
{
    try {
        $stmt = $pdo->query("
            SELECT 
                g.id,
                g.provider_name,
                g.is_active,
                g.priority,
                g.usage_count,
                COUNT(l.id) as total_attempts,
                SUM(CASE WHEN l.status = 'success' THEN 1 ELSE 0 END) as successful,
                SUM(CASE WHEN l.status = 'failed' THEN 1 ELSE 0 END) as failed,
                AVG(CASE WHEN l.metadata LIKE '%response_time%' THEN 
                    JSON_EXTRACT(l.metadata, '$.response_time') ELSE NULL END) as avg_response_time
            FROM sms_gateways g
            LEFT JOIN communication_logs l ON g.id = l.gateway_id AND l.type = 'sms'
            WHERE l.created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR) OR l.id IS NULL
            GROUP BY g.id
            ORDER BY g.priority DESC
        ");
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        error_log("Failed to get gateway stats: " . $e->getMessage());
        return [];
    }
}

/**
 * Manual retry for failed messages
 */
function retryFailedMessage(PDO $pdo, int $queueId): array
{
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM sms_queue 
            WHERE id = :id AND status IN ('failed', 'pending')
        ");
        $stmt->execute([':id' => $queueId]);
        $message = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$message) {
            return ['success' => false, 'message' => 'Message not found or already processed'];
        }
        
        // Reset the message for retry
        $updateStmt = $pdo->prepare("
            UPDATE sms_queue 
            SET status = 'pending',
                next_attempt = NOW(),
                error_message = NULL,
                updated_at = NOW()
            WHERE id = :id
        ");
        $updateStmt->execute([':id' => $queueId]);
        
        return ['success' => true, 'message' => 'Message queued for retry'];
        
    } catch (Exception $e) {
        return ['success' => false, 'message' => $e->getMessage()];
    }
}