<?php
/**
 * PayNow Integration Helper
 * 
 * Provides easy-to-use functions for PayNow payment integration
 * with proper error handling and logging.
 * 
 * Documentation: https://developers.paynow.co.zw/
 */

namespace PayNow;

// Exception class for PayNow errors
class PayNowException extends \Exception {}

/**
 * Initialize PayNow client with credentials from database
 * 
 * @param string $environment 'sandbox' or 'live'
 * @return PayNowClient
 */
function getPayNowClient(string $environment = 'sandbox'): PayNowClient
{
    $db = getDB();
    
    $stmt = $db->prepare("
        SELECT * FROM payment_gateway_settings
        WHERE gateway_name = 'paynow' AND gateway_type = ? AND is_active = 1
        LIMIT 1
    ");
    $stmt->execute([$environment]);
    $settings = $stmt->fetch();

    if (!$settings) {
        throw new PayNowException('PayNow is not configured for ' . $environment . ' environment');
    }

    if (empty($settings['api_key']) || empty($settings['api_secret'])) {
        throw new PayNowException('PayNow API credentials are missing');
    }

    return new PayNowClient(
        $settings['api_key'],
        $settings['api_secret'],
        $settings['merchant_id'] ?? '',
        ($environment !== 'live'),
        $settings['merchant_email'] ?? '',
        $settings['merchant_mobile'] ?? ''
    );
}

/**
 * Create a payment session for a course/exam purchase
 * 
 * @param int $userId User ID
 * @param string $itemType 'course' or 'exam'
 * @param int $itemId Course or Exam ID
 * @param float $amount Amount to charge
 * @param string $itemName Item name for description
 * @param string|null $discountCode Optional discount code
 * @param string $environment 'sandbox' or 'live'
 * @return array Payment session data
 */
function createPayNowPayment(
    int $userId,
    string $itemType,
    int $itemId,
    float $amount,
    string $itemName,
    ?string $discountCode = null,
    string $environment = 'sandbox'
): array {
    $db = getDB();
    
    try {
        // Get user email
        $stmt = $db->prepare("SELECT email, username FROM users WHERE id = ?");
        $stmt->execute([$userId]);
        $user = $stmt->fetch();
        
        if (!$user) {
            throw new PayNowException('User not found');
        }

        // Get gateway settings
        $stmt = $db->prepare("
            SELECT * FROM payment_gateway_settings
            WHERE gateway_name = 'paynow' AND gateway_type = ? AND is_active = 1
        ");
        $stmt->execute([$environment]);
        $settings = $stmt->fetch();

        if (!$settings) {
            throw new PayNowException('PayNow not configured for ' . $environment);
        }

        // Calculate final amount with discount
        $finalAmount = $amount;
        $discountAmount = 0;

        if ($discountCode) {
            $stmt = $db->prepare("
                SELECT * FROM discount_codes
                WHERE code = ? AND is_active = 1
                AND (valid_from IS NULL OR valid_from <= NOW())
                AND (valid_until IS NULL OR valid_until >= NOW())
            ");
            $stmt->execute([$discountCode]);
            $discount = $stmt->fetch();

            if ($discount) {
                if (!$discount['usage_limit'] || $discount['usage_count'] < $discount['usage_limit']) {
                    if ($amount >= $discount['min_purchase_amount']) {
                        if ($discount['discount_type'] === 'percentage') {
                            $discountAmount = ($amount * $discount['discount_value']) / 100;
                            if ($discount['max_discount_amount'] && $discountAmount > $discount['max_discount_amount']) {
                                $discountAmount = $discount['max_discount_amount'];
                            }
                        } else {
                            $discountAmount = min($discount['discount_value'], $amount);
                        }
                        $finalAmount = $amount - $discountAmount;
                    }
                }
            }
        }

        // Generate unique reference
        $referenceNumber = 'LMS_' . strtoupper($itemType) . '_' . time() . '_' . rand(1000, 9999);
        $transactionId = uniqid('txn_paynow_', true);

        // Create transaction record
        $stmt = $db->prepare("
            INSERT INTO payment_transactions
            (transaction_id, reference_number, gateway_name, gateway_type, user_id, item_type, item_id,
             amount, currency, discount_code, discount_amount, final_amount, status, created_at)
            VALUES (?, ?, 'paynow', ?, ?, ?, ?, ?, 'USD', ?, ?, ?, 'pending', NOW())
        ");

        $stmt->execute([
            $transactionId,
            $referenceNumber,
            $environment,
            $userId,
            $itemType,
            $itemId,
            $amount,
            $discountCode,
            $discountAmount,
            $finalAmount
        ]);

        // Initialize PayNow client
        $paynow = new PayNowClient(
            $settings['api_key'],
            $settings['api_secret'],
            $settings['merchant_id'] ?? '',
            ($environment !== 'live'),
            $settings['merchant_email'] ?? '',
            $settings['merchant_mobile'] ?? ''
        );

        // Create payment
        $description = $itemName . ' - ' . ucfirst($itemType) . ' Purchase';
        
        $response = $paynow->createPayment(
            $referenceNumber,
            $finalAmount,
            $description,
            $user['email']
        );

        if (!$response['success']) {
            // Update transaction as failed
            $stmt = $db->prepare("
                UPDATE payment_transactions
                SET status = 'failed', gateway_response = ?, processed_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([
                json_encode(['error' => $response['raw_response']['error'] ?? 'Payment initiation failed']),
                $transactionId
            ]);

            // Log error
            $stmt = $db->prepare("
                INSERT INTO payment_logs (action, level, message, context, created_at)
                VALUES ('paynow_init_failed', 'error', ?, ?, NOW())
            ");
            $stmt->execute([
                'PayNow payment initiation failed',
                json_encode(['user_id' => $userId, 'error' => $response])
            ]);

            return [
                'success' => false,
                'error' => $response['raw_response']['error'] ?? 'Failed to initiate payment'
            ];
        }

        // Update transaction with PayNow response
        $stmt = $db->prepare("
            UPDATE payment_transactions
            SET gateway_response = ?,
                gateway_metadata = JSON_OBJECT('poll_url', ?, 'browser_url', ?),
                status = 'processing'
            WHERE transaction_id = ?
        ");
        $stmt->execute([
            json_encode(['redirect_url' => $response['redirect_url']]),
            $response['poll_url'],
            $response['redirect_url'],
            $transactionId
        ]);

        // Log successful initiation
        $stmt = $db->prepare("
            INSERT INTO payment_logs (action, level, message, context, created_at)
            VALUES ('paynow_initiated', 'info', 'PayNow payment initiated', ?, NOW())
        ");
        $stmt->execute([json_encode([
            'transaction_id' => $transactionId,
            'reference' => $referenceNumber,
            'amount' => $finalAmount
        ])]);

        return [
            'success' => true,
            'transaction_id' => $transactionId,
            'reference_number' => $referenceNumber,
            'redirect_url' => $response['redirect_url'],
            'poll_url' => $response['poll_url'],
            'amount' => $finalAmount
        ];

    } catch (PayNowException $e) {
        // Log error
        $stmt = $db->prepare("
            INSERT INTO payment_logs (action, level, message, context, created_at)
            VALUES ('paynow_error', 'error', ?, ?, NOW())
        ");
        $stmt->execute([
            $e->getMessage(),
            json_encode(['user_id' => $userId, 'item_type' => $itemType, 'item_id' => $itemId])
        ]);

        return [
            'success' => false,
            'error' => $e->getMessage()
        ];
    } catch (\Exception $e) {
        // Log error
        $stmt = $db->prepare("
            INSERT INTO payment_logs (action, level, message, context, created_at)
            VALUES ('paynow_error', 'error', ?, ?, NOW())
        ");
        $stmt->execute([
            $e->getMessage(),
            json_encode(['user_id' => $userId])
        ]);

        return [
            'success' => false,
            'error' => 'An error occurred while processing your payment'
        ];
    }
}

/**
 * Check PayNow payment status
 * 
 * @param string $transactionId Transaction ID
 * @param string $environment 'sandbox' or 'live'
 * @return array Status information
 */
function checkPayNowPaymentStatus(string $transactionId, string $environment = 'sandbox'): array
{
    $db = getDB();
    
    try {
        // Get transaction
        $stmt = $db->prepare("
            SELECT * FROM payment_transactions
            WHERE transaction_id = ? OR reference_number = ?
        ");
        $stmt->execute([$transactionId, $transactionId]);
        $transaction = $stmt->fetch();

        if (!$transaction) {
            throw new PayNowException('Transaction not found');
        }

        // Get poll URL
        $metadata = json_decode($transaction['gateway_metadata'] ?? '{}', true);
        $pollUrl = $metadata['poll_url'] ?? null;

        if (!$pollUrl) {
            throw new PayNowException('Poll URL not available');
        }

        // Get settings
        $stmt = $db->prepare("
            SELECT * FROM payment_gateway_settings
            WHERE gateway_name = 'paynow' AND gateway_type = ? AND is_active = 1
        ");
        $stmt->execute([$transaction['gateway_type']]);
        $settings = $stmt->fetch();

        if (!$settings) {
            throw new PayNowException('PayNow settings not found');
        }

        // Check status
        $paynow = new PayNowClient(
            $settings['api_key'],
            $settings['api_secret'],
            $settings['merchant_id'] ?? '',
            ($transaction['gateway_type'] !== 'live')
        );

        $status = $paynow->checkStatus($pollUrl);

        // Map status to our status
        $newStatus = 'pending';
        if ($status['paid']) {
            $newStatus = 'completed';
        } elseif ($status['status'] === 'Cancelled') {
            $newStatus = 'cancelled';
        } elseif ($status['status'] === 'Failed' || $status['status'] === 'Error') {
            $newStatus = 'failed';
        }

        // Update transaction if status changed
        if ($newStatus !== $transaction['status']) {
            $stmt = $db->prepare("
                UPDATE payment_transactions
                SET status = ?, gateway_response = JSON_SET(COALESCE(gateway_response, '{}'), '$.status', ?),
                    processed_at = NOW()
                WHERE id = ?
            ");
            $stmt->execute([$newStatus, $status['status'], $transaction['id']]);

            // Process successful payment
            if ($newStatus === 'completed') {
                processSuccessfulPayment($transaction['transaction_id']);
            }
        }

        return [
            'success' => true,
            'status' => $newStatus,
            'paid' => $status['paid'],
            'paynow_status' => $status['status'] ?? null
        ];

    } catch (\Exception $e) {
        return [
            'success' => false,
            'error' => $e->getMessage()
        ];
    }
}

/**
 * Process successful payment and enroll user
 * 
 * @param string $transactionId Transaction ID
 * @return array Result
 */
function processSuccessfulPayment(string $transactionId): array
{
    $db = getDB();
    
    try {
        $stmt = $db->prepare("SELECT * FROM payment_transactions WHERE transaction_id = ?");
        $stmt->execute([$transactionId]);
        $transaction = $stmt->fetch();

        if (!$transaction) {
            throw new PayNowException('Transaction not found');
        }

        // Process based on item type
        if ($transaction['item_type'] === 'course') {
            // Check if already enrolled
            $stmt = $db->prepare("
                SELECT id FROM course_enrollments
                WHERE course_id = ? AND student_id = ?
            ");
            $stmt->execute([$transaction['item_id'], $transaction['user_id']]);

            if (!$stmt->fetch()) {
                // Create enrollment
                $stmt = $db->prepare("
                    INSERT INTO course_enrollments
                    (course_id, student_id, payment_status, payment_amount, status, enrollment_date, created_at)
                    VALUES (?, ?, 'paid', ?, 'enrolled', NOW(), NOW())
                ");
                $stmt->execute([
                    $transaction['item_id'],
                    $transaction['user_id'],
                    $transaction['final_amount']
                ]);
            }

            // Clear from cart if present
            $stmt = $db->prepare("
                DELETE FROM cart_items
                WHERE user_id = ? AND item_id = ? AND item_type = 'course'
            ");
            $stmt->execute([$transaction['user_id'], $transaction['item_id']]);

        } elseif ($transaction['item_type'] === 'exam') {
            // Create exam attempt record
            $stmt = $db->prepare("
                SELECT id FROM exam_attempts
                WHERE exam_id = ? AND student_id = ?
            ");
            $stmt->execute([$transaction['item_id'], $transaction['user_id']]);

            if (!$stmt->fetch()) {
                $stmt = $db->prepare("
                    INSERT INTO exam_attempts
                    (exam_id, student_id, payment_status, attempt_number, started_at, created_at)
                    VALUES (?, ?, 'paid', 1, NOW(), NOW())
                ");
                $stmt->execute([
                    $transaction['item_id'],
                    $transaction['user_id']
                ]);
            }
        } elseif ($transaction['item_type'] === 'cart') {
            // Process all items in cart
            $itemIds = json_decode($transaction['item_id'], true);
            
            if (is_array($itemIds)) {
                foreach ($itemIds as $courseId) {
                    // Create enrollment for each course
                    $stmt = $db->prepare("
                        SELECT id FROM course_enrollments
                        WHERE course_id = ? AND student_id = ?
                    ");
                    $stmt->execute([$courseId, $transaction['user_id']]);

                    if (!$stmt->fetch()) {
                        $stmt = $db->prepare("
                            INSERT INTO course_enrollments
                            (course_id, student_id, payment_status, payment_amount, status, enrollment_date, created_at)
                            VALUES (?, ?, 'paid', ?, 'enrolled', NOW(), NOW())
                        ");
                        $stmt->execute([
                            $courseId,
                            $transaction['user_id'],
                            $transaction['final_amount']
                        ]);
                    }
                }

                // Clear entire cart
                $stmt = $db->prepare("DELETE FROM cart_items WHERE user_id = ?");
                $stmt->execute([$transaction['user_id']]);
            }
        }

        // Send notification
        sendPaymentNotification('payment_success', $transactionId);

        // Log
        $stmt = $db->prepare("
            INSERT INTO payment_logs (action, level, message, context, created_at)
            VALUES ('payment_completed', 'info', 'Payment processed successfully', ?, NOW())
        ");
        $stmt->execute([json_encode(['transaction_id' => $transactionId])]);

        return ['success' => true];

    } catch (\Exception $e) {
        $stmt = $db->prepare("
            INSERT INTO payment_logs (action, level, message, context, created_at)
            VALUES ('payment_processing_error', 'error', ?, ?, NOW())
        ");
        $stmt->execute([
            $e->getMessage(),
            json_encode(['transaction_id' => $transactionId])
        ]);

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

/**
 * Handle payment callback from PayNow return URL
 * 
 * @param array $params Query parameters from return URL
 * @return array Result
 */
function handlePayNowCallback(array $params): array
{
    $db = getDB();
    
    try {
        $reference = $params['reference'] ?? $params['transactionreference'] ?? null;
        
        if (!$reference) {
            throw new PayNowException('No reference in callback');
        }

        // Find transaction
        $stmt = $db->prepare("
            SELECT * FROM payment_transactions
            WHERE reference_number = ?
        ");
        $stmt->execute([$reference]);
        $transaction = $stmt->fetch();

        if (!$transaction) {
            throw new PayNowException('Transaction not found');
        }

        // Check status
        $status = checkPayNowPaymentStatus($reference, $transaction['gateway_type']);

        if ($status['paid']) {
            // Process enrollment
            $result = processSuccessfulPayment($transaction['transaction_id']);
            
            return [
                'success' => true,
                'status' => 'completed',
                'message' => 'Payment successful',
                'transaction_id' => $transaction['transaction_id']
            ];
        }

        return [
            'success' => false,
            'status' => $status['status'] ?? 'unknown',
            'message' => 'Payment not yet completed',
            'transaction_id' => $transaction['transaction_id']
        ];

    } catch (\Exception $e) {
        return [
            'success' => false,
            'error' => $e->getMessage()
        ];
    }
}
