<?php
/**
 * Order functions.
 *
 * @package flo-sync
 */

namespace NMFS\Sync\Items\Orders;

use Exception;
use NMFS\Admin\Settings_Fields;
use NMFS\Shared;
use NMFS\Sync\Items\Articles\Stock\Stock;
use NMFS\Sync\Items\Orders\Models\Order_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Models\Hub_API_Properties;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Cron\Models\Lock_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Modules\Sync\Orders\Models\Order_Base_Model;
use WC_Abstract_Order;
use WC_Order;
use WC_Order_Item_Product;
use WC_Order_Refund;
use WC_Product;

defined( 'ABSPATH' ) || exit;

/**
 * Order sync class.
 */
class Orders {

	/**
	 * Constructs order sync.
	 */
	public static function setup(): void {
		add_action( 'woocommerce_new_order', array( self::class, 'save_order' ), 10, 2 ); // Also hooked to 'woocommerce_update_order' in self::set_hook_save_order().
		add_action( 'woocommerce_after_order_object_save', array( self::class, 'maybe_handle_order_after_object_save' ), 10, 2 ); // Also hooked to 'woocommerce_update_order' in self::set_hook_save_order().

		add_action( 'woocommerce_payment_complete', array( self::class, 'payment_complete_post_order' ), 1 ); // We want it early to happen before stock is reduced.
		add_action( 'mf_retry_save_order', array( self::class, 'retry_save_order' ) );

		add_action( 'woocommerce_create_refund', array( self::class, 'validate_refund_order' ) );
		add_action( 'woocommerce_refund_created', array( self::class, 'refund_order' ), 10, 2 );

		add_action( 'northmill_checkout_bnpl_transaction_note', array( self::class, 'add_bnpl_transaction_note' ), 10, 2 );

		self::set_hook_save_order( true );
	}

	/**
	 * Enable or disable save order hook.
	 * This is mainly used for disabling the hook when doing custom order updates.
	 * This is needed because the hook will otherwise trigger when updating order, causing a loop.
	 *
	 * @param bool $on Whether to enable or disable the hook.
	 */
	public static function set_hook_save_order( bool $on ): void {
		if ( $on ) {
			add_action( 'woocommerce_update_order', array( self::class, 'save_order' ), 10, 2 );
		} else {
			remove_action( 'woocommerce_update_order', array( self::class, 'save_order' ) );
		}
	}

	/**
	 * Function to retry saving order after having been postponed due to lock.
	 *
	 * @param int $order_id WC order ID.
	 */
	public static function retry_save_order( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( $order ) {
			$order->add_order_note( esc_html__( 'Retrying order sync.', 'flo-sync' ) );
			self::save_order( $order_id, $order );
		}
	}

	/**
	 * Sync order after payment has been completed.
	 * This is needed because the order status is not updated until after the payment has been completed.
	 * When doing payment, order sync is postponed until after payment has been completed.
	 *
	 * @param int $order_id WC order ID.
	 *
	 * @return void
	 */
	public static function payment_complete_post_order( int $order_id ): void {
		$order = wc_get_order( $order_id );
		$order->add_order_note( esc_html__( 'Syncing order after payment.', 'flo-sync' ) );

		Shared::instance()->logger->log(
			'order-sync',
			'Syncing order after payment',
			array(
				'order_id'     => $order_id,
				'order_status' => $order->get_status(),
			),
			Log_Level_Enum::DEBUG
		);

		self::save_order( $order_id, $order );
	}

	/**
	 * Save order to Flo.
	 *
	 * @param int           $order_id WC order ID.
	 * @param WC_Order|null $order    WC order.
	 */
	public static function save_order( int $order_id, ?WC_Order $order = null ): void {
		// Don't sync orders if order sync is disabled.
		if ( ! Shared::instance()->admin->settings->is_setting_enabled( Settings_Fields::SYNC_NEW_ORDERS ) ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Order sync is disabled',
				array(
					'order_id' => $order_id,
				),
				Log_Level_Enum::DEBUG
			);
			return;
		}

		if ( doing_action( 'woocommerce_update_order' ) && did_action( 'woocommerce_pre_payment_complete' ) ) {
			// Order is being updated after payment has been completed, but before the status has been changed, do not sync.
			// Wait for woocommerce_payment_complete hook to sync.
			Shared::instance()->logger->log(
				'order-sync',
				'Order sync postponed until after payment has been completed',
				array(
					'order_id'       => $order_id,
					'current_action' => current_action(),
				),
				Log_Level_Enum::DEBUG
			);
			return;
		}

