<?php
//mpesa-stk-member-api.php
require_once __DIR__ . '/includes/cors.php';
configureCors();
require_once __DIR__ . '/includes/config_loader.php';
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/audit_helper.php';
require_once __DIR__ . '/email.php';
require_once __DIR__ . '/STKPushPaymentProcessor.php';
require_once __DIR__ . '/c2b/c2b_config.php';

//============================================================================
//require_once __DIR__ .'../../backend-main/includes/sms_helper.php';




/* ================= SESSION ================= */
if (session_status() === PHP_SESSION_NONE) {
    session_start([
        'cookie_httponly' => true,
        'cookie_secure'  => true,     // HTTPS REQUIRED
        'cookie_samesite'=> 'None'
    ]);
}

/* ================= CORS HEADERS ================= */
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, X-Session-ID');
header('Content-Type: application/json');

/* ================= SECURITY HEADERS ================= */
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');

/* ================= PREFLIGHT ================= */
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

/* ================= DEBUG (DEV ONLY) ================= */
if (ENVIRONMENT === 'development') {
    error_log("Session ID: " . session_id());
    error_log("Session data: " . print_r($_SESSION, true));
    error_log("Cookies: " . print_r($_COOKIE, true));
}

/* ================= AUTH CHECK ================= */
if (empty($_SESSION['member_id'])) {
    http_response_code(401);
    echo json_encode([
        'success' => false,
        'error'   => 'Unauthorized - Please login first',
        'code'    => 'SESSION_EXPIRED'
    ]);
    exit;
}

/* ================= LOAD DEPENDENCIES ================= */


/* ================= REQUEST DATA ================= */
$method    = $_SERVER['REQUEST_METHOD'];
$member_id = $_SESSION['member_id'];
$action    = $_GET['action'] ?? '';
//=============================================================================


// Emergency fix: Define STK callback URL if missing
if (!defined('C2B_MPESA_STK_CALLBACK_URL')) {
    $baseUrl = defined('MPESA_BASE_URL') ? MPESA_BASE_URL : 'https://cbs.creativewebsolutions.co.ke';
    define('C2B_MPESA_STK_CALLBACK_URL', $baseUrl . '/stk_callback.php');
    
    // Log warning
    error_log("NOTICE: C2B_MPESA_STK_CALLBACK_URL was not defined. Using: " . C2B_MPESA_STK_CALLBACK_URL);
}

// Set timezone to Africa/Nairobi
date_default_timezone_set('Africa/Nairobi');

// Set content type
header('Content-Type: application/json; charset=UTF-8');

// Enhanced logging function
function logMemberSTKApiEvent($message, $level = 'INFO', $context = []) {
    $timestamp = date('Y-m-d H:i:s');
    $logMessage = "[$timestamp] [$level] $message";
    
    if (!empty($context)) {
        $logMessage .= " | Context: " . json_encode($context);
    }
    
    $logMessage .= PHP_EOL;
    
    // Ensure logs directory exists
    if (!is_dir('./logs')) {
        mkdir('./logs', 0755, true);
    }
    
    // Log to file
    error_log($logMessage, 3, './logs/member_stk_api_debug.log');
    
    if ($level === 'ERROR') {
        error_log("MEMBER_STK_API_ERROR: $message");
    }
}

function sendJsonResponse($success, $data = null, $error = null, $statusCode = 200) {
    http_response_code($statusCode);
    $response = ['success' => $success];
    if ($data !== null) $response['data'] = $data;
    if ($error !== null) $response['error'] = $error;
    echo json_encode($response);
    exit;
}

/**
 * Phone Number Formatter Class
 */
class PhoneNumberFormatter {
    public static function formatPhoneNumber($phone) {
        // Remove all non-digit characters
        $phone = preg_replace('/[^0-9]/', '', $phone);
        
        // Handle different formats
        if (strlen($phone) === 9 && substr($phone, 0, 1) === '7') {
            // Format: 712345678 -> 254712345678
            return '254' . $phone;
        } elseif (strlen($phone) === 10 && substr($phone, 0, 1) === '0') {
            // Format: 0712345678 -> 254712345678
            return '254' . substr($phone, 1);
        } elseif (strlen($phone) === 12 && substr($phone, 0, 3) === '254') {
            // Format: 254712345678 -> 254712345678 (already correct)
            return $phone;
        } else {
            // Return as is for validation to handle
            return $phone;
        }
    }
    
