<?php
namespace KnitPayUPI\Gateways\UpiQR\BharatPe;

use Exception;
use KnitPay\Gateways\PaymentMethods;
use KnitPayUPI\Gateways\UpiQR\KnitPayUPIGateway;
use Pronamic\WordPress\Pay\Core\PaymentMethod;
use Pronamic\WordPress\Pay\Payments\FailureReason;
use Pronamic\WordPress\Pay\Payments\Payment;
use Pronamic\WordPress\Pay\Payments\PaymentStatus as Core_Statuses;

/**
 * Title: BharatPe Gateway
 * Copyright: 2020-2026 Knit Pay
 *
 * @author Knit Pay
 * @version 1.6.1.0
 * @since 1.3.0.0
 */
class Gateway extends KnitPayUPIGateway {
	private $client;

	/**
	 * Constructs and initializes an BharatPe gateway
	 *
	 * @param Config $config
	 *            Config.
	 */
	public function __construct( Config $config ) {     
		// Supported features.
		$this->supports = [
			'payment_status_request',
		];
		
		parent::__construct( $config );

		$this->show_manual_confirmation = true;
		$this->merchant_verified        = true; // TODO: Implement merchant verification logic
		if ( 'auto' !== $config->payment_status_mode ) {
			$this->payment_expiry_seconds = 600;
			$this->enable_polling         = false;
		}

		$this->client = new Client( $config );
	}
	
	/**
	 * Start.
	 *
	 * @see KnitPayUPIGateway::start()
	 *
	 * @param Payment $payment
	 *            Payment.
	 */
	public function start( Payment $payment ) {
		parent::start( $payment );

		// Check connection.
		$this->client->get_transaction_details( $this->config->connection_data );

		// get the unique amount in case of auto mode.
		if ( 'auto' === $this->config->payment_status_mode ) {
			$this->make_amount_unique( $payment );
		}

		$result = $this->get_gateway_data(
			$payment,
			[
				'mid' => $this->config->merchant_id,
			]
		);

		$payment->set_meta( 'bharat_pe_data', $result->data->bharat_pe_data );
		$payment->set_meta( 'kpp_charge_id', $result->id );
	}

	protected function register_payment_methods() {
		parent::register_payment_methods();

		$this->register_payment_method( new PaymentMethod( PaymentMethods::UPI_INTENT_PHONEPE ) );
	}

	/**
	 * Update status of the specified payment.
	 *
	 * @param Payment $payment
	 *            Payment.
	 */
	public function update_status( Payment $payment ) {
		parent::update_status( $payment );

		$transactions = $this->client->get_transaction_details( $payment->get_meta( 'bharat_pe_data' ) );

		if ( ! empty( $_POST['knit_pay_utr'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			$transaction_utr = sanitize_text_field( $_POST['knit_pay_utr'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
			$this->find_transaction_by_utr( $payment, $transactions, $transaction_utr );
		} elseif ( 'auto' === $this->config->payment_status_mode ) {
			// Update status of all the pending payments having time same as the current payment.
			$this->bulk_update_status( $transactions );
			$payment = get_pronamic_payment( $payment->get_id() );
		}

		$this->expire_old_upi_payment( $payment );
	}

	private function find_transaction_by_utr( $payment, $bharat_pe_transactions, $transaction_utr ) {
		$payment->set_transaction_id( $transaction_utr );

		$old_payment = get_pronamic_payment_by_transaction_id( $transaction_utr );

		if ( null !== $old_payment ) {
			$failure_reason = new FailureReason();
			$failure_reason->set_message( 'Trying to use same UTR twice. UTR: ' . $transaction_utr );
			$failure_reason->set_code( 'FRAUD' );
			$payment->set_failure_reason( $failure_reason );
				
			$payment->set_status( Core_Statuses::FAILURE );
			return;
		}

		$found_transaction = array_filter(
			$bharat_pe_transactions,
			function ( $transaction ) use ( $transaction_utr ) {
				return $transaction->bankReferenceNo === $transaction_utr;
			}
		);

		if ( 1 !== count( $found_transaction ) ) {
			return;
		}

		$found_transaction = reset( $found_transaction );

		if ( property_exists( $found_transaction, 'status' ) && 'SUCCESS' === $found_transaction->status ) {
			$expected_amount = floatval( $payment->get_total_amount()->number_format( null, '.', '' ) );
			if ( floatval( $found_transaction->amount ) !== $expected_amount ) {
				$payment->set_status( Core_Statuses::FAILURE );
				return;
			}

			$payment->set_status( Core_Statuses::SUCCESS );

			//phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
			$payment->add_note( '<strong>BharatPe Response:</strong><br><pre>' . print_r( $found_transaction, true ) . '</pre><br>' );
		}
	}

	protected function bulk_update_status( $bharat_pe_transactions ) {
		if ( empty( $bharat_pe_transactions ) ) {
			return;
		}

		foreach ( $bharat_pe_transactions as $transaction ) {
			if ( 'SUCCESS' !== $transaction->status ) {
				continue;
			}

			$payment = get_pronamic_payment_by_transaction_id( $transaction->bankReferenceNo );

			if ( null !== $payment ) {
				continue;
			}

			$amount_in_paisa = $transaction->amount * 100;
			$payment_id      = get_transient( 'knit_pay_upi_' . $this->config->config_id . '_' . $amount_in_paisa );
			$payment         = get_pronamic_payment( $payment_id );

			$bharat_pe_data = json_decode( $payment->get_meta( 'bharat_pe_data' ) );
			if ( $bharat_pe_data->sDate > $transaction->paymentTimestamp
					|| $bharat_pe_data->eDate < $transaction->paymentTimestamp
					|| floatval( $payment->get_total_amount()->number_format( null, '.', '' ) ) !== floatval( $transaction->amount ) ) {
				continue;
			}

			/*
			* This transaction occured during the expected timeframe, amount of the transaction matches
			* and the transaction has not been used before. So, we are assuming this this the transaction we are looking for.
			*/
			$payment->set_transaction_id( $transaction->bankReferenceNo );
			$payment->set_status( Core_Statuses::SUCCESS );

			//phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
			$payment->add_note( '<strong>BharatPe Response:</strong><br><pre>' . print_r( $transaction, true ) . '</pre><br>' );
			$payment->save();

			// This amount is paid and now can be allocated to other customer.
			$current_amount = $payment->get_total_amount()->get_minor_units()->to_int();
			delete_transient( 'knit_pay_upi_' . $this->config->config_id . '_' . $current_amount );
		}
	}
}
