<?php
/**
 * M-Pesa Helper Functions
 * Handles M-Pesa STK Push integration for payment processing
 */

/**
 * Get M-Pesa credentials from system settings
 */
function getMpesaCredentials($conn) {
    $settings_query = "SELECT setting_key, setting_value FROM system_settings 
                      WHERE setting_key IN ('mpesa_consumer_key', 'mpesa_consumer_secret', 
                                           'mpesa_shortcode', 'mpesa_passkey', 'mpesa_environment')";
    $result = $conn->query($settings_query);
    
    $credentials = [];
    while ($row = $result->fetch_assoc()) {
        $credentials[$row['setting_key']] = $row['setting_value'];
    }
    
    // Validate required credentials
    $required = ['mpesa_consumer_key', 'mpesa_consumer_secret', 'mpesa_shortcode', 'mpesa_passkey'];
    foreach ($required as $key) {
        if (empty($credentials[$key])) {
            return false;
        }
    }
    
    // Set default environment if not specified
    if (empty($credentials['mpesa_environment'])) {
        $credentials['mpesa_environment'] = 'sandbox';
    }
    
    return $credentials;
}

/**
 * Get M-Pesa access token
 */
function getMpesaAccessToken($credentials) {
    $environment = $credentials['mpesa_environment'] ?? 'sandbox';
    
    if ($environment === 'production') {
        $url = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
    } else {
        $url = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
    }
    
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => $url,
        CURLOPT_HTTPHEADER => [
            'Authorization: Basic ' . base64_encode($credentials['mpesa_consumer_key'] . ':' . $credentials['mpesa_consumer_secret']),
            'Content-Type: application/json'
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT => 30
    ]);
    
    $response = curl_exec($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);
    
    if ($httpCode === 200) {
        $data = json_decode($response, true);
        return $data['access_token'] ?? false;
    }
    
    return false;
}

/**
 * Format phone number to M-Pesa format (254XXXXXXXXX)
 */
function formatPhoneNumber($phoneNumber) {
    // Remove any non-numeric characters
    $phoneNumber = preg_replace('/\D/', '', $phoneNumber);
    
    // Handle different formats
    if (strlen($phoneNumber) === 9 && substr($phoneNumber, 0, 1) === '7') {
        // 7XXXXXXXX format
        return '254' . $phoneNumber;
    } elseif (strlen($phoneNumber) === 10 && substr($phoneNumber, 0, 2) === '07') {
        // 07XXXXXXXX format
        return '254' . substr($phoneNumber, 1);
    } elseif (strlen($phoneNumber) === 12 && substr($phoneNumber, 0, 3) === '254') {
        // 254XXXXXXXXX format
        return $phoneNumber;
    } elseif (strlen($phoneNumber) === 13 && substr($phoneNumber, 0, 4) === '+254') {
        // +254XXXXXXXXX format
        return substr($phoneNumber, 1);
    }
    
    // If none of the above, assume it's already in correct format or invalid
    return $phoneNumber;
}

/**
 * Generate timestamp for M-Pesa
 */
function getMpesaTimestamp() {
    return date('YmdHis');
}

/**
 * Generate M-Pesa password
 */
function generateMpesaPassword($shortcode, $passkey, $timestamp) {
    return base64_encode($shortcode . $passkey . $timestamp);
}

/**
 * Initiate M-Pesa STK Push
 */
