<?php

namespace Yas\Ocbc\Payment;

use Yas\Core\Handler\Log;
use Yas\Ocbc\Models\Transaction;
use Yas\Sales\Repositories\OrderRepository;
use Yas\Sales\Repositories\InvoiceRepository;
use Yas\Ocbc\Repositories\OcbcTransactionRepository;
/**
 * CyberSource REST API client using HTTP Signature authentication.
 */
class CyberSourceRestClient
{
    protected string $merchantId;
    protected string $keyId;
    protected string $sharedSecret;
    protected string $host;

    public function __construct()
    {
        $this->merchantId   = core()->getConfigData('sales.payment_methods.ocbc.ocbc_merchant_id');
        $this->keyId        = decryptData(core()->getConfigData('sales.payment_methods.ocbc.ocbc_rest_api_key'));
        $this->sharedSecret = decryptData(core()->getConfigData('sales.payment_methods.ocbc.ocbc_rest_api_shared_secret'));
        $this->host         = core()->getConfigData('sales.payment_methods.ocbc.test_mode') ? config('ocbc.testaccount_rest_api.host') : config('ocbc.liveaccount_rest_api.host');
    }

    /**
     * Retrieve details of a transaction.
     */
    public function getTransactionDetails(string $transactionId, string $logfolder): array
    {
        return $this->request('get', "/tss/v2/transactions/{$transactionId}", $transactionId,$logfolder);
    }

