<?php

namespace KnitPayUPI\Gateways\UpiQR\GpayBiz;

use Pronamic\WordPress\Http\Facades\Http;
use Exception;
use Pronamic\WordPress\Pay\Payments\PaymentStatus;

/**
 * Prevent loading this file directly
 */
defined( 'ABSPATH' ) || exit();

/**
 * Title: Google Pay for Business API Client
 * Copyright: 2020-2026 Knit Pay
 *
 * @author  Knit Pay
 * @version 1.8.0.0
 * @since   1.8.0.0
 */
class Client {
	const CONNECTION_TIMEOUT = 20;

	private $config;

	public function __construct( Config $config ) {
		$this->config = $config;
	}

	private function rotate_cookies() {
		if ( get_transient( 'knit_pay_upi_gpay_rotate_cookies_' . $this->config->config_id ) ) {
			return;
		}

		$html_body = $this->create_connection( 'https://accounts.google.com/RotateCookiesPage?og_pid=453&rot=3&origin=https%3A%2F%2Fpay.google.com&exp_id=0', 'GET' );

		$init_array = [];
		if ( preg_match( '/<script\s+nonce="[^"]*">\s*init\s*\(\s*([^)]+)\s*\)\s*<\/script>/', $html_body, $matches ) ) {
			// Split the parameters by comma, handling quoted strings and numbers
			$params_string = $matches[1];
			preg_match_all( '/\'[^\']*\'|"[^"]*"|-?\d+\.?\d*/', $params_string, $param_matches );
			$init_array = array_map(
				function ( $param ) {
					// Remove quotes from string parameters
					$param = trim( $param );
					if ( ( $param[0] === "'" && $param[ strlen( $param ) - 1 ] === "'" ) ||
					( $param[0] === '"' && $param[ strlen( $param ) - 1 ] === '"' ) ) {
							return substr( $param, 1, -1 );
					}
					// Convert numeric strings to integers (convert float to int)
					if ( is_numeric( $param ) ) {
						return (int) $param;
					}
					return $param;
				},
				$param_matches[0] 
			);
		}

		if ( empty( $init_array ) ) {
			throw new Exception( 'Something went wrong while refreshing the UPI connection.' );
		}

		$cookie_rotate_data = wp_json_encode(
			[
				intval( $init_array[1] ),
				$init_array[0],
			]
		);

		$this->create_connection( 'https://accounts.google.com/RotateCookies', 'POST', $cookie_rotate_data, true );

		set_transient( 'knit_pay_upi_gpay_rotate_cookies_' . $this->config->config_id, true, intval( $init_array[4] ) );
	}

	public function get_merchant_details() {
		$merchant_info = $this->call_google_batchexecute( $this->config->connection_data, 1 );

		return [
			'vpa'        => $merchant_info[4][1][0][0],
			'payee_name' => $merchant_info[1][0],
		];
	}

	public function check_connection() {
		return $this->get_transaction_details( $this->config->connection_data );
	}

	public function get_transaction_details( $gpay_data ) {
		if ( '' === $this->config->cookies ) {
			throw new Exception( 'GPay for Business is not connected.' );
		}

		$transactions          = $this->call_google_batchexecute( $gpay_data, 0 );
		$formated_transactions = [];
		
		if ( ! is_array( $transactions ) ) {
			return $formated_transactions;
		}

		foreach ( $transactions as $transaction ) {
			if ( ! is_array( $transaction ) ) {
				continue;
			}

			$settlement_status = '';
			$status            = '';
			if ( isset( $transaction[5] ) ) {
				$transaction[5] = (int) $transaction[5];
				if ( 4 === $transaction[5] ) {
					$settlement_status = 'scheduled_to_settle';
					$status            = PaymentStatus::SUCCESS;
				} elseif ( 5 === $transaction[5] ) {
					$settlement_status = 'settled';
					$status            = PaymentStatus::SUCCESS;
				} elseif ( 8 === $transaction[5] ) {
					$settlement_status = 'on_hold';
					$status            = PaymentStatus::ON_HOLD;
				}
			}

			$amount = '';
			if ( isset( $transaction[3][1] ) ) {
				$amount = $transaction[3][1];
			}
			if ( isset( $transaction[3][2] ) ) {
				$amount .= '.' . str_pad( $transaction[3][2], 9, '0', STR_PAD_LEFT );
			}

			$provider_response = [
				'sender_name'        => $transaction[8][0] ?? null,
				'sender_upi'         => $transaction[8][1] ?? '',
				'transaction_notes'  => $transaction[9] ?? '',
				'utr'                => $transaction[1] ?? null,
				'amount'             => floatval( $amount ),
				'currency'           => $transaction[3][0] ?? null,
				'status'             => $status,
				'creation_timestamp' => $transaction[2][0] ?? null,
				'update_timestamp'   => $transaction[7][0] ?? null,
				'settlement_status'  => $settlement_status,
			];

			$formated_transactions[] = [
				'kp_transaction_id' => $provider_response['transaction_notes'],
				'utr'               => $provider_response['utr'],
				'amount'            => $provider_response['amount'],
				'currency'          => $provider_response['currency'],
				'status'            => $provider_response['status'],
				'provider_response' => $provider_response,
			];
		}

		return $formated_transactions;
	}

