<?php
namespace Svea_Webpay_For_Woocommerce\Traits;

use Svea_Webpay_For_Woocommerce\Helper;
use WC_Order;

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

/**
 * Shared helpers for Svea strong authentication callbacks.
 */
trait Strong_Auth {

	/**
	 * Query parameter used for strong authentication callback nonce transport.
	 *
	 * @return string
	 */
	protected function get_strong_auth_security_token_query_arg() {
		return 'security_token';
	}

	/**
	 * Create the nonce action for a strong authentication callback.
	 *
	 * @param string $callback_action The callback action.
	 * @param int    $order_id The WooCommerce order ID.
	 * @return string
	 */
	protected function get_strong_auth_nonce_action( string $callback_action, int $order_id ) {
		return sprintf( '%s_strong_auth_%s_%d', $this->id, $callback_action, $order_id );
	}

	/**
	 * Build a strong authentication callback URL.
	 *
	 * @param WC_Order $wc_order The WooCommerce order.
	 * @param string   $wc_api The WooCommerce API endpoint.
	 * @param string   $callback_action The callback action.
	 * @param bool     $include_order_key Whether to include the order key.
	 * @return string
	 */
	protected function get_strong_auth_callback_url( WC_Order $wc_order, string $wc_api, string $callback_action, bool $include_order_key = false ) {
		$order_id = $wc_order->get_id();
		$args = [
			'wc-api'   => $wc_api,
			'order-id' => $order_id,
		];
		$args[ $this->get_strong_auth_security_token_query_arg() ] = wp_create_nonce( $this->get_strong_auth_nonce_action( $callback_action, $order_id ) );

		if ( $include_order_key ) {
			$args['key'] = $wc_order->get_order_key();
		}

		return esc_url_raw( add_query_arg( $args, home_url( '/' ) ) );
	}

	/**
	 * Validate and return a strong authentication callback order.
	 *
	 * @param string $callback_action The callback action.
	 * @param bool   $requires_order_key Whether this callback requires a WooCommerce order key.
	 * @return array{order: WC_Order, svea_order_id: string}
	 */
	protected function get_valid_strong_auth_callback_order( string $callback_action, bool $requires_order_key = false ) {
		$order_id = ! empty( $_GET['order-id'] ) ? intval( $_GET['order-id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$security_token_key = $this->get_strong_auth_security_token_query_arg();
		$security_token = ! empty( $_GET[ $security_token_key ] ) ? sanitize_text_field( wp_unslash( $_GET[ $security_token_key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$order_key = ! empty( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		if ( empty( $order_id ) || empty( $security_token ) || ( $requires_order_key && empty( $order_key ) ) ) {
			self::log( 'Bad parameters in strong authentication callback' );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

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

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

		if ( $wc_order->get_payment_method() !== $this->id ) {
			self::log( 'Payment method did not match for order ' . $order_id );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		if ( ! wp_verify_nonce( $security_token, $this->get_strong_auth_nonce_action( $callback_action, $order_id ) ) ) {
			self::log( 'Security token was not valid for order ' . $order_id );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		if ( $requires_order_key && ! $wc_order->key_is_valid( $order_key ) ) {
			self::log( 'Order key was not valid for order ' . $order_id );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		if ( $wc_order->has_status( 'cancelled' ) ) {
			self::log( 'Strong authentication callback rejected for cancelled order ' . $order_id );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		$svea_order_id = $wc_order->get_meta( '_svea_order_id', true );

		if ( ! $svea_order_id || strlen( (string) $svea_order_id ) <= 0 ) {
			self::log( 'No Svea order ID was found for order ' . $order_id );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		return [
			'order'         => $wc_order,
			'svea_order_id' => (string) $svea_order_id,
		];
	}

	/**
	 * Query the Svea order used to verify a confirmed strong authentication callback.
	 *
	 * @param WC_Order $wc_order The WooCommerce order.
	 * @param string   $svea_order_id The Svea order ID.
	 * @return object
	 */
	protected abstract function query_strong_auth_svea_order( WC_Order $wc_order, string $svea_order_id );

	/**
	 * Check if the Svea order has confirmed strong authentication.
	 *
	 * @param WC_Order $wc_order The WooCommerce order.
	 * @param string   $svea_order_id The Svea order ID.
	 * @return bool|null Null when no Svea response was returned.
	 */
	public function check_strong_auth_svea_order( WC_Order $wc_order, string $svea_order_id ) {
		$response = $this->query_strong_auth_svea_order( $wc_order, $svea_order_id );

		if ( empty( $response ) ) {
			return null;
		}

		return $this->strong_auth_svea_order_is_confirmed( $response );
	}

	/**
	 * Check if a Svea order response is confirmed.
	 *
	 * @param object $response The response object from Svea API.
	 * @return bool Returns true if the order is confirmed, false otherwise.
	 */
	protected function strong_auth_svea_order_is_confirmed( $response ) {
		if ( empty( $response ) || ! isset( $response->orderStatus ) || ! isset( $response->pendingReasons ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
			return false;
		}

		return $response->orderStatus === 'Active' && empty( $response->pendingReasons ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
	}

	/**
	 * Handle callback request for confirmed authentication.
	 *
	 * @return void
	 */
	public function handle_callback_request_confirmed() {
		self::log( sprintf( 'Callback request for strong authentication initiated by client %s', isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing

		$callback_order = $this->get_valid_strong_auth_callback_order( 'confirmed', true );
		$wc_order = $callback_order['order'];
		$svea_order_id = $callback_order['svea_order_id'];

		if ( $wc_order->is_paid() ) {
			wp_safe_redirect( esc_url_raw( $wc_order->get_checkout_order_received_url() ) );
			exit;
		}

		try {
			$confirmed = $this->check_strong_auth_svea_order( $wc_order, $svea_order_id );
		} catch ( \Exception $e ) {
			self::log( 'Error when querying Svea order during authentication callback: ' . $e->getMessage() );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		if ( $confirmed !== true ) {
			self::log( 'Svea order was not confirmed for order #' . $wc_order->get_id() );
			wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );
			exit;
		}

		self::log( 'Authentication success for order #' . $wc_order->get_id() );

		$wc_order->payment_complete( $svea_order_id );

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

		// Remove cart
		if ( isset( WC()->cart ) && WC()->cart ) {
			WC()->cart->empty_cart();
		}

		wp_safe_redirect( esc_url_raw( $wc_order->get_checkout_order_received_url() ) );

		exit;
	}

	/**
	 * Handle callback request for rejected authentication.
	 *
	 * @return void
	 */
	public function handle_callback_request_rejected() {
		self::log( sprintf( 'Callback request for strong authentication initiated by client %s', isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing

		$callback_order = $this->get_valid_strong_auth_callback_order( 'rejected' );
		$wc_order = $callback_order['order'];

		wc_add_notice( Helper::get_svea_error_message( (int) 40006 ), 'error' );

		$wc_order->add_order_note( 'Authentication failed for order #' . $wc_order->get_id() );

		self::log( 'Authentication failed for order #' . $wc_order->get_id() );

		wp_safe_redirect( esc_url_raw( wc_get_checkout_url() ) );

		exit;
	}
}