    /**
     * Send a signed HTTP request to CyberSource using cURL.
     */
    protected function request(string $method, string $resourcePath,string $transactionId, string $logfolder, ?array $payload = null): array
    {
        $log       = new Log();
        $sigMethod = strtolower($method);
        $date      = gmdate('D, d M Y H:i:s \G\M\T');
        $jsonBody  = $payload !== null ? json_encode($payload, JSON_UNESCAPED_SLASHES) : null;

        // 1. Build Signed Headers List (ORDER MATTERS STRICTLY)
        $signedHeaders = [
            'host'           => $this->host,
            'v-c-date'       => $date,
            'request-target' => "{$sigMethod} {$resourcePath}",
        ];

        if ($jsonBody !== null) {
            $signedHeaders['digest'] = 'SHA-256=' . base64_encode(hash('sha256', $jsonBody, true));
        }

        $signedHeaders['v-c-merchant-id'] = $this->merchantId;

        // 2. Construct Signature Base String
        $signatureBaseParts = [];
        foreach ($signedHeaders as $key => $value) {
            $signatureBaseParts[] = "{$key}: {$value}";
        }
        $signatureBase = implode("\n", $signatureBaseParts);

        // 3. Generate HMAC-SHA256 Signature
        $secretKey = base64_decode($this->sharedSecret);
        $rawHmac   = hash_hmac('sha256', $signatureBase, $secretKey, true);
        $signature = base64_encode($rawHmac);

        // 4. Build Signature Header String
        $signedHeaderKeys = implode(' ', array_keys($signedHeaders));
        $signatureHeader  = sprintf(
            'keyid="%s", algorithm="HmacSHA256", headers="%s", signature="%s"',
            $this->keyId,
            $signedHeaderKeys,
            $signature
        );

        // 5. Construct Raw Request Headers
        $requestHeaders = [
            "Host: {$this->host}",
            "v-c-merchant-id: {$this->merchantId}",
            "v-c-date: {$date}",
            "Signature: {$signatureHeader}",
            "Accept: application/hal+json",
        ];

        if ($jsonBody !== null) {
            $requestHeaders[] = "Content-Type: application/json";
            $requestHeaders[] = "Digest: {$signedHeaders['digest']}";
        }

        $url = "https://{$this->host}{$resourcePath}";

        // 6. Execute via Native cURL
        $ch = curl_init();
        $curlOptions = [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => $requestHeaders,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_TIMEOUT        => 30,
        ];

        curl_setopt_array($ch, $curlOptions);

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

        $decodedResponse = json_decode($responseBody, true) ?? [];

        $transaction = app(OcbcTransactionRepository::class)->where("ocbc_transaction_id", $transactionId)->whereNotIn("status", [
                        core()->getConfigData('sales.payment_methods.ocbc.transaction_status.paid'),
                        core()->getConfigData('sales.payment_methods.ocbc.transaction_status.cancel')
                    ])->where('inquiry_count', '<', config('ocbc.max_inquiry_count'))->first();

        if (! $transaction) {
            return [];
        }

        // 7. Logging & Return
        if ($httpCode >= 200 && $httpCode < 300) {
            $log->generateLog('Info', 'CyberSource REST API request succeeded', [
                'status' => $httpCode,
                'body'   => $decodedResponse,
                'url'    => $url,
                'order_no' => $transaction->order_no,
                'transaction_id' => $transactionId,
            ], config("ocbc.{$logfolder}"));
        } else {
            $log->generateLog('Error', 'CyberSource REST API request failed', [
                'status'     => $httpCode,
                'curl_error' => $curlError,
                'body'       => $decodedResponse ?: $responseBody,
                'url'        => $url,
                'order_no' => $transaction->order_no,
                'transaction_id' => $transactionId,
            ], config("ocbc.{$logfolder}"));
        }

        $isSuccessful = $this->isPaymentSuccessful($decodedResponse);

        $orderRepository = app(OrderRepository::class);
        $invoiceRepository = app(InvoiceRepository::class);

        $orders = $orderRepository->where('order_no', $transaction->order_no)->where('status', 'pending_payment')->where('customer_id', $transaction->customer_id)->get();
        //$orders = $orderRepository->where('order_no', $transaction->order_no)->where('customer_id', $transaction->customer_id)->get();

        if($isSuccessful){

            $log->generateLog('Info', "Ocbc transaction is successful", [
                    'order_no' => $transaction->order_no,
                    'transaction_id' => $transactionId,
                ], config("ocbc.{$logfolder}"));

            //transaction successful, update the inquiry count and status
            $transactionParams['inquiry_count'] = $transaction->inquiry_count + 1;
            $transactionParams['last_inquiry_at'] = now();
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.paid');
            $transaction->update($transactionParams);
            

            if ($orders->isEmpty()) {
                $log->generateLog('Info', "Ocbc transaction inquiry- No matching orders found", [
                    'order_no' => $transaction->order_no,
                    'customer_id' => $transaction->customer_id,
                    'transaction_id' => $transactionId,
                ], config("ocbc.{$logfolder}"));

                return [];
            }

            foreach($orders as $order){
                $log->generateLog('Info', "Ocbc transaction inquiry Process Order - Payment Successful", [
                    'order_no' => $order->order_no,
                    'transaction_id' => $transactionId,
                    'order_id' => $order->id,
                    'order_status' => $order->status,
                ], config("ocbc.{$logfolder}"));

                $data = $this->prepareInvoiceData($order);
                request()->merge(['can_create_transaction' => '1']);
                $invoice = $invoiceRepository->create($data);
                $order->update(['status' => 'processing']);

                $log->generateLog('Info', "Ocbc transaction inquiry handle changes completed", [
                    'order_no' => $order->order_no,
                    'order_id' => $order->id,
                    'transaction_id' => $transactionId,
                ], config("ocbc.{$logfolder}"));
            }
            
        }else{
             

            //transaction not successful, update the inquiry count and status if needed
            $transactionParams['inquiry_count'] = $transaction->inquiry_count + 1;
            $transactionParams['last_inquiry_at'] = now();
            $transaction->update($transactionParams);
            $log->generateLog('Info', "Ocbc transaction is NOT successful", [
                    'order_no' => $transaction->order_no,
                    'transaction_id' => $transactionId,
                    'inquiry_count' => $transactionParams['inquiry_count'],
                ], config("ocbc.{$logfolder}"));

            if($transactionParams['inquiry_count'] >= config('ocbc.max_inquiry_count')){
                //order needs to be process in which status if not payment sucessful and max inquiry count reached
                foreach($orders as $order){
                    $order->update(['status' => 'failed']);
                }
            }
        }
        // echo "<pre>";
        // print_r($decodedResponse);
        // echo "</pre>";
        return [
            'status_code' => $httpCode,
            'data'        => $decodedResponse,
            'is_successful' => $isSuccessful,
        ];
    }

    /**
     * Check whether a CyberSource REST API transaction response is successful.
     *
     * @param array $response The JSON response array from CyberSource REST API
     * @return bool
     */
    public function isPaymentSuccessful(array $response): bool
    {
        // 1. Check overall reason code (100 = Success)
        $reasonCode = $response['applicationInformation']['reasonCode'] ?? null;
        if ((string)$reasonCode == '100' || (string)$reasonCode == '110') {
            return true;
        }else {
            return false;
        }
        
    }

    /**
     * Prepares order's invoice data for creation.
     */
    protected function prepareInvoiceData($order): array
    {
        $invoiceData = [
            'order_id' => $order->id,
            'invoice'  => ['items' => []],
        ];

        foreach ($order->items as $item) {
            $invoiceData['invoice']['items'][$item->id] = $item->qty_to_invoice;
        }

        return $invoiceData;
    }
}