<?php
/**
 * Fingpay AEPS API Integration
 * 
 * This class provides integration with Fingpay AEPS services including:
 * - Cash Withdrawal
 * - Balance Inquiry
 * - Mini Statement
 * - Cash Deposit
 * 
 * @package FingpayAEPS
 * @version 1.0
 */

class FingpayAEPS
{
    /**
     * API Configuration
     */
    private $config;
    
    /**
     * Base URL for Fingpay API
     */
    private $baseUrl;
    
    /**
     * IV for AES encryption (fixed as per API documentation)
     */
    private $aesIv;
    
    /**
     * API Endpoints
     */
    private $endpoints;
    
    /**
     * Path to public certificate file
     */
    private $publicCertPath;
    
    /**
     * Request timeout in seconds
     */
    private $timeout;
    
    /**
     * Constructor
     * 
     * @param string $publicCertPath Path to the Fingpay public certificate file
     * @param int $timeout Request timeout in seconds (default: 30)
     * @param array|null $customConfig Custom API configuration (optional)
     */
    public function __construct($publicCertPath, $timeout = 30, $customConfig = null)
    {
        $this->publicCertPath = $publicCertPath;
        
        // Load API configuration
        $configFile = __DIR__ . '/api_config.php';
        if (file_exists($configFile)) {
            $this->config = require $configFile;
        } else {
            // Fallback to default config if file doesn't exist
            $this->config = [
                'base_url' => 'https://fingpayap.tapits.in',
                'endpoints' => [
                    'cash_withdrawal' => '/fpaepsservice/api/cashWithdrawal/merchant/php/withdrawal',
                    'balance_inquiry' => '/fpaepsservice/api/balanceInquiry/merchant/php/inquiry',
                    'mini_statement' => '/fpaepsservice/api/miniStatement/merchant/php/statement',
                    'cash_deposit' => '/fpaepsservice/api/cashDeposit/merchant/php/deposit',
                    // User Onboarding APIs - NOT AVAILABLE IN DOCUMENTATION (Disabled)
                    // 'user_onboard' => '/fpaepsservice/api/userOnboard/merchant/php/register',
                    // 'user_verify' => '/fpaepsservice/api/userOnboard/merchant/php/verify',
                    'merchant_onboard' => '/fpaepsservice/api/merchantOnboard/merchant/php/register',
                    'merchant_status' => '/fpaepsservice/api/merchantOnboard/merchant/php/status',
                    'ekyc_verify' => '/fpaepsservice/api/ekyc/merchant/php/verify',
                    'ekyc_status' => '/fpaepsservice/api/ekyc/merchant/php/status',
                    'ekyc_enroll' => '/fpaepsservice/api/ekyc/merchant/php/enroll',
                    'bank_list' => '/fpaepsservice/api/bankList',
                ],
                'aes_iv' => '06f2f04cc530364f',
                'timeout' => $timeout,
                'bank_list_config' => [
                    'requires_encryption' => false,
                    'method' => 'GET',
                ],
            ];
        }
        
        // Merge custom config if provided
        if ($customConfig !== null) {
            $this->config = array_merge($this->config, $customConfig);
        }
        
        $this->baseUrl = $this->config['base_url'];
        $this->aesIv = $this->config['aes_iv'];
        $this->endpoints = $this->config['endpoints'];
        $this->timeout = $timeout ?: ($this->config['timeout'] ?? 30);
        
        if (!file_exists($publicCertPath)) {
            throw new Exception("Public certificate file not found: {$publicCertPath}");
        }
    }
    
    /**
     * Generate random 16-byte AES session key
     * 
     * @return string 16-byte key as string
     */
    private function generateSessionKey()
    {
        $key = '';
        $mt_rand = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
        foreach ($mt_rand as $chr) {
            $key .= chr($chr);
        }
        return $key;
    }
    
    /**
     * Generate SHA256 hash of data and return base64 encoded
     * 
     * @param string $data Data to hash
     * @return string Base64 encoded hash
     */
    private function generateHash($data)
    {
        return base64_encode(hash("sha256", $data, true));
    }
    
