<?php

namespace Yas\Ocbc\Payment;

//use Stripe\Webhook;
use Yas\Sales\Order;
//use Stripe\StripeClient;
use Yas\Core\Handler\Log;
use Illuminate\Support\Str;
use Yas\Checkout\Facades\Cart;
use Yas\Ocbc\Models\Transaction;
use Webkul\Payment\Payment\Payment;
use Illuminate\Support\Facades\Storage;
use Yas\Sales\Transformers\OrderResource;
use Yas\Sales\Repositories\OrderRepository;
use Yas\Checkout\Repositories\CartRepository;
use Yas\Sales\Repositories\InvoiceRepository;
use Illuminate\Support\Facades\Http;
use Yas\Product\Repositories\ProductRepository;

class Ocbc extends Payment
{
    /**
     * Payment method code
     *
     * @var string
     */
    protected $code = 'ocbc';
    protected $accessKey;
    protected $merchantId ;
    protected $secretKey;
    protected $profileId;
    protected $baseUrl;
    protected $returnUrlSuccess;
    protected $returnUrlFailure;
    protected $returnUrlCancel; 
    protected $currency;
    protected $locale;
    protected $transactionType;
    protected $ocbc;

    public function __construct()
    {
        $this->accessKey = core()->getConfigData('sales.payment_methods.ocbc.ocbc_access_key');
        $this->profileId = core()->getConfigData('sales.payment_methods.ocbc.ocbc_profile_id');
        $this->merchantId = core()->getConfigData('sales.payment_methods.ocbc.ocbc_merchant_id');
        $this->secretKey = core()->getConfigData('sales.payment_methods.ocbc.ocbc_secret_key');
        $this->baseUrl = config('ocbc.base_url');
        //$this->baseUrl = 'https://secureacceptance.cybersource.com/pay';
       
        $this->returnUrlSuccess = route('ocbc.payment.callback');
        $this->returnUrlFailure = route('ocbc.payment.callback');
        $this->returnUrlCancel = route('yas_theme.checkout.onepage.index');
        
        $this->currency = config('ocbc.currency');
        $this->locale = config('ocbc.locale');
        $this->transactionType = config('ocbc.transaction_type');
    }

    /**
     * Get Redirect Url
     * 
     * return string
     */
    public function getRedirectUrl(): string
    {
        return route('ocbc.payment.initiate');
    }

    /**
     * Checks if the cart grand total is greater than 0.5.
     *
     * @return bool
     */
    public function isAvailable(): bool
    {
        if($this->getConfigData('active')){
            if(empty(Cart::getCart())) {
                return false;
            }
            return Cart::getCart()->grand_total >= 0.5;
        }
        return $this->getConfigData('active');
        
    }

    /**
     * Returns payment method image.
     */
    public function getImage(): string
    {
        $url = $this->getConfigData('image');

        return $url ? Storage::url($url) : bagisto_asset('images/money-transfer.png', 'shop');
    }

      /**
     * Generates the HMAC SHA256 signature for CyberSource Secure Acceptance.
     * Refer to PDF Page 14 ("Hashing the Fields") and Page 16 (Java example).
     *
     * The method is: HMAC SHA256 over the concatenated values of signed fields (comma-separated),
     * using the secret key, then Base64 encoded.
     *
     * @param array $params The full set of parameters to be sent (or received).
     * @param string $signedFieldNamesString Comma-separated string of field names to sign.
     * @param string $secretKey The merchant's shared secret key.
     * @return string Base64 encoded signature.
     */
    protected function generateSignature(array $params, string $signedFieldNamesString, string $secretKey): string
    {
        $signedFieldNames = explode(',', $signedFieldNamesString);
        $dataValues = [];

        foreach ($signedFieldNames as $fieldName) {
            
            $dataValues[] = $params[$fieldName] ?? '';
        }

        $message = implode(',', $dataValues);

        $signature = hash_hmac('sha256', $message, $secretKey, true);

        return base64_encode($signature);
    }

    