		Shared::instance()->logger->log(
			'order-sync',
			'Syncing order with WP ID: ' . $order_id . ', from action ' . current_action(),
			null,
			Log_Level_Enum::DEBUG
		);

		$lock = new Lock_Model( __FUNCTION__ . '-' . $order_id );
		if ( ! $lock->lock() ) {
			// Save order is already running for this order, retry later.
			wp_schedule_single_event( time() + 60, 'mf_retry_save_order', array( $order_id ) );
			$order->add_order_note( esc_html__( 'Aborted syncing order. It is already running.', 'flo-sync' ) );

			return;
		}

		/**
		 * Validate order to sync.
		 */
		$validated_order_data = self::validate_order_to_sync( $order );

		if ( ! $validated_order_data ) {
			$lock->clear_lock();
			return;
		}

		/**
		 * Variables from self::validated_order_data()
		 *
		 * @var string $new_order_hash
		 * @var string $action
		 * @var array $mf_order
		 * @var Order_Model $order_instance
		 */
		$new_order_hash = $validated_order_data['new_order_hash'];
		$action         = $validated_order_data['action'];
		$mf_order       = $validated_order_data['mf_order'];
		$order_instance = $validated_order_data['order_instance'];

		/**
		 * Filter order before posting to Hub.
		 *
		 * @param array     $mf_order Order data formatted for API.
		 * @param WC_Order  $order    WC order.
		 * @param string    $action   Action to be performed for order. Either 'add' or 'update'.
		 */
		$mf_order = apply_filters( 'moreflo_sync_before_order_sync', $mf_order, $order, $action );
		$mf_order = apply_filters( 'northmill_online_shared_before_order_sync_' . Shared::instance()->package_info->get_service_name_underscored(), $mf_order, $order, $action );

		/**
		 * Post order to Hub.
		 */
		$order_post_response = $order_instance->get_backoffice_account()->api->post_order( $mf_order );
		$order_instance->handle_hub_order_sync_response( $order_post_response, $action );

		/**
		 * Order receipt.
		 */
		Shared::instance()->sync->receipts->maybe_post_receipt_to_hub( $order );

		if ( $order_post_response ) { // If order was posted successfully.
			update_post_meta( $order_id, Order_Base_Model::META_KEY_HASH, $new_order_hash );

			Shared::instance()->sync->receipts->status->maybe_reset_receipt_status( $order_id );

			self::handle_posted_order_stock_changes( $order, $mf_order );

			Shared::instance()->logger->log(
				'order-sync',
				'Order pushed and updated',
				null,
				Log_Level_Enum::DEBUG
			);
		}