    /**
     * Encrypt data using AES-128-CBC
     * 
     * @param string $data Data to encrypt
     * @param string $key AES key (16 bytes)
     * @return string Base64 encoded encrypted data
     */
    private function encryptAES($data, $key)
    {
        $ciphertext_raw = openssl_encrypt(
            $data, 
            'AES-128-CBC', 
            $key, 
            OPENSSL_RAW_DATA, 
            $this->aesIv
        );
        return base64_encode($ciphertext_raw);
    }
    
    /**
     * Encrypt data using RSA public key
     * 
     * @param string $data Data to encrypt
     * @return string Base64 encoded encrypted data
     */
    private function encryptRSA($data)
    {
        $fp = fopen($this->publicCertPath, "r");
        if (!$fp) {
            throw new Exception("Failed to open public certificate file");
        }
        
        $pub_key_string = fread($fp, 8192);
        fclose($fp);
        
        $encrypted = '';
        if (!openssl_public_encrypt($data, $encrypted, $pub_key_string)) {
            throw new Exception("RSA encryption failed");
        }
        
        return base64_encode($encrypted);
    }
    
    /**
     * Prepare encrypted request with headers
     * 
     * @param array $payload Request payload as array
     * @param string $deviceIMEI Device IMEI number
     * @return array Array containing encrypted body and headers
     */
    private function prepareRequest($payload, $deviceIMEI = "")
    {
        // Convert payload to JSON string
        $jsonPayload = json_encode($payload);
        
        // Generate AES session key
        $sessionKey = $this->generateSessionKey();
        
        // Encrypt payload with AES
        $encryptedPayload = $this->encryptAES($jsonPayload, $sessionKey);
        
        // Encrypt AES key with RSA
        $encryptedKey = $this->encryptRSA($sessionKey);
        
        // Generate hash
        $hashValue = $this->generateHash($jsonPayload);
        
        // Prepare headers
        $timestamp = date('d/m/Y H:i:s');
        $headers = [
            'Content-Type: text/xml',
            'trnTimestamp: ' . $timestamp,
            'hash: ' . $hashValue,
            'deviceIMEI: ' . $deviceIMEI,
            'eskey: ' . $encryptedKey
        ];
        
        return [
            'body' => $encryptedPayload,
            'headers' => $headers
        ];
    }
    