function initiateMpesaSTKPush($credentials, $phoneNumber, $amount, $accountReference, $transactionDesc = null) {
    try {
        // Get access token
        $accessToken = getMpesaAccessToken($credentials);
        if (!$accessToken) {
            return ['success' => false, 'message' => 'Failed to get M-Pesa access token'];
        }
        
        // Format phone number
        $phoneNumber = formatPhoneNumber($phoneNumber);
        
        // Generate timestamp and password
        $timestamp = getMpesaTimestamp();
        $password = generateMpesaPassword($credentials['mpesa_shortcode'], $credentials['mpesa_passkey'], $timestamp);
        
        // Set callback URLs
        $environment = $credentials['mpesa_environment'] ?? 'sandbox';
        $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
        
        $callbackUrl = $baseUrl . '/mpesa_callback.php';
        
        // Prepare STK Push URL
        if ($environment === 'production') {
            $stkUrl = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
        } else {
            $stkUrl = 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
        }
        
        // Prepare request data
        $requestData = [
            'BusinessShortCode' => $credentials['mpesa_shortcode'],
            'Password' => $password,
            'Timestamp' => $timestamp,
            'TransactionType' => 'CustomerPayBillOnline',
            'Amount' => $amount,
            'PartyA' => $phoneNumber,
            'PartyB' => $credentials['mpesa_shortcode'],
            'PhoneNumber' => $phoneNumber,
            'CallBackURL' => $callbackUrl,
            'AccountReference' => $accountReference,
            'TransactionDesc' => $transactionDesc ?? "Payment for $accountReference"
        ];
        
        // Make STK Push request
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $stkUrl,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($requestData),
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_CONNECTTIMEOUT => 30,
            CURLOPT_TIMEOUT => 30
        ]);
        
        $response = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        
        if ($httpCode === 200) {
            $data = json_decode($response, true);
            
            if (isset($data['ResponseCode']) && $data['ResponseCode'] === '0') {
                return [
                    'success' => true,
                    'message' => 'STK Push sent successfully',
                    'MerchantRequestID' => $data['MerchantRequestID'],
                    'CheckoutRequestID' => $data['CheckoutRequestID'],
                    'ResponseCode' => $data['ResponseCode'],
                    'ResponseDescription' => $data['ResponseDescription']
                ];
            } else {
                return [
                    'success' => false,
                    'message' => $data['ResponseDescription'] ?? 'STK Push failed'
                ];
            }
        } else {
            return [
                'success' => false,
                'message' => 'HTTP Error: ' . $httpCode
            ];
        }
        
    } catch (Exception $e) {
        return [
            'success' => false,
            'message' => 'Exception: ' . $e->getMessage()
        ];
    }
}

/**
 * Query STK Push status
 */
function queryMpesaSTKStatus($credentials, $checkoutRequestId) {
    try {
        // Get access token
        $accessToken = getMpesaAccessToken($credentials);
        if (!$accessToken) {
            return ['success' => false, 'message' => 'Failed to get M-Pesa access token'];
        }
        
        // Generate timestamp and password
        $timestamp = getMpesaTimestamp();
        $password = generateMpesaPassword($credentials['mpesa_shortcode'], $credentials['mpesa_passkey'], $timestamp);
        
        // Prepare query URL
        $environment = $credentials['mpesa_environment'] ?? 'sandbox';
        if ($environment === 'production') {
            $queryUrl = 'https://api.safaricom.co.ke/mpesa/stkpushquery/v1/query';
        } else {
            $queryUrl = 'https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query';
        }
        
        // Prepare request data
        $requestData = [
            'BusinessShortCode' => $credentials['mpesa_shortcode'],
            'Password' => $password,
            'Timestamp' => $timestamp,
            'CheckoutRequestID' => $checkoutRequestId
        ];
        
        // Make query request
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $queryUrl,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($requestData),
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_CONNECTTIMEOUT => 30,
            CURLOPT_TIMEOUT => 30
        ]);
        
        $response = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        
        if ($httpCode === 200) {
            $data = json_decode($response, true);
            return [
                'success' => true,
                'data' => $data
            ];
        } else {
            return [
                'success' => false,
                'message' => 'HTTP Error: ' . $httpCode
            ];
        }
        
    } catch (Exception $e) {
        return [
            'success' => false,
            'message' => 'Exception: ' . $e->getMessage()
        ];
    }
}

/**
 * Validate M-Pesa callback signature
 */
function validateMpesaCallback($headers, $body, $credentials) {
    // This is a basic validation - you might want to implement more robust validation
    // based on your security requirements
    
    // Check if the request contains expected M-Pesa fields
    $data = json_decode($body, true);
    
    if (!isset($data['Body']['stkCallback'])) {
        return false;
    }
    
    return true;
}

/**
 * Process M-Pesa callback
 */