	public function get_gpay_biz_connection_data() {
		$data = [
			'mode'                => 'live',
			'gateway'             => 'gpay-biz',
			'source'              => 'config',
			'amount'              => '0',
			'currency'            => 'INR',
			'knitpay_version'     => KNITPAY_VERSION,
			'knitpay_upi_version' => KNITPAY_UPI_VERSION,
			'php_version'         => PHP_VERSION,
			'website_url'         => home_url( '/' ),
			'data'                => [
				'request_type' => 'connection_data',
				'mid'          => $this->config->merchant_id,
			],
		];

		\KnitPayPro::check_knit_pay_pro_setup();

		$response = Http::post(
			\KNIT_PAY_UPI_RAPIDAPI_BASE_URL . 'payments/upi/request',
			[
				'body'    => wp_json_encode( $data ),
				'headers' => [
					'X-RapidAPI-Host' => \KNIT_PAY_UPI_RAPIDAPI_HOST,
					'X-RapidAPI-Key'  => get_option( 'knit_pay_pro_setup_rapidapi_key' ),
				],
				'timeout' => self::CONNECTION_TIMEOUT,
			]
		);

		$result = $response->json();

		if ( '403' === (string) $response->status() ) {
			$api_link = ' https://rapidapi.com/knitpay/api/knit-pay-upi/pricing';
			throw new Exception( 'RapidAPI Error: ' . $result->message . $api_link );
		} elseif ( '200' !== (string) $response->status() ) {
			throw new Exception( 'RapidAPI Error: ' . $result->message );
		}

		return $result->data->gpay_biz_data;
	}

	private function call_google_batchexecute( $payload_json, $data_key ) {
		$this->rotate_cookies();

		$global_data = $this->get_wiz_global_data();

		if ( '' === $payload_json ) {
			throw new Exception( 'Something went wrong.' );
		}

		$payload_array = json_decode( $payload_json, true );

		$bl   = isset( $global_data['cfb2h'] ) ? $global_data['cfb2h'] : 'boq_payments-merchant-console-ui_20251116.00_p1';
		$path = isset( $global_data['Im6cmf'] ) ? $global_data['Im6cmf'] : '/g4b/_/SMBConsoleUI';

		$api_url = add_query_arg(
			[
				'rpcids'       => $payload_array[0][0][0],
				'f.sid'        => $global_data['FdrFJe'],
				'bl'           => $bl,
				'hl'           => 'en',
				'soc-app'      => '1',
				'soc-platform' => '1',
				'soc-device'   => '1',
				'_reqid'       => wp_rand( 1000000, 9999999 ),
				'rt'           => 'b',
			],
			"https://pay.google.com{$path}/data/batchexecute"
		);

		$post_body = http_build_query(
			[
				'f.req' => $payload_json,
				'at'    => $global_data['SNlM0e'],
			] 
		);

		$result_json = $this->create_connection( $api_url, 'POST', $post_body );

		$result_array = json_decode( $result_json, true );

		if ( json_last_error() !== JSON_ERROR_NONE || ! isset( $result_array ) ) {
			throw new Exception( 'Something went wrong.' );
		}

		return $result_array[ $data_key ];
	}

	public function get_wiz_global_data() {
		if ( get_transient( 'knit_pay_upi_gpay_get_wiz_data_' . $this->config->config_id ) ) {
			return $this->config->wiz_global_data;
		}

		$wiz_data = [];

		$dashUrl   = 'https://pay.google.com/g4b/activity/' . $this->config->merchant_id . '?utm_source=g4bapp';
		$html_body = $this->create_connection( $dashUrl, 'GET', null, true );

		// Extract window.WIZ_global_data from the script tag
		if ( preg_match( '/window\.WIZ_global_data\s*=\s*({[^;]+});/', $html_body, $matches ) ) {
			$wiz_data = json_decode( $matches[1], true );
		}

		if ( empty( $wiz_data ) ) {
			throw new Exception( 'Something went wrong while fetching the global data.' );
		}

		update_post_meta( $this->config->config_id, '_pronamic_gateway_upi_qr_gpay_biz_wiz_global_data', $wiz_data );
		$this->config->wiz_global_data = $wiz_data;

		set_transient( 'knit_pay_upi_gpay_get_wiz_data_' . $this->config->config_id, true, HOUR_IN_SECONDS );
		
		return $wiz_data;
	}

	private function create_connection( $url, $method, $data = null, $save_cookies = true ) {
		$args = [
			'method'      => $method,
			'timeout'     => self::CONNECTION_TIMEOUT,
			'redirection' => 0,
			'cookies'     => $this->config->cookies,
		];

		if ( isset( $data ) && null !== json_decode( $data ) && json_last_error() === JSON_ERROR_NONE ) {
			$args['headers'] = [
				'Content-Type' => 'application/json',
			];
		}

		if ( 'POST' === $method ) {
			$args['body'] = $data;
		}

		$response      = wp_remote_request( $url, $args );
		$response_body = wp_remote_retrieve_body( $response );

		$status_code = wp_remote_retrieve_response_code( $response );
		if ( 302 === $status_code || 401 === $status_code ) {
			Integration::clear_config( $this->config->config_id );

			// If redirected to profiles page, clear merchant ID and throw error.
			if ( str_contains( wp_remote_retrieve_header( $response, 'location' ), 'profiles' ) ) {
				delete_post_meta( $this->config->config_id, '_pronamic_gateway_upi_qr_gpay_biz_merchant_id' );
				throw new Exception( 'Provided merchant ID is incorrect.' );
			}

			// If redirected to login page, throw error.
			throw new Exception( 'Google Pay for Business is not connected.', $status_code );
		}
		
		$response_cookies = wp_remote_retrieve_cookies( $response );
		
		if ( $save_cookies ) {
			update_post_meta( $this->config->config_id, '_pronamic_gateway_upi_qr_gpay_biz_cookies', $response_cookies );
		}
		$this->config->cookies = $response_cookies;
		
		if ( 'GET' === $method ) {
			return $response_body;
		}

		// Extract JSON by removing extra characters from beginning and end of protobuf result.
		preg_match( '/\[.*\]/s', $response_body, $matches );
		$google_response_json = $matches[0] ?? '';

		return $google_response_json;
	}
}