    /**
     * Make API request to Fingpay (encrypted)
     * 
     * @param string $endpoint API endpoint path
     * @param array $payload Request payload
     * @param string $deviceIMEI Device IMEI number
     * @return array API response as array
     */
    private function makeRequest($endpoint, $payload, $deviceIMEI = "")
    {
        $url = $this->baseUrl . $endpoint;
        $requestData = $this->prepareRequest($payload, $deviceIMEI);
        
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_TIMEOUT => $this->timeout,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $requestData['body'],
            CURLOPT_HTTPHEADER => $requestData['headers']
        ]);
        
        $result = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $curlError = curl_error($curl);
        curl_close($curl);
        
        if ($curlError) {
            return [
                'status' => 'error',
                'message' => 'Request failed: ' . $curlError,
                'error' => $curlError,
                'http_code' => $httpCode
            ];
        }
        
        $response = json_decode($result, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            return [
                'status' => 'error',
                'message' => 'Invalid JSON response',
                'raw_response' => $result,
                'http_code' => $httpCode
            ];
        }
        
        return $response ?: [
            'status' => 'error',
            'message' => 'Empty response',
            'http_code' => $httpCode
        ];
    }
    
    /**
     * Make simple API request (without encryption)
     * Used for endpoints that don't require encryption like bank list
     * 
     * @param string $endpoint API endpoint path
     * @param array $params Query parameters (for GET) or body (for POST)
     * @param string $method HTTP method (GET or POST)
     * @return array API response as array
     */
    private function makeSimpleRequest($endpoint, $params = [], $method = 'GET')
    {
        $url = $this->baseUrl . $endpoint;
        
        $curl = curl_init();
        $curlOptions = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_TIMEOUT => $this->timeout,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => $method,
        ];
        
        if ($method === 'GET' && !empty($params)) {
            $url .= '?' . http_build_query($params);
            $curlOptions[CURLOPT_URL] = $url;
        } elseif ($method === 'POST' && !empty($params)) {
            $curlOptions[CURLOPT_POSTFIELDS] = json_encode($params);
            $curlOptions[CURLOPT_HTTPHEADER] = [
                'Content-Type: application/json',
            ];
        }
        
        curl_setopt_array($curl, $curlOptions);
        
        $result = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $curlError = curl_error($curl);
        curl_close($curl);
        
        if ($curlError) {
            return [
                'status' => 'error',
                'message' => 'Request failed: ' . $curlError,
                'error' => $curlError,
                'http_code' => $httpCode
            ];
        }
        
        $response = json_decode($result, true);
        
        if (json_last_error() !== JSON_ERROR_NONE) {
            return [
                'status' => 'error',
                'message' => 'Invalid JSON response',
                'raw_response' => $result,
                'http_code' => $httpCode
            ];
        }
        
        return $response ?: [
            'status' => 'error',
            'message' => 'Empty response',
            'http_code' => $httpCode
        ];
    }
    
    /**
     * Cash Withdrawal API
     * 
     * @param string $merchantId Merchant ID
     * @param string $terminalId Terminal ID
     * @param string $aadhaarNumber Aadhaar number
     * @param float $amount Transaction amount
     * @param string $customerName Customer name
     * @param string $transactionRef Unique transaction reference
     * @param string $bankCode Bank code
     * @param string $deviceIMEI Device IMEI (optional)
     * @param array $additionalParams Additional parameters (optional)
     * @return array API response
     */
    public function cashWithdrawal(
        $merchantId,
        $terminalId,
        $aadhaarNumber,
        $amount,
        $customerName,
        $transactionRef,
        $bankCode,
        $deviceIMEI = "",
        $additionalParams = []
    ) {
        $payload = array_merge([
            'merchantId' => $merchantId,
            'terminalId' => $terminalId,
            'aadhaarNumber' => $aadhaarNumber,
            'amount' => $amount,
            'customerName' => $customerName,
            'transactionRef' => $transactionRef,
            'bankCode' => $bankCode
        ], $additionalParams);
        
        return $this->makeRequest($this->endpoints['cash_withdrawal'], $payload, $deviceIMEI);
    }
    
    /**
     * Balance Inquiry API
     * 
     * @param string $merchantId Merchant ID
     * @param string $terminalId Terminal ID
     * @param string $aadhaarNumber Aadhaar number
     * @param string $bankCode Bank code
     * @param string $transactionRef Unique transaction reference
     * @param string $deviceIMEI Device IMEI (optional)
     * @param array $additionalParams Additional parameters (optional)
     * @return array API response
     */
    public function balanceInquiry(
        $merchantId,
        $terminalId,
        $aadhaarNumber,
        $bankCode,
        $transactionRef,
        $deviceIMEI = "",
        $additionalParams = []
    ) {
        $payload = array_merge([
            'merchantId' => $merchantId,
            'terminalId' => $terminalId,
            'aadhaarNumber' => $aadhaarNumber,
            'bankCode' => $bankCode,
            'transactionRef' => $transactionRef
        ], $additionalParams);
        
        return $this->makeRequest($this->endpoints['balance_inquiry'], $payload, $deviceIMEI);
    }
    
    /**
     * Mini Statement API
     * 
     * @param string $merchantId Merchant ID
     * @param string $terminalId Terminal ID
     * @param string $aadhaarNumber Aadhaar number
     * @param string $bankCode Bank code
     * @param string $transactionRef Unique transaction reference
     * @param string $deviceIMEI Device IMEI (optional)
     * @param array $additionalParams Additional parameters (optional)
     * @return array API response
     */
    public function miniStatement(
        $merchantId,
        $terminalId,
        $aadhaarNumber,
        $bankCode,
        $transactionRef,
        $deviceIMEI = "",
        $additionalParams = []
    ) {
        $payload = array_merge([
            'merchantId' => $merchantId,
            'terminalId' => $terminalId,
            'aadhaarNumber' => $aadhaarNumber,
            'bankCode' => $bankCode,
            'transactionRef' => $transactionRef
        ], $additionalParams);
        
        return $this->makeRequest($this->endpoints['mini_statement'], $payload, $deviceIMEI);
    }
    
    /**
     * Cash Deposit API
     * 
     * @param string $merchantId Merchant ID
     * @param string $terminalId Terminal ID
     * @param string $aadhaarNumber Aadhaar number
     * @param float $amount Transaction amount
     * @param string $customerName Customer name
     * @param string $transactionRef Unique transaction reference
     * @param string $bankCode Bank code
     * @param string $deviceIMEI Device IMEI (optional)
     * @param array $additionalParams Additional parameters (optional)
     * @return array API response
     */
    public function cashDeposit(
        $merchantId,
        $terminalId,
        $aadhaarNumber,
        $amount,
        $customerName,
        $transactionRef,
        $bankCode,
        $deviceIMEI = "",
        $additionalParams = []
    ) {
        $payload = array_merge([
            'merchantId' => $merchantId,
            'terminalId' => $terminalId,
            'aadhaarNumber' => $aadhaarNumber,
            'amount' => $amount,
            'customerName' => $customerName,
            'transactionRef' => $transactionRef,
            'bankCode' => $bankCode
        ], $additionalParams);
        
        return $this->makeRequest($this->endpoints['cash_deposit'], $payload, $deviceIMEI);
    }
    
    /**
     * Get Bank List API
     * Fetches list of supported banks from Fingpay API
     * 
     * @param string $merchantId Merchant ID (optional, may be required by API)
     * @param array $additionalParams Additional parameters (optional)
     * @return array API response with bank list
     */
    public function getBankList($merchantId = null, $additionalParams = [])
    {
        $endpoint = $this->endpoints['bank_list'] ?? '/fpaepsservice/api/bankList';
        $bankListConfig = $this->config['bank_list_config'] ?? [
            'requires_encryption' => false,
            'method' => 'GET',
        ];
        
        $payload = $additionalParams;
        if ($merchantId !== null) {
            $payload['merchantId'] = $merchantId;
        }
        
        // Check if encryption is required
        if ($bankListConfig['requires_encryption'] ?? false) {
            // Use encrypted request
            $deviceIMEI = $payload['deviceIMEI'] ?? '';
            return $this->makeRequest($endpoint, $payload, $deviceIMEI);
        } else {
            // Use simple request (GET or POST without encryption)
            $method = $bankListConfig['method'] ?? 'GET';
            return $this->makeSimpleRequest($endpoint, $payload, $method);
        }
    }
    
    /**
     * User Onboarding API
     * Registers a new user/customer with Fingpay
     * 
     * @param array $userData User information
     * @param string $deviceIMEI Device IMEI
     * @return array API response
     */
    public function userOnboard($userData, $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['user_onboard'] ?? '/fpaepsservice/api/userOnboard/merchant/php/register';
        
        $payload = array_merge([
            'firstName' => $userData['firstName'] ?? '',
            'lastName' => $userData['lastName'] ?? '',
            'mobileNumber' => $userData['mobileNumber'] ?? '',
            'email' => $userData['email'] ?? '',
            'aadhaarNumber' => $userData['aadhaarNumber'] ?? '',
            'dateOfBirth' => $userData['dateOfBirth'] ?? '',
            'address' => $userData['address'] ?? '',
            'pincode' => $userData['pincode'] ?? '',
            'deviceIMEI' => $deviceIMEI,
        ], $userData);
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * User Verification API
     * Verifies user/customer details
     * 
     * @param string $aadhaarNumber Aadhaar number
     * @param string $mobileNumber Mobile number (optional)
     * @param string $deviceIMEI Device IMEI
     * @return array API response
     */
    public function userVerify($aadhaarNumber, $mobileNumber = "", $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['user_verify'] ?? '/fpaepsservice/api/userOnboard/merchant/php/verify';
        
        $payload = [
            'aadhaarNumber' => $aadhaarNumber,
        ];
        
        if (!empty($mobileNumber)) {
            $payload['mobileNumber'] = $mobileNumber;
        }
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * Merchant Onboarding API
     * Registers a new merchant with Fingpay
     * 
     * @param array $merchantData Merchant information
     * @param string $deviceIMEI Device IMEI for registration
     * @return array API response
     */
    public function merchantOnboard($merchantData, $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['merchant_onboard'] ?? '/fpaepsservice/api/merchantOnboard/merchant/php/register';
        
        $payload = array_merge([
            'merchantName' => $merchantData['merchantName'] ?? '',
            'merchantMobile' => $merchantData['merchantMobile'] ?? '',
            'merchantEmail' => $merchantData['merchantEmail'] ?? '',
            'merchantAddress' => $merchantData['merchantAddress'] ?? '',
            'merchantCity' => $merchantData['merchantCity'] ?? '',
            'merchantState' => $merchantData['merchantState'] ?? '',
            'merchantPincode' => $merchantData['merchantPincode'] ?? '',
            'businessType' => $merchantData['businessType'] ?? '',
            'gstNumber' => $merchantData['gstNumber'] ?? '',
            'panNumber' => $merchantData['panNumber'] ?? '',
            'deviceIMEI' => $deviceIMEI,
        ], $merchantData);
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * Merchant Status API
     * Check merchant onboarding status
     * 
     * @param string $merchantId Merchant ID
     * @param string $deviceIMEI Device IMEI
     * @return array API response
     */
    public function merchantStatus($merchantId, $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['merchant_status'] ?? '/fpaepsservice/api/merchantOnboard/merchant/php/status';
        
        $payload = [
            'merchantId' => $merchantId,
        ];
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * EKYC Verification API
     * Verify customer using Aadhaar EKYC
     * 
     * @param string $aadhaarNumber Aadhaar number
     * @param string $otp OTP received (if required)
     * @param string $deviceIMEI Device IMEI
     * @param array $additionalParams Additional parameters
     * @return array API response
     */
    public function ekycVerify($aadhaarNumber, $otp = "", $deviceIMEI = "", $additionalParams = [])
    {
        $endpoint = $this->endpoints['ekyc_verify'] ?? '/fpaepsservice/api/ekyc/merchant/php/verify';
        
        $payload = array_merge([
            'aadhaarNumber' => $aadhaarNumber,
        ], $additionalParams);
        
        if (!empty($otp)) {
            $payload['otp'] = $otp;
        }
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * EKYC Status API
     * Check EKYC verification status
     * 
     * @param string $referenceId Reference ID from EKYC request
     * @param string $deviceIMEI Device IMEI
     * @return array API response
     */
    public function ekycStatus($referenceId, $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['ekyc_status'] ?? '/fpaepsservice/api/ekyc/merchant/php/status';
        
        $payload = [
            'referenceId' => $referenceId,
        ];
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * EKYC Enrollment API
     * Enroll customer for EKYC services
     * 
     * @param array $customerData Customer information
     * @param string $deviceIMEI Device IMEI
     * @return array API response
     */
    public function ekycEnroll($customerData, $deviceIMEI = "")
    {
        $endpoint = $this->endpoints['ekyc_enroll'] ?? '/fpaepsservice/api/ekyc/merchant/php/enroll';
        
        $payload = array_merge([
            'aadhaarNumber' => $customerData['aadhaarNumber'] ?? '',
            'mobileNumber' => $customerData['mobileNumber'] ?? '',
            'name' => $customerData['name'] ?? '',
            'dateOfBirth' => $customerData['dateOfBirth'] ?? '',
            'address' => $customerData['address'] ?? '',
            'deviceIMEI' => $deviceIMEI,
        ], $customerData);
        
        return $this->makeRequest($endpoint, $payload, $deviceIMEI);
    }
    
    /**
     * Update transaction methods to support biometric data
     * Biometric data should be passed in additionalParams as:
     * ['biometricData' => '...', 'biometricType' => 'FINGERPRINT']
     */
}