function processMpesaCallback($callbackData, $conn) {
    try {
        $stkCallback = $callbackData['Body']['stkCallback'];
        $checkoutRequestId = $stkCallback['CheckoutRequestID'];
        $resultCode = $stkCallback['ResultCode'];
        
        // Update transaction status in database
        if ($resultCode == 0) {
            // Payment successful
            $callbackMetadata = $stkCallback['CallbackMetadata']['Item'];
            
            $mpesaReceiptNumber = '';
            $transactionDate = '';
            $phoneNumber = '';
            $amount = 0;
            
            // Extract callback metadata
            foreach ($callbackMetadata as $item) {
                switch ($item['Name']) {
                    case 'MpesaReceiptNumber':
                        $mpesaReceiptNumber = $item['Value'];
                        break;
                    case 'TransactionDate':
                        $transactionDate = $item['Value'];
                        break;
                    case 'PhoneNumber':
                        $phoneNumber = $item['Value'];
                        break;
                    case 'Amount':
                        $amount = $item['Value'];
                        break;
                }
            }
            
            // Convert transaction date
            $formattedDate = null;
            if ($transactionDate) {
                $dateTime = DateTime::createFromFormat('YmdHis', $transactionDate);
                $formattedDate = $dateTime ? $dateTime->format('Y-m-d H:i:s') : null;
            }
            
            // Update transaction
            $update_query = "UPDATE mpesa_transactions 
                           SET status = 'COMPLETED', 
                               mpesa_receipt_number = ?, 
                               transaction_date = ?,
                               callback_data = ?,
                               updated_at = NOW()
                           WHERE checkout_request_id = ?";
            
            $stmt = $conn->prepare($update_query);
            $callbackJson = json_encode($callbackData);
            $stmt->bind_param("ssss", $mpesaReceiptNumber, $formattedDate, $callbackJson, $checkoutRequestId);
            $stmt->execute();
            
            // Get transaction details
            $transaction_query = "SELECT * FROM mpesa_transactions WHERE checkout_request_id = ?";
            $stmt_trans = $conn->prepare($transaction_query);
            $stmt_trans->bind_param("s", $checkoutRequestId);
            $stmt_trans->execute();
            $transaction = $stmt_trans->get_result()->fetch_assoc();
            
            if ($transaction) {
                // Create payment record using the ProcessPayment procedure
                $process_payment = "CALL ProcessPayment(?, ?, 'MOBILE_MONEY', ?, ?, 1)";
                $stmt_payment = $conn->prepare($process_payment);
                $stmt_payment->bind_param("idss", 
                    $transaction['visit_id'], 
                    $amount, 
                    $mpesaReceiptNumber, 
                    $phoneNumber
                );
                $stmt_payment->execute();
                
                // Update visit amounts
                $visit_query = "SELECT * FROM patient_visits WHERE visit_id = ?";
                $stmt_visit = $conn->prepare($visit_query);
                $stmt_visit->bind_param("i", $transaction['visit_id']);
                $stmt_visit->execute();
                $visit = $stmt_visit->get_result()->fetch_assoc();
                
                if ($visit) {
                    $new_amount_paid = $visit['amount_paid'] + $amount;
                    $new_balance = $visit['total_amount'] - $new_amount_paid;
                    
                    $update_visit = "UPDATE patient_visits 
                                    SET amount_paid = ?, 
                                        balance = ?, 
                                        status = CASE WHEN ? <= 0 THEN 'COMPLETED' ELSE status END
                                    WHERE visit_id = ?";
                    $stmt_update_visit = $conn->prepare($update_visit);
                    $stmt_update_visit->bind_param("dddi", $new_amount_paid, $new_balance, $new_balance, $transaction['visit_id']);
                    $stmt_update_visit->execute();
                }
            }
            
        } else {
            // Payment failed
            $update_query = "UPDATE mpesa_transactions 
                           SET status = 'FAILED', 
                               callback_data = ?,
                               updated_at = NOW()
                           WHERE checkout_request_id = ?";
            
            $stmt = $conn->prepare($update_query);
            $callbackJson = json_encode($callbackData);
            $stmt->bind_param("ss", $callbackJson, $checkoutRequestId);
            $stmt->execute();
        }
        
        return ['success' => true];
        
    } catch (Exception $e) {
        error_log("M-Pesa callback processing error: " . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage()];
    }
}

