<?php
namespace Svea_Webpay_For_Woocommerce\Traits;

use Svea\WebPay\Response\SveaResponse;
use Svea_Webpay_For_Woocommerce\Gateways\WC_Gateway_Abstract;
use Svea_Webpay_For_Woocommerce\Helper;
use Svea_Webpay_For_Woocommerce\Service_Providers\Config_Production;
use Svea_Webpay_For_Woocommerce\Service_Providers\Config_Test;

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

/**
 * A default payment callback trait for handling payment callbacks.
 *
 */
trait Default_Payment_Callback {

	/**
	 * Accepted request interprets the returned object and response code from Sveawebpay.
	 *
	 * @since   1.0.0
	 * @access  public
	 * @return  void
	 */
	public function handle_callback_request() {
		$request = $_REQUEST; // phpcs:ignore

		self::log( sprintf( 'Callback request for payment initiated by client %s', isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) );

		/**
		 * Fetch response string directly from query string
		 * if parameter max length prevents it from being read.
		 */
		if ( ( ! isset( $request['response'] ) || strlen( $request['response'] ) <= 0 )
			&& isset( $_SERVER['QUERY_STRING'] )
			&& strlen( sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) ) > 0 ) {

			$params_array = explode( '&', sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) );

			$params = [];

			foreach ( $params_array as $pair ) {
				list( $key, $value ) = explode( '=', $pair );
				$params[ urldecode( $key ) ] = urldecode( $value );
			}

			if ( isset( $params['response'] ) ) {
				$request['response'] = $params['response'];
			}
		}

		/**
		 * If the response-parameter is not set, do not produce a PHP-error
		 */
		if ( ! isset( $request['response'] ) ) {
			status_header( 400 );
			self::log( 'Response parameter in request is not set' );
			exit;
		}

		if ( ! isset( $request['svea-country'] ) || ! is_string( $request['svea-country'] )
			|| ! in_array( strtoupper( $request['svea-country'] ), Helper::ALLOWED_COUNTRIES_ASYNC, true ) ) {
			status_header( 400 );
			self::log( 'Svea country parameter in request is not set or not valid' );
			exit;
		}

		$this->set_config( strtolower( $request['svea-country'] ) );

		$config = $this->get_config();

		/**
		 * Wrap the response in Sveas response-parser
		 */
		$svea_response = new SveaResponse( $request, null, $config );
		$response = $svea_response->getResponse();

		$payment_method = $response->paymentMethod; // phpcs:ignore

		if ( is_null( $payment_method ) ) {
			status_header( 400 );
			self::log( 'Payment method is null' );
			exit;
		}

		if ( ! isset( $request['order-id'] ) || ! isset( $request['key'] ) ) {
			status_header( 400 );
			self::log( 'Order ID-parameter in request is not set' );
			exit;
		}

		$order_id = intval( $request['order-id'] );
		$order_key = sanitize_text_field( $request['key'] );

		$wc_order = wc_get_order( (int) $order_id );

		if ( ! $wc_order ) {
			self::log( 'No order was found by ID' );
			exit;
		}

		if ( ! $wc_order->key_is_valid( $order_key ) ) {
			self::log( 'Order key was not valid for order' );
			exit;
		}

		/**
		 * Check if the callback was initiated by Svea Callback Service
		 * and save in variable for future use.
		 */
		$is_svea_callback = isset( $request['svea-callback'] ) && $request['svea-callback'];

		/**
		 * If the order is already paid, redirect to the order received page
		 */
		if ( $wc_order->is_paid() ) {

			/**
			 * Check if request was initiated by Svea Callback Service
			 */
			if ( $is_svea_callback ) {
				self::log( sprintf( 'Order %s was already paid, request was initiated by Svea Callback Service, exiting.', $order_id ) );
				status_header( 200 );
			} else {
				self::log( sprintf( 'Order %s was already paid, redirecting to order received page', $order_id ) );
				wp_safe_redirect( $wc_order->get_checkout_order_received_url() );
			}

			exit;
		}

		$cancel_url = esc_url_raw( wc_get_checkout_url() );

		$redirect_url = $cancel_url;
		$complete_url = $wc_order->get_checkout_order_received_url();

		if ( isset( $response->accepted ) && $response->accepted ) {

			/**
			 * Guard against duplicate callback processing. Svea sends both a
			 * browser redirect and a server-side callback for the same payment,
			 * so two requests can arrive before either has completed. Flag the
			 * order as being processed before calling payment_complete() so the
			 * second request sees the flag and skips. Same pattern as used in
			 * the svea-checkout plugin (ORDER_VALIDATION_STATUS_META_KEY).
			 */
			if ( $wc_order->get_meta( WC_Gateway_Abstract::CALLBACK_PROCESSED_META_KEY ) === WC_Gateway_Abstract::CALLBACK_PROCESSED_VALUE ) {
				self::log( sprintf( 'Duplicate callback for order %s detected, skipping.', $order_id ) );

				if ( $is_svea_callback ) {
					status_header( 200 );
					exit;
				}

				wp_safe_redirect( $complete_url );
				exit;
			}

			$wc_order->update_meta_data( WC_Gateway_Abstract::CALLBACK_PROCESSED_META_KEY, WC_Gateway_Abstract::CALLBACK_PROCESSED_VALUE );
			$wc_order->save();

			// translators: %s is the IP-address of the user
			$wc_order->add_order_note( sprintf( __( 'Order was completed by client on IP: %s', 'svea-webpay-for-woocommerce' ), isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) );
			$wc_order->payment_complete( $response->transactionId ); // phpcs:ignore

			// Remove cart
			WC()->cart->empty_cart();

			$redirect_url = $complete_url;
			$wc_order->update_meta_data( '_svea_order_id', $response->transactionId ); // phpcs:ignore
			$wc_order->save();

			self::log( sprintf( 'Payment was successful and order %s is complete', $wc_order->get_id() ) );

			/**
			 * If Svea initiated the callback, prevent unnecessary requests
			 * by returning 200 status code instead of redirecting.
			 */
			if ( $is_svea_callback ) {
				self::log( 'Request was initiated by Svea Callback Service, exiting instead of redirect.' );
				status_header( 200 );
				exit;
			}
		} else {
			wc_add_notice( Helper::get_svea_error_message( (int) $response->resultcode ), 'error' );
			self::log( 'Payment failed, response: ' . $response->errormessage );

			$redirect_url = $cancel_url;
		}

		wp_safe_redirect( $redirect_url );
		exit;
	}
}