    public static function validatePhoneNumber($phone) {
        $formatted = self::formatPhoneNumber($phone);
        return preg_match('/^2547\d{8}$/', $formatted);
    }
    
    public static function getDisplayFormat($phone) {
        $formatted = self::formatPhoneNumber($phone);
        if (preg_match('/^2547(\d{8})$/', $formatted, $matches)) {
            return '0' . $matches[1];
        }
        return $phone;
    }
}

/**
 * M-Pesa Integration Class
 */
class MpesaIntegration {
    private $consumerKey;
    private $consumerSecret;
    private $env;
    private $baseUrl;
    private $callbackUrl;
    private $shortCode;
    private $passKey;

    public function __construct() {
        // Use C2B config constants
        $this->consumerKey = C2B_MPESA_CONSUMER_KEY;
        $this->consumerSecret = C2B_MPESA_CONSUMER_SECRET;
        $this->env = C2B_MPESA_ENVIRONMENT;
        $this->shortCode = C2B_MPESA_SHORTCODE;
        $this->passKey = C2B_MPESA_PASSKEY;
        $this->callbackUrl = C2B_MPESA_STK_CALLBACK_URL;
        
        $this->baseUrl = $this->env === 'sandbox' 
            ? 'https://sandbox.safaricom.co.ke' 
            : 'https://api.safaricom.co.ke';
        
        // Validate required configuration
        $this->validateConfig();
    }
    
    private function validateConfig() {
        $required = [
            'consumerKey' => $this->consumerKey,
            'consumerSecret' => $this->consumerSecret,
            'shortCode' => $this->shortCode,
            'passKey' => $this->passKey
        ];
        
        $missing = [];
        foreach ($required as $key => $value) {
            if (empty($value)) {
                $missing[] = $key;
            }
        }
        
        if (!empty($missing)) {
            throw new Exception("Missing required M-Pesa configuration: " . implode(', ', $missing) . ". Please check your .env file.");
        }
    }

    private function getAccessToken() {
        $url = $this->baseUrl . '/oauth/v1/generate?grant_type=client_credentials';
        $credentials = base64_encode($this->consumerKey . ':' . $this->consumerSecret);
        
        $headers = ['Authorization: Basic ' . $credentials];

        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $url,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_TIMEOUT => 30
        ]);

        $response = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $error = curl_error($curl);
        curl_close($curl);

        if ($status != 200) {
            throw new Exception('Failed to get access token. Status: ' . $status . ' Error: ' . $error . ' URL: ' . $url);
        }

        $result = json_decode($response);
        if (!$result || !isset($result->access_token)) {
            throw new Exception('Invalid response from access token request: ' . $response);
        }
        
        return $result->access_token;
    }

    public function stkPush($phoneNumber, $amount, $accountReference, $transactionDesc) {
        $timestamp = date('YmdHis');
        $password = base64_encode($this->shortCode . $this->passKey . $timestamp);

        $payload = [
            'BusinessShortCode' => $this->shortCode,
            'Password' => $password,
            'Timestamp' => $timestamp,
            'TransactionType' => 'CustomerPayBillOnline',
            'Amount' => $amount,
            'PartyA' => $phoneNumber,
            'PartyB' => $this->shortCode,
            'PhoneNumber' => $phoneNumber,
            'CallBackURL' => $this->callbackUrl,
            'AccountReference' => $accountReference,
            'TransactionDesc' => $transactionDesc
        ];

        $url = $this->baseUrl . '/mpesa/stkpush/v1/processrequest';
        $response = $this->makeApiRequest($url, $payload);
        
        return [
            'MerchantRequestID' => $response['MerchantRequestID'] ?? '',
            'CheckoutRequestID' => $response['CheckoutRequestID'] ?? '',
            'ResponseCode' => $response['ResponseCode'] ?? '0',
            'ResponseDescription' => $response['ResponseDescription'] ?? 'Success. Request accepted for processing',
            'CustomerMessage' => $response['CustomerMessage'] ?? 'Success. Request accepted for processing'
        ];
    }

    private function makeApiRequest($url, $payload) {
        $token = $this->getAccessToken();
        $headers = [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json'
        ];

        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $url,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_TIMEOUT => 30
        ]);

        $response = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $error = curl_error($curl);
        curl_close($curl);

        if ($status != 200) {
            // Log detailed error information
            error_log("M-Pesa API Request Failed:");
            error_log("Status: " . $status);
            error_log("Error: " . $error);
            error_log("URL: " . $url);
            
            throw new Exception('M-Pesa API request failed. Status: ' . $status . ' Error: ' . $error);
        }

        $result = json_decode($response, true);
        if (!$result) {
            throw new Exception('Invalid JSON response from M-Pesa API: ' . $response);
        }

        return $result;
    }
}