/**
 * Get M-Pesa transaction by checkout request ID
 */
function getMpesaTransaction($conn, $checkoutRequestId) {
    $query = "SELECT * FROM mpesa_transactions WHERE checkout_request_id = ?";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("s", $checkoutRequestId);
    $stmt->execute();
    return $stmt->get_result()->fetch_assoc();
}

/**
 * Log M-Pesa API calls for debugging
 */
function logMpesaApiCall($type, $data, $response = null) {
    $logData = [
        'timestamp' => date('Y-m-d H:i:s'),
        'type' => $type,
        'data' => $data,
        'response' => $response
    ];
    
    // Log to file (make sure logs directory exists and is writable)
    $logFile = 'logs/mpesa_' . date('Y-m-d') . '.log';
    file_put_contents($logFile, json_encode($logData) . "\n", FILE_APPEND | LOCK_EX);
}

/**
 * Generate M-Pesa account reference
 */
function generateMpesaAccountReference($visitNumber, $patientNumber = null) {
    if ($patientNumber) {
        return substr($visitNumber . '-' . $patientNumber, 0, 20); // Max 20 characters
    }
    return substr($visitNumber, 0, 20);
}

/**
 * Check if M-Pesa credentials are configured
 */
function isMpesaConfigured($conn) {
    $credentials = getMpesaCredentials($conn);
    return $credentials !== false;
}

/**
 * Get M-Pesa shortcode from settings
 */
function getMpesaShortcode($conn) {
    $query = "SELECT setting_value FROM system_settings WHERE setting_key = 'mpesa_shortcode'";
    $result = $conn->query($query);
    $row = $result->fetch_assoc();
    return $row ? $row['setting_value'] : null;
}

/**
 * Format M-Pesa amount (ensure it's a valid amount)
 */
function formatMpesaAmount($amount) {
    $amount = floatval($amount);
    return max(1, $amount); // Minimum amount is 1 KES
}

/**
 * Validate M-Pesa phone number
 */
function isValidMpesaPhoneNumber($phoneNumber) {
    $formatted = formatPhoneNumber($phoneNumber);
    
    // Check if it's a valid Kenyan mobile number
    if (strlen($formatted) === 12 && substr($formatted, 0, 3) === '254') {
        $prefix = substr($formatted, 3, 1);
        return in_array($prefix, ['7', '1']); // Safaricom and Airtel
    }
    
    return false;
}

/**
 * Get M-Pesa transaction fees (if applicable)
 */
function getMpesaTransactionFee($amount) {
    // M-Pesa transaction fees structure (as of 2024)
    // Note: These fees are usually charged to the customer, not the merchant
    if ($amount <= 100) return 0;
    if ($amount <= 500) return 1;
    if ($amount <= 1000) return 5;
    if ($amount <= 1500) return 8;
    if ($amount <= 2500) return 13;
    if ($amount <= 3500) return 18;
    if ($amount <= 5000) return 25;
    if ($amount <= 7500) return 35;
    if ($amount <= 10000) return 40;
    if ($amount <= 15000) return 50;
    if ($amount <= 20000) return 55;
    if ($amount <= 25000) return 60;
    if ($amount <= 30000) return 65;
    if ($amount <= 35000) return 70;
    if ($amount <= 40000) return 75;
    if ($amount <= 45000) return 80;
    if ($amount <= 50000) return 85;
    
    return 100; // For amounts above 50,000
}

/**
 * Convert M-Pesa timestamp to readable format
 */
function formatMpesaTimestamp($timestamp) {
    if (strlen($timestamp) === 14) {
        $dateTime = DateTime::createFromFormat('YmdHis', $timestamp);
        return $dateTime ? $dateTime->format('Y-m-d H:i:s') : $timestamp;
    }
    return $timestamp;
}
?>