<?php

namespace Inspire_Labs\Alsendo_Connect;

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


use Alsendo\AlsendoWrapper\Api\Ecolet\Model\AdditionalService;
use Alsendo\AlsendoWrapper\Model\Address;
use Alsendo\AlsendoWrapper\Model\Cod;
use Alsendo\AlsendoWrapper\Model\Contact;
use Alsendo\AlsendoWrapper\Model\Notification;
use Alsendo\AlsendoWrapper\Model\NotificationDetail;
use Alsendo\AlsendoWrapper\Model\Order\OrderRequest;
use Alsendo\AlsendoWrapper\Model\Pickup;
use Alsendo\AlsendoWrapper\Model\Shipment;
use DateTimeImmutable;
use Exception;
use Alsendo\AlsendoWrapper\ApiClientFactory;


// Wrapper autoload.
require_once __DIR__ . '../../vendor/alsendo-wrapper/vendor/autoload.php';

/**
 * Zaslat
 */
class Zaslat {


	/**
	 * ZASLAT_API_PRODUCTION
	 */
	const ZASLAT_API_PRODUCTION = 'https://www.zaslat.cz/api/v1';
	/**
	 * ZASLAT_API_SANDBOX
	 */
	const ZASLAT_API_SANDBOX = 'https://test.zaslat.cz/api/v1';

	/**
	 * API Token.
	 *
	 * @var string
	 */
	private $api_token;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->api_token = $this->get_plugin_option( 'alsendo_connect_settings_general_zaslat_api_key' );

