<?php

namespace Inspire_Labs\Alsendo_Connect;

use Exception;
use Inspire_Labs\Alsendo_Connect\Alsendo_API;
use Inspire_Labs\Alsendo_Connect\Ecolet;
use Inspire_Labs\Alsendo_Connect\Gateway_Settings_Templates_Helper;
use Inspire_Labs\Alsendo_Connect\Sender_Settings_Templates_Helper;
use Inspire_Labs\Alsendo_Connect\Service_Structure_Helper;
use Inspire_Labs\Alsendo_Connect\Global_Settings;
use Inspire_Labs\Alsendo_Connect\Alerts;
use WC_Order;
use WC_Shipping_Method;
use WP_REST_Request;
use Inspire_Labs\Alsendo_Connect\Woo_Blocks;

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

/**
 * Shipping_Method_Alsendo_Connect
 */
class Shipping_Method_Alsendo_Connect extends WC_Shipping_Method {

	const APACZKA_PICKUP_COURIER = 1;

	const APACZKA_PICKUP_SELF = 2;

	/**
	 * Cached shipping zones \WC_Shipping_Zones::get_zones()
	 *
	 * @var $cached_zones
	 */
	private $cached_zones = array();

	const PARCEL_LOCKER_GEOWIDGET_IDS = array(
		14  => 'UPS',
		15  => 'UPS',
		16  => 'UPS',
		23  => 'DPD',
		26  => 'DPD',
		50  => 'RUCH',
		41  => 'INPOST',
		163 => 'EKSPRES24',
		86  => 'DHL',
		162 => 'KURIER48',
	);

	/**
	 * API connection instance
	 *
	 * @var bool|object
	 */
	public $api = false;

	/**
	 * Available shipping services
	 *
	 * @var array
	 */
	public static $services = array();

	/**
	 * Auto-complete order status setting
	 *
	 * @var bool|string
	 */
	public static $order_status_completed_auto;

	/**
	 * Flag to prevent duplicate order reviews after shipping
	 *
	 * @var bool
	 */
	public static $review_order_after_shipping_once = false;


	/**
	 * Form fields configuration
	 *
	 * @var array
	 */
	private $fields = array();





	/**
	 * Base shipping cost
	 *
	 * @var float|string
	 */
	private $cost;

	/**
	 * COD (Cash on Delivery) cost
	 *
	 * @var float|string
	 */
	private $cost_cod;

	/**
	 * Per-order cost
	 *
	 * @var float|string
	 */
	private $cost_per_order;

	/**
	 * Flat rate shipping cost
	 *
	 * @var float|string
	 */
	private $flat_rate;

	/**
	 * Free shipping threshold cost
	 *
	 * @var float|string
	 */
	private $free_shipping_cost;

	/**
	 * API type (production or sandbox)
	 *
	 * @var string|null
	 */
	private $api_type;


	/**
	 * Constructor for your shipping class
	 *
	 * @param mixed $instance_id $instance_id.
	 * @access public
	 * @return void
	 */
	public function __construct( $instance_id = 0 ) {
		parent::__construct();
		$this->instance_id = absint( $instance_id );
		$this->id          = 'alsendo_connect';
		$this->enabled     = 'yes';
		self::$services    = array();

		$this->api_type = alsendo_connect()->get_main_settings_api();

		$this->supports = array(
			'instance-settings',
		);

		$this->method_title       = 'Alsendo';
		$this->method_description = 'Alsendo Connect';

		$this->title = $this->get_option( 'title' );

		self::$order_status_completed_auto
			= $this->get_option( 'order_status_completed_auto' );
		$this->init();

		$this->hooks();
	}

	/**
	 * Register hooks
	 *
	 * @access public
	 * @return void
	 */
	public function hooks() {
		add_action(
			'woocommerce_update_options_shipping_' . $this->id,
			array( $this, 'process_admin_options' )
		);

		add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 );

		add_action(
			'woocommerce_checkout_update_order_meta',
			array( $this, 'woocommerce_checkout_update_order_meta' ),
			100
		);

		add_action( 'save_post', array( $this, 'save_post' ) );
		add_action( 'woocommerce_process_shop_order_meta', array( $this, 'save_post' ) );

		add_action(
			'woocommerce_after_checkout_validation',
			array( $this, 'woocommerce_checkout_process' ),
			10,
			2
		);

		add_action( 'woocommerce_store_api_checkout_update_order_from_request', array( $this, 'save_shipping_point_in_order_meta' ), 10, 2 );

