<?php
/**
 * Class PrepareShipmentFullData
 *
 * @package DhlPolska\Shipment
 */

namespace DhlPolska\Shipment;

use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\AddressData;
use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\PaymentData;
use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\PieceDefinition;
use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\ReceiverAddressData;
use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\ServiceDefinition;
use DhlPolskaVendor\Octolize\DhlPolska\API\WebApi\Struct\ShipmentFullData;
use DhlPolska\Shipment\DhlServices;
use DhlPolska\Shipment\WorkingDayDateCalculator;
use DhlPolska\WooCommerceSettings\WooCommerceDHLPolskaSettings;
use WC_Order;

/**
 * Prepares shipment data for DHL API.
 */
class PrepareShipmentFullData {
	use UnitsConversionTrait;

	/**
	 * @var ShipmentData
	 */
	private ShipmentData $shipment_data;

	/**
	 * @var WC_Order
	 */
	private WC_Order $order;

	/**
	 * @var WooCommerceDHLPolskaSettings
	 */
	private WooCommerceDHLPolskaSettings $settings;

	/**
	 * PrepareShipmentFullData constructor.
	 *
	 * @param ShipmentData                 $shipment_data .
	 * @param WC_Order                     $order .
	 * @param WooCommerceDHLPolskaSettings $settings .
	 */
	public function __construct( ShipmentData $shipment_data, WC_Order $order, WooCommerceDHLPolskaSettings $settings ) {
		$this->shipment_data = $shipment_data;
		$this->order         = $order;
		$this->settings      = $settings;
	}

	/**
	 * @return ShipmentFullData
	 */
	public function get_prepared_data(): ShipmentFullData {
		return new ShipmentFullData(
			false,
			$this->get_shipper_data(),
			$this->get_receiver_data(),
			$this->get_billing_data(),
			$this->get_service_data(),
			$this->get_shipment_date(),
			$this->shipment_data->get_content(),
			$this->get_pieces_data(),
			$this->shipment_data->get_notes(),
			$this->shipment_data->get_references()
		);
	}

	private function get_shipment_date(): string {
		$shipment_date = trim( $this->shipment_data->get_shipment_date() );
		if ( '' !== $shipment_date ) {
			return $shipment_date;
		}

		$calculator             = new WorkingDayDateCalculator();
		$shipment_date_fallback = new ShipmentDateFallback();
		$cutoff                 = $this->settings->get_shipment_date_cutoff_time();
		$date                   = $calculator->get_date_after_cutoff( $cutoff );

		if ( null !== $date ) {
			return $date;
		}

		return $shipment_date_fallback->tomorrow();
	}

	/**
	 * @return AddressData
	 */
	private function get_shipper_data(): AddressData {
		$postal_code_normalizer = new PostalCodeNormalizer();

		return new AddressData(
			(string) $this->settings->get_sender_name(),
			(string) $this->settings->get_sender_street(),
			(string) $this->settings->get_sender_house_number(),
			(string) $this->settings->get_sender_city(),
			$postal_code_normalizer->normalize( (string) $this->settings->get_sender_postcode(), 'PL' ),
			null,
			(string) $this->settings->get_sender_name(),
			(string) $this->settings->get_sender_phone(),
			(string) $this->settings->get_sender_email()
		);
	}

	/**
	 * @return ReceiverAddressData
	 */
	private function get_receiver_data(): ReceiverAddressData {
		$postal_code_normalizer = new PostalCodeNormalizer();
		$shipping_country       = (string) $this->order->get_shipping_country();

		$name = $this->order->get_shipping_first_name() . ' ' . $this->order->get_shipping_last_name();
		if ( $this->order->get_shipping_company() ) {
			$name = $this->order->get_shipping_company();
		}

		$full_address = trim( $this->order->get_shipping_address_1() . ' ' . $this->order->get_shipping_address_2() );
		$parts        = preg_split( '/\s+/', $full_address );

		$house_number = array_pop( $parts );
		$street       = implode( ' ', $parts );

		if ( empty( $street ) ) {
			$street       = $house_number;
			$house_number = ' ';
		}

		return new ReceiverAddressData(
			$name,
			$street,
			$house_number,
			(string) $this->order->get_shipping_city(),
			$postal_code_normalizer->normalize( (string) $this->order->get_shipping_postcode(), $shipping_country ),
			$shipping_country,
			$this->order->get_shipping_company() ? 'B' : 'C',
			null,
			$this->order->get_shipping_first_name() . ' ' . $this->order->get_shipping_last_name(),
			(string) $this->order->get_billing_phone(),
			(string) $this->order->get_billing_email()
		);
	}

	/**
	 * @return PaymentData
	 */
	private function get_billing_data(): PaymentData {
		return new PaymentData(
			'BANK_TRANSFER',
			'SHIPPER',
			(string) $this->settings->get_webapi_sap_number()
		);
	}

	/**
	 * @return ServiceDefinition
	 */
	private function get_service_data(): ServiceDefinition {
		$selected_service = $this->shipment_data->get_service();

		$product      = $selected_service;
		$self_collect = null;

		if ( $selected_service === DhlServices::SERVICE_POINT ) {
			$product      = DhlServices::SERVICE_AH;
			$self_collect = true;
		}

		$delivery_evening = null;
		if ( $selected_service === DhlServices::SERVICE_DW ) {
			$delivery_evening = true;
		}

		$collect_on_delivery       = null;
		$collect_on_delivery_value = null;
		$collect_on_delivery_form  = null;
		$insurance                 = null;
		$insurance_value           = null;

		if ( $this->shipment_data->get_cod_status() ) {
			$collect_on_delivery       = true;
			$collect_on_delivery_value = $this->shipment_data->get_cod_amount();
			$collect_on_delivery_form  = 'BANK_TRANSFER';
		}

		if ( $this->shipment_data->get_insurance_status() ) {
			$insurance       = true;
			$insurance_value = $this->shipment_data->get_insurance_amount();
		}

		return new ServiceDefinition(
			(string) $product,
			$delivery_evening,
			null,
			null,
			$collect_on_delivery,
			$collect_on_delivery_value,
			$collect_on_delivery_form,
			null,
			$insurance,
			$insurance_value,
			null,
			null,
			null,
			$self_collect
		);
	}

	/**
	 * @return PieceDefinition[]
	 */
	private function get_pieces_data(): array {
		$pieces = [];

		foreach ( $this->shipment_data->get_packages() as $package ) {
			$weight = $this->get_weight_in_kg( (float) ( $package['weight'] ?? 0 ) );
			$width  = $this->get_dimension_in_cm( (float) ( $package['width'] ?? 0 ) );
			$height = $this->get_dimension_in_cm( (float) ( $package['height'] ?? 0 ) );
			$length = $this->get_dimension_in_cm( (float) ( $package['length'] ?? 0 ) );

			$pieces[] = new PieceDefinition(
				$package['type'] ?? 'PACKAGE',
				(int) ( $package['quantity'] ?? 1 ),
				$weight,
				$width,
				$height,
				$length
			);
		}

		return $pieces;
	}
}
