<?php
/**
 * QrisP2P Client Wrapper (PHP Native SDK)
 * Standalone, zero-dependency helper class untuk integrasi cepat merchant ke REST API & Webhook QrisP2P.
 * 
 * Lisensi: MIT
 * Website: https://qrisp2p.com
 */

class QrisP2PClient {
    private $baseUrl;
    private $apiKey;
    private $webhookSecret;
    private $timeout;

    /**
     * Inisialisasi Client
     * 
     * @param string $baseUrl       URL root server QrisP2P (cth: https://qrisp2p.com)
     * @param string $apiKey        Kunci API Live (iak_live_xxxxxxxx...)
     * @param string $webhookSecret Kunci rahasia webhook (whsec_xxxxxxxx...) untuk verifikasi signature
     * @param int    $timeout       Waktu timeout koneksi cURL dalam detik (default: 15)
     */
    public function __construct(string $baseUrl, string $apiKey, string $webhookSecret = '', int $timeout = 15) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey = trim($apiKey);
        $this->webhookSecret = trim($webhookSecret);
        $this->timeout = $timeout;
    }

    /**
     * Membuat tagihan pembayaran baru & Dynamic QRIS
     * 
     * @param int    $amount       Nominal pembayaran (min: 1000)
     * @param string $reference    ID referensi pesanan unik merchant (opsional)
     * @param string $customerName Nama pembeli (opsional)
     * @param string $description  Deskripsi pesanan (opsional)
     * @param string $callbackUrl  URL callback webhook khusus transaksi ini (opsional)
     * @return array Response JSON array dari API
     */
    public function createPayment(int $amount, string $reference = '', string $customerName = '', string $description = '', string $callbackUrl = ''): array {
        $payload = [
            'amount' => $amount
        ];
        if (!empty($reference))    $payload['reference'] = $reference;
        if (!empty($customerName)) $payload['customer_name'] = $customerName;
        if (!empty($description))  $payload['description'] = $description;
        if (!empty($callbackUrl))  $payload['callback_url'] = $callbackUrl;

        return $this->request('POST', '/api/v1/payment/create', $payload);
    }

    /**
     * Memeriksa status transaksi secara real-time
     * 
     * @param string $referenceOrInvoice ID referensi merchant atau nomor invoice platform
     * @return array
     */
    public function checkStatus(string $referenceOrInvoice): array {
        return $this->request('GET', '/api/v1/payment/status/' . rawurlencode($referenceOrInvoice));
    }

    /**
     * Membatalkan transaksi pending
     * 
     * @param string $referenceOrInvoice ID referensi merchant atau nomor invoice platform
     * @return array
     */
    public function cancelPayment(string $referenceOrInvoice): array {
        return $this->request('POST', '/api/v1/payment/cancel', [
            'reference' => $referenceOrInvoice
        ]);
    }

    /**
     * Memeriksa status kesehatan koneksi sesi GoBiz merchant
     * 
     * @return array
     */
    public function getAccountStatus(): array {
        return $this->request('GET', '/api/v1/account/status');
    }

    /**
     * Memverifikasi payload & HMAC-SHA256 signature pada Webhook endpoint toko Anda
     * 
     * @param string|null $rawPayload        Payload JSON mentah (otomatis ambil dari php://input jika null)
     * @param string|null $receivedSignature Signature yang diterima (otomatis ambil dari header jika null)
     * @return array ['isValid' => bool, 'data' => array|null, 'error' => string|null]
     */
    public function verifyWebhook(?string $rawPayload = null, ?string $receivedSignature = null): array {
        if ($rawPayload === null) {
            $rawPayload = file_get_contents('php://input');
        }

        $data = json_decode($rawPayload, true);
        if (!$data || !is_array($data)) {
            return [
                'isValid' => false,
                'data' => null,
                'error' => 'Payload JSON tidak valid'
            ];
        }

        if ($receivedSignature === null) {
            $receivedSignature = $_SERVER['HTTP_X_SIGNATURE'] ?? ($_SERVER['HTTP_X_QRISP2P_SIGNATURE'] ?? ($_SERVER['HTTP_X_AUTOGOPAY_SIGNATURE'] ?? ($data['signature'] ?? '')));
        }

        if (empty($receivedSignature)) {
            return [
                'isValid' => false,
                'data' => $data,
                'error' => 'Header atau field signature tidak ditemukan'
            ];
        }

        // 1. Verifikasi langsung dari raw JSON string (Standar Utama Stripe / GitHub)
        $expectedRaw = hash_hmac('sha256', (string)$rawPayload, $this->webhookSecret);
        $isValid = hash_equals($expectedRaw, (string)$receivedSignature);

        // 2. Fallback jika body memuat field signature
        if (!$isValid) {
            $cleanData = $data;
            unset($cleanData['signature']);
            $expectedClean = hash_hmac('sha256', json_encode($cleanData), $this->webhookSecret);
            $isValid = hash_equals($expectedClean, (string)$receivedSignature);
        }

        return [
            'isValid' => $isValid,
            'data' => $data,
            'error' => $isValid ? null : 'Signature mismatch (verifikasi HMAC gagal)'
        ];
    }

    /**
     * Alias untuk verifyWebhook() agar kompatibel dengan berbagai gaya penamaan
     */
    public function validateWebhook(?string $rawPayload = null, ?string $receivedSignature = null): array {
        return $this->verifyWebhook($rawPayload, $receivedSignature);
    }

    /**
     * Helper respon cepat HTTP 200 OK untuk Webhook Handler
     * 
     * @param bool   $success Status berhasil
     * @param string $message Pesan
     * @param int    $httpCode HTTP Code (default: 200)
     */
    public static function respondWebhook(bool $success = true, string $message = 'OK', int $httpCode = 200): void {
        http_response_code($httpCode);
        header('Content-Type: application/json');
        echo json_encode([
            'status' => $success ? 'success' : 'failed',
            'message' => $message
        ]);
        exit;
    }

    /**
     * Eksekusi HTTP request menggunakan cURL native
     */
    private function request(string $method, string $path, array $data = []): array {
        $url = $this->baseUrl . $path;
        $ch = curl_init();

        $headers = [
            'X-API-Key: ' . $this->apiKey,
            'Accept: application/json',
            'User-Agent: QrisP2P-PHP-SDK/1.0'
        ];

        if ($method === 'POST') {
            $headers[] = 'Content-Type: application/json';
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => $this->timeout,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_SSL_VERIFYPEER => true
        ]);

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

        if ($curlError) {
            return [
                'success' => false,
                'http_code' => 0,
                'message' => 'cURL Error: ' . $curlError,
                'data' => null
            ];
        }

        $json = json_decode($rawResponse, true);
        if ($json === null) {
            return [
                'success' => false,
                'http_code' => $httpCode,
                'message' => 'Respons server bukan JSON valid: ' . substr($rawResponse, 0, 200),
                'raw' => $rawResponse
            ];
        }

        $json['http_code'] = $httpCode;
        return $json;
    }
}

// Backward compatibility & dynamic brand aliases
if (!class_exists('AutoGopayClient', false)) {
    class_alias('QrisP2PClient', 'AutoGopayClient');
}

$dynamicBrandClass = preg_replace('/[^a-zA-Z0-9]/', '', defined('APP_NAME') ? APP_NAME : '') . 'Client';
if (!empty($dynamicBrandClass) && !class_exists($dynamicBrandClass, false)) {
    class_alias('QrisP2PClient', $dynamicBrandClass);
}