		$this->hooks();
	}

	/**
	 * Get plugin option
	 *
	 * @param string $key name of option.
	 *
	 * @return mixed
	 */
	private function get_plugin_option( $key ) {
		$setting = get_option( $key );
		return ! empty( $setting ) ? $setting : false;
	}

	/**
	 * Hooks.
	 */
	public function hooks() {
	}

	/**
	 * Generates the tracking URL for an InTime shipment.
	 *
	 * @param string $shipment_id The shipment tracking number.
	 * @return string The complete tracking URL for the InTime shipping service
	 */
	public function get_tracking_url( $shipment_id ) {
		// return 'https://trace.intime.cz/index.php?orderNumber=&customerNumber=' . $shipment_id;
		// https://test.zaslat.cz/en/tracking/IZ11A443E117
		$url      = '';
		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$url = 'https://zaslat.cz/';
		} else {
			$url = 'https://test.zaslat.cz/';
		}
		return $url . 'tracking/' . $shipment_id;
	}

	/**
	 * Retrieves shipping rates from the Zaslat.cz API.
	 *
	 * Makes a POST request to either production or sandbox API endpoint based on settings.
	 * Handles the API response and any potential errors during the request.
	 *
	 * @param array $order Order data to be sent to the API.
	 * @return array {
	 *     Response data from the Zaslat API
	 *
	 *     @type int    $status_code HTTP response code
	 *     @type string $errors      Error message if exception occurred
	 *     @type mixed  $result         Additional data from API response.
	 * }
	 *
	 * @throws Exception Handled internally, messages included in return array.
	 */
	public function get_rates_direct( $order ) {

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/rates/get';

		/*
		$request_body = array(
			//'currency' => get_option( 'woocommerce_currency' ),
			'currency' => 'CZK',
			'from' => array(
				'country' => 'CZ'
			),
			'to' => array(
				'country' => 'CZ'
			),
			'packages' => array(
				0 => array(
					'weight' => 1,
					'width' => 10,
					'height' => 20,
					'length' => 30,
				)
			),
		);*/

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $order ),
		);

			$response      = wp_remote_post( $api_url, $args );
			$response_code = wp_remote_retrieve_response_code( $response );

		try {
			$response_body         = wp_remote_retrieve_body( $response );
			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;

		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['errors']      = $e->getMessage();
		}

		return $result;
	}




	/**
	 * Gets shipping rates from Zaslat API for order valuation.
	 *
	 * Prepares order request, calls Zaslat API for rate calculation, handles
	 * production/test environment selection, processes response data to extract
	 * service details and pricing, and logs errors with detailed context.
	 *
	 * @param array $order The order data for rate calculation.
	 * @return array Result array with status, rates data, or error information.
	 */
	public function get_rates( $order ) {

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );

		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		$failure  = null;
		$response = array();

		try {

			$order_request = $this->prepare_zaslat_order_valuation_request( $order );

			$response = $api_client->getOrderValuation( $order_request );

			$result['status'] = 200;

		} catch ( Exception $e ) {

			$failure = $e->getMessage();

			$result['errors'] = $e->getMessage();

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

			if ( function_exists( 'wc_get_logger' ) ) {
				\wc_get_logger()->debug(
					'SOME_ERROR_ON_REQUEST: ZASLAT ORDER VALUATION',
					array( 'source' => 'alsendo-connect-order-' . $order_id )
				);
				\wc_get_logger()->debug(
					print_r( 'Place in code: ' . __METHOD__ . ': ' . __LINE__, true ),
					array( 'source' => 'alsendo-connect-order-' . $order_id )
				);
				\wc_get_logger()->debug(
					print_r( 'API type: ' . $api_type_wrapper, true ),
					array( 'source' => 'alsendo-connect-order-' . $order_id )
				);
				\wc_get_logger()->debug(
					print_r( $e->getMessage(), true ),
					array( 'source' => 'alsendo-connect-order-' . $order_id )
				);

			}
		}

		if ( ! empty( $response ) && is_array( $response ) ) {
			$result['status'] = 200;

			foreach ( $response as $i => $operator ) {
				if ( property_exists( $operator, 'service' ) ) {
					$result['rates'][ $i ]['service'] = $operator->service;
				}
				if ( property_exists( $operator, 'serviceId' ) ) {
					$result['rates'][ $i ]['service_id'] = $operator->serviceId;
				}
				if ( property_exists( $operator, 'carrier' ) ) {
					$result['rates'][ $i ]['carrier'] = $operator->carrier;
				}

				if ( property_exists( $operator, 'priceTable' ) ) {
					$price_table = $operator->priceTable;

					if ( ! empty( $price_table ) && property_exists( $price_table, 'price' ) ) {
						$result['rates'][ $i ]['price']['value']    = $price_table->price;
						$result['rates'][ $i ]['price']['currency'] = $price_table->currency;
					}
					if ( ! empty( $price_table ) && property_exists( $price_table, 'priceGross' ) ) {
						$result['rates'][ $i ]['price_vat']['value'] = $price_table->priceGross;
					}
				}
			}
		}

		return $result;
	}


	/**
	 * Creates a new shipment through the Zaslat.cz API.
	 *
	 * Sends shipment data to either production or sandbox API endpoint based on settings.
	 * Removes payer information from the order data before submission.
	 *
	 * @param array $order Shipment order data containing shipments array with carrier information.
	 * @return array {
	 *     Response data from the Zaslat API
	 *
	 *     @type int    $status_code HTTP response code
	 *     @type string $operator    Carrier code from the order data
	 *     @type string $errors      Error message if exception occurred
	 *     @type mixed  $result       Additional data from API response
	 * }
	 *
	 * @throws Exception Handled internally, messages included in return array.
	 */
	public function create_shipment_direct( $order ) {

		unset( $order['payer'] );

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/shipments/create';

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $order ),
		);

		try {
			$response      = wp_remote_post( $api_url, $args );
			$response_code = wp_remote_retrieve_response_code( $response );

			$response_body = wp_remote_retrieve_body( $response );

			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;
			$result['operator']    = ! empty( $order['shipments'][0]['carrier'] ) ? $order['shipments'][0]['carrier'] : '';

		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['errors']      = $e->getMessage();
		}

		return $result;
	}



	/**
	 * Creates a shipment via Zaslat API.
	 *
	 * Removes payer data, prepares order creation request, sends order to Zaslat API,
	 * handles production/test environment selection, and processes response to extract
	 * shipment ID and carrier information.
	 *
	 * @param array $order The order data for shipment creation.
	 * @return array Result array with status code, shipment data, or error information.
	 */
	public function create_shipment( $order ) {

		unset( $order['payer'] );

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		$response = array();

		try {

			$order_request = $this->prepare_zaslat_order_create_request( $order );
			$response      = $api_client->sendOrder( $order_request );

			if ( is_object( $response ) && property_exists( $response, 'id' ) ) {
				if ( ! empty( $response->id ) ) {
					$result['status_code']          = 200;
					$result['data']['operator']     = ! empty( $order['shipments'][0]['carrier'] ) ? $order['shipments'][0]['carrier'] : '';
					$result['data']['order']        = $response->id;
					$result['data']['order_number'] = '';
					$result['data']['shipments'][]  = ! empty( $response->shipments[0] ) ? $response->shipments[0] : '';
				}
			}
		} catch ( Exception $e ) {
			$result['error'] = $e->getMessage();

		}

		return $result;
	}


	/**
	 * Retrieves detailed information about shipment(s) from the Zaslat.cz API.
	 *
	 * Accepts either a single shipment ID or an array of shipment IDs.
	 * Makes a POST request to the API's detail endpoint.
	 *
	 * @param string|array $shipments Single shipment ID or array of shipment IDs.
	 * @return array {
	 *     Response data from the Zaslat API
	 *
	 *     @type int    $status_code HTTP response code
	 *     @type string $error       Error message if exception occurred
	 *     @type mixed  $result      Shipment details from API response
	 * }
	 *
	 * @throws Exception Handled internally, messages included in return array.
	 */
	public function get_details_direct( $shipments ) {

		if ( ! is_array( $shipments ) ) {
			$shipments = array( $shipments );
		}

		$data = array( 'shipments' => $shipments );

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/shipments/detail';

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $data ),
		);

		$response      = wp_remote_post( $api_url, $args );
		$response_code = wp_remote_retrieve_response_code( $response );

		try {
			$response_body         = wp_remote_retrieve_body( $response );
			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;
		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['error']       = $e->getMessage();
		}

		return $result;
	}



	/**
	 * Gets shipment details from Zaslat API.
	 *
	 * Retrieves order information for a specific shipment using Zaslat API,
	 * handles production/test environment selection, and returns formatted
	 * response data or error information.
	 *
	 * @param string $shipment The shipment identifier to retrieve details for.
	 * @return array Result array with status, shipment data, or error information.
	 */
	public function get_details( $shipment ) {

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		$response = array();

		try {
			$response = $api_client->getOrder( $shipment );

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

		if ( ! empty( $response ) && property_exists( $response, 'id' ) ) {
			$result['status'] = 200;
			$result['data']   = $response;
		}

		return $result;
	}


	/**
	 * Cancels one or more shipments via the Zaslat API.
	 *
	 * Sends a POST request to the Zaslat API storno endpoint to cancel specified shipments.
	 * The method handles both single shipment IDs and arrays of shipment IDs.
	 *
	 * @param string|array $shipments Single shipment ID or array of shipment IDs to cancel.
	 * @return array API response containing status_code and response data, or error message on failure.
	 */
	public function cancel_order_direct( $shipments ) {

		if ( ! is_array( $shipments ) ) {
			$shipments = array( $shipments );
		}

		$data = array( 'shipments' => $shipments );

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/shipments/storno';

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $data ),
		);

		$response      = wp_remote_post( $api_url, $args );
		$response_code = wp_remote_retrieve_response_code( $response );

		try {
			$response_body         = wp_remote_retrieve_body( $response );
			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;
		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['error']       = $e->getMessage();
		}

		return $result;
	}



	/**
	 * Cancels an order via Zaslat API.
	 *
	 * Sends cancellation request for a specific shipment using Zaslat API,
	 * handles production/test environment selection, and returns success
	 * message or error information.
	 *
	 * @param string $shipment The shipment identifier to cancel.
	 * @return array Result array with status code, success message, or error information.
	 */
	public function cancel_order( $shipment ) {

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		$response = array();

		try {
			$response              = $api_client->cancelOrder( $shipment );
			$result['status_code'] = 200;
			$result['message']     = esc_html__( 'Order cancelled', 'alsendo-connect' );

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

		}

		return $result;
	}


	/**
	 * Retrieves shipping waybill/label for one or more shipments via the Zaslat API.
	 *
	 * Sends a POST request to the Zaslat API label endpoint to generate and retrieve
	 * waybill labels for specified shipments.
	 *
	 * @param string|array $zaslat_ids Single Zaslat shipment ID or array of shipment IDs.
	 * @param int|string   $order_id   WooCommerce order ID associated with the shipments.
	 * @return array API response containing status_code and label data, or error message on failure.
	 */
	public function get_waybill_direct( $zaslat_ids, $order_id ) {
		$result = array();

		if ( ! is_array( $zaslat_ids ) ) {
			$shipments = array( $zaslat_ids );
		}

		$data = array( 'shipments' => $shipments );

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/shipments/label';

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $data ),
		);

		$response      = wp_remote_post( $api_url, $args );
		$response_code = wp_remote_retrieve_response_code( $response );

		try {
			$response_body         = wp_remote_retrieve_body( $response );
			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;
		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['error']       = $e->getMessage();
		}

		return $result;
	}


	/**
	 * Gets waybill PDF from Zaslat API.
	 *
	 * Retrieves waybill document for a specific Zaslat shipment using API,
	 * handles production/test environment selection, validates PDF response
	 * format, and returns document data or error information.
	 *
	 * @param string $zaslat_id The Zaslat shipment identifier.
	 * @param int    $order_id The WooCommerce order ID.
	 * @return array Result array with status code, PDF data, or error information.
	 */
	public function get_waybill( $zaslat_id, $order_id ) {
		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		$response = array();

		try {
			$response = $api_client->getWayBill( $zaslat_id );

			if ( 0 === strpos( $response, 'data:application/pdf;' ) ) {
				$result['status_code'] = 200;
				$result['data']        = $response;
			}
		} catch ( Exception $e ) {
			$result['error'] = $e->getMessage();

		}

		return $result;
	}


	/**
	 * Retrieves shipping manifest for one or more shipments via the Zaslat API.
	 *
	 * Sends a POST request to the Zaslat API manifest endpoint to generate and retrieve
	 * manifest documentation for specified shipments.
	 *
	 * @param string|array $zaslat_ids Single Zaslat shipment ID or array of shipment IDs.
	 * @param int|string   $order_id   WooCommerce order ID associated with the shipments.
	 * @return array API response containing status_code and manifest data, or error message on failure.
	 */
	public function get_manifest( $zaslat_ids, $order_id ) {
		$result = array();

		if ( ! is_array( $zaslat_ids ) ) {
			$shipments = array( $zaslat_ids );
		}

		$data = array( 'shipments' => $shipments );

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_url = self::ZASLAT_API_PRODUCTION;
		} else {
			$api_url = self::ZASLAT_API_SANDBOX;
		}

		$api_url = $api_url . '/shipments/manifest';

		$args = array(
			'method'      => 'POST',
			'timeout'     => 30,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'headers'     => array(
				'X-Apikey'     => $this->api_token,
				'Accept'       => 'application/json',
				'Content-Type' => 'application/json',
			),
			'body'        => wp_json_encode( $data ),
		);

		$response      = wp_remote_post( $api_url, $args );
		$response_code = wp_remote_retrieve_response_code( $response );

		try {
			$response_body         = wp_remote_retrieve_body( $response );
			$result                = json_decode( $response_body, true );
			$result['status_code'] = $response_code;
		} catch ( Exception $e ) {
			$result['status_code'] = $response_code;
			$result['error']       = $e->getMessage();
		}

		return $result;
	}


	/**
	 * Retrieves the address book from Zaslat API with caching mechanism.
	 *
	 * Fetches contact list from Zaslat API and caches the result for 12 hours.
	 * If cached data exists and is still valid, returns cached data instead of making API call.
	 * Cache can be bypassed by defining ALSENDO_CONNECT_DISABLE_CACHE_ZASLAT constant.
	 *
	 * @return array Address book data from API or cached result, empty array on failure.
	 */
	public function get_address_book_direct() {

		$result = array();

		if ( defined( 'ALSENDO_CONNECT_DISABLE_CACHE_ZASLAT' )
			|| time() - (int) get_option( 'alsendo_connect_zaslat_address_book_timestamp' ) > 43200
		) {

			$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
			if ( 'prod' === $api_type ) {
				$api_url = self::ZASLAT_API_PRODUCTION;
			} else {
				$api_url = self::ZASLAT_API_SANDBOX;
			}

			$api_url = $api_url . '/contacts/list';

			$args = array(
				'method'  => 'GET',
				'headers' => array(
					'X-Apikey'     => $this->api_token,
					'Accept'       => 'application/json',
					'Content-Type' => 'application/json',
				),
			);

			$response      = wp_remote_get( $api_url, $args );
			$response_code = wp_remote_retrieve_response_code( $response );

			if ( 200 !== $response_code ) {

				try {
					$response_body = wp_remote_retrieve_body( $response );
					$res           = json_decode( $response_body, true );
				} catch ( Exception $e ) {
					$res = $e->getMessage();
				}

				if ( function_exists( 'wc_get_logger' ) ) {
					$logger = wc_get_logger();
					$logger->log(
						'debug',
						'API RESPONSE ZASLAT get address book list:',
						array(
							'source'        => 'alsendo-connect-zaslat-address-list',
							'response_code' => $response_code,
							'response'      => $res,
						)
					);
				}
				return array();
			}

			try {
				$response_body         = wp_remote_retrieve_body( $response );
				$result                = json_decode( $response_body, true );
				$result['status_code'] = $response_code;

				update_option(
					'alsendo_connect_zaslat_address_book',
					$result,
					false
				);
				update_option(
					'alsendo_connect_zaslat_address_book_timestamp',
					time()
				);

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

				if ( function_exists( 'wc_get_logger' ) ) {
					$logger = wc_get_logger();
					$logger->log(
						'debug',
						'API RESPONSE ZASLAT get address book list:',
						array(
							'source'        => 'alsendo-connect-zaslat-address-list',
							'response_code' => $response_code,
							'response'      => $result,
						)
					);
				}
			}
		} else {

			$result = get_option( 'alsendo_connect_zaslat_address_book' );
		}

		return is_array( $result ) ? $result : array();
	}


	/**
	 * Gets address book from Zaslat API with caching mechanism.
	 *
	 * Retrieves contact list from Zaslat API with 12-hour cache expiration,
	 * handles production/test environment selection, processes contact data
	 * into structured format, and logs errors when API requests fail.
	 *
	 * @return array Address book data with contact information or empty array.
	 */
	public function get_address_book( $forced = false ) {

		$result = array();

		static $is_request_running = false;

		if ( empty( $this->api_token ) ) {
			return $result;
		}

		$can_update_services = is_admin() && ( current_user_can( 'manage_woocommerce' ) || current_user_can( 'manage_options' ) );

		if ( ! $can_update_services ) {
			$result = get_option( 'alsendo_connect_zaslat_address_book' );
			return is_array( $result ) ? $result : array();
		}

		if ( $forced
			|| time() - (int) get_option( 'alsendo_connect_zaslat_address_book_timestamp' ) > 43200
		) {

			if ( $forced ) {
				delete_option( 'alsendo_zaslat_api_returned_error' );
			}			

			$maybe_timeout = get_option( 'alsendo_zaslat_api_returned_error' );
			$now           = time();
			if ( $maybe_timeout && $maybe_timeout > $now ) {
				return array();
			} else {
				delete_option( 'alsendo_zaslat_api_returned_error' );
			}

			$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
			if ( 'prod' === $api_type ) {
				$api_type_wrapper = ApiClientFactory::ZASLAT;
			} else {
				$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
			}

			// init Wrapper.
			$api_client = ( new ApiClientFactory() )->create(
				$api_type_wrapper,
				array(
					'api_key' => $this->api_token,
				)
			);

			if ( ! $is_request_running ) {

				$is_request_running = true;

				try {

					$response = $api_client->getContactList();

					if ( is_array( $response ) && ! empty( $response[0] ) && property_exists( $response[0], 'id' ) ) {
						$result['status_code'] = 200;
						$result['data']        = array();
						$address               = array();
						foreach ( $response as $adr ) {
							$address['id']        = $adr->id;
							$address['company']   = ! empty( $adr->company ) ? $adr->company : '';
							$address['firstname'] = ! empty( $adr->firstname ) ? $adr->firstname : '';
							$address['surname']   = ! empty( $adr->surname ) ? $adr->surname : '';
							$address['street']    = ! empty( $adr->street ) ? $adr->street : '';
							$address['city']      = ! empty( $adr->city ) ? $adr->city : '';
							$address['zip']       = ! empty( $adr->zip ) ? $adr->zip : '';
							$address['country']   = ! empty( $adr->country ) ? $adr->country : '';
							$address['phone']     = ! empty( $adr->phone ) ? $adr->phone : '';
							$address['email']     = ! empty( $adr->email ) ? $adr->email : '';

							$result['data'][ $adr->id ] = $address;
						}
					}

					update_option( 'alsendo_connect_zaslat_address_book', $result, false );
					update_option( 'alsendo_connect_zaslat_address_book_timestamp', time() );

					$is_request_running = false;

				} catch ( Exception $e ) {

					$is_request_running = false;

					$current_timestamp = time();
					$timeout           = $current_timestamp + 300;
					update_option( 'alsendo_zaslat_api_returned_error', $timeout );

					if ( function_exists( 'wc_get_logger' ) ) {
						$logger = wc_get_logger();
						$logger->log(
							'debug',
							'API RESPONSE ZASLAT get address book list:',
							array(
								'source'   => 'alsendo-connect-zaslat-address-list',
								'response' => $e->getMessage(),
							)
						);
					}
				}
			}
		} else {

			$result = get_option( 'alsendo_connect_zaslat_address_book' );
		}

		return is_array( $result ) ? $result : array();
	}


	/**
	 * Converts Zaslat address book data into formatted sender templates.
	 *
	 * Retrieves address book from Zaslat API and transforms the contact data into
	 * a structured array of sender templates with standardized option keys for
	 * use in plugin settings and shipping forms.
	 *
	 * @return array Formatted sender templates with contact details and settings options.
	 */
	public function get_sender_templates_for_zaslat() {
		$res = $this->get_address_book();
		if ( ! empty( $res['data'] ) && is_array( $res['data'] ) ) {
			$address_book = $res['data'];
		}

		$sender_templates = array();

		if ( is_array( $address_book ) ) {
			foreach ( $address_book as $key => $val ) {

				$first_name     = '';
				$surname        = '';
				$company        = '';
				$contact_person = '';
				$street         = '';
				$city           = '';
				$zip            = '';
				$state          = '';
				$country        = '';
				$phone          = '';
				$email          = '';

				if ( ! empty( $val['firstname'] ) ) {
					$first_name = esc_html( $val['firstname'] );
				}
				if ( ! empty( $val['surname'] ) ) {
					$surname = esc_html( $val['surname'] );

				}

				if ( ! empty( $val['company'] ) ) {
					$contact_person = esc_html( $val['company'] );
				} else {
					$contact_person = $first_name . ' ' . $surname;
				}

				$address  = $key . ' (';
				$address .= $contact_person;

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

				$address .= ')';

				if ( ! empty( $val['phone'] ) ) {
					$phone = esc_html( $val['phone'] );
				}
				if ( ! empty( $val['email'] ) ) {
					$email = esc_html( $val['email'] );
				}

				$sender_templates[ $key ]['name'] = $address;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_is_residential']      = ! empty( $val['company'] ) ? 0 : 1;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_country']             = $country;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_company_name']        = ! empty( $val['company'] ) ? $val['company'] : '';
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_first_name']          = $first_name;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_last_name']           = $surname;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_street']              = $street;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_building_number']     = '';
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_apartment_number']    = '';
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_postal_code']         = $zip;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_province']            = $state;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_city']                = $city;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_contact_person']      = $contact_person;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_phone']               = $phone;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_email']               = $email;
				$sender_templates[ $key ]['options']['alsendo_connect_settings_general_sender_bank_account_number'] = '';
			}
		}

		return $sender_templates;
	}



	/**
	 * Prepares Zaslat order valuation request from order data.
	 *
	 * Converts order data into Zaslat API format including sender/receiver contacts,
	 * shipment dimensions, pickup options, COD services, notifications, and handles
	 * special cases for pickup branch suppliers like Balikovna and Zasilkovna.
	 *
	 * @param array $data The order data to convert.
	 * @return OrderRequest The formatted Zaslat order request object.
	 */
	private function prepare_zaslat_order_valuation_request( $data ) {

		$sender_phone   = str_replace( ' ', '', $data['from']['phone'] );
		$sender_phone   = trim( $sender_phone );
		$receiver_phone = str_replace( ' ', '', $data['to']['phone'] );
		$receiver_phone = trim( $receiver_phone );

		$order_request = new OrderRequest();

		$order_request->currency = 'CZK';

		$sender_pickup_branch = false;
		if ( ! empty( $data['to']['supplier'] ) ) {
			if ( 'BALIKOVNA' === $data['to']['supplier'] || 'ZASILKOVNA' === $data['to']['supplier'] || 'PACKETA' === $data['to']['supplier'] ) {
				$sender_pickup_branch = 1;
			}
		}

		$order_request->address = new Address(
			new Contact(
				$data['from']['country'],
				$data['from']['company'],
				$data['from']['street'],
				'',
				$data['from']['zip'],
				'',
				$data['from']['city'],
				1,
				! empty( $data['from']['company'] ) ? $data['from']['company'] : $data['from']['firstname'] . ' ' . $data['from']['surname'],
				$data['from']['email'],
				$sender_phone,
				'',
				0,
				'',
				'',
				'',
				'',
				'',
				'',
				$sender_pickup_branch,
				null,
			),
			new Contact(
				$data['to']['country'],
				$data['to']['company'],
				$data['to']['street'],
				'',
				$data['to']['zip'],
				'',
				$data['to']['city'],
				1,
				! empty( $data['to']['company'] ) ? $data['to']['company'] : $data['to']['firstname'] . ' ' . $data['to']['surname'],
				$data['to']['email'],
				$receiver_phone,
				'',
				0,
				'',
				'',
				'',
				'',
				'',
				'',
				! empty( $data['to']['supplier'] ) ? true : false,
				! empty( $data['to']['delivery_branch'] ) ? $data['to']['delivery_branch'] : null,
			)
		);

		/*if ( function_exists( 'wc_get_logger' ) ) {
			\wc_get_logger()->debug( 'ZASLAT AJAX DATA ', array( 'source' => 'zaslat_shipping_method_settings' ) );
			\wc_get_logger()->debug( print_r( $data, true ), array( 'source' => 'zaslat_shipping_method_settings' ) );

		}*/

		if ( ! empty( $data['carrier'] ) ) {

			$order_request->carrier = $data['carrier'];

		} elseif ( ! empty( $data['to']['supplier'] ) ) {
			$order_request->carrier = $data['to']['supplier'];
			if ( 'PACKETA' === $data['to']['supplier'] ) {
				$order_request->carrier = 'ZASILKOVNA';
			}
		}

		$order_request->carrier = $this->normalize_operator_name( $order_request->carrier );

		$order_request->address->sender->id = $data['from']['id'];

		$order_request->option = array(
			'31' => 1,
		);

		$order_request->notification = new Notification(
			new NotificationDetail(
				1,
				1,
				1
			),
			new NotificationDetail(
				1
			),
			new NotificationDetail(
				1,
				0,
				1
			),
			new NotificationDetail(
				1,
				0,
				1
			),
		);

		$order_request->shipmentValue = ! empty( $data['packages'][0]['value']['value'] ) ? floatval( $data['packages'][0]['value']['value'] ) : 0;

		if ( ! empty( $data['services'][0]['code'] ) && 'cod' === $data['services'][0]['code'] ) {
			$order_request->cod              = new Cod();
			$order_request->cod->amount      = ! empty( $data['services'][0]['data']['value']['value'] ) ? $data['services'][0]['data']['value']['value'] : 0;
			$order_request->cod->bankaccount = '';
		}

		$order_request->pickup = new Pickup();

		$order_request->pickup->type = ! empty( $data['type'] ) ? $data['type'] : '';
		if ( 'BALIKOVNA' === strtoupper( $order_request->carrier ) || 'ZASILKOVNA' === strtoupper( $order_request->carrier ) || 'PACKETA' === strtoupper( $order_request->carrier ) ) {
			$order_request->pickup->type = null;
		}

		// pickup_request for valuation - not required
		/*
		if( ! empty( $data['pickup_request'] ) ) {
			$order_request->pickupRequest = 1;
			if( ! empty( $data['pickup_date'] ) ) {
				$order_request->pickup->date = ! empty( $data['pickup_date'] ) ? $data['pickup_date'] : '';
				$order_request->pickup->hoursFrom = '10:00';
				$order_request->pickup->hoursTo = '18:00';
			}
		}
		*/

		$shipment                   = new Shipment();
		$shipment->dimension1       = $data['packages'][0]['width'];
		$shipment->dimension2       = $data['packages'][0]['height'];
		$shipment->dimension3       = $data['packages'][0]['length'];
		$shipment->weight           = $data['packages'][0]['weight'];
		$shipment->isNstd           = 0;
		$shipment->shipmentTypeCode = 'PACZKA';
		$order_request->shipment[]  = $shipment;

		$order_request->content = ! empty( $data['packages'][0]['content'] ) ? $data['packages'][0]['content'] : '';

		return $order_request;
	}



	/**
	 * Prepares Zaslat order creation request from shipment data.
	 *
	 * Converts shipment data into Zaslat API format for order creation including
	 * carrier normalization, sender/receiver contacts, pickup scheduling, COD services,
	 * and handles special cases for pickup branch suppliers with delivery branch options.
	 *
	 * @param array $data The shipment data to convert.
	 * @return OrderRequest The formatted Zaslat order creation request object.
	 */
	private function prepare_zaslat_order_create_request( $data ) {

		$order_request = new OrderRequest();
		// Set up the order request data.
		$order_request->carrier  = ! empty( $data['shipments'][0]['carrier'] ) ? $this->normalize_operator_name( $data['shipments'][0]['carrier'] ) : '';
		$order_request->currency = 'CZK';

		$sender_phone   = str_replace( ' ', '', $data['shipments'][0]['from']['phone'] );
		$sender_phone   = trim( $sender_phone );
		$receiver_phone = str_replace( ' ', '', $data['shipments'][0]['to']['phone'] );
		$receiver_phone = trim( $receiver_phone );

		$sender_pickup_branch = false;
		if ( 'BALIKOVNA' === strtoupper( $order_request->carrier ) || 'ZASILKOVNA' === strtoupper( $order_request->carrier ) || 'PACKETA' === strtoupper( $order_request->carrier ) ) {
			$sender_pickup_branch = 1;
			if ( 'PACKETA' === strtoupper( $order_request->carrier ) ) {
				$order_request->carrier = 'ZASILKOVNA';
			}
		}

		$order_request->address = new Address(
			new Contact(
				$data['shipments'][0]['from']['country'],
				$data['shipments'][0]['from']['company'],
				$data['shipments'][0]['from']['street'],
				'',
				$data['shipments'][0]['from']['zip'],
				'',
				$data['shipments'][0]['from']['city'],
				1,
				! empty( $data['shipments'][0]['from']['company'] ) ? $data['shipments'][0]['from']['company'] : $data['shipments'][0]['from']['firstname'] . ' ' . $data['shipments'][0]['from']['surname'],
				$data['shipments'][0]['from']['email'],
				$sender_phone,
				'',
				0,
				'',
				'',
				'',
				'',
				'',
				'',
				$sender_pickup_branch,
				! empty( $data['shipments'][0]['pickup_branch'] ) ? 1 : null,
			),
			new Contact(
				$data['shipments'][0]['to']['country'],
				$data['shipments'][0]['to']['company'],
				$data['shipments'][0]['to']['street'],
				'',
				$data['shipments'][0]['to']['zip'],
				'',
				$data['shipments'][0]['to']['city'],
				1,
				! empty( $data['shipments'][0]['to']['company'] ) ? $data['shipments'][0]['to']['company'] : $data['shipments'][0]['to']['firstname'] . ' ' . $data['shipments'][0]['to']['surname'],
				$data['shipments'][0]['to']['email'],
				$receiver_phone,
				'',
				0,
				'',
				'',
				'',
				'',
				'',
				'',
				! empty( $data['shipments'][0]['to']['delivery_branch'] ) ? true : false,
				! empty( $data['shipments'][0]['to']['delivery_branch'] ) ? $data['shipments'][0]['to']['delivery_branch'] : null,
			)
		);

		$order_request->address->sender->id = $data['shipments'][0]['from']['id'];

		$order_request->option = array(
			'31' => 1,
		);

		$order_request->shipmentValue = ! empty( $data['shipments'][0]['packages'][0]['value']['value'] ) ? floatval( $data['shipments'][0]['packages'][0]['value']['value'] ) : 0;

		if ( ! empty( $data['shipments'][0]['services'][0]['code'] ) && 'cod' === $data['shipments'][0]['services'][0]['code'] ) {
			$order_request->cod              = new Cod();
			$order_request->cod->amount      = ! empty( $data['shipments'][0]['services'][0]['data']['value']['value'] ) ? $data['shipments'][0]['services'][0]['data']['value']['value'] : 0;
			$order_request->cod->bankaccount = '';
		}

		$order_request->pickup = new Pickup();

		$order_request->pickup->type = $data['shipments'][0]['type'];
		if ( 'BALIKOVNA' === strtoupper( $order_request->carrier ) || 'ZASILKOVNA' === strtoupper( $order_request->carrier ) || 'PACKETA' === strtoupper( $order_request->carrier ) ) {
			$order_request->pickup->type = null;
		}

		if ( ! empty( $data['pickup_request'] ) ) {
			$order_request->pickupRequest     = 1;
			$order_request->pickup->date      = ! empty( $data['shipments'][0]['pickup_date'] ) ? $data['shipments'][0]['pickup_date'] : '';
			$order_request->pickup->hoursFrom = '10:00';
			$order_request->pickup->hoursTo   = '18:00';
		}

		$shipment                   = new Shipment();
		$shipment->dimension1       = $data['shipments'][0]['packages'][0]['width'];
		$shipment->dimension2       = $data['shipments'][0]['packages'][0]['height'];
		$shipment->dimension3       = $data['shipments'][0]['packages'][0]['length'];
		$shipment->weight           = $data['shipments'][0]['packages'][0]['weight'];
		$shipment->isNstd           = 0;
		$shipment->shipmentTypeCode = 'PACZKA';
		$order_request->shipment[]  = $shipment;

		$order_request->content = $data['shipments'][0]['packages'][0]['content'];

		$current_plugin_version = alsendo_connect()->get_plugin_version();
		//$order_request->source = 'WooCommerce Alsendo Connect ver:' . $current_plugin_version;

		return $order_request;
	}


	/**
	 * Normalizes operator name by removing Czech diacritics and mapping to standard format.
	 *
	 * Converts operator names to uppercase, removes Czech special characters,
	 * and maps common variations to standardized operator names for API consistency.
	 *
	 * @param string $operator_name The operator name to normalize.
	 * @return string The normalized operator name.
	 */
	private function normalize_operator_name( $operator_name ) {

		$normalized = strtoupper( trim( $operator_name ) );

		$czech_chars = array(
			'Á' => 'A',
			'Č' => 'C',
			'Ď' => 'D',
			'É' => 'E',
			'Ě' => 'E',
			'Í' => 'I',
			'Ň' => 'N',
			'Ó' => 'O',
			'Ř' => 'R',
			'Š' => 'S',
			'Ť' => 'T',
			'Ú' => 'U',
			'Ů' => 'U',
			'Ý' => 'Y',
			'Ž' => 'Z',
			'á' => 'A',
			'č' => 'C',
			'ď' => 'D',
			'é' => 'E',
			'ě' => 'E',
			'í' => 'I',
			'ň' => 'N',
			'ó' => 'O',
			'ř' => 'R',
			'š' => 'S',
			'ť' => 'T',
			'ú' => 'U',
			'ů' => 'U',
			'ý' => 'Y',
			'ž' => 'Z',
		);

		$normalized = strtr( $normalized, $czech_chars );

		$operator_mapping = array(
			'BALIKOVNA'    => 'BALIKOVNA',
			'BALÍKOVNA'    => 'BALIKOVNA',
			'ZASILKOVNA'   => 'ZASILKOVNA',
			'ZÁSILKOVNA'   => 'ZASILKOVNA',
			'PPL'          => 'PPL',
			'DPD'          => 'DPD',
			'GLS'          => 'GLS',
			'MESSENGER'    => 'MESSENGER',
			'TOPTRANS'     => 'TOPTRANS',
			'GEIS'         => 'GEIS',
			'MAPAKURYR'    => 'MAPAKURYR',
			'UPS'          => 'UPS',
			'GLS_SK'       => 'GLS_SK',
			'LIFTAGO'      => 'LIFTAGO',
			'TNT'          => 'TNT',
			'FEDEX'        => 'FEDEX',
			'ONE_DELIVERY' => 'WEDO',
			'ONE'          => 'WEDO',
		);

		if ( isset( $operator_mapping[ $normalized ] ) ) {
			return $operator_mapping[ $normalized ];
		}

		return $normalized;
	}



	/**
	 * Gets service structure for Zaslat.
	 *
	 * Retrieves array hardcoded in Wrapper.
	 *
	 * @return array service structure.
	 */
	public function get_service_structure() {

		$result = array();

		$api_type = get_option( 'alsendo_connect_settings_general_zaslat_api_type' );
		if ( 'prod' === $api_type ) {
			$api_type_wrapper = ApiClientFactory::ZASLAT;
		} else {
			$api_type_wrapper = ApiClientFactory::ZASLAT_TEST;
		}

		// init Wrapper.
		$api_client = ( new ApiClientFactory() )->create(
			$api_type_wrapper,
			array(
				'api_key' => $this->api_token,
			)
		);

		try {

			$result = $api_client->getServiceStructure();

		} catch ( Exception $e ) {

			if ( function_exists( 'wc_get_logger' ) ) {
				$logger = wc_get_logger();
				$logger->log(
					'debug',
					'API RESPONSE ZASLAT get service structure:',
					array(
						'source'   => 'alsendo-connect-zaslat-service-structure',
						'response' => $e->getMessage(),
					)
				);
			}
		}

		return is_array( $result ) ? $result : array();
	}
}