		if ( ! is_plugin_active( 'apaczka-pl-mapa-punktow/apaczka-points-map.php' ) ) {
			add_action(
				'woocommerce_review_order_after_shipping',
				array( $this, 'woocommerce_review_order_after_shipping' )
			);

			if ( ! class_exists( 'Apaczka_Points_Map\Points_Map_Plugin' ) ) {
				// integration with Woocommerce blocks start.
				add_action(
					'woocommerce_blocks_checkout_block_registration',
					function ( $integration_registry ) {
						if ( ! $integration_registry->is_registered( 'alsendo-connect-checkout-block' ) ) {
							$integration_registry->register( new Woo_Blocks() );
						}
					}
				);

				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_blocks_scripts' ) );
				// integration with Woocommerce blocks end.
			}
		}
	}

	/**
	 * Init settings
	 *
	 * @access public
	 * @return void
	 */
	public function init() {
		$this->init_form_fields();
		$this->init_settings();

		$this->title = $this->get_option( 'title' );

		$this->cost               = $this->get_option( 'cost' );
		$this->cost_cod           = $this->get_option( 'cost_cod' );
		$this->free_shipping_cost = '';
		$this->flat_rate          = '';
		$this->cost_per_order     = '';
	}

	/**
	 * Check is parcel locker service
	 *
	 * @param int $service_id service_id.
	 *
	 * @return bool
	 */
	private function is_parcel_locker_service( int $service_id ): bool {
		return key_exists( $service_id, self::PARCEL_LOCKER_GEOWIDGET_IDS );
	}


	/**
	 * Validates checkout process for delivery map requirements.
	 *
	 * Checks if cart contains physical products and validates that a parcel locker
	 * is selected when delivery map is enabled. Adds error notice if validation fails.
	 *
	 * @return void
	 */
	public function woocommerce_checkout_process() {

		$cart_contents = array();
		if ( is_object( WC()->session ) ) {
			$cart_contents = WC()->session->get( 'cart' );
		}

		$physical_product_in_cart = false;

		if ( ! empty( $cart_contents ) ) {
			foreach ( $cart_contents as $cart_key => $value ) {
				if ( isset( $value['product_id'] ) && ! empty( $value['product_id'] ) ) {
					$product = wc_get_product( $value['product_id'] );

					if ( $product && ! $product->is_virtual() ) {
						$physical_product_in_cart = true;
					}
				}
			}
		}

		if ( $this->is_delivery_map_button_display() && $physical_product_in_cart ) {
			if ( empty( $_POST['apm_access_point_id'] ) ) {
				wc_add_notice( esc_html__( 'Parcel locker must be choosen.', 'alsendo-connect' ), 'error' );
			}
		}
	}

	/**
	 * Hook review_order_after_shipping to show template with map button
	 * for classic checkout page
	 *
	 * @return void
	 */
	public function woocommerce_review_order_after_shipping() {
		if ( ! class_exists( 'Apaczka_Points_Map\Points_Map_Plugin' ) ) {
			if ( ! self::$review_order_after_shipping_once ) {
				if ( 'yes' !== get_option( 'alsendo_connect_settings_general_map_debug_mode' ) ) {
					wc_get_template(
						'checkout/alsendo-connect-review-order-after-shipping-map-button.php',
						array(),
						'',
						alsendo_connect()->get_plugin_templates_dir( true )
					);
				}
				self::$review_order_after_shipping_once = true;
			}
		}
	}


	/**
	 * Adds shipping management meta box to WooCommerce order pages.
	 *
	 * Conditionally displays the Alsendo Connect shipment management meta box
	 * on order edit pages if the order has associated shipping data or delivery
	 * point information. Supports both legacy and HPOS (High-Performance Order Storage) systems.
	 *
	 * @param string $post_type The post type or screen ID.
	 * @param object $post      The post object or order object.
	 * @return void
	 */
	public function add_meta_boxes( $post_type, $post ) {

		$apaczka_wc_order_data          = array();
		$alsendo_connect_delivery_point = array();

		$show_apaczka_metabox = false;

		if ( 'yes' === get_option( 'woocommerce_custom_orders_table_enabled' ) ) {
			if ( 'woocommerce_page_wc-orders' === $post_type ) {

				$order_id                       = $post->get_id();
				$apaczka_wc_order_data          = alsendo_connect()->get_order_meta_data( $order_id );
				$alsendo_connect_delivery_point = alsendo_connect()->get_delivery_point_meta_data( $order_id );
				if ( ! empty( $apaczka_wc_order_data ) || ! empty( $alsendo_connect_delivery_point ) ) {
					$show_apaczka_metabox = true;
				}
			}
		} else {

			global $post;

			if ( is_object( $post ) ) {
				$order_id  = $post->ID;
				$post_type = get_post_type( $order_id );

				if ( 'shop_order' === $post_type ) {
					$apaczka_wc_order_data          = alsendo_connect()->get_order_meta_data( $order_id );
					$alsendo_connect_delivery_point = alsendo_connect()->get_delivery_point_meta_data( $order_id );
					if ( ! empty( $apaczka_wc_order_data ) || ! empty( $alsendo_connect_delivery_point ) ) {
						$show_apaczka_metabox = true;
					}
				}
			}
		}

		if ( $show_apaczka_metabox ) {

			add_meta_box(
				$this->id,
				esc_html__( 'Alsendo Connect Manage Shipment', 'alsendo-connect' ),
				array( $this, 'order_metabox' ),
				null,
				'normal',
				'default',
				array(
					'wc_order_apaczka_meta_data'          => $apaczka_wc_order_data,
					'alsendo_connect_delivery_point'      => ! empty( $alsendo_connect_delivery_point ) ? $alsendo_connect_delivery_point : null,
					'sender_templates'                    => ( new Sender_Settings_Templates_Helper() )
						->get_all_templates_list(),
					'sender_templates_json'               => ( new Sender_Settings_Templates_Helper() )
						->get_all_templates_json(),
					'package_properties_templates'        => ( new Gateway_Settings_Templates_Helper() )
						->get_all_templates_list(),
					'package_properties_templates_json'   => ( new Gateway_Settings_Templates_Helper() )
						->get_all_templates_json(),
					'package_properties_services'         => ( new Alsendo_API() )->get_service_structure_apaczka(),
					'package_properties_parcel_types'     => $this->fields['parcel_type']['options'],
					'package_properties_shipping_methods' => $this->fields['shipping_method']['options'],
					'package_properties_hours'            => $this->fields['pickup_hour_from']['options'],
				)
			);
		}
	}


	/**
	 * Initialise Settings Form Fields
	 */
	public function init_form_fields() {

		$options_hours = array();
		for ( $h = 9; $h < 20; $h++ ) {
			$options_hours[ $h . ':00' ] = $h . ':00';
			if ( $h < 19 ) {
				$options_hours[ $h . ':30' ] = $h . ':30';
			}
		}

		$this->fields = array(
			array(
				'title'                    => esc_html__(
					'Method settings',
					'alsendo-connect'
				),
				'type'                     => 'title',
				'description'              => '',
				'id'                       => 'section_general_settings',
				'visible_on_order_details' => false,
			),
			'title'                  => array(
				'title'                    => esc_html__(
					'Method title',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'default'                  => esc_html__(
					'Method title',
					'alsendo-connect'
				),
				'desc_tip'                 => false,
				'visible_on_order_details' => false,
			),

			'cod'                    => array(
				'title'                    => esc_html__(
					'COD method',
					'alsendo-connect'
				),
				'type'                     => 'checkbox',
				'label'                    => '',
				'default'                  => 'no',
				'visible_on_order_details' => false,
			),
			'declared_content'       => array(
				'title'                    => esc_html__(
					'Declared value',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'label'                    => '',
				'visible_on_order_details' => true,
			),
			'insurance'              => array(
				'title'                    => esc_html__(
					'Insurance',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'options'                  => array(
					'yes' => esc_html__( 'Yes', 'alsendo-connect' ),
					'no'  => esc_html__( 'No', 'alsendo-connect' ),
				),
				'visible_on_order_details' => true,
			),
			array(
				'title'                    => esc_html__(
					'Default shipping settings',
					'alsendo-connect'
				),
				'type'                     => 'title',
				'description'              => '',
				'visible_on_order_details' => false,
			),
			'service'                => array(
				'title'                    => esc_html__(
					'Service',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),

			'parcel_type'            => array(
				'title'                    => esc_html__(
					'Parcel type',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'desc_tip'                 => '',
				'options'                  => array(
					'box'             => esc_html__(
						'Box',
						'alsendo-connect'
					),
					'europalette'     => esc_html__(
						'Europallet',
						'alsendo-connect'
					),
					'palette_60x80'   => esc_html__(
						'Pallet 60x80',
						'alsendo-connect'
					),
					'palette_120x100' => esc_html__(
						'Pallet 120x100',
						'alsendo-connect'
					),
					'palette_120x120' => esc_html__(
						'Pallet 120x120',
						'alsendo-connect'
					),
				),
				'visible_on_order_details' => true,
			),

			'is_nstd'                => array(
				'title'                    => esc_html__(
					'Non standard package',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => 'no',
				'desc_tip'                 => true,
				'options'                  => array(
					'yes' => esc_html__( 'Yes', 'alsendo-connect' ),
					'no'  => esc_html__( 'No', 'alsendo-connect' ),
				),
				'visible_on_order_details' => true,
			),

			'shipping_method'        => array(
				'title'                    => esc_html__(
					'Shipping method',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'desc_tip'                 => true,
				'options'                  => array(
					'POINT'   => esc_html__(
						'Shipment directly at the point',
						'alsendo-connect'
					),
					'COURIER' => esc_html__(
						'Courier pickup request',
						'alsendo-connect'
					),
					'SELF'    => esc_html__(
						'Pickup self',
						'alsendo-connect'
					),
				),
				'visible_on_order_details' => true,
			),

			'pickup_hour_from'       => array(
				'title'                    => esc_html__(
					'Pickup hour from',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'options'                  => $options_hours,
				'visible_on_order_details' => true,
			),
			'pickup_hour_to'         => array(
				'title'                    => esc_html__(
					'Pickup hour to',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'options'                  => $options_hours,
				'visible_on_order_details' => true,
			),

			'dispath_point_inpost'   => array(
				'title'                    => esc_html__(
					'Default dispatch point (InPost)',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),

			'dispath_point_kurier48' => array(
				'title'                    => esc_html__(
					'Default dispatch point (Kurier48)',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),
			'dispath_point_ups'      => array(
				'title'                    => esc_html__(
					'Default dispatch point (UPS)',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),
			'dispath_point_dpd'      => array(
				'title'                    => esc_html__(
					'Default dispatch point (DPD)',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),

			'dispath_point_dhl'      => array(
				'title'                    => esc_html__(
					'Default dispatch point (DHL)',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),

			'package_length'         => array(
				'title'                    => esc_html__(
					'Package length [cm]',
					'alsendo-connect'
				),
				'type'                     => 'number',
				'description'              => esc_html__(
					'Package length [cm].',
					'alsendo-connect'
				),
				'default'                  => '',
				'desc_tip'                 => true,
				'custom_attributes'        => array(
					'min'      => 0,
					'max'      => 10000,
					'step'     => 1,
					'required' => 'required',
				),
				'visible_on_order_details' => true,
			),
			'package_width'          => array(
				'title'                    => esc_html__(
					'Package width [cm]',
					'alsendo-connect'
				),
				'type'                     => 'number',
				'description'              => esc_html__(
					'Package width [cm].',
					'alsendo-connect'
				),
				'default'                  => '',
				'desc_tip'                 => true,
				'custom_attributes'        => array(
					'min'      => 0,
					'max'      => 10000,
					'step'     => 1,
					'required' => 'required',
				),
				'visible_on_order_details' => true,
			),
			'package_height'         => array(
				'title'                    => esc_html__(
					'Package height [cm]',
					'alsendo-connect'
				),
				'type'                     => 'number',
				'description'              => esc_html__(
					'Package height [cm].',
					'alsendo-connect'
				),
				'default'                  => '',
				'desc_tip'                 => true,
				'custom_attributes'        => array(
					'min'      => 0,
					'max'      => 10000,
					'step'     => 1,
					'required' => 'required',
				),
				'visible_on_order_details' => true,
			),
			'package_weight'         => array(
				'title'                    => esc_html__(
					'Package weight [kg]',
					'alsendo-connect'
				),
				'type'                     => 'number',
				'description'              => esc_html__(
					'Package weight [kg].',
					'alsendo-connect'
				),
				'default'                  => '',
				'desc_tip'                 => true,
				'custom_attributes'        => array(
					'min'      => 0,
					'max'      => 10000,
					'step'     => 'any',
					'required' => 'required',
				),
				'visible_on_order_details' => true,
			),
			'package_contents'       => array(
				'title'                    => esc_html__( 'Package contents', 'alsendo-connect' ),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => true,
			),

			'create_template'        => array(
				'title'                    => esc_html__(
					'Create new template from this settings?',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => 'no',
				'desc_tip'                 => true,
				'options'                  => array(
					'no'  => esc_html__( 'Nie', 'alsendo-connect' ),
					'yes' => esc_html__( 'Tak', 'alsendo-connect' ),
				),
				'visible_on_order_details' => false,
			),

			'new_template_name'      => array(
				'title'                    => esc_html__(
					'New template name',
					'alsendo-connect'
				),
				'type'                     => 'text',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'visible_on_order_details' => false,
			),

			'select_template'        => array(
				'title'                    => esc_html__(
					'Choose template to load',
					'alsendo-connect'
				),
				'type'                     => 'select',
				'description'              => '',
				'default'                  => '',
				'desc_tip'                 => true,
				'options'                  => ( new Gateway_Settings_Templates_Helper() )->get_all_templates_list(),
				'visible_on_order_details' => true,
			),
			'load_from_template'     => array(
				'name'                     => '',
				'title'                    => '',
				'type'                     => 'load_from_template',
				'id'                       => 'load_from_template',
				'visible_on_order_details' => true,
			),

		);

		$form_settings = array(
			'cod' => array(
				'title'                    => esc_html__(
					'COD method',
					'alsendo-connect'
				),
				'type'                     => 'checkbox',
				'label'                    => '',
				'default'                  => 'no',
				'visible_on_order_details' => false,
			),
		);

		$this->form_fields = $form_settings;
	}


	/**
	 * Renders the order meta box content.
	 *
	 * Wrapper method that calls the static content rendering method for the
	 * Alsendo Connect shipment management meta box.
	 *
	 * @param object $post         The post object or order object.
	 * @param array  $metabox_data Meta box data including order and shipping information.
	 * @return void
	 */
	public function order_metabox( $post, $metabox_data ) {
		self::order_metabox_content( $post, $metabox_data );
	}



	/**
	 * Sets default values for WooCommerce order shipping data.
	 *
	 * Populates missing shipping data fields with default values based on order information,
	 * payment method, and plugin settings. Handles COD amounts, sender configuration,
	 * declared content, pickup dates, and receiver phone numbers.
	 *
	 * @param mixed    $apaczka_wc_order_data Existing order shipping data.
	 * @param WC_Order $order                 WooCommerce order object.
	 * @return array Updated order data with default values applied.
	 */
	private static function set_defaults_to_wc_order_data(
		$apaczka_wc_order_data,
		$order
	): array {

		// sender.
		// Package properties.
		// Additional options.

		if ( empty( $apaczka_wc_order_data ) || ! is_array( $apaczka_wc_order_data ) ) {
			$apaczka_wc_order_data = array();
		}

		$payment_method = $order->get_payment_method();

		if ( empty( $apaczka_wc_order_data['additional_options']['cod_amount'] ) ) {

			if ( 'cod' === $payment_method ) {
				$apaczka_wc_order_data['additional_options']['cod_amount'] = $order->get_total();
			} else {
				$apaczka_wc_order_data['additional_options']['cod_amount'] = 0;
			}
		}

		$sender = ( new Global_Settings() )->get_current_sender_config();
		if ( ! isset( $apaczka_wc_order_data['sender'] ) ) {
			$apaczka_wc_order_data['sender'] = $sender;
		}

		if ( empty( $apaczka_wc_order_data['package_properties']['declared_content'] ) ) {

			if ( 'cod' === $payment_method ) {
				$apaczka_wc_order_data['package_properties']['declared_content'] = $order->get_total();
			} elseif ( 'yes' === Plugin::get_option( 'declared_content_auto' ) ) {
					$apaczka_wc_order_data['package_properties']['declared_content'] = $order->get_total();
			} else {
				$apaczka_wc_order_data['package_properties']['declared_content'] = 0;
			}
		}

		if ( empty( $apaczka_wc_order_data['package_properties']['pickup_date'] ) ) {
			$apaczka_wc_order_data['package_properties']['pickup_date'] = gmdate( 'Y-m-d' );
		}

		if ( ! isset( $apaczka_wc_order_data['receiver'] ) || ! is_array( $apaczka_wc_order_data['receiver'] ) ) {
			$apaczka_wc_order_data['receiver'] = array();
		}

		if ( empty( $apaczka_wc_order_data['receiver']['phone'] ) ) {
			$apaczka_wc_order_data['receiver']['phone'] = empty( $order->get_shipping_phone() ) ? $order->get_billing_phone() : $order->get_shipping_phone();
		}

		return $apaczka_wc_order_data;
	}



	/**
	 * Get Apaczka order status
	 *
	 * @param int $apaczka_order_id apaczka_order_id.
	 *
	 * @return string
	 */
	public static function get_apaczka_order_status( int $apaczka_order_id ): ?string {
		// $api_order = ( new Alsendo_API() )->get_order_details_apaczka( $apaczka_order_id );
		$api_order = ( new Alsendo_API() )->apaczka_get_order_direct( $apaczka_order_id );

		if ( isset( $api_order->order->status ) ) {

			return $api_order->order->status;
		}

		return null;
	}


	/**
	 * Renders the complete order meta box content for shipment management.
	 *
	 * Displays the shipping management interface including sender templates, package properties,
	 * and shipment status. Handles different API types (Apaczka, Ecolet, Zaslat) and manages
	 * order status updates based on shipment cancellation status.
	 *
	 * @param object $post         The post object or WC_Order instance.
	 * @param array  $metabox_data Meta box data containing templates and configuration.
	 * @param bool   $output       Whether to output directly or return as string.
	 * @return string|void Returns HTML string if $output is false, otherwise outputs directly.
	 */
	public static function order_metabox_content(
		$post,
		$metabox_data,
		$output = true
	) {

		if ( ! $output ) {
			ob_start();
		}

		if ( is_a( $post, 'WC_Order' ) ) {
			$order_id = $post->get_id();
		} else {
			$order_id = $post->ID;
		}

		$order = wc_get_order( $order_id );

		if ( ! $order || is_wp_error( $order ) ) {
			return;
		}

		$api_type = alsendo_connect()->get_main_settings_api();

		$alsendo_connect_metabox_order_data  = $metabox_data['args']['wc_order_apaczka_meta_data'];
		$sender_templates                    = $metabox_data['args']['sender_templates'];
		$sender_templates_json               = $metabox_data['args']['sender_templates_json'];
		$package_properties_templates        = $metabox_data['args']['package_properties_templates'];
		$package_properties_templates_json   = $metabox_data['args']['package_properties_templates_json'];
		$package_properties_services         = $metabox_data['args']['package_properties_services'];
		$package_properties_parcel_types     = $metabox_data['args']['package_properties_parcel_types'];
		$package_properties_shipping_methods = $metabox_data['args']['package_properties_shipping_methods'];
		$package_properties_hours            = $metabox_data['args']['package_properties_hours'];
		$alsendo_connect_delivery_point      = alsendo_connect()->get_delivery_point_meta_data( $order_id );

		//$services     = ( new Alsendo_API() )->get_service_structure_apaczka();
		$package_send = false;

		$alsendo_connect_metabox_order_data = self::set_defaults_to_wc_order_data(
			$alsendo_connect_metabox_order_data,
			$order
		);

		$options_hours = array();
		for ( $h = 9; $h < 20; $h++ ) {
			if ( $h < 10 ) {
				$h = '0' . $h;
			}
			$options_hours[ $h . ':00' ] = $h . ':00';
			if ( $h < 19 ) {
				$options_hours[ $h . ':30' ] = $h . ':30';
			}
		}

		$alsendo_order_id = alsendo_connect()->get_alsendo_order_id_from_order_meta( $order_id );

		if ( isset( $alsendo_connect_metabox_order_data['package_send'] )
			&& 1 === $alsendo_connect_metabox_order_data['package_send'] ) {

			if ( 'apaczka' === $api_type ) {

				if ( ! empty( $alsendo_order_id ) ) {

					$status = self::get_apaczka_order_status( intval( $alsendo_order_id ) );
					if ( 'CANCELLED' === $status ) {
						$alsendo_connect_metabox_order_data['package_send'] = 0;
						$meta_data = alsendo_connect()->get_order_meta_data( $order_id );

						// APACZKA update as cancelled.

						$meta_data['package_send']      = 0;
						$meta_data['package_cancelled'] = 1;
						$order->update_meta_data( 'alsendo_connect_wc_order', $meta_data );

						$current_order_status = $order->get_status();
						if ( $order->get_date_paid() ) {
							if ( 'processing' !== $current_order_status ) {
								$order->update_status( 'wc-processing' );
							}
						} elseif ( 'on-hold' !== $current_order_status ) {
							$order->update_status( 'wc-on-hold' );
						}
						$order->save();

					} else {

						$package_send = true;

						$url_waybill
							= admin_url(
								'admin-ajax.php?action=alsendo_connect&alsendo_connect_action=get_waybill&security='
								. wp_create_nonce( 'alsendo_connect_nonce_param' )
								. '&apaczka_order_id='
								. $alsendo_order_id
							);

						include alsendo_connect()->get_plugin_templates_dir() . '/html-order-metabox-cancel-popup.php';
					}
				}
			}

			if ( 'ecolet' === $api_type ) {

				if ( ! empty( $alsendo_order_id ) ) {
					$ecolet    = new Ecolet();
					$repsponse = $ecolet->get_order( $alsendo_order_id );
					if ( is_object( $repsponse ) && property_exists( $repsponse, 'status' ) && 'canceled' === $repsponse->status ) {
						$alsendo_connect_metabox_order_data['package_send'] = 0;
						$meta_data = alsendo_connect()->get_order_meta_data( $order_id );

						$meta_data['package_send'] = 0;
						$order->update_meta_data( 'alsendo_connect_wc_order', $meta_data );

						$current_order_status = $order->get_status();
						if ( $order->get_date_paid() ) {
							if ( 'processing' !== $current_order_status ) {
								$order->update_status( 'wc-processing' );
							}
						} elseif ( 'on-hold' !== $current_order_status ) {
							$order->update_status( 'wc-on-hold' );
						}
						$order->save();
					} else {
						$package_send = true;
					}
				}
			}

			if ( 'zaslat' === $api_type ) {
				$package_send = true;
			}
		}

		$gateway_opts_templates = ( new Gateway_Settings_Templates_Helper() )->get_all_templates_list();
		wp_nonce_field( 'alsendo_connect_order_metabox', 'alsendo_connect_order_metabox_nonce' );
		if ( 'apaczka' === $api_type ) {
			include alsendo_connect()->get_plugin_templates_dir() . '/html-order-metabox-apaczka.php';
		}
		if ( 'zaslat' === $api_type ) {
			include alsendo_connect()->get_plugin_templates_dir() . '/html-order-metabox-zaslat.php';
		}
		if ( 'ecolet' === $api_type ) {
			include alsendo_connect()->get_plugin_templates_dir() . '/html-order-metabox-ecolet.php';
		}

		if ( ! $output ) {
			$out = ob_get_clean();

			return $out;
		}
	}


	/**
	 * Save data in order metabox.
	 */
	public function save_post( $post_id ) {

		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		global $post;
		$order_id = null;

		if ( 'yes' === get_option( 'woocommerce_custom_orders_table_enabled' ) ) {
			// HPOS usage is enabled.
			if ( is_a( $post, 'WC_Order' ) ) {
				$order_id = $post->get_id();
			}
		} else {
			// Legacy orders DB tables are in use.
			if ( is_object( $post ) && 'shop_order' === $post->post_type ) {
				$order_id = $post->ID;
			}
		}

		if ( ! $order_id ) {
			return;
		}

		$order = wc_get_order( $order_id );

		if ( ! $order || is_wp_error( $order ) ) {
			return;
		}

		$meta_data = alsendo_connect()->get_order_meta_data( $order_id );
		if ( ! empty( $meta_data ) ) {
			$title = $meta_data['package_properties']['title'];
			$cod   = $meta_data['package_properties']['cod'];

			$package_properties = ! empty( $_POST['alsendo_connect_wc_order']['package_properties'] ) ? map_deep( wp_unslash( $_POST['alsendo_connect_wc_order']['package_properties'] ), 'sanitize_text_field' ) : '';
			if ( ! empty( $package_properties ) ) {
				$package_properties['title']     = $title;
				$package_properties['cod']       = $cod;
				$meta_data['package_properties'] = $package_properties;

				$order->update_meta_data( 'alsendo_connect_wc_order', $meta_data );
				$order->save();
			}
		}
	}


	/**
	 * Update order meta
	 *
	 * @param int|string $order_id $order_id.
	 *
	 * @return void
	 */
	public function woocommerce_checkout_update_order_meta( $order_id ) {

		$order = wc_get_order( $order_id );

		if ( ! $order || is_wp_error( $order ) ) {
			return;
		}

		$package_properties = $this->get_package_properties( $order );

		if ( ! empty( $_POST['apm_supplier'] ) ) {
			$alsendo_connect_delivery_point =
				array(
					'apm_access_point_id'         => ! empty( $_POST['apm_access_point_id'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_access_point_id'] ) ) : '',
					'apm_supplier'                => ! empty( $_POST['apm_supplier'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_supplier'] ) ) : '',
					'apm_name'                    => ! empty( $_POST['apm_name'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_name'] ) ) : '',
					'apm_foreign_access_point_id' => ! empty( $_POST['apm_foreign_access_point_id'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_foreign_access_point_id'] ) ) : '',
					'apm_street'                  => ! empty( $_POST['apm_street'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_street'] ) ) : '',
					'apm_city'                    => ! empty( $_POST['apm_city'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_city'] ) ) : '',
					'apm_postal_code'             => ! empty( $_POST['apm_postal_code'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_postal_code'] ) ) : '',
					'apm_country_code'            => ! empty( $_POST['apm_country_code'] ) ? sanitize_text_field( wp_unslash( $_POST['apm_country_code'] ) ) : '',
				);
		} else {
			$alsendo_connect_delivery_point = null;
		}

		$receiver_company = '';
		if ( ! empty( $order->get_shipping_company() ) ) {
			$receiver_company = $order->get_shipping_company();
		} elseif ( ! empty( $order->get_billing_company() ) ) {
			$receiver_company = $order->get_billing_company();
		}

		$receiver = array(
			'country_code'       => $order->get_shipping_country(),
			// Kod ISO 3166.
			'name'               => ! empty( $receiver_company )
				? $receiver_company
				: $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
			'line1'              => $order->get_shipping_address_1(),
			'line2'              => $order->get_shipping_address_2(),
			'postal_code'        => $order->get_shipping_postcode(),
			'city'               => $order->get_shipping_city(),
			'is_residential'     => 0,
			'contact_person'     => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
			'email'              => $order->get_billing_email(),
			'phone'              => $order->get_shipping_phone(),
			'foreign_address_id' => ! empty( $alsendo_connect_delivery_point['apm_foreign_access_point_id'] )
				? $alsendo_connect_delivery_point['apm_foreign_access_point_id'] : '',
		);

		$order_meta                       = array();
		$order_meta['package_properties'] = $package_properties;
		$order_meta['receiver']           = $receiver;

		// save wc order data.
		if ( $alsendo_connect_delivery_point ) {
			$order->update_meta_data( 'alsendo_connect_delivery_point', $alsendo_connect_delivery_point );
		}

		$order->update_meta_data( 'alsendo_connect_wc_order', $order_meta );
		$order->save();
	}



	/**
	 * Creates an API order array for shipping services based on the selected API type.
	 *
	 * Generates order data structures for different shipping APIs (apaczka, ecolet, zaslat)
	 * by processing WooCommerce order information, shipping data, and package properties.
	 * Each API requires different data formats and field mappings.
	 *
	 * @param WC_Order           $wc_order The WooCommerce order object containing customer and shipping details.
	 * @param array              $data Shipping data including sender, receiver, package properties, and additional options.
	 * @param WC_Shipping_Method $shipping_method The selected WooCommerce shipping method.
	 * @param mixed              $point Delivery point ID for pickup/drop-off locations (optional).
	 * @param string|null        $service_id Specific shipping service identifier (optional).
	 *
	 * @return array Formatted order array structure specific to the configured API type
	 *
	 * @throws Exception When API type is not supported or required data is missing.
	 */
	private static function create_api_order(
		WC_Order $wc_order,
		array $data,
		WC_Shipping_Method $shipping_method,
		$point,
		$service_id = null
	): array {

		$helper = new Helper();

		$api_type = alsendo_connect()->get_main_settings_api();

		$pickup_method = $data['package_properties']['shipping_method'];

		$shipping_name    = $wc_order->get_shipping_company();
		$shipping_contact = '';
		if ( empty( $shipping_name ) ) {
			$shipping_name    = $wc_order->get_shipping_first_name() . ' '
				. $wc_order->get_shipping_last_name();
			$shipping_contact = $wc_order->get_shipping_first_name() . ' '
				. $wc_order->get_shipping_last_name();

		} else {
			$shipping_contact = $wc_order->get_shipping_first_name() . ' '
				. $wc_order->get_shipping_last_name();
		}

		$sender_country = ! empty( $data['sender']['country'] ) ? $data['sender']['country'] : get_option( ( new Global_Settings() )->get_setting_id( 'sender_country' ) );

		if ( ! empty( $data['sender']['province'] ) ) {
			$sender_province = trim( $data['sender']['province'] );
		} else {
			$sender_province = get_option( ( new Global_Settings() )->get_setting_id( 'sender_province' ) );
		}

		$receiver_province = '';
		if ( ! empty( $wc_order->get_shipping_state() ) ) {
			$receiver_province = $wc_order->get_shipping_state();
		} elseif ( ! empty( $wc_order->get_billing_state() ) ) {
			$receiver_province = $wc_order->get_billing_state();
		}

		$data['receiver']['foreign_address_id'] = $point;

		if ( ! empty( $data['sender']['apm_foreign_access_point_id'] ) ) {
			$sender_foreign_address_id = $data['sender']['apm_foreign_access_point_id'];
		} else {
			$sender_foreign_address_id = '';
		}

		// use Company if exists as name in request.
		$sender_name = $data['sender']['first_name'] . ' ' . $data['sender']['last_name'];
		if ( isset( $data['sender']['company_name'] ) && ! empty( $data['sender']['company_name'] ) ) {
			$sender_name = $data['sender']['company_name'];
		}

		$receiver_name = '';
		if ( ! empty( $wc_order->get_billing_company() ) ) {
			$receiver_name = $wc_order->get_billing_company();
		} elseif ( ! empty( $wc_order->get_shipping_company() ) ) {
			$receiver_name = $wc_order->get_shipping_company();
		} else {
			$receiver_name = $shipping_contact;
		}

		if ( 'ecolet' === $api_type ) {

			$ecolet = new Ecolet();

			$service_id = '';

			if ( ! empty( $data['selected_service'] ) ) {
				// order, we have already selected service.
				$service_id = $data['selected_service'];

			} else {
				// only valuation: check if some exact methos is linked.
				if ( empty( $data['alsendo_full'] ) ) {
					$service_id = $helper->get_linked_carrier_id( $wc_order->get_id() );

				}
			}

			$api_service_slug = $ecolet->get_service_slug( $service_id );

			if ( 'RO' === strtoupper( $data['sender']['country'] ) || 'BG' === strtoupper( $data['sender']['country'] ) ) {

				$county_full_name   = $helper->get_full_county_name( $sender_province, $data['sender']['country'] );
				$zip                = trim( $data['sender']['postal_code'] );
				$sender_locality_id = $ecolet->get_locality_id( $data['sender']['country'], $data['sender']['city'], $county_full_name, $zip, 'sender' );
				//sleep( 1 );
			} else {
				$sender_locality_id = null;
			}

			if ( 'RO' === strtoupper( $data['receiver']['country_code'] ) || 'BG' === strtoupper( $data['receiver']['country_code'] ) ) {
				$county_full_name     = $helper->get_full_county_name( $receiver_province, $data['receiver']['country_code'] );
				$zip                  = trim( $data['receiver']['postal_code'] );
				$receiver_locality_id = $ecolet->get_locality_id( $data['receiver']['country_code'], $data['receiver']['city'], $county_full_name, $zip, 'receiver' );
			} else {
				$receiver_locality_id = null;
			}

			// Map parcel type to appropriate value for Romania.
			$parcel_type = 'package'; // Default value.
			if ( in_array( $data['package_properties']['parcel_type'], array( 'palette_60x80', 'europalette', 'palette_120x100', 'palette_120x120' ), true ) ) {
				$parcel_type = 'pallet';
			}
			if ( in_array( $data['package_properties']['parcel_type'], array( 'envelope' ), true ) ) {
				$parcel_type = 'envelope';
			}

			$parcels = array();
			if ( ! empty( $data['parcels'] ) && is_array( $data['parcels'] ) ) {

				foreach ( $data['parcels'] as $parcel ) {

					$parcel_content = ! empty( $parcel['package_contents'] ) ? $parcel['package_contents'] : '';

					if ( ! empty( $parcel['contents_tag'] ) ) {
						$parcel_content = $helper->get_parcel_content( $wc_order->get_id(), $parcel['contents_tag'] );

					}

					$parcels[] = array(
						'weight'         => (int) $parcel['package_weight'],
						'dimensions'     => array(
							'length' => (int) $parcel['package_length'],
							'width'  => (int) $parcel['package_width'],
							'height' => (int) $parcel['package_height'],
						),
						'declared_value' => (float) $parcel['declared_content'],
						'content'        => $parcel_content,
					);
				}
			}

			$order = array(
				'woo_order_id'        => $wc_order->get_id(),
				'sender'              => array(
					'name'           => $sender_name,
					'country'        => $sender_country,
					'county'         => $data['sender']['province'],
					'locality_id'    => $sender_locality_id,
					'locality'       => $data['sender']['city'],
					'postal_code'    => $data['sender']['postal_code'],
					'street_name'    => $data['sender']['street'],
					'street_number'  => $data['sender']['building_number'],
					'block'          => isset( $data['sender']['block'] ) ? $data['sender']['block'] : '',
					'entrance'       => isset( $data['sender']['entrance'] ) ? $data['sender']['entrance'] : '',
					'floor'          => isset( $data['sender']['floor'] ) ? $data['sender']['floor'] : '',
					'flat'           => isset( $data['sender']['apartment_number'] ) ? $data['sender']['apartment_number'] : '',
					'contact_person' => $data['sender']['contact_person'],
					'email'          => $data['sender']['email'],
					'phone'          => $data['sender']['phone'],
					'has_map_point'  => false,
					'map_point_id'   => null,
				),
				'receiver'            => array(
					'name'           => $receiver_name,
					'country'        => $data['receiver']['country_code'],
					'county'         => $receiver_province,
					'locality_id'    => $receiver_locality_id,
					'locality'       => $data['receiver']['city'],
					'postal_code'    => $data['receiver']['postal_code'],
					'street_name'    => isset( $data['receiver']['line1'] ) ? $data['receiver']['line1'] : '',
					'street_number'  => isset( $data['receiver']['line2'] ) ? $data['receiver']['line2'] : '',
					'block'          => isset( $data['receiver']['block'] ) ? $data['receiver']['block'] : '',
					'entrance'       => isset( $data['receiver']['entrance'] ) ? $data['receiver']['entrance'] : '',
					'floor'          => isset( $data['receiver']['floor'] ) ? $data['receiver']['floor'] : '',
					'flat'           => isset( $data['receiver']['apartment'] ) ? $data['receiver']['apartment'] : '',
					'contact_person' => $data['receiver']['contact_person'], // $shipping_contact,
					'email'          => $data['receiver']['email'], // $wc_order->get_billing_email(),
					'phone'          => $data['receiver']['phone'], // $wc_order->get_billing_phone(),
					'has_map_point'  => ! empty( $point ),
					'map_point_id'   => ! empty( $point ) ? $point : null,
				),
				'parcel'              => array(
					'type'         => $parcel_type,
					'shape'        => 'yes' === $data['package_properties']['is_nstd'] ? 'nonstandard' : 'standard',
					'observations' => $data['additional_options']['comment'],
				),
				'parcels'             => $parcels,
				'additional_services' => array(
					'cod'               => array(
						'status' => ! empty( $data['additional_options']['cod_amount'] ),
						'amount' => ! empty( $data['additional_options']['cod_amount'] ) ? (float) $data['additional_options']['cod_amount'] : 0,
					),
					'open_package'      => array(
						'status' => false,
					),
					'rod'               => array(
						'status' => false,
					),
					'rop'               => array(
						'status' => false,
					),
					'saturday_delivery' => array(
						'status' => false,
					),
					'sms_notify'        => array(
						'status' => false,
					),
					'swap'              => array(
						'status' => false,
					),
					'epod'              => array(
						'status' => false,
					),
				),
				'courier'             => array(
					'service'     => $api_service_slug,
					'pickup'      => array(
						'type' => strtolower( $pickup_method ),
						'date' => $data['package_properties']['pickup_date'],
						'time' => gmdate( 'H:i', strtotime( $data['package_properties']['pickup_hour_to'] ) ),
					),
					'contract_id' => isset( $data['contract_id'] ) ? $data['contract_id'] : null,
				),
			);

		}

		if ( 'apaczka' === $api_type ) {

			if ( ! empty( $data['selected_service'] ) ) {
				// case: create Order
				// service selected by Merchant after valuation.
				$service_id = $data['selected_service'];

			} else {
				// case: Order valuation
				if ( empty( $data['alsendo_full'] ) ) {
					$alsendo_api_carrier_id = $helper->get_linked_carrier_id( $wc_order->get_id() );
					if ( ! empty( $alsendo_api_carrier_id ) ) {
						$service_id = $alsendo_api_carrier_id;
					}
				}
			}

			if ( $service_id ) {
				if ( 316 === (int) $service_id || 317 === (int) $service_id ) {
					$sender_foreign_address_id = '27630';
				}
				if ( 310 === (int) $service_id || 311 === (int) $service_id ) {
					$sender_foreign_address_id = 'DEFAULT';
				}
				if ( 90 === (int) $service_id ) {
					$sender_foreign_address_id = 'LW4-001';
				}
			}

			$apaczka_content = ! empty( $data['package_properties']['package_contents'] ) ? $data['package_properties']['package_contents'] : '';
			if ( ! empty( $data['package_properties']['package_contents_tag'] ) ) {
				$contents_tag    = $data['package_properties']['package_contents_tag'];
				$apaczka_content = $helper->get_parcel_content( $wc_order->get_id(), $contents_tag );
			}

			$receiver_address_line_full = '';
			if ( ! empty( $data['receiver']['line1'] ) ) {
				$receiver_address_line_full = trim( $data['receiver']['line1'] );
			}

			if ( ! empty( $data['receiver']['line2'] ) ) {
				$receiver_address_line_2     = trim( $data['receiver']['line2'] );
				$receiver_address_line_full .= ', ' . $receiver_address_line_2;
			}

			$data['receiver']['line1'] = $receiver_address_line_full;
			$data['receiver']['line2'] = '';

			$apaczka_shipments = array();
			if ( ! empty( $data['parcels'] ) && is_array( $data['parcels'] ) ) {
				foreach ( $data['parcels'] as $parcel ) {

					$parcel_shipment_code = 'PACZKA';
					if ( 'palette_60x80' === $parcel['parcel_type'] ) {
						$parcel_shipment_code = 'POLPALETA';
					}

					if ( 'europalette' === $parcel['parcel_type'] ) {
						$parcel_shipment_code = 'PALETA';
					}

					if ( 'palette_120x100' === $parcel['parcel_type'] ) {
						$parcel_shipment_code = 'PALETA_PRZEMYSLOWA';
					}

					if ( 'palette_120x120' === $parcel['parcel_type'] ) {
						$parcel_shipment_code = 'PALETA_PRZEMYSLOWA_B';
					}

					$apaczka_shipments[] = array(
						'dimension1'         => (int) $parcel['package_length'],
						// cm
						'dimension2'         => (int) $parcel['package_width'],
						// cm
						'dimension3'         => (int) $parcel['package_height'],
						// cm
						'weight'             => (int) $parcel['package_weight'],
						// kg
						'is_nstd'            => 'yes' === $parcel['is_nstd'] ? 1 : 0,
						// todo fix
						// 0 / 1
						'shipment_type_code' => $parcel_shipment_code,
					);
				}
			}

			$order = array(
				'woo_order_id'   => $wc_order->get_id(),
				'service_id'     => $service_id,
				'address'        => array(
					'sender'   => array(
						'country_code'       => $sender_country,
						// Kod ISO 3166.
						'name'               => $sender_name,
						'line1'              => $data['sender']['street']
							. ' ' . $data['sender']['building_number']
							. ( ! empty( $data['sender']['apartment_number'] ) ? '/'
								. $data['sender']['apartment_number'] : '' ),
						'line2'              => '',
						'postal_code'        => $data['sender']['postal_code'],
						'city'               => $data['sender']['city'],
						'is_residential'     => (int) $data['sender']['is_residential'],
						// 0 / 1
						'contact_person'     => $data['sender']['contact_person'],
						'email'              => $data['sender']['email'],
						'phone'              => $data['sender']['phone'],
						'foreign_address_id' => $sender_foreign_address_id,
						// paczkomat??
						// enpoint: points
					),
					'receiver' => $data['receiver'],
				),
				'option'         => array(
					/*
					'31'  => 0, // powiadomienie sms,
					'11 ' => 0, // rod
					'19'  => 0, // dostawa w sobotę,
					'25'  => 0, // dostawa w godzinach,
					'58'  => 0,*/  // ostrożnie
				),
				'notification'   => array(
					'new'       => array(  // Powiadomienia o utworzeniu przesyłki>
						'isReceiverEmail' => null, // 0 / 1
						'isReceiverSms'   => null, // 0 / 1
						'isSenderEmail'   => null, // 0 / 1
					),
					'sent'      => array(  // Powiadomienia o wysłaniu przesyłki
						'isReceiverEmail' => null, // 0 / 1
						'isReceiverSms'   => null, // 0 / 1
						'isSenderEmail'   => null, // 0 / 1
					),
					'exception' => array( // Powiadomienia o wyjątku
						'isReceiverEmail' => null, // 0 / 1
						'isReceiverSms'   => null, // 0 / 1
						'isSenderEmail'   => null, // 0 / 1
					),
					'delivered' => array(  // Powiadomienia o doręczeniu
						'isReceiverEmail' => null, // 0 / 1
						'isReceiverSms'   => null, // 0 / 1
						'isSenderEmail'   => null, // 0 / 1
					),
				),
				// 'shipment_value' => (int) ( ( (float) $data['package_properties']['declared_content'] ) * 100 ),
				'shipment_value' => (int) ( ( (float) $data['package_properties']['declared_content'] ) ),

				'cod'            => array(
					'amount'      => ! empty( $data['additional_options']['cod_amount'] )
						? $data['additional_options']['cod_amount'] * 100 // wartość w groszach.
						: 0,  // // wartość w groszach
					'bankaccount' => $data['sender']['bank_account_number'],
				),

				'pickup'         => array(
					'type'       => $pickup_method,
					// endpoint: service_structure
					'date'       => $data['package_properties']['pickup_date'],
					// Y-m-d
					'hours_from' => gmdate( 'H:i', strtotime( $data['package_properties']['pickup_hour_from'] ) ),
					// H:i - pickup_hours
					'hours_to'   => gmdate( 'H:i', strtotime( $data['package_properties']['pickup_hour_to'] ) ),
					// H:i - pickup_hours
				),
				'shipment'       => $apaczka_shipments,
				'comment'        => $data['additional_options']['comment'],
				'content'        => $apaczka_content,
				'is_zebra'       => null,
				// 0 / 1 (wartość opcjonalna, w przypadku nie podania etykieta będzie zgodna z ustawieniami konta).
			);

		}

		if ( 'zaslat' === $api_type ) {

			$order_placed_data = alsendo_connect()->get_delivery_point_meta_data( $wc_order->get_id() );
			$service_supplier  = ! empty( $order_placed_data['apm_supplier'] ) ? $order_placed_data['apm_supplier'] : null;

			$order = array();

			$from = array(
				'id'        => $data['sender']['from_id'], // get_option( 'alsendo_connect_settings_general_zaslat_address_id' ),
				'country'   => $sender_country,
				'company'   => $sender_name,
				'firstname' => $data['sender']['first_name'],
				'surname'   => $data['sender']['last_name'],
				'street'    => $data['sender']['street'],
				'city'      => $data['sender']['city'],
				'zip'       => $data['sender']['postal_code'],
				'phone'     => $data['sender']['phone'],
				'email'     => $data['sender']['email'],
			);

			$to = array(
				'country'   => $wc_order->get_shipping_country(),
				'company'   => $data['receiver']['name'],
				'firstname' => $wc_order->get_shipping_first_name(),
				'surname'   => $wc_order->get_shipping_last_name(),
				'street'    => $data['receiver']['line1'] . '-' . $data['receiver']['line2'],
				'city'      => $data['receiver']['city'],
				'zip'       => $data['receiver']['postal_code'],
				'phone'     => $data['receiver']['phone'],
				'email'     => $data['receiver']['email'],
			);

			if ( ! empty( $service_supplier ) ) {
				$to['supplier']  = $service_supplier;
				$delivery_branch = ! empty( $order_placed_data['apm_access_point_id'] ) ? $order_placed_data['apm_access_point_id'] : null;
				if ( ! empty( $delivery_branch ) ) {
					if ( 0 === strpos( $delivery_branch, 'GLS_CZ-' ) ) {
						$delivery_branch = substr( $delivery_branch, strlen( 'GLS_CZ-' ) );
					}
					$to['delivery_branch'] = $delivery_branch;
				}
			}

			$services = array();
			if ( ! empty( $data['additional_options']['cod_amount'] ) ) {
				$cod_amount = ! empty( $data['additional_options']['cod_amount'] )
					? $data['additional_options']['cod_amount']
					: 0;
				$services[] = array(
					'code' => 'cod',
					'data' => array(
						'value' => array(
							'value'    => $cod_amount,
							'currency' => 'CZK',
						),
					),
				);
			}

			if ( ! empty( $data['additional_options']['insurance'] ) ) {
				$insurance_amount = ! empty( $data['additional_options']['insurance_amount'] )
					? $data['additional_options']['insurance_amount']
					: 0;
				$services[]       = array(
					'code' => 'ins',
					'data' => array(
						'value' => array(
							'value'    => $insurance_amount,
							'currency' => 'CZK',
						),
					),
				);
			}
			$package = array(
				0 => array(
					'weight' => (float) $data['package_properties']['package_weight'],
					'length' => (int) $data['package_properties']['package_length'],
					'width'  => (int) $data['package_properties']['package_width'],
					'height' => (int) $data['package_properties']['package_height'],
				),
			);

			$package_content = '';

			if ( ! empty( $data['package_properties']['package_contents'] ) ) {
				$package_content = $data['package_properties']['package_contents'];
			}

			if ( ! empty( $data['package_properties']['contents_tag'] ) ) {
				$package_content = $helper->get_parcel_content( $wc_order->get_id(), $data['package_properties']['contents_tag'] );
			}

			if ( ! empty( $package_content ) ) {
				$package[0]['content'] = $package_content;
			}

			if ( ! empty( $data['package_properties']['declared_content'] ) ) {
				$package[0]['value']['value']    = $data['package_properties']['declared_content'];
				$package[0]['value']['currency'] = 'CZK';
			}

			if ( empty( $service_id ) ) {
				// VALUATE ORDER.

				if ( ! empty( $data['linked_carrier'] ) && empty( $data['alsendo_full'] ) ) {
					$order['carrier'] = $data['linked_carrier'];
				}

				$order['currency'] = 'CZK';
				if ( ! empty( $data['package_properties']['pickup_request'] ) ) {
					$order['pickup_request'] = 1;
					$order['pickup_date']    = ! empty( $data['package_properties']['pickup_date'] ) ? $data['package_properties']['pickup_date'] : '';
				}
				if ( ! empty( $data['package_properties']['shipping_method'] ) ) {
					$order['type'] = $data['package_properties']['shipping_method'];
				}
				$order['from']     = $from;
				$order['to']       = $to;
				$order['packages'] = $package;
				if ( ! empty( $services ) ) {
					$order['services'] = $services;
				}
				if ( $service_supplier ) {
					if ( 'BALIKOVNA' === strtoupper( $service_supplier ) || 'PACKETA' === strtoupper( $service_supplier ) ) {
						$order['pickup_branch'] = 1;
					}
				}
				if ( ! empty( $point ) ) {
					$order['delivery_branch'] = $point;
				}
			} else {
				// CREATE_ORDER.

				$pickup_date = '';
				if ( ! empty( $data['package_properties']['pickup_request'] ) ) {
					$pickup_date             = ! empty( $data['package_properties']['pickup_date'] ) ? $data['package_properties']['pickup_date'] : '';
					$order['pickup_request'] = 1;
				}

				$type = '';
				if ( ! empty( $data['package_properties']['shipping_method'] ) ) {
					$type = $data['package_properties']['shipping_method'];
				}
				$order['shipments'] = array();

				$shipment = array(
					'carrier'  => $service_id,
					'type'     => $type,
					'from'     => $from,
					'to'       => $to,
					'packages' => $package,
				);
				if ( ! empty( $pickup_date ) ) {
					$shipment['pickup_date']    = $pickup_date;
					$shipment['pickup_request'] = 1;
				}
				if ( ! empty( $services ) ) {
					$shipment['services'] = $services;
				}
				if ( $service_supplier ) {
					if ( 'BALIKOVNA' === strtoupper( $service_supplier ) || 'ZASILKOVNA' === strtoupper( $service_supplier ) || 'PACKETA' === strtoupper( $service_supplier ) ) {
						$shipment['pickup_branch'] = 1;
						if ( 'PACKETA' === strtoupper( $service_supplier ) ) {
							$shipment['carrier'] = 'ZASILKOVNA';
						}
					}
				}
				if ( ! empty( $point ) ) {
					if ( 0 === strpos( $point, 'GLS_CZ-' ) ) {
						$point = substr( $point, strlen( 'GLS_CZ-' ) );
					}
					$shipment['delivery_branch'] = $point;
				}

				$order['shipments'][] = $shipment;

			}
		}

		return $order;
	}


	/**
	 * Validate declared value
	 *
	 * @param float  $declared_value $declared_value.
	 * @param float  $cod_amount $cod_amount.
	 * @param string $api_context $api_context.
	 *
	 * @return bool
	 */
	private static function declared_value_validate(
		float $declared_value,
		float $cod_amount,
		string $api_context
	): bool {

		if ( $cod_amount > 0 ) {
			if ( $declared_value === 0.0 ) {
				( new Alerts() )->add_error(
					esc_html__(
						'The Declared value field cannot be empty',
						'alsendo-connect'
					),
					$api_context
				);

				return false;
			}
			if ( $declared_value < $cod_amount ) {
				( new Alerts() )->add_error(
					esc_html__(
						'The Declared Value field must be equal to or higher than the COD amount',
						'alsendo-connect'
					),
					$api_context
				);

				return false;
			}
		}

		return true;
	}

	/**
	 * Ajax cancel package
	 *
	 * @return void
	 */
	public static function ajax_cancel_package() {

		check_ajax_referer( 'alsendo_connect_order_metabox', 'security' );

		$apaczka_id = ! empty( $_POST['apaczka_order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['apaczka_order_id'] ) ) : null;
		$order_id   = ! empty( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : '';

		if ( function_exists( 'wc_get_logger' ) ) {
			\wc_get_logger()->debug( 'CANCEL Apaczka_ID:', array( 'source' => 'alsendo-connect-order-' . $order_id ) );
			\wc_get_logger()->debug( print_r( $apaczka_id, true ), array( 'source' => 'alsendo-connect-order-' . $order_id ) );
		}

		if ( ! $apaczka_id ) {
			$ret['status']         = 'error';
			$ret['error_messages'] = esc_html__( 'No apaczka_id found in meta data', 'alsendo-connect' );
			echo wp_json_encode( $ret );
			wp_die();
		}

		$order = wc_get_order( $order_id );
		if ( ! $order || is_wp_error( $order ) ) {
			$ret['status']         = 'error';
			$ret['error_messages'] = 'Problem with this order: no order for ' . $order_id . ' or order returned error';
			echo wp_json_encode( $ret );
			wp_die();
		}

		$meta_data = alsendo_connect()->get_order_meta_data( $order_id );

		if ( ! empty( $meta_data['package_cancelled'] ) ) {
			//$ret['status']         = 'error';
			//$ret['error_messages'] = esc_html__( 'Already cancelled', 'alsendo-connect' );
			//echo wp_json_encode( $ret );
			//wp_die();
		}

		try {

			$apaczka_response  = ( new Alsendo_API() )->cancel_order( $apaczka_id );
			$response_messages = ( new Alerts() )->get_alerts_unformatted_by_context( 'cancel_order' );

			if ( ! empty( $apaczka_response['error'] ) ) {

				$ret['status'] = 'error';

				if ( is_array( $apaczka_response['error'] ) ) {
					$ret['error_messages'] = implode( ',', $response_messages['error'] );
				} else {
					$ret['error_messages'] = $apaczka_response['error'];
				}

				if ( function_exists( 'wc_get_logger' ) ) {
					\wc_get_logger()->debug( 'APACZKA CANCEL ORDER # ' . $order_id, array( 'source' => 'alsendo-connect-order-' . $order_id ) );
					\wc_get_logger()->debug( 'Apaczka_ID:', array( 'source' => 'alsendo-connect-order-' . $order_id ) );
					\wc_get_logger()->debug( print_r( $apaczka_id, true ), array( 'source' => 'alsendo-connect-order-' . $order_id ) );
					\wc_get_logger()->debug( 'API RESPONSE ERROR:', array( 'source' => 'alsendo-connect-order-' . $order_id ) );
					\wc_get_logger()->debug( print_r( $ret['error_messages'], true ), array( 'source' => 'alsendo-connect-order-' . $order_id ) );
				}
			} else {

				$ret['status'] = 'ok';

				$meta_data['package_send']      = 0;
				$meta_data['package_cancelled'] = 1;
				unset( $meta_data['alsendo_order'] );

				$order->update_meta_data( 'alsendo_connect_wc_order', $meta_data );

				$current_order_status = $order->get_status();
				if ( $order->get_date_paid() ) {
					if ( 'processing' !== $current_order_status ) {
						$order->update_status( 'wc-processing' );
					}
				} elseif ( 'on-hold' !== $current_order_status ) {
					$order->update_status( 'wc-on-hold' );
				}

				$order->save();
			}
		} catch ( Exception $e ) {
			$ret['error_messages'] = $e->getMessage();

			if ( function_exists( 'wc_get_logger' ) ) {
				\wc_get_logger()->debug( 'APACZKA CANCEL ORDER # ' . $order_id, array( 'source' => 'alsendo-connect-order-' . $order_id ) );
				\wc_get_logger()->debug( 'Apaczka_ID:', array( 'source' => 'alsendo-connect-order-' . $order_id ) );
				\wc_get_logger()->debug( print_r( $apaczka_id, true ), array( 'source' => 'alsendo-connect-order-' . $order_id ) );
				\wc_get_logger()->debug( 'Error:', array( 'source' => 'alsendo-connect-order-' . $order_id ) );
				\wc_get_logger()->debug( print_r( $e->getMessage(), true ), array( 'source' => 'alsendo-connect-order-' . $order_id ) );
			}
		}

		echo wp_json_encode( $ret );
		wp_die();
	}



	/**
	 * Download turn_in
	 *
	 * @return void
	 */
	public static function download_turn_in() {

		check_ajax_referer( 'alsendo_connect_order_metabox', 'security' );

		$order_id = ! empty( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : '';

		$apaczka_order_id = alsendo_connect()->get_alsendo_order_id_from_order_meta( $order_id );

		if ( empty( $apaczka_order_id ) ) {
			$ret['error_messages'] = esc_html__( 'No apaczka_id found in meta data', 'alsendo-connect' );
			wp_send_json_error( $ret );
			exit();
		}

		try {

			$apaczka_response  = ( new Alsendo_API() )->turn_in( array( $apaczka_order_id ) );
			$response_messages = ( new Alerts() )->get_alerts_unformatted_by_context( 'cancel_order' );

			if ( is_object( $apaczka_response ) && property_exists( $apaczka_response, 'turnIn' ) ) {
				$ret['status'] = 'ok';
				$ret['base64'] = $apaczka_response->turnIn;
			} else {
				if ( ! empty( $apaczka_response['exception'] ) ) {
					$ret['error_messages'] = $apaczka_response['exception'];
				} else {
					$ret['error_messages'] = esc_html__( 'Some error during downloading turn-in is occured', 'alsendo-connect' );
				}
				$ret['status'] = 'error';
			}
		} catch ( Exception $e ) {
			$ret['error_messages'] = $e->getMessage();
		}

		if ( 'ok' === $ret['status'] ) {
			$ret['error_messages'] = '';
		}

		echo wp_json_encode( $ret );
		wp_die();
	}



	/**
	 * Ajax calculate package
	 *
	 * @return void
	 * @throws Exception Exception.
	 */
	public static function ajax_calculate_package() {

		check_ajax_referer( 'alsendo_connect_order_metabox', 'security' );

		$ret                   = array();
		$ret['status']         = 'error';
		$ret['error_messages'] = '';

		$api_type = alsendo_connect()->get_main_settings_api();

		$shipping_methods = WC()->shipping()->get_shipping_methods();
		if ( empty( $shipping_methods ) ) {
			$shipping_methods = WC()->shipping()->load_shipping_methods();
		}
		$shipping_method = $shipping_methods['alsendo_connect'];

		$order_id = ! empty( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : null;

		if ( ! $order_id ) {
			return;
		}
		$order = wc_get_order( $order_id );

		$data = ! empty( $_POST['alsendo_connect_calculate'] ) ? map_deep( wp_unslash( $_POST['alsendo_connect_calculate'] ), 'sanitize_text_field' ) : null;

		$meta_data = alsendo_connect()->get_order_meta_data( $order_id );

		if ( 'apaczka' === $api_type ) {
			$declared_value_valid = self::declared_value_validate(
				(float) $data['package_properties']['declared_content'],
				(float) $data['additional_options']['cod_amount'],
				'order_valuation'
			);

			if ( ! $declared_value_valid ) {
				$response_messages     = ( new Alerts() )->get_alerts_unformatted_by_context( 'order_valuation' );
				$ret['error_messages'] = implode( ',', $response_messages['error'][0] );
				$ret['content']        = '';

				echo wp_json_encode( $ret );
				wp_die();
			}
		}

		// TEST
		//$test_status = self::get_apaczka_order_status( 469343793 );

		// order data for valuation.
		$calculate_order = self::create_api_order(
			$order,
			$data,
			$shipping_method,
			null,
			null
		);

		if ( 'ecolet' === $api_type ) {
			$shipping_type         = null;
			$order_placed_data     = alsendo_connect()->get_delivery_point_meta_data( $order_id );
			$ecolet_delivery_point = ! empty( $order_placed_data['apm_access_point_id'] ) ? $order_placed_data['apm_access_point_id'] : null;
			$service_supplier      = ! empty( $order_placed_data['apm_supplier'] ) ? $order_placed_data['apm_supplier'] : null;

			if ( ! empty( $_POST['alsendo_connect_calculate']['package_properties']['shipping_method'] ) ) {
				$shipping_type = sanitize_text_field( wp_unslash( $_POST['alsendo_connect_calculate']['package_properties']['shipping_method'] ) );
			}

			$calculate_order = self::get_ecolet_data_for_order_valuation( $calculate_order, $ecolet_delivery_point, $service_supplier, $shipping_type );
		}

		$calculating_apaczka = false;
		$calculating_ecolet  = false;
		$calculating_zaslat  = false;

		$default_service = '';

		$calculate_response = null;
		$price_table        = array();

		try {

			$calculate_response = ( new Alsendo_API() )->order_valuation( $calculate_order );

		} catch ( Exception $e ) {
			$ret['error_messages']           = $e->getMessage();
			$calculate_response['exception'] = $e->getMessage();
		}

		if ( 'ecolet' === $api_type ) {

			$calculating_ecolet = true;
			$default_service    = get_option( ( new Global_Settings() )->get_setting_id( 'service' ) );

			$alsendo_api_carrier_id = '';
			if ( empty( $data['alsendo_full'] ) ) {
				$helper                 = new Helper();
				$alsendo_api_carrier_id = $helper->get_linked_carrier_id( $order_id );

			}

			$price_table = self::get_price_table_ecolet( $calculate_response, $calculate_order, $alsendo_api_carrier_id );

			if ( ! empty( $price_table ) ) {

				$ret['status'] = 'ok';

			} else {
				$woo_logs_url          = esc_url( alsendo_connect()->get_woocommerce_logs_section_url() );
				$ret['error_messages'] = '';
				if ( ! empty( $calculate_response['errors'] ) ) {
					if ( is_array( $calculate_response['errors'] ) ) {

						if ( count( $calculate_response['errors'] ) !== count( $calculate_response['errors'], COUNT_RECURSIVE ) ) {
							$ret = $calculate_response['errors'];
						} else {
							// simple array.
							foreach ( $calculate_response['errors'] as $error ) {
								$ret['error_messages'] .= esc_html( $error ) . '<br>';
							}
						}
					}
				} else {
					$ret['error_messages'] = esc_html__( 'No services for found.', 'alsendo-connect' )
											. '<br>'
											. esc_html__(
												"Check Woo logs with name 'alsendo-connect-evaluate-order-",
												'alsendo-connect'
											)
											. $order_id
											. '<a href="' . $woo_logs_url . '" target="_blank">' . esc_html__(
												' here',
												'alsendo-connect'
											) . '</a>';
				}
			}
		}

		if ( 'apaczka' === $api_type ) {
			// APACZKA.
			$calculating_apaczka = true;
			$default_service     = (int) get_option( ( new Global_Settings() )->get_setting_id( 'service' ) );

			if ( ! empty( $calculate_response['error'] ) || ! empty( $calculate_response['exception'] ) ) {

				$ret['status'] = 'error';
				if ( ! empty( $calculate_response['error'] ) ) {
					$ret['errors'] = $calculate_response['error'];
				}
				if ( ! empty( $calculate_response['exception'] ) ) {
					$ret['errors'] = $calculate_response['exception'];
				}
			} elseif ( is_array( $calculate_response ) ) {

				$price_table = self::get_price_table_apaczka( $calculate_response );

				if ( ! empty( $price_table ) ) {
					$ret['status'] = 'ok';
				}
			} else {
				$ret['status'] = 'error';
				$ret['errors'] = 'Some error response from API';
			}
		}

		if ( 'zaslat' === $api_type ) {
			// Zaslat wycena.
			$calculating_zaslat = true;

			if ( is_array( $calculate_response ) ) {

				if ( empty( $calculate_response['rates'] ) && ! empty( $calculate_response['status'] ) && 200 === $calculate_response['status'] ) {
					$ret['status'] = 'error';
					$ret['errors'] = 'Nalezeno 0 položek.';
				}

				if ( isset( $calculate_response['status'] ) && isset( $calculate_response['rates'] ) && is_array( $calculate_response['rates'] ) ) {

					if ( empty( $calculate_response['rates'] ) ) {
						$ret['status'] = '';
						if ( ! empty( $calculate_response['message'] ) ) {
							$ret['errors'] = $calculate_response['message'];
						}
					} else {

						$ret['status'] = 'ok';
						foreach ( $calculate_response['rates'] as $service ) {
							$price_table[ $service['service_id'] ] =
								array(
									'name'           => $service['service'],
									'price'          => isset( $service['price']['value'] ) ? $service['price']['value'] : '',
									'price_gross'    => isset( $service['price_vat']['value'] ) ? $service['price_vat']['value'] : '',
									'freight_type'   => isset( $service['freight_type'] ) ? $service['freight_type'] : '',
									'supplier'       => $service['carrier'],
									'pickup_courier' => isset( $service['class'] ) ? $service['class'] : '',
									'currency'       => isset( $service['price']['currency'] ) ? $service['price']['currency'] : '',
								);
						}
					}
				}

				if ( ! empty( $calculate_response['errors'] ) ) {
					$ret['status'] = 'error';
					if ( is_array( $calculate_response['errors'] ) ) {
						$ret['errors'] = array();
						foreach ( $calculate_response['errors'] as $key => $error_data ) {
							if ( is_array( $error_data ) ) {
								foreach ( $error_data as $key2 => $error ) {
									$ret['errors'][ $key2 ] = $error;
								}
							} else {
								$ret['errors'][ $key ] = $error_data;
							}
						}
					} else {
						$ret['errors'] = $calculate_response;
					}
				}
			} else {
				$ret['status'] = 'error';
				$ret['errors'] = $calculate_response;
			}
		}

		if ( key_exists( $default_service, $price_table ) ) {
			$copy = array( $default_service => $price_table[ $default_service ] );
			unset( $price_table[ $default_service ] );
			$price_table = $copy + $price_table;

		}

		if ( isset( $ret['status'] ) && 'ok' === $ret['status'] ) {
			$price_table = self::sort_by_supplier( $price_table );
			$calculate   = $price_table;

			ob_start();

			if ( $calculating_apaczka ) {
				include alsendo_connect()->get_plugin_templates_dir() . DIRECTORY_SEPARATOR . 'html-order-metabox-calculate-result.php';
			} elseif ( $calculating_ecolet ) {
				include alsendo_connect()->get_plugin_templates_dir() . DIRECTORY_SEPARATOR . 'html-order-metabox-calculate-result-ecolet.php';
			} elseif ( $calculating_zaslat ) {
				include alsendo_connect()->get_plugin_templates_dir() . DIRECTORY_SEPARATOR . 'html-order-metabox-calculate-result-zaslat.php';
			}

			$ret['calculate_html'] = ob_get_clean();
			$ret['error_messages'] = '';
		}

		$ret['content'] = '';

		self::update_merchant_made_edits_in_fields( $order_id, $data );

		echo wp_json_encode( $ret );
		wp_die();
	}

	/**
	 * Format calculate price
	 *
	 * @param string $price $price.
	 *
	 * @return string
	 */
	public static function format_calculate_price( $price ): string {
		return str_replace( '.', ',', (string) ( number_format( (int) $price / 100, 2, '.', '' ) ) );
	}


	/**
	 * Ajax create package
	 */
	public static function ajax_create_package() {

		check_ajax_referer( 'alsendo_connect_order_metabox', 'security' );

		$shipping_methods = WC()->shipping()->get_shipping_methods();
		if ( empty( $shipping_methods ) ) {
			$shipping_methods = WC()->shipping()->load_shipping_methods();
		}
		$shipping_method       = $shipping_methods['alsendo_connect'];
		$ret                   = array();
		$ret['status']         = 'error';
		$ret['error_messages'] = '';
		$order_id              = ! empty( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : null;

		if ( ! $order_id ) {
			return;
		}

		$api_type = alsendo_connect()->get_main_settings_api();

		$order = wc_get_order( $order_id );

		if ( ! $order || is_wp_error( $order ) ) {
			return;
		}

		$data = ! empty( $_POST['alsendo_connect_create'] ) ? map_deep( wp_unslash( $_POST['alsendo_connect_create'] ), 'sanitize_text_field' ) : null;

		if ( empty( $data['delivery_point'] ) ) {
			$delivery_point         = alsendo_connect()->get_delivery_point_meta_data( $order_id );
			$data['delivery_point'] = $delivery_point;
		}

		$meta = alsendo_connect()->get_order_meta_data( $order_id );

		if ( 'apaczka' === $api_type ) {
			$declared_value_valid = self::declared_value_validate(
				(float) $data['package_properties']['declared_content'],
				(float) $data['additional_options']['cod_amount'],
				'order_valuation'
			);

			if ( ! $declared_value_valid ) {
				$response_messages     = ( new Alerts() )->get_alerts_unformatted_by_context( 'order_send' );
				$ret['error_messages'] = implode( ',', $response_messages['error'][0] );
				$ret['content']        = '';

				echo wp_json_encode( $ret );
				wp_die();
			}
		}

		$delivery_point = ! empty( $_POST['alsendo_connect_create']['delivery_point_id'] ) ? sanitize_text_field( wp_unslash( $_POST['alsendo_connect_create']['delivery_point_id'] ) ) : '';

		$create_order = self::create_api_order(
			$order,
			$data,
			$shipping_method,
			$delivery_point,
			$data['selected_service']
		);

		if ( 'ecolet' === $api_type ) {

			$shipping_type         = null;
			$order_placed_data     = alsendo_connect()->get_delivery_point_meta_data( $order_id );
			$ecolet_delivery_point = ! empty( $order_placed_data['apm_access_point_id'] ) ? $order_placed_data['apm_access_point_id'] : null;
			$service_supplier      = ! empty( $order_placed_data['apm_supplier'] ) ? $order_placed_data['apm_supplier'] : null;

			if ( ! empty( $_POST['alsendo_connect_create']['package_properties']['shipping_method'] ) ) {
				$shipping_type = sanitize_text_field( wp_unslash( $_POST['alsendo_connect_create']['package_properties']['shipping_method'] ) );
			}

			$create_order = self::fix_data_for_ecolet_api( $create_order, $data, $service_supplier, $shipping_type );
		}

		try {

			$create_order_response = ( new Alsendo_API() )->order_send( $create_order );

		} catch ( Exception $e ) {
			$create_order_response['error'] = $e->getMessage();
		}

		if ( 'ecolet' === $api_type ) {
			$ret['ecolet_order'] = 1;

			if ( is_array( $create_order_response ) && ( ! empty( $create_order_response['exception'] ) || ! empty( $create_order_response['error'] ) ) ) {
				$ret['error'] = $create_order_response;
			}

			if ( is_object( $create_order_response ) && property_exists( $create_order_response, 'status' ) ) {
				if ( 'ordered' === $create_order_response->status ) {
					$ret['message']   = esc_html__( 'Package created', 'alsendo-connect' );
					$ret['status']    = 'ok';
					$ret['ecolet_ok'] = 1;

					$meta['package_send'] = 1;
				}

				if ( 'error' === $create_order_response->status ) {
					$ret['message']      = esc_html__( 'Error is occured', 'alsendo-connect' );
					$ret['status']       = 'error';
					$ret['ecolet_error'] = 1;
					if ( property_exists( $create_order_response, 'api_error' ) ) {
						$ret['api_error'] = $create_order_response->api_error;
					}
				}

				$meta['used_api']            = 'ecolet';
				$meta['alsendo_order']['id'] = property_exists( $create_order_response, 'id' ) ? $create_order_response->id : '';

				if ( property_exists( $create_order_response, 'service' ) ) {
					$obj = $create_order_response->service;
					if ( is_object( $obj ) && property_exists( $obj, 'slug' ) ) {
						$meta['alsendo_order']['slug'] = $obj->slug;
					}
				}

				if ( empty( $meta['package_properties']['title'] ) ) {
					$meta['package_properties']['title'] = ! empty( $data['package_properties']['title'] ) ? $data['package_properties']['title'] : '';
				}
				if ( empty( $meta['package_properties']['cod'] ) ) {
					$meta['package_properties']['cod'] = ! empty( $data['package_properties']['cod'] ) ? $data['package_properties']['cod'] : '';
				}
				if ( empty( $meta['receiver'] ) ) {
					$meta['receiver'] = ! empty( $data['receiver'] ) ? $data['receiver'] : '';
				}

				$order->update_meta_data( 'alsendo_connect_wc_order', $meta );

				$order->save();
			}
		}

		if ( 'apaczka' === $api_type ) {
			// response from API APACZKA.

			$ret['apaczka_order'] = 1;
			if ( is_array( $create_order_response ) ) {
				if ( ! empty( $create_order_response['exception'] ) || ! empty( $create_order_response['error'] ) ) {
					$ret['error_messages'] = $create_order_response;
					$ret['status']         = 'error';
				}
			}

			if ( is_object( $create_order_response ) && property_exists( $create_order_response, 'status' ) && property_exists( $create_order_response, 'id' ) ) {

				if ( ! empty( $create_order_response->status ) ) {
					$meta['package_send'] = 1;

					$ret['error_messages'] = '';
					$ret['message']        = esc_html__( 'Package created', 'alsendo-connect' );
					$ret['status']         = 'ok';
					$ret['apaczka_ok']     = 1;
				}

				$meta['used_api']                        = 'apaczka';
				$meta['alsendo_order']['id']             = $create_order_response->id;
				$meta['alsendo_order']['service_id']     = property_exists( $create_order_response, 'service_id' ) ? $create_order_response->service_id : '';
				$meta['alsendo_order']['service_name']   = property_exists( $create_order_response, 'service_name' ) ? $create_order_response->service_name : '';
				$meta['alsendo_order']['waybill_number'] = property_exists( $create_order_response, 'waybill_number' ) ? $create_order_response->waybill_number : '';
				$meta['alsendo_order']['tracking_url']   = property_exists( $create_order_response, 'tracking_url' ) ? $create_order_response->tracking_url : '';

				if ( empty( $meta['package_properties']['title'] ) ) {
					$meta['package_properties']['title'] = ! empty( $data['package_properties']['title'] ) ? $data['package_properties']['title'] : '';
				}
				if ( empty( $meta['package_properties']['cod'] ) ) {
					$meta['package_properties']['cod'] = ! empty( $data['package_properties']['cod'] ) ? $data['package_properties']['cod'] : '';
				}
				if ( empty( $meta['receiver'] ) ) {
					$meta['receiver'] = ! empty( $data['receiver'] ) ? $data['receiver'] : '';
				}

				$order->update_meta_data( 'alsendo_connect_wc_order', $meta );
				$order->save();

			}
		}

		if ( 'zaslat' === $api_type ) {
			// response from API ZASLAT.

			$ret['zaslat_order'] = 1;
			$meta['used_api']    = 'zaslat';
			if ( is_array( $create_order_response ) ) {
				if ( ! empty( $create_order_response['errors'] ) ) {

					$ret['status'] = 'error';
					if ( is_array( $create_order_response['errors'] ) ) {
						$ret['errors'] = array();
						foreach ( $create_order_response['errors'] as $key => $error_data ) {
							if ( is_array( $error_data ) ) {

								$details = '';
								foreach ( $error_data as $key2 => $error ) {
									$details .= '<br>' . $key2 . ' => ' . $error;
								}
								$ret['errors'] = $details;
							} else {
								$ret['errors'] = '<br>' . $key . ' => ' . $error_data;
							}
						}
					} else {
						$ret['errors'] = $create_order_response;
					}
				}

				if ( empty( $create_order_response['status_code'] ) || 200 !== $create_order_response['status_code'] ) {
					$ret['errors'] = $create_order_response;
					$ret['status'] = 'error';
				}

				if ( isset( $create_order_response['status_code'] ) && 200 === $create_order_response['status_code'] ) {
					$meta['package_send'] = 1;

					$ret['error_messages'] = '';
					$ret['message']        = esc_html__( 'Package created', 'alsendo-connect' );
					$ret['status']         = 'ok';
					$ret['zaslat_ok']      = 1;

					if ( ! empty( $create_order_response['data']['order'] ) ) {

						$meta['alsendo_order']['operator']        = ! empty( $create_order_response['data']['operator'] ) ? $create_order_response['data']['operator'] : $data['selected_service'];
						$meta['alsendo_order']['shipping_method'] = ! empty( $data['package_properties']['shipping_method'] ) ? $data['package_properties']['shipping_method'] : '';
						$meta['alsendo_order']['id']              = $create_order_response['data']['order'];
						$meta['alsendo_order']['order_number']    = ! empty( $create_order_response['data']['order_number'] ) ? $create_order_response['data']['order_number'] : '';
						$meta['alsendo_order']['shipment']        = ! empty( $create_order_response['data']['shipments'][0] ) ? $create_order_response['data']['shipments'][0] : '';

						$order_placed_data = alsendo_connect()->get_delivery_point_meta_data( $order_id );
						$service_supplier  = ! empty( $order_placed_data['apm_supplier'] ) ? $order_placed_data['apm_supplier'] : null;
						if ( empty( $service_supplier ) ) {
							$meta['alsendo_order']['is_courier'] = 1;
						}
					}
				}

				if ( empty( $meta['package_properties']['title'] ) ) {
					$meta['package_properties']['title'] = ! empty( $data['package_properties']['title'] ) ? $data['package_properties']['title'] : '';
				}
				if ( empty( $meta['package_properties']['cod'] ) ) {
					$meta['package_properties']['cod'] = ! empty( $data['package_properties']['cod'] ) ? $data['package_properties']['cod'] : '';
				}
				if ( empty( $meta['receiver'] ) ) {
					$meta['receiver'] = ! empty( $data['receiver'] ) ? $data['receiver'] : '';
				}

				$order->update_meta_data( 'alsendo_connect_wc_order', $meta );
				$order->save();
			}
		}

		if ( 'ok' === $ret['status'] ) {
			// set order to status 'wc-completed' if checkbox is set during parcel create.
			if ( 'yes' === Plugin::get_option( 'set_order_status_completed' ) ) {
				if ( $order && ! is_wp_error( $order ) ) {
					$current_order_status = $order->get_status();
					if ( 'completed' !== $current_order_status ) {
						$order->update_status( 'wc-completed' );
					}
				}
			}
		}

		$ret['content'] = '';

		self::update_merchant_made_edits_in_fields( $order_id, $data );

		echo wp_json_encode( $ret );
		wp_die();
	}



	/**
	 * Check apaczka_maps plugin
	 *
	 * @param int $selected_service $selected_service.
	 *
	 * @return bool
	 */
	private function check_apaczka_maps_plugin( int $selected_service ): bool {
		if ( $this->is_parcel_locker_service( $selected_service ) ) {
			if ( class_exists( 'Apaczka_Points_Map\Points_Map_Plugin' ) ) {
				( new Alerts() )
					->add_error(
						'Parcel locker has been selected, but "Apaczka.pl Map of Points" plugin was not found or it is inactive.'
					);
			}
		}

		return class_exists( 'Apaczka_Points_Map\Points_Map_Plugin' );
	}


	/**
	 * Processes admin options for a specific shipping method instance.
	 *
	 * Handles form submission for shipping method instance settings, validates the correct
	 * instance is being processed, updates instance settings, and optionally creates
	 * configuration templates. Also performs API-specific plugin checks.
	 *
	 * @return bool|mixed Returns parent method result if no instance ID, false if wrong instance,
	 *                   or update_option result on successful processing
	 */
	public function process_admin_options() {

		if ( ! $this->instance_id ) {
			return parent::process_admin_options();
		}

		// Check we are processing the correct form for this instance.
		if ( ! isset( $_REQUEST['instance_id'] ) || absint( $_REQUEST['instance_id'] ) !== $this->instance_id ) { // WPCS: input var ok, CSRF ok.
			return false;
		}

		$this->init_instance_settings();
		$post_data            = $this->get_post_data();
		$instance_form_fields = $this->get_instance_form_fields();

		$this->check_apaczka_maps_plugin(
			(int) $this->get_field_value(
				'service',
				$instance_form_fields['service'],
				$post_data
			)
		);

		$create_template = $this->get_field_value(
			'create_template',
			$instance_form_fields['create_template'],
			$post_data
		) === 'yes';

		$new_template_name = $this->get_field_value(
			'new_template_name',
			$instance_form_fields['new_template_name'],
			$post_data
		);

		$created_template = array();
		foreach ( $instance_form_fields as $key => $field ) {
			if ( 'new_template_name' === $key
				|| 'create_template' === $key
				|| 'load_from_template' === $key
				|| 'select_template' === $key ) {
				continue;
			}

			if ( 'title' !== $this->get_field_type( $field ) ) {
				try {
					$value                           = $this->get_field_value(
						$key,
						$field,
						$post_data
					);
					$this->instance_settings[ $key ] = $value;

					if ( $create_template ) {
						$created_template[ $key ] = $value;
					}
				} catch ( Exception $e ) {
					$this->add_error( $e->getMessage() );
				}
			}
		}

		if ( $create_template ) {
			( new Gateway_Settings_Templates_Helper() )->create(
				$created_template,
				$new_template_name
			);
		}

		$shipping_settings = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_settings_values', $this->instance_settings, $this );

		return update_option(
			$this->get_instance_option_key(),
			$shipping_settings,
			'yes'
		);
	}


	/**
	 * Adds Apaczka map fields to shipping method settings.
	 *
	 * @param array $settings .
	 *
	 * @return array
	 */
	public static function settings_field( $settings ) {
		// Same fields for every shipping method instance; this filter runs once per method
		// type on heavy admin pages — avoid repeating API/cache reads and field building.
		static $cached_addon_fields = null;
		if ( null !== $cached_addon_fields ) {
			return array_merge( $settings, $cached_addon_fields );
		}

		$addon_fields    = array();
		$api_type        = alsendo_connect()->get_main_settings_api();
		$list_of_methods = array();

		$options = array();

		if ( 'apaczka' === $api_type ) {

			$apaczka_services = ( new Alsendo_API() )->get_service_structure_apaczka();
			if ( ! empty( $apaczka_services ) ) {
				foreach ( $apaczka_services as $key => $value ) {
					$list_of_methods[ $key ] = ! empty( $value['name'] ) ? $value['name'] : 'No services received form API Apaczka';
				}
			}

			if ( is_array( $list_of_methods ) ) {
				asort( $list_of_methods );
				$list_of_methods = array( 'FULL' => esc_html__( 'None', 'alsendo-connect' ) ) + $list_of_methods;
			}

			$options = array(
				'ALL_APACZKA'          => esc_html__( 'All Apaczka', 'alsendo-connect' ),
				'DHL_PARCEL'           => esc_html__( 'DHL', 'alsendo-connect' ),
				'DPD'                  => esc_html__( 'DPD', 'alsendo-connect' ),
				'INPOST'               => esc_html__(
					'Inpost',
					'alsendo-connect'
				),
				'INPOST_INTERNATIONAL' => esc_html__(
					'Inpost International',
					'alsendo-connect'
				),
				'PWR'                  => esc_html__(
					'Orlen Paczka',
					'alsendo-connect'
				),
				'PACKETA'              => esc_html__(
					'Packeta',
					'alsendo-connect'
				),
				'POCZTA'               => esc_html__(
					'Poczta Polska',
					'alsendo-connect'
				),
				'UPS'                  => esc_html__( 'UPS', 'alsendo-connect' ),
				'GLS'                  => esc_html__( 'GLS', 'alsendo-connect' ),
			);
		}

		if ( 'ecolet' === $api_type ) {

			$ecolet          = new Ecolet();
			$ecolet_services = $ecolet->get_service_structure_ecolet();

			if ( ! empty( $ecolet_services ) ) {
				foreach ( $ecolet_services as $key => $value ) {
					$list_of_methods[ $key ] = $value['name'];
				}
			}

			if ( is_array( $list_of_methods ) ) {
				asort( $list_of_methods );
				$list_of_methods = array( 'FULL' => esc_html__( 'None', 'alsendo-connect' ) ) + $list_of_methods;
			}

			$options = array(
				'ALL_ECOLET'  => esc_html__( 'All Ecolet', 'alsendo-connect' ),
				'ECOLET_DPD'  => esc_html__( 'Ecolet DPD', 'alsendo-connect' ),
				'SAMEDAY'     => esc_html__( 'Ecolet SAMEDAY', 'alsendo-connect' ),
				'FAN_COURIER' => esc_html__( 'Ecolet FAN_COURIER', 'alsendo-connect' ),
				'CARGUS'      => esc_html__( 'Ecolet CARGUS', 'alsendo-connect' ),
				'GLS'         => esc_html__( 'Ecolet GLS', 'alsendo-connect' ),
			);
		}

		if ( 'zaslat' === $api_type ) {

			$options = array(
				'ALL_ZASLAT'   => esc_html__( 'All Zaslat', 'alsendo-connect' ),
				'BALIKOVNA'    => esc_html__( 'Zaslat Balikovna', 'alsendo-connect' ),
				'PACKETA'      => esc_html__( 'Zaslat Zasilkovna', 'alsendo-connect' ),
				'ZASLAT_DPD'   => esc_html__( 'Zaslat DPD', 'alsendo-connect' ),
				'ZASLAT_GLS'   => esc_html__( 'Zaslat GLS', 'alsendo-connect' ),
				'PPL'          => esc_html__( 'Zaslat PPL', 'alsendo-connect' ),
				'ONE_DELIVERY' => esc_html__( 'Zaslat One by Allegro', 'alsendo-connect' ),
			);

			$zaslat = new Zaslat();

			$list_of_methods = $zaslat->get_service_structure();
			if ( is_array( $list_of_methods ) ) {
				asort( $list_of_methods );
				$list_of_methods = array( '' => esc_html__( 'None', 'alsendo-connect' ) ) + $list_of_methods;
			}

			$res = $zaslat->get_address_book();

			$zaslat_address_book = array();
			if ( ! empty( $res['data'] ) && is_array( $res['data'] ) ) {
				$zaslat_address_book = $res['data'];
			}

			$zaslat_addresses_id_options = array();

			foreach ( $zaslat_address_book as $key => $val ) {

				$address = $key . ' (';
				if ( ! empty( $val['firstname'] ) ) {
					$address .= esc_html( $val['firstname'] );
				}
				if ( ! empty( $val['surname'] ) ) {
					$address .= ' ' . esc_html( $val['surname'] );
				}
				if ( ! empty( $val['street'] ) ) {
					$address .= ', ' . esc_html( $val['street'] );
				}
				if ( ! empty( $val['city'] ) ) {
					$address .= ', ' . esc_html( $val['city'] );
				}
				if ( ! empty( $val['zip'] ) ) {
					$address .= ' ' . esc_html( $val['zip'] );
				}
				if ( ! empty( $val['country'] ) ) {
					$address .= ', ' . esc_html( $val['country'] );
				}

				$address .= ')';

				$zaslat_addresses_id_options[ $key ] = $address;

			}

			//$zaslat_addresses = ( new Global_Settings() )->get_zaslat_sender_settings( $zaslat_address_book );

			if ( is_array( $zaslat_addresses_id_options ) ) {
				asort( $zaslat_addresses_id_options );
				$zaslat_addresses_id_options = array( '' => esc_html__( 'None', 'alsendo-connect' ) ) + $zaslat_addresses_id_options;
			}

			$addon_fields['alsendo_connect_linked_zaslat_address'] = array(
				'title'       => esc_html__(
					'Linked Zaslat Sender Address',
					'alsendo-connect'
				),
				'type'        => 'select',
				'description' => esc_html__(
					'Predefining the Sender Address will allow for mass order sending.',
					'alsendo-connect'
				),
				'desc_tip'    => true,
				'default'     => 'NONE',
				'options'     => $zaslat_addresses_id_options,
			);
		}

		$addon_fields['alsendo_connect_linked_api_shipping_method'] = array(
			'title'       => esc_html__(
				'Linked Alsendo Delivery Method',
				'alsendo-connect'
			),
			'type'        => 'select',
			'description' => esc_html__(
				'Predefining the carrier ID when creating a shipping method will allow for mass order sending. However, you can leave this option set to „None” and select a carrier later during valuation your package.',
				'alsendo-connect'
			),
			'desc_tip'    => true,
			'default'     => 'FULL',
			'options'     => $list_of_methods,
		);

		if ( 'zaslat' === $api_type ) {
			$addon_fields['alsendo_connect_pickup_branch'] = array(
				'title'       => esc_html__(
					'Pickup branch',
					'alsendo-connect'
				),
				'type'        => 'checkbox',
				'description' => esc_html__(
					'Handover shipment at pickup point.',
					'alsendo-connect'
				),
				'desc_tip'    => true,
				'default'     => '',
				'class'       => 'alsendo-zaslat-pickup-branch alsendo-zaslat-branch-toggle',
			);
		}

		$addon_fields['alsendo_connect_skip_valuation'] = array(
			'title'       => esc_html__(
				'Skip valuation Alsendo Connect',
				'alsendo-connect'
			),
			'type'        => 'checkbox',
			'description' => esc_html__(
				'This setting allows you to skip the valuation step on Order Details page.',
				'alsendo-connect'
			),
			'desc_tip'    => true,
			'default'     => '',
		);

		$zaslat_map_setting_class = '';
		$display_map_title        = esc_html__(
			'Alsendo Delivery Points Map',
			'alsendo-connect'
		);
		if ( 'zaslat' === $api_type ) {
			$zaslat_map_setting_class = 'alsendo-zaslat-map-setting';
			$display_map_title        = esc_html__(
				'Delivery branch',
				'alsendo-connect'
			);
		}

		$addon_fields['display_apaczka_map'] = array(
			'title'       => $display_map_title,
			'type'        => 'checkbox',
			'description' => esc_html__(
				'Displays a map of delivery points on the checkout form for this shipping method.',
				'alsendo-connect'
			),
			'desc_tip'    => true,
			'default'     => '',
			'class'       => $zaslat_map_setting_class,
		);

		$addon_fields['supplier_apaczka_map'] = array(
			'title'   => esc_html__(
				'Alsendo Supplier',
				'alsendo-connect'
			),
			'type'    => 'select',
			'default' => '',
			'options' => $options,
			'class'   => $zaslat_map_setting_class,
		);

		$addon_fields['only_cod_apaczka_map'] = array(
			'title'       => esc_html__(
				'Alsendo only C.O.D points',
				'alsendo-connect'
			),
			'type'        => 'checkbox',
			'description' => esc_html__(
				'Displays only points with Collect on Delivery.',
				'alsendo-connect'
			),
			'desc_tip'    => true,
			'default'     => '',
			'class'       => $zaslat_map_setting_class,
		);

		$cached_addon_fields = $addon_fields;

		return array_merge( $settings, $cached_addon_fields );
	}

	/**
	 * Adds a map field to the provided settings array.
	 *
	 * Processes the settings through the settings_field method to include
	 * map-related form fields or configurations.
	 *
	 * @param array $settings The existing settings array to modify.
	 *
	 * @return array Modified settings array with map field added
	 */
	public function add_map_field( $settings ) {
		$settings = self::settings_field( $settings );

		return $settings;
	}

	/**
	 * Adds custom field to each shipping method.
	 */
	public function filtering_shipping_fields() {
		$shipping_methods = WC()->shipping->get_shipping_methods();

		foreach ( $shipping_methods as $shipping_method ) {
			add_filter(
				'woocommerce_shipping_instance_form_fields_' . $shipping_method->id,
				array( $this, 'add_map_field' )
			);
		}
	}


	/**
	 * Determines whether the delivery map button should be displayed.
	 *
	 * Checks if the currently chosen shipping method has the 'display_apaczka_map'
	 * setting enabled by iterating through all shipping zones and their methods
	 * to find a match with the selected shipping method.
	 *
	 * @return bool True if map button should be displayed, false otherwise
	 */
	private function is_delivery_map_button_display() {
		// Get all your existing shipping zones IDS.
		$delivery_zones          = alsendo_connect()->get_cached_zones();
		$zone_ids                = array_keys( array( '' ) + $delivery_zones );
		$chosen_shipping_methods = WC()->session->chosen_shipping_methods;

		// Loop through shipping Zones IDs.
		foreach ( $zone_ids as $zone_id ) {
			// Get the shipping Zone object.
			$shipping_zone    = new \WC_Shipping_Zone( $zone_id );
			$shipping_methods = $shipping_zone->get_shipping_methods( true, 'values' );
			if ( is_array( $shipping_methods ) ) {
				// Loop through each shipping methods set for the current shipping zone.
				foreach ( $shipping_methods as $instance_id => $shipping_method ) {
					if ( isset( $chosen_shipping_methods[0] ) && $shipping_method->id . ':' . $instance_id === $chosen_shipping_methods[0] ) {
						if ( isset( $shipping_method->instance_settings['display_apaczka_map'] ) && 'yes' === $shipping_method->instance_settings['display_apaczka_map'] ) {
							return true;
						}
					}
				}
			}
		}

		return false;
	}



	/**
	 * Saves shipping point and order data to WooCommerce order meta.
	 *
	 * Extracts delivery point information from the checkout request, sanitizes the data,
	 * and stores it in order meta. Also processes receiver information from billing/shipping
	 * data and package properties, saving everything as order metadata for later use.
	 *
	 * @param WC_Order|null   $order The WooCommerce order object to update.
	 * @param WP_REST_Request $request The REST API request containing checkout data.
	 *
	 * @return void
	 */
	public function save_shipping_point_in_order_meta( $order, $request ) {

		if ( ! $order ) {
			return;
		}

		$request_body = json_decode( $request->get_body(), true );

		if ( isset( $request_body['extensions']['apaczka_pl']['apaczka-point'] )
			&& ! empty( $request_body['extensions']['apaczka_pl']['apaczka-point'] ) ) {
			$checkout_delivery_point = json_decode( $request_body['extensions']['apaczka_pl']['apaczka-point'], true );
			$checkout_delivery_point = array_map( 'sanitize_text_field', $checkout_delivery_point );
			$order->update_meta_data( 'alsendo_connect_delivery_point', $checkout_delivery_point );
			$order->save();
		}

		$receiver_company = '';
		if ( ! empty( $order->get_shipping_company() ) ) {
			$receiver_company = $order->get_shipping_company();
		} elseif ( ! empty( $order->get_billing_company() ) ) {
			$receiver_company = $order->get_billing_company();
		}

		$receiver = array(
			'country_code'       => $order->get_shipping_country(),
			// Kod ISO 3166.
			'name'               => ! empty( $receiver_company )
				? $receiver_company
				: $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
			'line1'              => $order->get_shipping_address_1(),
			'line2'              => $order->get_shipping_address_2(),
			'postal_code'        => $order->get_shipping_postcode(),
			'city'               => $order->get_shipping_city(),
			'is_residential'     => 0,
			'contact_person'     => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
			'email'              => $order->get_billing_email(),
			'phone'              => $order->get_shipping_phone(),
			'foreign_address_id' => ! empty( $checkout_delivery_point )
				? $checkout_delivery_point['apm_foreign_access_point_id'] : '',
		);

		$package_properties = $this->get_package_properties( $order );

		$meta_data                       = array();
		$meta_data['package_properties'] = $package_properties;
		$meta_data['receiver']           = $receiver;

		$order->update_meta_data( 'alsendo_connect_wc_order', $meta_data );
		$order->save();
	}


	/**
	 * Enqueues frontend JavaScript for WooCommerce blocks on checkout pages.
	 *
	 * Loads the front-blocks.js script and localizes it with map configuration,
	 * button texts, and API settings for delivery point selection functionality.
	 * Only executes if the Apaczka Points Map plugin is not active and the page
	 * is a checkout page or contains a checkout block.
	 *
	 * @return void
	 */
	public function enqueue_frontend_blocks_scripts() {
		if ( ! class_exists( 'Apaczka_Points_Map\Points_Map_Plugin' ) ) {
			if ( is_checkout() || has_block( 'woocommerce/checkout' ) ) {
				$map_config             = alsendo_connect()->get_map_config();
				$current_plugin_version = alsendo_connect()->get_plugin_version();
				$front_blocks_js_path   = alsendo_connect()->get_plugin_dir() . 'assets/js/front-blocks.js';
				$front_blocks_js_ver    = file_exists( $front_blocks_js_path ) ? filemtime( $front_blocks_js_path ) : $current_plugin_version;

				$lang = alsendo_connect()->get_website_language();

				wp_enqueue_script(
					alsendo_connect()->get_front_blocks_script_id(),
					alsendo_connect()->get_plugin_js_url() . '/front-blocks.js',
					array( 'jquery' ),
					$front_blocks_js_ver,
					array( 'in_footer' => true ),
				);
				wp_localize_script(
					alsendo_connect()->get_front_blocks_script_id(),
					'alsendo_connect_block',
					array(
						'button_text1'  => esc_html__( 'Select point', 'alsendo-connect' ),
						'button_text2'  => esc_html__( 'Change point', 'alsendo-connect' ),
						'selected_text' => esc_html__( 'Selected Parcel Locker:', 'alsendo-connect' ),
						'alert_text'    => esc_html__( 'Delivery point must be chosen!', 'alsendo-connect' ),
						'map_config'    => $map_config,
						'lang'          => $lang,
						'api_type'      => alsendo_connect()->get_main_settings_api(),
					)
				);
			}
		}
	}



	/**
	 * Retrieves package properties and shipping configuration for order processing.
	 *
	 * Builds an array of package properties from global settings including dimensions,
	 * weight, contents, and shipping options. Calculates COD and declared content amounts
	 * based on order payment method and plugin settings.
	 *
	 * @param WC_Order|null $order Optional WooCommerce order object for COD/declared amount calculation.
	 *
	 * @return array Array of package properties including dimensions, shipping method, COD settings,
	 *               and dispatch point configurations
	 */
	private function get_package_properties( $order = null ) {

		$settings = new Global_Settings();

		$cod_amount      = 0;
		$declared_amount = 0;

		if ( $order && ! is_wp_error( $order ) ) {
			$payment_method = $order->get_payment_method();

			if ( 'cod' === $payment_method ) {
				$cod_amount      = $order->get_total();
				$declared_amount = $order->get_total();
			} else {

				$cod_amount = get_option( $settings->get_setting_id( 'cod' ), '' );
				if ( 'yes' === Plugin::get_option( 'declared_content_auto' ) ) {
					$declared_amount = $order->get_total();
				} else {
					$declared_amount = Plugin::get_option( 'declared_content' );
				}
			}
		}

		$package_properties = array(
			'dispath_point_ups'      => get_option(
				$settings->get_setting_id( 'dispath_point_ups' ),
				''
			),
			'dispath_point_kurier48' => get_option(
				$settings->get_setting_id( 'dispath_point_kurier48' ),
				''
			),
			'dispath_point_inpost'   => get_option(
				$settings->get_setting_id( 'dispath_point_inpost' ),
				''
			),
			'dispath_point_dpd'      => get_option(
				$settings->get_setting_id( 'dispath_point_dpd' ),
				''
			),
			'dispath_point_dhl'      => get_option(
				$settings->get_setting_id( 'dispath_point_dhl' ),
				''
			),
			'shipping_method'        => get_option(
				$settings->get_setting_id( 'shipping_method' ),
				''
			),
			'is_nstd'                => get_option(
				$settings->get_setting_id( 'is_nstd' ),
				''
			),
			'parcel_type'            => get_option(
				$settings->get_setting_id( 'parcel_type' ),
				''
			),
			'declared_content'       => $declared_amount,
			'cod'                    => $cod_amount,
			'service'                => get_option(
				$settings->get_setting_id( 'service' ),
				''
			),
			'package_width'          => get_option(
				$settings->get_setting_id( 'package_width' ),
				''
			),
			'package_length'         => get_option(
				$settings->get_setting_id( 'package_length' ),
				''
			),
			'package_height'         => get_option(
				$settings->get_setting_id( 'package_height' ),
				''
			),
			'package_weight'         => get_option(
				$settings->get_setting_id( 'package_weight' ),
				''
			),
			'package_contents'       => get_option(
				$settings->get_setting_id( 'package_contents' ),
				''
			),
			'cod_amount'             => '',
			'pickup_date'            => '',
			'pickup_hour_from'       => get_option(
				$settings->get_setting_id( 'pickup_hour_from' ),
				''
			),
			'pickup_hour_to'         => get_option(
				$settings->get_setting_id( 'pickup_hour_to' ),
				''
			),
		);

		return $package_properties;
	}


	/**
	 * Clears the cached shipping zones.
	 *
	 * @return void
	 */
	public function clear_zones_cache() {
		$this->cached_zones = null;
	}




	/**
	 * Configures Ecolet shipping data for order valuation based on delivery method and supplier.
	 *
	 * Determines the appropriate service name and map point configurations based on shipping type
	 * (self-pickup vs courier), service supplier (CARGUS, FAN_COURIER, SAMEDAY, DPD, GLS), and
	 * delivery point availability. Updates sender and receiver map point data accordingly.
	 *
	 * @param array  $data The shipping data array to be modified.
	 * @param mixed  $delivery_point The delivery point ID for receiver location.
	 * @param string $service_supplier The shipping service supplier name.
	 * @param string $shipping_type The shipping method type (SELF_* or COURIER).
	 *
	 * @return array Modified shipping data with service name and map point configurations
	 */
	public static function get_ecolet_data_for_order_valuation( $data, $delivery_point, $service_supplier, $shipping_type ) {

		$service_name = null;

		if ( ! $shipping_type ) {
			return $data;
		}

		if ( $delivery_point ) {

			$data['receiver']['has_map_point'] = true;
			$data['receiver']['map_point_id']  = $delivery_point;

		} elseif ( 0 === strpos( strtoupper( $shipping_type ), 'SELF_' ) ) {

			if ( 'CARGUS' === $service_supplier ) {
				$service_name = 'cargus_easy_collect';
			} elseif ( 'FAN_COURIER' === $service_supplier ) {
				$service_name = 'fan_courier_locker_to_locker'; // 60.
			} elseif ( 'SAMEDAY' === $service_supplier ) {
				$service_name = 'sameday_locker_automatul_automatul'; // 63.
				// $service_name = 'sameday'; // 63.
			} elseif ( 'DPD' === $service_supplier ) {
				$service_name = 'dpd_automat_locker_to_locker'; // 43
			} elseif ( 'GLS' === $service_supplier ) {
				$service_name = 'locker_automat_automat'; // 75
			}
		}

		if ( 0 === strpos( $shipping_type, 'SELF_' ) ) {
			// pre-defined by choice of byuer during checkout.
			if ( $service_supplier ) {
				$helper       = new Helper();
				$sender_point = $helper->get_ecolet_merchant_default_point( $service_supplier );
				if ( $sender_point ) {
					$data['sender']['has_map_point'] = true;
					$data['sender']['map_point_id']  = $sender_point;
				}
			} else {

				// Choice of Merchant in metabox creating module.
				if ( 'SELF_GLS' === $shipping_type ) {
					$service_name = 'locker_automat_automat';
					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_gls' );
				} elseif ( 'SELF_SAMEDAY' === $shipping_type ) {
					$service_name = 'sameday_locker';
					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_sameday' );
				} elseif ( 'SELF_DPD' === $shipping_type ) {
					$service_name = 'dpd_automat_locker_to_courier';
					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_dpd' );
				} elseif ( 'SELF_CARGUS' === $shipping_type ) {
					$service_name = 'standard_30_plus';
					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_cargus' );
				} elseif ( 'SELF_FAN_COURIER' === $shipping_type ) {
					$service_name = 'fan_courier_locker_to_courier';
					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_fan_courier' );
				}

				$data['sender']['has_map_point'] = true;
				$data['sender']['map_point_id']  = $sender_point;
			}
		}

		$shipping_type = strtoupper( $shipping_type );
		if ( 0 === strpos( $shipping_type, 'SELF_' ) || 0 === strpos( $shipping_type, 'self_' ) ) {
			$data['courier']['pickup']['type'] = 'self';
		}

		return $data;
	}





	/**
	 * Fixes and adjusts shipping data for Ecolet API compatibility.
	 *
	 * Applies necessary data transformations including setting sender map point from AJAX data,
	 * normalizing pickup type from 'self_*' to 'self', and ensuring receiver phone is properly set.
	 *
	 * @param array $data_to_api The shipping data array to be sent to Ecolet API.
	 * @param array $ajax_data The AJAX request data containing additional shipping information.
	 * @param string $service_supplier The shipping service supplier name.
	 * @param string $shipping_type The shipping method type (SELF_* or COURIER).
	 *
	 * @return array Fixed shipping data array compatible with Ecolet API, or empty array if invalid input
	 */
	public static function fix_data_for_ecolet_api( $data_to_api, $ajax_data, $service_supplier, $shipping_type ) {

		if ( ! is_array( $data_to_api ) ) {
			return array();
		}

		if ( 0 === strpos( $shipping_type, 'SELF_' ) ) {
			// pre-defined by choice of byuer during checkout.
			if ( $service_supplier ) {
				$helper       = new Helper();
				$sender_point = $helper->get_ecolet_merchant_default_point( $service_supplier );
				if ( $sender_point ) {
					$data_to_api['sender']['has_map_point'] = true;
					$data_to_api['sender']['map_point_id']  = $sender_point;
				}
			} else {

				// Choice of Merchant in metabox creating module.
				if ( 'SELF_GLS' === $shipping_type ) {

					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_gls' );
				} elseif ( 'SELF_SAMEDAY' === $shipping_type ) {

					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_sameday' );
				} elseif ( 'SELF_DPD' === $shipping_type ) {

					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_dpd' );
				} elseif ( 'SELF_CARGUS' === $shipping_type ) {

					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_cargus' );
				} elseif ( 'SELF_FAN_COURIER' === $shipping_type ) {

					$sender_point = get_option( 'alsendo_connect_settings_general_dispath_point_fan_courier' );
				}

				$data_to_api['sender']['has_map_point'] = true;
				$data_to_api['sender']['map_point_id']  = $sender_point;
			}
		}

		if ( 0 === strpos( $data_to_api['courier']['pickup']['type'], 'self_' ) || 0 === strpos( $data_to_api['courier']['pickup']['type'], 'SELF_' ) ) {
			$data_to_api['courier']['pickup']['type'] = 'self';
		}

		$data_to_api['receiver']['phone'] = $ajax_data['receiver']['phone'];

		return $data_to_api;
	}


	/**
	 * Generates a price table for Ecolet shipping services from API calculation response.
	 *
	 * Processes the Ecolet API response to extract pricing information and matches it
	 * with available service structures to create a formatted price table with service
	 * names, net prices, and gross prices.
	 *
	 * @param array|object $calculate_response The Ecolet API response containing service calculations.
	 * @param array        $calculate_order The order data used for calculation.
	 * @param mixed        $linked_service_id The linked in shipping method settings.
	 *
	 * @return array Formatted price table with service names, net prices, and gross prices
	 *               indexed by service ID
	 */
	public static function get_price_table_ecolet( $calculate_response, $calculate_order, $linked_service_id ) {
		$ecolet          = new Ecolet();
		$ecolet_services = $ecolet->get_service_structure_ecolet();

		$is_multi_parcel = ! empty( $calculate_order['parcels'] ) &&
							is_array( $calculate_order['parcels'] ) &&
							count( $calculate_order['parcels'] ) > 1;

		$allowed_for_multi_parcel = array(
			'dpd_standard',
			'urgent_cargus_standard',
			'sameday_nextday',
		);

		$price_table = array();

		foreach ( $calculate_response as $ecolet_service_slug => $data ) {
			foreach ( $ecolet_services as $k => $service ) {
				if ( $ecolet_service_slug !== $service['slug'] ) {
					continue;
				}

				if ( $is_multi_parcel && ! in_array( $ecolet_service_slug, $allowed_for_multi_parcel ) ) {
					continue;
				}

				if ( ! property_exists( $data, 'priceTable' ) ||
					! property_exists( $data->priceTable, 'price' ) ) {
					continue;
				}

				$price_data        = $data->priceTable;
				$price_table[ $k ] = array(
					'name'        => $service['name'],
					'price'       => $price_data->price,
					'price_gross' => $price_data->priceGross,
				);

				if ( ! empty( $linked_service_id ) && (int) $linked_service_id === $k ) {
					return $price_table;
				}
			}
		}

		return $price_table;
	}


	/**
	 * Updates order metadata with merchant-edited shipping information.
	 *
	 * Merges provided data (package properties, additional options, receiver, sender)
	 * with existing order metadata and marks the order as updated. Saves changes to
	 * WooCommerce order meta.
	 *
	 * @param int   $order_id The WooCommerce order ID to update.
	 * @param array $data Array containing updated shipping data with keys:
	 *                    package_properties, additional_options, receiver, sender.
	 *
	 * @return void
	 */
	public static function update_merchant_made_edits_in_fields( $order_id, $data ) {

		$order_meta = alsendo_connect()->get_order_meta_data( $order_id );

		if ( ! empty( $data['package_properties'] ) ) {
			$order_meta['package_properties'] = $data['package_properties'];
		}

		if ( ! empty( $data['additional_options'] ) ) {
			$order_meta['additional_options'] = $data['additional_options'];
		}

		if ( ! empty( $data['receiver'] ) ) {
			$order_meta['receiver'] = $data['receiver'];
		}

		if ( ! empty( $data['sender'] ) ) {
			$order_meta['sender'] = $data['sender'];
		}

		$order_meta['updated'] = 1;

		$order = wc_get_order( $order_id );
		if ( $order ) {
			$order->update_meta_data( 'alsendo_connect_wc_order', $order_meta );
			$order->save();
		}
	}


	public static function get_price_table_apaczka( $calculate_response ) {
		$services = ( new Alsendo_API() )->get_service_structure_apaczka();

		if ( empty( $services ) || ( ! empty( $services[0] ) && is_object( $services[0] ) ) ) {
			$services = ( new Alsendo_API() )->get_service_structure_apaczka( true );
		}

		$services_cache = array();

		foreach ( $services as $service_id => $service ) {
			$services_cache[ $service_id ] = array(
				'pickup_courier' => $service['pickup_courier'],
				'supplier'       => $service['supplier'],
				'name'           => $service['name'],
			);
		}

		$price_table = array();

		foreach ( $calculate_response as $service_id => $service_price ) {

			if ( property_exists( $service_price, 'priceTable' ) ) {
				$price_data = $service_price->priceTable;

				$price_table[ $service_id ] =
					array(
						'name'           => $services_cache[ $service_id ]['name'],
						'price'          => $price_data->price,
						'price_gross'    => $price_data->priceGross,
						'pickup_courier' => $services_cache[ $service_id ]['pickup_courier'],
						'supplier'       => $services_cache[ $service_id ]['supplier'],
					);

			}
		}

		return $price_table;
	}


	/**
	 * Sorts shipping methods by supplier name and price.
	 *
	 * Performs a two-level sort: primarily by supplier name alphabetically,
	 * and secondarily by price in ascending order when names are identical.
	 *
	 * @param array $shipping_methods The array of shipping methods to sort.
	 *
	 * @return array The sorted shipping methods array.
	 */
	public static function sort_by_supplier( $shipping_methods ) {
		uasort(
			$shipping_methods,
			function ( $a, $b ) {
				$name_comparison = strcmp( $a['name'], $b['name'] );
				if ( 0 !== $name_comparison ) {
					return $name_comparison;
				}
				return $a['price'] <=> $b['price'];
			}
		);

		return $shipping_methods;
	}
}