       /**
     * Prepares the payment request parameters and returns the HTML for auto-submission.
     *
     * @param Order $order The order model instance.
     * @param array $customerDetails Optional array like ['email', 'forename', 'surname', 'phone', 'address_line1', 'city', 'state', 'postal_code', 'country']
     * @return string HTML form ready for auto-submission.
     */
    public function initiatePayment(): string
    {
        $log = new Log();
        $cart = Cart::getCart();
        $totalAmount = $cart->grand_total;
        $customerDetails = auth()->user('customer');
        $customerDefaultAddress = $customerDetails->addresses()->where('default_address', 1)->first();

        if(!$customerDefaultAddress){
            $customerDefaultAddress = $customerDetails->addresses()->first();
        }

        $transactionUuid = (string) Str::orderedUuid();
        $signedDateTime = now()->timezone('UTC')->toIso8601String(); 

        $params = [
            // --- Core Authentication Fields (MUST BE SIGNED) ---
            'access_key' => $this->accessKey,
            'profile_id' => $this->profileId,
            'transaction_uuid' => $transactionUuid,
            //'signed_date_time' => $signedDateTime,
            'signed_field_names'     => '', // Placeholder for the signed field list
            // 'unsigned_field_names'   => '', // Fields to ignore in the signature
            'signed_date_time' => gmdate("Y-m-d\TH:i:s\Z"),
            'locale' => $this->locale,

            // --- Transaction Details (MUST BE SIGNED) ---
            'transaction_type' => $this->transactionType,
            'reference_number' => $transactionUuid, // Your internal order ID
            'amount' => number_format($totalAmount, 2, '.', ''), 
            'currency' => $this->currency,

            // --- Customer & Billing Information (MUST BE SIGNED if included) ---
            'bill_to_email' => $customerDetails->email ?? '',
            'bill_to_forename' => $customerDetails->first_name ?? '',
            'bill_to_surname' => $customerDetails->last_name ?? '',
            'bill_to_phone' => $customerDetails->phone ?? '',
            'bill_to_address_line1' => $customerDefaultAddress->address ?? '',
            'bill_to_address_city' => $customerDefaultAddress->city ?? '',
            'bill_to_address_state' => $customerDefaultAddress->state ?? '', 
            'bill_to_address_postal_code' => $customerDefaultAddress->postcode ?? '',
            'bill_to_address_country' => $customerDefaultAddress->country ?? '',
            'payment_method'         => 'card',

            // --- Return URLs (MUST BE SIGNED) ---
            'return_url' => $this->returnUrlSuccess, 
            'cancel_url' => $this->returnUrlCancel, 
            'return_url_fail' => $this->returnUrlSuccess, 

        ];
        
        // ---------------Signature code change Start--------------------//

        // --- 3. Prepare the Field Names for the Signature ---
        // This list MUST contain every field you are sending EXCEPT the signature field itself.

        //  Explicitly define only the keys you want to include in the signature
        $signed_fields_list = [
            'access_key',
            'amount',
            'currency',
            'locale',
            'profile_id',
            'reference_number',
            'signed_date_time',
            'signed_field_names', // CyberSource requires this field name to be included in the signature list
            'transaction_uuid',
            'transaction_type'
        ];

        //  Generate the comma-separated string of these field names
        $params['signed_field_names'] = implode(",", $signed_fields_list);

        //  Map everything else in $params as 'unsigned_field_names'
        $all_keys = array_keys($params);
        
        // --- 4. Generate the Signature (The Security Step) ---
        $signature_string = '';
        foreach ($signed_fields_list as $field) {
            // Append the value of each signed field, followed by a comma
            $signature_string .= $field . '=' . $params[$field] . ',';
        }
        // Remove the trailing comma and concatenate with the secret key
        $signature_string = rtrim($signature_string, ',');

        // Generate the SHA256 hash of the string
        $signature = base64_encode(hash_hmac('sha256', $signature_string, $this->secretKey, true));

        // --- 5. Add the Signature to the Data Array ---
        $params['signature'] = $signature;
        // ---------------Signature code change end--------------------//

        
         // create ocbc transaction request
        $transactionParams = [
            'request_id' => $transactionUuid,
            'customer_id' => $customerDetails->id,
            'cart_id' => $cart->id,
            'channel_id' => $cart->channel_id,
            'payment_intent_request' => json_encode($params),
            'amount' => $totalAmount,
        ];
        $transaction = Transaction::firstOrCreate($transactionParams);

        $log->generateLog('Info', "Ocbc Transaction Request", [
            'params' => $transactionParams,
            "signature_string" => $signature_string
        ], config("ocbc.order_process"));

        //create order with pending status and update order id in transaction
        $cart = Cart::getCart();
        $orderRepository = app(OrderRepository::class);
        $productRepository = app(ProductRepository::class);

        if($cart) {
            // foreach($cart->items as $cartItem){
            //     $productRepository->where('id', $cartItem->product_id)->update(['holding_status'=>0]);
            // }

            $companyIds = [];
            foreach($cart->company_items as $item)
            {
                $companyIds[$item->company_id][] = $item->id;
            }

            $order = null;
            $orderNo = $orderRepository->generateOrderNo($companyIds);

            foreach ($companyIds as $companyId => $items) {

                $order = new Order();
                $order->initCart($companyId, $cart);

                foreach($cart->company_items as $item)
                {
                    if(in_array($item->id, $items)) {
                        $order->addItemToCart($item);
                    }
                }

                $updatedCart = $order->getCart();

                $data = (new OrderResource($updatedCart))->jsonSerialize();
                if ($orderNo) {
                    $data['order_no'] = $orderNo;
                }
        
                $log->generateLog('Info', "Ocbc Order before order create", [
                    'cart_data' => $data,
                ], config("ocbc.order_process"));

                $order = $orderRepository->create($data);

                $log->generateLog('Info', "Ocbc Order created", [
                    'order' => $order->id,
                    'order_no' => $order->order_no
                ], config("ocbc.order_process"));

                if($order){
                    $order->update(['status' => 'pending_payment']);
                }
                
            }

            //update params
            if($order && $orderNo) {
                $transaction->update([
                    'order_no' => $orderNo
                ]);

                $log->generateLog('Info', "Order created and update order_no in ocbc_transaction", [
                    'order_no' => $orderNo,
                ], config("ocbc.order_process"));
            }
            
        }

        // Build the HTML form for auto-submission
        $htmlForm = '<!DOCTYPE html><html><head><title>Redirecting to Payment Gateway...</title></head><body>';
        $htmlForm .= '<p>Please do not close your browser. You are being redirected to the payment gateway.</p>';
        $htmlForm .= '<form id="cybersource_payment_form" action="' . htmlspecialchars($this->baseUrl) . '" method="POST">';
        foreach ($params as $key => $value) {
            $htmlForm .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($value) . '">';
        }
        $htmlForm .= '</form>';
        $htmlForm .= '<script type="text/javascript">document.getElementById("cybersource_payment_form").submit();</script>';
        $htmlForm .= '</body></html>';

        return $htmlForm;
    }