/**
 * Database Wrapper Class
 */
class Database {
    private $pdo;

    public function __construct($pdo) {
        $this->pdo = $pdo;
    }

    public function fetchAll($sql, $params = []) {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function fetchOne($sql, $params = []) {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    public function execute($sql, $params = []) {
        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute($params);
    }

    public function beginTransaction() {
        return $this->pdo->beginTransaction();
    }

    public function commit() {
        return $this->pdo->commit();
    }

    public function rollBack() {
        return $this->pdo->rollBack();
    }

    public function lastInsertId() {
        return $this->pdo->lastInsertId();
    }
}

/**
 * Member M-Pesa Transaction Handler
 */
class MemberMpesaTransactionHandler {
    private $db;
    private $stkProcessor;

    public function __construct(Database $db, $stkProcessor = null) {
        $this->db = $db;
        $this->stkProcessor = $stkProcessor;
    }

    public function fetchMemberDetails($memberId) {
        return $this->db->fetchOne("SELECT * FROM members WHERE id = ?", [$memberId]);
    }

    public function fetchMemberAccountTypes($memberId) {
        // Fetch account types that the member has access to
        return $this->db->fetchAll("
            SELECT at.id, at.account_name, at.type_code 
            FROM account_types at
            LEFT JOIN member_accounts a ON at.id = a.account_type_id 
            WHERE a.member_id = ? AND at.is_active = 1
            GROUP BY at.id
            ORDER BY at.account_name
        ", [$memberId]);
    }

    public function fetchMemberSubAccounts($memberId, $accountTypeId = null) {
        $query = "
            SELECT a.id, a.account_number, a.account_name, at.account_name as account_type_name,
                   a.current_balance, a.opening_balance, a.is_active
            FROM member_accounts a
            JOIN account_types at ON a.account_type_id = at.id
            WHERE a.member_id = ?
        ";
        
        $params = [$memberId];
        
        if ($accountTypeId) {
            $query .= " AND a.account_type_id = ?";
            $params[] = $accountTypeId;
        }
        
        $query .= " ORDER BY at.account_name, a.account_name";
        
        return $this->db->fetchAll($query, $params);
    }

    public function getAccountTypeCode($accountTypeId) {
        $result = $this->db->fetchOne(
            "SELECT type_code FROM account_types WHERE id = ?", 
            [$accountTypeId]
        );
        return $result ? $result['type_code'] : 'SAV';
    }

    public function getMemberAccountReference($memberId, $accountTypeId) {
        // Get member details
        $member = $this->fetchMemberDetails($memberId);
        if (!$member) {
            throw new Exception("Member not found");
        }

        // Get account type code
        $accountTypeCode = $this->getAccountTypeCode($accountTypeId);
        
        // Get the first sub-account for this account type (default sub-account code '1')
        // You can modify this logic based on your business rules
        $subAccountCode = '1';
        
        // Format: memberNumber-accountTypeCode-subAccountCode
        return $member['personal_number'] . '-' . $accountTypeCode . '-' . $subAccountCode;
    }

    public function recordMemberMpesaTransaction($data) {
        if (empty($data['checkout_request_id'])) {
            throw new Exception("checkout_request_id is required and cannot be null");
        }

        $transactionType = 'STK';
        $transactionTime = date('Y-m-d H:i:s');

        return $this->db->execute(
            "INSERT INTO mpesa_transactions (
                checkout_request_id, 
                merchant_request_id,
                transaction_id,
                transaction_type, 
                amount, 
                phone_number,
                account_reference, 
                transaction_time, 
                status, 
                result_code,
                result_desc, 
                raw_data, 
                processed_at, 
                member_id, 
                account_type_id,
                business_short_code,
                initiated_by_member
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [
                $data['checkout_request_id'],
                $data['merchant_request_id'] ?? null,
                $data['transaction_id'] ?? null,
                $transactionType,
                $data['amount'],
                $data['phone_number'],
                $data['account_reference'],
                $transactionTime,
                $data['status'] ?? 'pending',
                $data['result_code'] ?? null,
                $data['result_desc'] ?? null,
                $data['raw_data'] ?? null,
                $data['processed_at'] ?? null,
                $data['member_id'] ?? null,
                $data['account_type_id'] ?? null,
                C2B_MPESA_SHORTCODE,
                1 // Flag indicating member-initiated transaction
            ]
        );
    }

    public function fetchMemberTransactions($memberId, $filters = []) {
        $page = max(1, intval($filters['page'] ?? 1));
        $limit = min(100, max(1, intval($filters['limit'] ?? 10)));
        $offset = ($page - 1) * $limit;
        
        $status = $filters['status'] ?? '';
        $accountTypeId = $filters['account_type_id'] ?? '';
        $startDate = $filters['start_date'] ?? '';
        $endDate = $filters['end_date'] ?? '';
        
        // Build query with filters
        $query = "
            SELECT mt.*, at.account_name 
            FROM mpesa_transactions mt
            LEFT JOIN account_types at ON mt.account_type_id = at.id
            WHERE mt.member_id = ?
        ";
        
        $params = [$memberId];
        
        if ($status) {
            $query .= " AND mt.status = ?";
            $params[] = $status;
        }
        
        if ($accountTypeId) {
            $query .= " AND mt.account_type_id = ?";
            $params[] = $accountTypeId;
        }
        
        if ($startDate) {
            $query .= " AND DATE(mt.created_at) >= ?";
            $params[] = $startDate;
        }
        
        if ($endDate) {
            $query .= " AND DATE(mt.created_at) <= ?";
            $params[] = $endDate;
        }
        
        $query .= " ORDER BY mt.created_at DESC LIMIT ? OFFSET ?";
        $params[] = $limit;
        $params[] = $offset;
        
        $transactions = $this->db->fetchAll($query, $params);
        
        // Get total count for pagination
        $countQuery = "SELECT COUNT(*) as total FROM mpesa_transactions WHERE member_id = ?";
        $countParams = [$memberId];
        
        if ($status) {
            $countQuery .= " AND status = ?";
            $countParams[] = $status;
        }
        
        if ($accountTypeId) {
            $countQuery .= " AND account_type_id = ?";
            $countParams[] = $accountTypeId;
        }
        
        if ($startDate) {
            $countQuery .= " AND DATE(created_at) >= ?";
            $countParams[] = $startDate;
        }
        
        if ($endDate) {
            $countQuery .= " AND DATE(created_at) <= ?";
            $countParams[] = $endDate;
        }
        
        $totalResult = $this->db->fetchOne($countQuery, $countParams);
        $total = $totalResult['total'] ?? 0;
        
        return [
            'transactions' => $transactions,
            'pagination' => [
                'page' => $page,
                'limit' => $limit,
                'total' => $total,
                'pages' => ceil($total / $limit)
            ]
        ];
    }

    public function getMemberTransactionStats($memberId) {
        $stats = [];
        
        // Total processed
        $total = $this->db->fetchOne(
            "SELECT COALESCE(SUM(amount), 0) as total 
             FROM mpesa_transactions 
             WHERE member_id = ? AND status = 'completed'",
            [$memberId]
        );
        $stats['total_processed'] = $total['total'] ?? 0;
        
        // Count by status
        $statuses = ['completed', 'pending', 'failed'];
        foreach ($statuses as $status) {
            $result = $this->db->fetchOne(
                "SELECT COUNT(*) as count 
                 FROM mpesa_transactions 
                 WHERE member_id = ? AND status = ?",
                [$memberId, $status]
            );
            $stats[$status] = $result['count'] ?? 0;
        }
        
        // Recent activity
        $recent = $this->db->fetchAll(
            "SELECT * FROM mpesa_transactions 
             WHERE member_id = ? 
             ORDER BY created_at DESC 
             LIMIT 5",
            [$memberId]
        );
        $stats['recent_activity'] = $recent;
        
        return $stats;
    }
}

// Main API Logic
try {
    // Check database connection
    if (!$pdo) {
        throw new Exception('Database connection failed');
    }

    // Check if user is logged in and is a member
    if (!isset($_SESSION['member_id'])) {
        sendJsonResponse(false, null, 'Authentication required. Please login to access M-Pesa services.', 401);
    }

    $memberId = $_SESSION['member_id'];
    
    // Initialize classes
    $db = new Database($pdo);
    $stkProcessor = new STKPushPaymentProcessor($pdo);
    $memberMpesaHandler = new MemberMpesaTransactionHandler($db, $stkProcessor);

    // Initialize M-Pesa integration with error handling
    try {
        $mpesa = new MpesaIntegration();
        $mpesaInitialized = true;
        $configInfo = [
            'environment' => C2B_MPESA_ENVIRONMENT,
            'shortCode' => C2B_MPESA_SHORTCODE,
            'callbackUrl' => C2B_MPESA_STK_CALLBACK_URL
        ];
    } catch (Exception $e) {
        logMemberSTKApiEvent('Failed to initialize MpesaIntegration', 'ERROR', ['error' => $e->getMessage()]);
        $mpesaInitialized = false;
        $mpesaError = $e->getMessage();
    }

    $method = $_SERVER['REQUEST_METHOD'];

    switch ($method) {
        // 🟢 GET — fetch member's M-Pesa data
        case 'GET':
            $action = $_GET['action'] ?? '';
            
            if ($action === 'get_member_phone') {
                // Get member's own phone number
                $member = $memberMpesaHandler->fetchMemberDetails($memberId);
                if ($member && !empty($member['phone_number'])) {
                    $displayPhone = PhoneNumberFormatter::getDisplayFormat($member['phone_number']);
                    sendJsonResponse(true, ['phone' => $displayPhone]);
                } else {
                    sendJsonResponse(true, ['phone' => '']);
                }
                
            } elseif ($action === 'get_account_types') {
                // Get member's account types
                $accountTypes = $memberMpesaHandler->fetchMemberAccountTypes($memberId);
                sendJsonResponse(true, ['account_types' => $accountTypes]);
                
            } elseif ($action === 'get_sub_accounts') {
                // Get member's sub-accounts (optional filtering by account type)
                $accountTypeId = $_GET['account_type_id'] ?? null;
                $subAccounts = $memberMpesaHandler->fetchMemberSubAccounts($memberId, $accountTypeId);
                sendJsonResponse(true, ['sub_accounts' => $subAccounts]);
                
            } elseif ($action === 'get_transactions') {
                // Get member's transactions with filters
                $filters = [
                    'page' => $_GET['page'] ?? 1,
                    'limit' => $_GET['limit'] ?? 10,
                    'status' => $_GET['status'] ?? '',
                    'account_type_id' => $_GET['account_type_id'] ?? '',
                    'start_date' => $_GET['start_date'] ?? '',
                    'end_date' => $_GET['end_date'] ?? ''
                ];
                
                $result = $memberMpesaHandler->fetchMemberTransactions($memberId, $filters);
                sendJsonResponse(true, $result);
                
            } elseif ($action === 'get_stats') {
                // Get member's transaction statistics
                $stats = $memberMpesaHandler->getMemberTransactionStats($memberId);
                sendJsonResponse(true, ['stats' => $stats]);
                
            } else {
                // Fetch all data for the member dashboard
                logMemberSTKApiEvent('Fetching member M-Pesa data', 'INFO', [
                    'member_id' => $memberId,
                    'timezone' => date_default_timezone_get(),
                    'server_time' => date('Y-m-d H:i:s')
                ]);
                
                $pdo->beginTransaction();
                
                try {
                    // Fetch member details
                    $member = $memberMpesaHandler->fetchMemberDetails($memberId);
                    
                    // Format member phone for display
                    if ($member && !empty($member['phone_number'])) {
                        $member['display_phone'] = PhoneNumberFormatter::getDisplayFormat($member['phone_number']);
                    }
                    
                    // Fetch account types
                    $accountTypes = $memberMpesaHandler->fetchMemberAccountTypes($memberId);
                    
                    // Fetch recent transactions
                    $recentTransactions = $db->fetchAll(
                        "SELECT mt.*, at.account_name 
                         FROM mpesa_transactions mt
                         LEFT JOIN account_types at ON mt.account_type_id = at.id
                         WHERE mt.member_id = ?
                         ORDER BY mt.created_at DESC 
                         LIMIT 10",
                        [$memberId]
                    );
                    
                    // Fetch statistics
                    $stats = $memberMpesaHandler->getMemberTransactionStats($memberId);
                    
                    $pdo->commit();
                    
                    sendJsonResponse(true, [
                        'member' => $member,
                        'account_types' => $accountTypes,
                        'recent_transactions' => $recentTransactions,
                        'stats' => $stats,
                        'config' => [
                            'initialized' => $mpesaInitialized,
                            'environment' => $configInfo['environment'] ?? '',
                            'shortCode' => $configInfo['shortCode'] ?? '',
                            'callbackUrl' => $configInfo['callbackUrl'] ?? '',
                            'error' => $mpesaError ?? null
                        ],
                        'timezone' => date_default_timezone_get(),
                        'generated_at' => date('Y-m-d H:i:s')
                    ]);
                    
                } catch (Exception $e) {
                    $pdo->rollBack();
                    throw new Exception("Error fetching member M-Pesa data: " . $e->getMessage());
                }
            }
            break;
            
        // 🟢 POST — process STK push (member-initiated)
        case 'POST':
            // Parse JSON input if available
            $input = json_decode(file_get_contents('php://input'), true);
            if ($input === null && json_last_error() !== JSON_ERROR_NONE) {
                $input = $_POST;
            }
            
            $action = $input['action'] ?? '';
            
            if ($action === 'stk_push') {
                logMemberSTKApiEvent('Member STK Push request received', 'INFO', [
                    'member_id' => $memberId,
                    'timezone' => date_default_timezone_get()
                ]);
                
                try {
                    // Validate required fields
                    $required = ['account_type_id', 'amount'];
                    foreach ($required as $field) {
                        if (empty($input[$field])) {
                            throw new Exception("$field is required");
                        }
                    }
                    
                    $amount = filter_var($input['amount'], FILTER_VALIDATE_FLOAT);
                    if ($amount === false || $amount <= 0) {
                        throw new Exception("Invalid amount");
                    }
                    
                    // Get member details
                    $member = $memberMpesaHandler->fetchMemberDetails($memberId);
                    if (!$member) {
                        throw new Exception("Member not found");
                    }
                    
                    // Use member's phone number or provided phone
                    $phone = $input['phone'] ?? $member['phone_number'];
                    if (empty($phone)) {
                        throw new Exception("Phone number is required. Please update your profile or provide a phone number.");
                    }
                    
                    if (!PhoneNumberFormatter::validatePhoneNumber($phone)) {
                        throw new Exception("Invalid phone number format. Use formats like: 0727576400, 254727576400, +254727576400, or 727576400");
                    }
                    
                    $formattedPhone = PhoneNumberFormatter::formatPhoneNumber($phone);
                    
                    // Get account type name
                    $accountTypes = $memberMpesaHandler->fetchMemberAccountTypes($memberId);
                    $accountTypeId = $input['account_type_id'];
                    $accountType = array_filter($accountTypes, function($at) use ($accountTypeId) {
                        return $at['id'] == $accountTypeId;
                    });
                    $accountType = reset($accountType);
                    
                    if (!$accountType) {
                        throw new Exception("Account type not found or you don't have access to this account type");
                    }
                    
                    // Create proper account reference
                    $reference = $memberMpesaHandler->getMemberAccountReference($memberId, $accountTypeId);
                    
                    $description = $input['description'] ?? ($accountType['account_name'] . " payment by " . $member['first_name'] . " " . $member['last_name']);
                    
                    // Check if MpesaIntegration was initialized successfully
                    if (!$mpesaInitialized || !isset($mpesa)) {
                        throw new Exception("M-Pesa service is not available due to configuration issues. Please try again later.");
                    }
                    
                    // Make actual M-Pesa API call
                    $response = $mpesa->stkPush($formattedPhone, $amount, $reference, $description);
                    
                    // Record transaction in database with member-initiated flag
                    $memberMpesaHandler->recordMemberMpesaTransaction([
                        'merchant_request_id' => $response['MerchantRequestID'] ?? '',
                        'checkout_request_id' => $response['CheckoutRequestID'] ?? '',
                        'amount' => $amount,
                        'phone_number' => $formattedPhone,
                        'account_reference' => $reference,
                        'result_desc' => $description,
                        'member_id' => $memberId,
                        'account_type_id' => $accountTypeId,
                        'raw_data' => json_encode($response),
                        'status' => 'pending'
                    ]);
                    
                    // Log successful transaction
                    logMemberSTKApiEvent('Member STK Push initiated successfully', 'INFO', [
                        'member_id' => $memberId,
                        'amount' => $amount,
                        'phone' => $formattedPhone,
                        'reference' => $reference,
                        'account_type' => $accountType['account_name']
                    ]);
                    
                    // Send SMS notification to member
                    if (!empty($member['phone_number'])) {
                        try {
                            $smsMessage = "Hello " . $member['first_name'] . ", KES " . number_format($amount, 2) . " payment request sent to " . PhoneNumberFormatter::getDisplayFormat($formattedPhone) . " for " . $accountType['account_name'] . ". Check your phone for STK prompt.";
                            // Uncomment if SMS helper is configured
                            // sendSMS($member['phone_number'], $smsMessage);
                        } catch (Exception $smsError) {
                            logMemberSTKApiEvent('SMS notification failed', 'WARNING', ['error' => $smsError->getMessage()]);
                        }
                    }
                    
                    // Send email notification to admin (optional)
                    try {
                        $adminEmail = 'admin@example.com'; // Replace with actual admin email
                        $emailSubject = "Member Payment Initiated: " . $member['first_name'] . " " . $member['last_name'];
                        $emailMessage = "
                            <h3>Member Payment Initiated</h3>
                            <p><strong>Member:</strong> " . $member['first_name'] . " " . $member['last_name'] . " (" . $member['personal_number'] . ")</p>
                            <p><strong>Amount:</strong> KES " . number_format($amount, 2) . "</p>
                            <p><strong>Account:</strong> " . $accountType['account_name'] . "</p>
                            <p><strong>Phone:</strong> " . PhoneNumberFormatter::getDisplayFormat($formattedPhone) . "</p>
                            <p><strong>Reference:</strong> " . $reference . "</p>
                            <p><strong>Time:</strong> " . date('Y-m-d H:i:s') . "</p>
                        ";
                        // Uncomment if email helper is configured
                        // sendEmail($adminEmail, $emailSubject, $emailMessage);
                    } catch (Exception $emailError) {
                        logMemberSTKApiEvent('Admin email notification failed', 'WARNING', ['error' => $emailError->getMessage()]);
                    }
                    
                    sendJsonResponse(true, [
                        'message' => 'Payment request initiated successfully',
                        'mpesa_response' => $response,
                        'user_reference' => $reference,
                        'customer_message' => $response['CustomerMessage'] ?? 'Check your phone for payment prompt',
                        'checkout_request_id' => $response['CheckoutRequestID'] ?? '',
                        'merchant_request_id' => $response['MerchantRequestID'] ?? '',
                        'member_name' => $member['first_name'] . ' ' . $member['last_name'],
                        'account_name' => $accountType['account_name']
                    ]);
                    
                } catch (Exception $e) {
                    logMemberSTKApiEvent('Member STK Push Error', 'ERROR', [
                        'error' => $e->getMessage(),
                        'member_id' => $memberId
                    ]);
                    
                    sendJsonResponse(false, null, $e->getMessage(), 400);
                }
                
            } else {
                sendJsonResponse(false, null, 'Invalid action', 400);
            }
            break;
            
        default:
            logMemberSTKApiEvent('Method not allowed', 'WARNING', [
                'method' => $method,
                'timezone' => date_default_timezone_get()
            ]);
            sendJsonResponse(false, null, 'Method not allowed', 405);
    }

} catch (PDOException $e) {
    logMemberSTKApiEvent('Database error in Member M-Pesa API', 'ERROR', [
        'error' => $e->getMessage(),
        'member_id' => $_SESSION['member_id'] ?? 'unknown',
        'timezone' => date_default_timezone_get(),
        'server_time' => date('Y-m-d H:i:s')
    ]);
    sendJsonResponse(false, null, 'Service temporarily unavailable', 503);
} catch (Exception $e) {
    logMemberSTKApiEvent('Application error in Member M-Pesa API', 'ERROR', [
        'error' => $e->getMessage(),
        'member_id' => $_SESSION['member_id'] ?? 'unknown',
        'timezone' => date_default_timezone_get(),
        'server_time' => date('Y-m-d H:i:s')
    ]);
    sendJsonResponse(false, null, 'Internal server error: ' . $e->getMessage(), 500);
}
?>