		$lock->clear_lock();
	}

	/**
	 * Validate order to sync.
	 *
	 * @param object|null $order WC Order object.
	 *
	 * @return false|array Returns an array containing the following:
	 *                     - 'new_order_hash' (string): The new order hash.
	 *                     - 'action' (string): The action to perform on the order (add or update).
	 *                     - 'mf_order' (array): The order data to sync with Flo.
	 *                     - 'order_instance' (Order_Model): The Order_Model instance of the order.
	 *                     Returns false if the order is not valid to sync.
	 */
	private static function validate_order_to_sync( ?object $order ): false|array {
		// Only proceed if order is a WC_Order.
		if ( empty( $order ) || ! $order instanceof WC_Order ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Order is not instance of WC_Order',
				array(
					'type'  => is_object( $order ) ? get_class( $order ) : gettype( $order ),
					'order' => $order,
				),
				Log_Level_Enum::ERROR
			);
			return false;
		}

		$order_id       = $order->get_id();
		$order_instance = Order_Model_Factory::get_order_model( $order );
		$mf_order       = $order_instance->order_to_nmflo_order();
		$mf_order_type  = $order_instance->get_order_type();
		$mf_order_total = $order_instance->get_order_total();

		// Only proceed with valid orders.
		if ( empty( $mf_order ) || 'NORMAL_ORDER' !== $mf_order_type ) {
			$order->add_order_note( __( 'Order is invalid and cannot be pushed to Hub.', 'flo-sync' ) );
			Shared::instance()->logger->log(
				'order-sync',
				'Order is invalid and cannot be pushed to Hub',
				array(
					'mf_order'      => $mf_order,
					'mf_order_type' => $mf_order_type,
				),
				Log_Level_Enum::ERROR
			);
			return false;
		}

		// Don't post orders with a total of 0 or less.
		if ( $mf_order_total <= 0 ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Order total is less than zero, skipping sync',
				array(
					'order_id' => $order_id,
					'total'    => $mf_order_total,
				),
				Log_Level_Enum::DEBUG
			);

			return false;
		}

		$old_hash = get_post_meta( $order_id, Order_Base_Model::META_KEY_HASH, true );
		$new_hash = self::generate_order_hash( $mf_order );

		// Only proceed if the order has changed.
		if ( $old_hash === $new_hash ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Hash verification failed, order has not changed since last sync',
				array(
					'order_id' => $order_id,
					'mf_order' => $mf_order,
				),
				Log_Level_Enum::DEBUG
			);

			return false;
		}

		return array(
			'new_order_hash' => $new_hash,
			'action'         => $old_hash ? 'update' : 'add',
			'mf_order'       => $mf_order,
			'order_instance' => $order_instance,
		);
	}

	/**
	 * Handle posted order stock changes.
	 *
	 * @param WC_Order $order    The order object.
	 * @param array    $mf_order Order data as sent to API.
	 *
	 * @return void
	 */
	private static function handle_posted_order_stock_changes( WC_Order $order, array $mf_order ): void {
		if ( did_action( 'woocommerce_pre_payment_complete' ) ) {
			/**
			 * Doing payment (theoretically including processing free orders, but we currently don't support orders with total 0).
			 *
			 * We don't update stock here, since it will be synced and updated when payment is completed.
			 * Function wc_maybe_reduce_stock_levels() is hooked to 'woocommerce_payment_complete'.
			 */
			Shared::instance()->logger->log(
				'order-sync',
				'Skipping stock after sync update because doing payment',
				array(
					'order_id' => $order->get_id(),
				),
				Log_Level_Enum::DEBUG
			);
		} elseif ( doing_action( 'woocommerce_new_order' ) ) {
			if ( $mf_order[ Hub_API_Properties::ORDER_ADJUST_STOCK_IF_OPEN_STATE ] ?? false ) {
				/**
				 * Adjust stock on order open.
				 *
				 * If Flo Sync is set up to adjust stock when order is opened.
				 * Maybe update stock for order items, if not already done.
				 *
				 * We can't update stock here, since the order items aren't added to the order yet.
				 * Wait for action 'woocommerce_after_order_object_save' to update stock.
				 */
				Shared::instance()->logger->log(
					'order-sync',
					'AdjustStockIfOpenState, wait for order to be saved.',
					array(
						'order_id'       => $order->get_id(),
						'current_action' => current_action(),
					),
					Log_Level_Enum::DEBUG
				);
			} else {
				/**
				 * Creating unpaid order.
				 *
				 * The order is being created (doing action 'woocommerce_new_order'), and is not paid.
				 * Creating orders should be quick, so we don't sync stock here.
				 */
				Shared::instance()->logger->log(
					'order-sync',
					'Skipping stock after sync update, creating unpaid order.',
					array(
						'order_id'       => $order->get_id(),
						'current_action' => current_action(),
					),
					Log_Level_Enum::NOTICE
				);
			}
		} else {
			/**
			 * Only updating order.
			 *
			 * The order is being updated – rather than created – and a payment is not being processed.
			 * Update stock for order items.
			 */
			self::maybe_reset_order_items_stock_quantity( $order->get_id() );
		}
	}

	/**
	 * Handle order after object save.
	 * Hooked to 'woocommerce_after_order_object_save', which is called after the order is saved in woocommerce/includes/abstracts/abstract-wc-order.php
	 *
	 * @param WC_Abstract_Order $order Order to handle.
	 *
	 * @return void
	 */
	public static function maybe_handle_order_after_object_save( WC_Abstract_Order $order ): void {
		if (
			did_action( 'woocommerce_new_order' ) &&
			Settings_Fields::ORDER_STOCK_ADJUSTMENT_ORDER_OPEN === Stock::get_stock_adjustment_setting()
		) {
			Shared::instance()->logger->log(
				'order-sync',
				'New order with AdjustStockIfOpenState, maybe reduce stock. And release reserved stock.',
				array(
					'order_id' => $order->get_id(),
				),
				Log_Level_Enum::DEBUG
			);
			wc_maybe_reduce_stock_levels( $order->get_id() );
		}
	}

	/**
	 * Checks if refund can be made in a way that is valid for Apps.
	 *
	 * @param WC_Order_Refund $wc_order_refund WC Order Refund.
	 *
	 * @throws Exception If refund is not valid.
	 */
	public static function validate_refund_order( WC_Order_Refund $wc_order_refund ): void {
		$order_refund = wc_get_order( $wc_order_refund );
		$order_items  = $order_refund->get_items();

		$quantity_order_items     = array_filter(
			$order_items,
			function ( $order_item ) {
				return $order_item->get_quantity() !== 0;
			}
		);
		$non_quantity_order_items = array_filter(
			$order_items,
			function ( $order_item ) {
				return $order_item->get_quantity() === 0;
			}
		);
		if ( count( $quantity_order_items ) > 0 && count( $non_quantity_order_items ) > 0 ) {
			// Since row quantity cannot be 0, we cannot have some rows with quantity and some without.
			// If stock management is off, we can set the quantity to 1, but not if it's on.
			// That would mess with the stock in Apps.
			throw new Exception(
				esc_html__(
					'Refunds cannot contain both rows with quantity and rows without. Please make the refund in two steps.',
					'flo-sync'
				)
			);
		}
	}

	/**
	 * Hooked on 'woocommerce_refund_created'.
	 *
	 * @param int   $refund_order_id Refund order ID.
	 * @param array $args            Refund arguments.
	 */
	public static function refund_order( int $refund_order_id, array $args ): void {
		$refund_order = new WC_Order_Refund( $refund_order_id );
		$parent_order = new WC_Order( $refund_order->get_parent_id() );

		// If the order is fully refunded, we don't need to create a new refund order.
		// The original order will be synced as canceled.
		if ( did_action( 'woocommerce_order_fully_refunded' ) ) {
			self::handle_full_refund( $refund_order, $parent_order, $args );
		} else {
			self::handle_partial_refund( $refund_order, $parent_order, $args );
		}
	}

	/**
	 * Handle full refund.
	 *
	 * @param WC_Order_Refund $refund_order Refund order.
	 * @param WC_Order        $parent_order Parent order.
	 * @param array           $args         Refund arguments.
	 */
	private static function handle_full_refund( WC_Order_Refund $refund_order, WC_Order $parent_order, array $args ): void {
		Shared::instance()->logger->log(
			'order-sync',
			'Creating receipt for fully refunded order.',
			array(
				'refund_order_id' => $refund_order->get_id(),
				'parent_order_id' => $parent_order->get_id(),
			),
		);

		// We still need to sync a refund receipt.
		Shared::instance()->sync->receipts->maybe_post_receipt_to_hub(
			$refund_order,
			$args['restock_items']
		);

		self::maybe_reset_order_items_stock_quantity( $refund_order->get_id() );
	}

	/**
	 * Handle partial refund.
	 *
	 * @param WC_Order_Refund $refund_order Refund order.
	 * @param WC_Order        $parent_order Parent order.
	 * @param array           $args         Refund arguments.
	 */
	private static function handle_partial_refund( WC_Order_Refund $refund_order, WC_Order $parent_order, array $args ): void {
		$refund_order_id = $refund_order->get_id();

		// Create a negative order in Hub.
		// Use parent order as a template to get all customer info.
		$parent_refund_order_instance = Order_Model_Factory::get_order_model( $parent_order );
		$parent_mf_order              = $parent_refund_order_instance->order_to_nmflo_order();

		// Create partial refund info.
		$refund_order_instance = Order_Model_Factory::get_order_refund_model( $refund_order );
		$refund_mf_order       = $refund_order_instance->refund_order_to_moreflo_order( $parent_mf_order );

		// Ensure don't have an empty order.
		if ( empty( $refund_mf_order ) || empty( $refund_mf_order[ Hub_API_Properties::ORDER_ROWS ] ) ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Refund order failed, empty.',
				array(
					'refund_order_id' => $refund_order_id,
					'refund_order'    => $refund_mf_order,
				),
				Log_Level_Enum::ERROR
			);

			return;
		}

		/**
		 * Filter order before posting to Hub.
		 * Should return an array.
		 *
		 * @param array           $refund_mf_order Refund order data, formatted for Hub.
		 * @param WC_Order_Refund $refund_order    Refund order.
		 * @param WC_Order        $parent_order    Parent order.
		 */
		$refund_mf_order = apply_filters( 'moreflo_sync_before_order_refund_sync', $refund_mf_order, $refund_order, $parent_order );
		$refund_mf_order = apply_filters( 'northmill_online_shared_before_order_refund_sync_' . Shared::instance()->package_info->get_service_name_underscored(), $refund_mf_order, $refund_order, $parent_order );

		// Post to Hub.
		$response = $refund_order_instance->get_backoffice_account()->api->post_order( $refund_mf_order );
		$refund_order_instance->handle_hub_order_sync_response( $response );
		$new_hash = self::generate_order_hash( $refund_mf_order );

		// Check error response.
		if ( ! $response ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Refund order failed, no response from Hub',
				array(
					'order_id' => $refund_order_id,
				),
				Log_Level_Enum::ERROR
			);

			return;
		}

		// Update last sync.
		update_post_meta( $refund_order_id, Order_Base_Model::META_KEY_HASH, $new_hash );
		update_post_meta( $refund_order_id, Order_Base_Model::META_KEY_SYNC_STATUS, __( 'Synced', 'flo-sync' ) );
		update_post_meta( $refund_order_id, Order_Base_Model::META_KEY_LAST_SYNC_RESPONSE, $response );

		Shared::instance()->sync->receipts->maybe_post_receipt_to_hub(
			$refund_order,
			$args['restock_items']
		);

		self::maybe_reset_order_items_stock_quantity( $refund_order_id );

		Shared::instance()->logger->log(
			'order-sync',
			'Refund order pushed and updated',
			null,
			Log_Level_Enum::DEBUG
		);
	}

	/**
	 * Resets stock quantity for updated items.
	 * If not already done in this request.
	 *
	 * @param int $order_id WC Order ID.
	 */
	public static function maybe_reset_order_items_stock_quantity( int $order_id ): void {
		static $already_reset = array();
		Shared::instance()->logger->log(
			'order-sync',
			'Start resetting product stock for order.',
			array(
				'order_id'       => $order_id,
				'current_action' => current_action(),
			),
			Log_Level_Enum::DEBUG
		);

		if ( ! isset( $already_reset[ $order_id ] ) ) {
			$already_reset[ $order_id ] = array();
		}

		$exclude_order_statuses = array( 'failed', 'cancelled', 'completed' );
		$order                  = wc_get_order( $order_id );

		if ( $order instanceof WC_Order_Refund ) {
			$order = wc_get_order( $order->get_parent_id() );
		}

		if ( ! $order instanceof WC_Order ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Order is not instance of WC_Order',
				array(
					'order_id' => $order_id,
					'type'     => is_object( $order ) ? get_class( $order ) : gettype( $order ),
				),
				Log_Level_Enum::ERROR
			);
			return;
		}

		$order_status = $order->get_status();

		if ( in_array( $order_status, $exclude_order_statuses, true ) ) {
			Shared::instance()->logger->log(
				'order-sync',
				'Skipping resetting product stock for order with status.',
				array(
					'order_id'       => $order_id,
					'order_status'   => $order_status,
					'current_action' => current_action(),
				),
				Log_Level_Enum::DEBUG
			);
			return;
		}

		foreach ( $order->get_items() as $item ) {
			/**
			 * Method get_items defaults to line items.
			 *
			 * @var WC_Order_Item_Product $item
			 */

			if ( $item->get_variation_id() > 0 ) {
				$product = wc_get_product( $item->get_variation_id() );
			} elseif ( ! empty( $item->get_product_id() ) ) {
				$product = wc_get_product( $item->get_product_id() );
			} else {
				$product = null;
			}

			if ( is_a( $product, 'WC_Product' ) ) {

				// Check if this article has already been reset in this request.
				if ( in_array( $product->get_id(), $already_reset[ $order_id ], true ) ) {
					continue;
				}

				// Try resetting stock quantity.
				self::reset_order_product_quantity( $order, $product );

				$already_reset[ $order_id ][] = $product->get_id();
			}
		}
		Shared::instance()->logger->log(
			'order-sync',
			'Done resetting product stocks.',
			array(
				'order_id' => $order_id,
			),
			Log_Level_Enum::DEBUG
		);
	}

	/**
	 * Get article from API and update WC article quantity.
	 *
	 * @param WC_Order   $wc_order Order to reset stock for.
	 * @param WC_Product $product  Product to reset stock for.
	 */
	public static function reset_order_product_quantity( WC_Order $wc_order, WC_Product $product ): void {
		$include_product_types = array( 'simple', 'variation', 'grouped' ); // Product types that can be reset.

		$article_sku = $product->get_sku();
		$product_id  = $product->get_id();

		// Only reset stock for predefined product types.
		if ( ! in_array( $product->get_type(), $include_product_types, true ) ) {
			Shared::instance()->logger->log(
				'sync',
				'Product quantity cannot be reset. Invalid product type.',
				array(
					'product_id'   => $product_id,
					'product_sku'  => $article_sku,
					'product_type' => $product->get_type(),
				),
				Log_Level_Enum::ERROR
			);

			$wc_order->add_order_note(
				sprintf(
					// translators: %s: Order number.
					__(
						'Cannot reset quantity for product of type "%1$s". Trying to reset product with SKU: %2$s',
						'flo-sync'
					),
					$product->get_type(),
					$article_sku
				)
			);
			return;
		}

		$stock_updated = Stock::get_and_update_article_stock_data( $article_sku );

		if ( is_wp_error( $stock_updated ) ) {
			$wc_order->add_order_note( $stock_updated->get_error_message() );

			Shared::instance()->logger->log(
				'sync',
				'Failed to get stock data for article.',
				array(
					'product_id'  => $product_id,
					'product_sku' => $article_sku,
					'order_id'    => $wc_order->get_id(),
					'error'       => $stock_updated->get_error_message(),
				),
				Log_Level_Enum::ERROR
			);
			return;
		}

		// Reload the product to get the updated stock quantity.
		$product = wc_get_product( $product_id );

		$new_stock_quantity = $product->get_stock_quantity();

		Shared::instance()->logger->log(
			'sync',
			'Reset stock for product',
			array(
				'product_id' => $product_id,
				'status'     => $wc_order->get_status(),
				'quantity'   => $new_stock_quantity,
				'order_id'   => $wc_order->get_id(),
			),
			Log_Level_Enum::DEBUG
		);
		$wc_order->add_order_note(
			sprintf(
				// translators: %1$s: Product SKU, %2$d: Quantity, %3$s: Order status.
				__( 'Reset stock for product %1$s, Quantity: %2$d. Due to order status %3$s.', 'flo-sync' ),
				$article_sku,
				$new_stock_quantity,
				$wc_order->get_status()
			)
		);
	}

	/**
	 * Generate order hash.
	 *
	 * @param array $order Order to generate hash for.
	 *
	 * @return string
	 */
	public static function generate_order_hash( array $order ): string {
		$hash_string = '';

		if ( ! empty( $order ) ) {
			$hash_string = wp_hash( wp_json_encode( $order ), 'nonce' );
		}

		if ( ! is_string( $hash_string ) ) {
			$hash_string = '';
		}

		return $hash_string;
	}

	/**
	 * Add BNPL transaction note to the given note.
	 *
	 * @param string $note     The note to add BNPL transaction information to.
	 * @param int    $order_id The ID of the order.
	 *
	 * @return string The updated note with BNPL transaction information.
	 */
	public static function add_bnpl_transaction_note( string $note, int $order_id ): string {
		$wc_order = wc_get_order( $order_id );
		if ( ! $wc_order ) {
			return $note;
		}

		$order_model      = new Order_Base_Model( $wc_order );
		$hub_order_number = $order_model->get_hub_order_number();

		if ( empty( $hub_order_number ) ) {
			return $note;
		}

		if ( ! empty( $note ) ) {
			$note .= ' ';
		}

		$note .= __( 'Order nr', 'flo-sync' ) . ': ' . $hub_order_number . '. ';
		$note .= __( 'Name', 'flo-sync' ) . ': ' . $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name() . '. ';
		$note .= __( 'Phone', 'flo-sync' ) . ': ' . $wc_order->get_billing_phone() . '. ';
		$note .= __( 'Email', 'flo-sync' ) . ': ' . $wc_order->get_billing_email() . '.';

		return $note;
	}
}