    /**
     * Handles the callback from CyberSource after payment.
     * This method expects POST data as per CyberSource's server-to-server notification.
     *
     * @param array $cybersourceResponse The array of parameters received from CyberSource.
     * @return array An array containing status, message, and updated order (if found).
     */
    public function handleCallback(array $cybersourceResponse): array
    {
        $log = new Log();
        $log->generateLog('Info', "OCBC Payment Callback Received:", [
            'ocbc response' => $cybersourceResponse
        ], config("ocbc.order_process"));

        // --- 1. Signature Verification ---
        $receivedSignature = $cybersourceResponse['signature'] ?? null;
        $signedFieldNames = $cybersourceResponse['signed_field_names'] ?? '';

        if (!$receivedSignature || !$signedFieldNames) {
            $log->generateLog('error', "OCBC Payment Callback Error: Missing signature or signed_field_names.", [
                'ocbc response' => $cybersourceResponse
            ], config("ocbc.order_process"));

            return ['status' => 'error', 'message' => 'Invalid payment callback: Missing signature or signed fields.', 'order' => null];
        }

        //$expectedSignature = $this->generateSignature($cybersourceResponse, $signedFieldNamesString, $this->secretKey);
        $signedFieldNamesArray = explode(",", $signedFieldNames);

        $signature_string = '';
        foreach ($signedFieldNamesArray as $field) {
            // Append the value of each signed field, followed by a comma
            $signature_string .= $field . '=' . $cybersourceResponse[$field] . ',';
        }
        // Remove the trailing comma and concatenate with the secret key
        $signature_string = rtrim($signature_string, ',');

        $expectedSignature = base64_encode(hash_hmac('sha256', $signature_string, $this->secretKey, true));

        if ($receivedSignature !== $expectedSignature) {
            
            $log->generateLog('error', "OCBC Payment Callback Error: Signature verification failed.", [
                'received' => $receivedSignature,
                'expected' => $expectedSignature,
                'req_transaction_uuid'=> $cybersourceResponse['req_transaction_uuid'] ?? null,
                'response' => $cybersourceResponse
            ], config("ocbc.order_process"));
            return ['status' => 'error', 'message' => 'Payment verification failed: Invalid signature.', 'order' => null];
        }else{
            $log->generateLog('Info', "OCBC Payment Callback Signature Verified Successfully.", [
                'req_transaction_uuid'=> $cybersourceResponse['req_transaction_uuid'] ?? null,
            ], config("ocbc.order_process"));
        }

        if (!$cybersourceResponse['req_transaction_uuid']) {
            
            $log->generateLog('error', "OCBC Payment Callback Error: Missing request_transaction_uuid in response.", [
                'req_transaction_uuid'=> $cybersourceResponse['req_transaction_uuid'] ?? null,
                'response' => $cybersourceResponse
            ], config("ocbc.order_process"));
            return ['status' => 'error', 'message' => 'Missing request transaction ID in payment callback.'];
        }

        $cart = Cart::getCart();
        $orderRepository = app(OrderRepository::class);
        $invoiceRepository = app(InvoiceRepository::class);
        $cartRepository = app(CartRepository::class);
       
        $transaction = Transaction::where("request_id", $cybersourceResponse['req_transaction_uuid'])
                ->where("status", [
                    core()->getConfigData('sales.payment_methods.ocbc.transaction_status.pending'),
                ])
                ->first();

        $transactionParams['payment_intent_response'] = json_encode($cybersourceResponse);
        $transactionParams['payment_token'] = $cybersourceResponse['request_token'] ?? null;

        if ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.paid')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.paid');
        } else if ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.pending')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.pending');
        } elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.fail')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.fail');
        }elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.error')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.error');
        }elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.error')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.error');
        }elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.cancel')) {
            $transactionParams['status'] = core()->getConfigData('sales.payment_methods.ocbc.transaction_status.cancel');
        }

        if($transaction) {
            $log->generateLog('Info', "Ocbc Payment Process Order transaction found", [
                    'request_id' => $cybersourceResponse['req_transaction_uuid'],
                    'status' => $cybersourceResponse['decision'],
                    'transaction' => $transaction->toArray(),
                ], config("ocbc.order_process"));

            $transaction->update($transactionParams);
            $cartRepository->update(['is_active' => false], $transaction->cart_id);
            
            $orders = $orderRepository->where('order_no', $transaction->order_no)->where('customer_id', $transaction->customer_id)->get();

            if(isset($cybersourceResponse['decision']) && $cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.paid')) {

                foreach($orders as $order){
                    //$order->update(['status' => 'pending']);
                    $log->generateLog('Info', "Ocbc Payment Process Order - Payment Successful", [
                        'request_id' => $cybersourceResponse['req_transaction_uuid'],
                        'order_id' => $order->id,
                        'order_status' => $order->status,
                    ], config("ocbc.order_process"));

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

                    $log->generateLog('Info', "Ocbc Payment handle callback changes completed", [
                        'request_id' => $cybersourceResponse['req_transaction_uuid'],
                        'order_id' => $order->id,
                    ], config("ocbc.order_process"));
                }
                
            } 
            else if ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.pending')) {

                foreach($orders as $order){
                    $order->update(['status' => 'pending_payment']);
                }
                $log->generateLog('Info', "Ocbc order payment status pending", [
                        'request_id' => $cybersourceResponse['req_transaction_uuid'],
                        'order_no' => $transaction->order_no,
                    ], config("ocbc.order_process"));
                
            } elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.fail')) {
                //do here for fail status if needed
                foreach($orders as $order){
                    $order->update(['status' => 'failed']);
                }
                $log->generateLog('Info', "Ocbc order status failed", [
                    'request_id' => $cybersourceResponse['req_transaction_uuid'],
                    'order_no' => $transaction->order_no,
                ], config("ocbc.order_process"));
            }elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.error')) {
                //do here for error status if needed
                foreach($orders as $order){
                    $order->update(['status' => 'failed']);
                }
                $log->generateLog('Info', "Ocbc order status error", [
                    'request_id' => $cybersourceResponse['req_transaction_uuid'],
                    'order_no' => $transaction->order_no,
                ], config("ocbc.order_process"));
            }elseif ($cybersourceResponse['decision'] == core()->getConfigData('sales.payment_methods.ocbc.payment_status.cancel')) {
                //do here for cancel status if needed
                foreach($orders as $order){
                    $order->update(['status' => 'canceled']);
                }
                $log->generateLog('Info', "Ocbc order status cancel", [
                    'request_id' => $cybersourceResponse['req_transaction_uuid'],
                    'order_no' => $transaction->order_no,
                ], config("ocbc.order_process"));
            }
            
            return ['order_id'=>$order->id, 'status'=>$cybersourceResponse['decision']];
        }

       return [];
    }


    /**
     * 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;
    }
}
