<?php
/**
 * Handles Swish client functions.
 *
 * @package flo-checkout
 */

namespace Flo_Checkout\Gateways\Swish;

use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Helpers\HTTP_Request_Helpers;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use WC_Order;

/**
 * Swish client class.
 */
class Swish_Client {

	private const ORDER_META_CALLBACK_ID = '_swish_callback_id';

	/**
	 * Prepares the payment client for the given order, payer, and merchant.
	 *
	 * @param WC_Order $order The order object.
	 * @param string   $payer The payer alias.
	 *
	 * @return array The payment details.
	 */
	public static function prepare_payment_data( WC_Order $order, string $payer ): array {

		$merchant = Swish_Settings::get_swish_setting( 'payee_number' );

		$order_id    = $order->get_id();
		$callback_id = Swish_Request_IDs::generate();

		$transaction_text_array = array();

		$customer_number = $order->get_user_id();

		$items = $order->get_items();

		$product_list = array();
		foreach ( $items as $item ) {
			$product_list[] = $item->get_name();
		}

		$products = implode( ',', $product_list );

		if ( $customer_number ) {
			$transaction_text_array[] = sprintf(
				// translators: %s is the customer number.
				__( 'Customer number %s', 'flo-checkout-plugin' ),
				$customer_number
			);
		} else {
			$transaction_text_array[] = $products;
		}

		$transaction_text = mb_substr( preg_replace( '/[^a-zA-Z0-9åäöÅÄÖ :;.,?!()]+/u', '', implode( ', ', $transaction_text_array ) ), 0, 49 );

		return array(
			'payeePaymentReference' => $order->get_order_number(),
			'callbackUrl'           => self::get_callback_url(
				$order_id,
				'payment'
			),
			'payerAlias'            => $payer,
			'payeeAlias'            => $merchant,
			'amount'                => str_replace( ',', '.', $order->get_total() ),
			'currency'              => $order->get_currency(),
			'message'               => $transaction_text,
			'callbackIdentifier'    => $callback_id,
		);
	}

	/**
	 * Prepares the payment client for a refund transaction.
	 *
	 * @param string     $transaction_id The ID of the original payment transaction.
	 * @param WC_Order   $order          The order object.
	 * @param float|null $amount         The refund amount. If null, the total order amount will be used.
	 * @param string     $reason         The reason for the refund.
	 *
	 * @return array The refund details.
	 */
	public static function prepare_refund_payment_data(
		string $transaction_id,
		WC_Order $order,
		float|null $amount,
		string $reason
	): array {
		$merchant = Swish_Settings::get_swish_setting( 'payee_number' );
		$order_id = $order->get_id();

		$callback_id = Swish_Request_IDs::generate();

		if ( null === $amount ) {
			$amount = $order->get_total();
		}

		$transaction_text = mb_substr( preg_replace( '/[^a-zA-Z0-9åäöÅÄÖ :;.,?!()]+/u', '', $reason ), 0, 49 );

		return array(
			'payerPaymentReference'    => 'R-' . $order_id . '-' . date( 'Y-m-d H:i', current_time( 'timestamp' ) ),
			'originalPaymentReference' => $transaction_id,
			'callbackUrl'              => self::get_callback_url( $order_id, 'refund' ),
			'payerAlias'               => $merchant,
			'amount'                   => str_replace( ',', '.', $amount ),
			'currency'                 => $order->get_currency(),
			'message'                  => $transaction_text,
			'callbackIdentifier'       => $callback_id,
		);
	}

	/**
	 * Retrieves the callback ID for the specified order ID and request type.
	 *
	 * @param int $order_id The ID of the order.
	 *
	 * @return string The callback ID if found, or empty string if not found.
	 */
	public static function get_callback_id( int $order_id ): string {
		$callback_id = get_post_meta( $order_id, self::ORDER_META_CALLBACK_ID, true );

		if ( empty( $callback_id ) ) {
			return '';
		}

		return $callback_id;
	}

	/**
	 * Stores the callback ID for the specified order ID and request type.
	 *
	 * @param int    $order_id The ID of the order.
	 * @param string $id       The callback ID to be stored.
	 *
	 * @return string|null Callback ID if it was successfully stored, null otherwise.
	 */
	public static function store_callback_id( int $order_id, string $id ): ?string {
		$updated = update_post_meta( $order_id, self::ORDER_META_CALLBACK_ID, $id );

		return $updated ? $id : null;
	}

	/**
	 * Retrieves the callback URL for Swish payments.
	 *
	 * @param int|string $order_id The ID of the order. String 'test_connection' can be used to test the connection.
	 * @param string     $type     The event type.
	 * @param string     $wc_api   The WooCommerce API endpoint. The default value is 'swish_pay_gateway'.
	 *
	 * @return string The callback URL generated based on the given parameters.
	 */
	public static function get_callback_url(
		int|string $order_id,
		string $type,
		string $wc_api = 'swish_pay_gateway'
	): string {
		$args = array(
			'wc-api'   => $wc_api,
			'event'    => $type,
			'order_id' => $order_id,
		);

		return add_query_arg( $args, site_url( '/' ) );
	}

	/**
	 * Verifies the callback by comparing the received nonce with the security key generated from the secret and order details.
	 *
	 * @param string|int  $order_id    The ID of the order.
	 * @param string|null $callback_id The secret value associated with the order (optional).
	 *
	 * @return bool Returns true if the callback is valid, false otherwise.
	 */
	public static function is_callback_valid(
		string|int $order_id,
		string $callback_id = null
	): bool {
		$callback_id        = $callback_id ? $callback_id : self::get_callback_id( $order_id );
		$header_callback_id = HTTP_Request_Helpers::get_header( 'callbackIdentifier' );

		if ( $header_callback_id === $callback_id ) {
			return true;
		} else {
			Swish_Settings::log(
				'Invalid callback identifier',
				array(
					'order_id'            => $order_id,
					'callback_identifier' => $callback_id,
					'header_callback_id'  => $header_callback_id,
				),
				Log_Level_Enum::ERROR
			);

			return false;
		}
	}
}
