<?php

namespace Simplyin\Simplyin_W_Plugin\Dto\Order;

use WC_Order;
use WC_Order_Item;

class Order_DTO_Factory {
	/**
	 * @param WC_Order_Item $item
	 * @param WC_Order $order
	 *
	 * @return Order_Item_DTO
	 */
	public static function create_order_item(
		WC_Order_Item $item,
		WC_Order $order
	): Order_Item_DTO {
		$simplyin_order_item = new Order_Item_DTO();
		$product_id          = $item->get_product_id();
		$product             = wc_get_product( $product_id );

		$taxes       = $item->get_taxes();
		$quantity    = $item->get_quantity() ?? 1;
		$tax_amount  = self::normalize_price( array_sum( $taxes['total'] ) / $quantity );
		$tax_rate    = self::normalize_price( $tax_amount > 0 ? ( $tax_amount / $item->get_total() ) * 100 : 0 );
		$price_net   = self::normalize_price( $order->get_item_total( $item ) );
		$price_total = self::normalize_price( $price_net + $tax_amount );

		$simplyin_order_item->set_name( $item->get_name() );
		$simplyin_order_item->set_url( get_permalink( $product_id ) );
		$simplyin_order_item->set_price( $price_total );
		$simplyin_order_item->set_price_net( $price_net );
		$simplyin_order_item->set_quantity( $quantity );
		$simplyin_order_item->set_tax_amount( $tax_amount );
		$simplyin_order_item->set_tax_rate( $tax_rate );
		$simplyin_order_item->set_thumbnail_url( self::get_product_thumbnail_url( $item, $product ) );
		$simplyin_order_item->set_currency( $order->get_currency() );

		return $simplyin_order_item;


	}

	public static function build_create_order_body_data_dto(
		WC_Order $order,
		array $payment_method_data,
		string $plugin_version,
		string $woocommerce_version,
		$tax_id = '',
		string $language_code = '',
		string $parcel_machine_id = '',
		string $parcel_provider_name = '',
		string $phone_app_input_field = '',
		string $simply_billing_id = '',
		string $simply_shipping_id = '',
		string $shop_user_email = ''

	): Order_Body_Data_DTO {

		if ( '' === $shop_user_email ) {
			$shop_user_email = $order->get_billing_email() ?? '';
		}

		$items_data_dto = self::create_order_items( $order );
		$order_data_dto = self::create_order_data_dto( $order,
			$items_data_dto,
			$payment_method_data,
			$plugin_version,
			$woocommerce_version,
			$shop_user_email,
			$tax_id,
			$simply_billing_id,
			false
		);

		// Always include shippingData, prefer parcel point address when available
		$shipping_data_dto = self::create_shipping_data_dto( $order, $simply_shipping_id );
		self::override_shipping_from_session_parcel( $shipping_data_dto );
		$order_data_dto->set_shipping_data_dto( $shipping_data_dto );

		if ( $parcel_machine_id !== '' && $parcel_provider_name !== '' ) {
			$parcel_locker_minimal_info_dto = self::create_parcel_locker_minimal_info_dto(
				$parcel_machine_id,
				$parcel_provider_name
			);
			$order_data_dto->set_parcel_locker_minimal_info_DTO( $parcel_locker_minimal_info_dto );
		}

		// Attach full parcel locker data when available (in addition to minimal info and shippingData override)
		//self::attach_parcel_locker_data_from_session( $order_data_dto );

		$order_body_data_dto = new Order_Body_Data_DTO();
		$order_body_data_dto->set_order_data_dto( $order_data_dto );
		$order_body_data_dto->set_shop_user_email( $shop_user_email );

		return $order_body_data_dto;
	}

	public static function build_create_order_without_account_body_data_dto(
		WC_Order $order,
		array $payment_method_data,
		string $plugin_version,
		string $woocommerce_version,
		$tax_id = '',
		string $language_code = '',
		string $parcel_machine_id = '',
		string $parcel_provider_name = '',
		string $phone_app_input_field = '',
		string $simply_billing_id = '',
		string $simply_shipping_id = '',
		string $shop_user_email = ''

	): Order_Without_Account_Body_Data_DTO {

		// If e-mail not provided, prefer address used in the current order. This keeps the
		// shopUserEmail consistent with the user represented by the api_token.
		if ( '' === $shop_user_email ) {
			$shop_user_email = $order->get_billing_email() ?: ( wp_get_current_user()->data->user_email ?? '' );
		}

		$items_data_dto = self::create_order_items( $order );
		$order_data_dto = self::create_order_data_dto( $order,
			$items_data_dto,
			$payment_method_data,
			$plugin_version,
			$woocommerce_version,
			$shop_user_email,
			$tax_id,
			$simply_billing_id,
			false
		);

		// Always include shippingData
		$shipping_data_dto = self::create_shipping_data_dto( $order, $simply_shipping_id );
		self::override_shipping_from_session_parcel( $shipping_data_dto );
		$order_data_dto->set_shipping_data_dto( $shipping_data_dto );

		if ( $parcel_machine_id !== '' && $parcel_provider_name !== '' ) {
			$parcel_locker_minimal_info_dto = self::create_parcel_locker_minimal_info_dto(
				$parcel_machine_id,
				$parcel_provider_name
			);
			$order_data_dto->set_parcel_locker_minimal_info_DTO( $parcel_locker_minimal_info_dto );
		}

		// Attach full parcel locker data when available
		//self::attach_parcel_locker_data_from_session( $order_data_dto );

		$order_without_account_body_data_dto = new Order_Without_Account_Body_Data_DTO();
		$order_without_account_body_data_dto->set_order_data_dto( $order_data_dto );

		return $order_without_account_body_data_dto;
	}

	public static function build_create_order_and_account_body_data_dto(
		WC_Order $order,
		array $payment_method_data,
		string $plugin_version,
		string $woocommerce_version,
		$tax_id = '',
		string $language_code = '',
		string $parcel_machine_id = '',
		string $parcel_provider_name = '',
		string $phone_app_input_field = '',
		string $simply_billing_id = '',
		string $simply_shipping_id = '',
		string $shop_user_email = ''

	): Order_And_Account_Body_Data_DTO {

		// Keep shopUserEmail aligned with e-mail address used for the new account / order.
		if ( '' === $shop_user_email ) {
			$shop_user_email = $order->get_billing_email() ?: ( wp_get_current_user()->data->user_email ?? '' );
		}

		$items_data_dto = self::create_order_items( $order );


		// Use the order number as unique ID
		$unique_id = $order->get_order_number() . '-' . uniqid();

		$new_account_data_dto = self::create_new_account_data_dto(
			trim( $order->get_billing_first_name() ),
			trim( $order->get_billing_last_name() ),
			trim( $phone_app_input_field ),
			trim( $order->get_billing_email() ),
			$unique_id,
			$language_code,
			false );


		$order_data_dto = self::create_order_data_dto( $order,
			$items_data_dto,
			$payment_method_data,
			$plugin_version,
			$woocommerce_version,
			$shop_user_email,
			$tax_id,
			$simply_billing_id,
			true
		);

		// Always include shippingData
		$shipping_data_dto = self::create_shipping_data_dto( $order, $simply_shipping_id );
		self::override_shipping_from_session_parcel( $shipping_data_dto );
		$order_data_dto->set_shipping_data_dto( $shipping_data_dto );

		if ( $parcel_machine_id !== '' && $parcel_provider_name !== '' ) {
			$parcel_locker_minimal_info_dto = self::create_parcel_locker_minimal_info_dto(
				$parcel_machine_id,
				$parcel_provider_name
			);
			$order_data_dto->set_parcel_locker_minimal_info_DTO( $parcel_locker_minimal_info_dto );
		}

		// Attach full parcel locker data when available
		//self::attach_parcel_locker_data_from_session( $order_data_dto );

		$order_and_account_body_data_dto = new Order_And_Account_Body_Data_DTO();
		$order_and_account_body_data_dto->set_new_account_data_dto( $new_account_data_dto );
		$order_and_account_body_data_dto->set_order_data_dto( $order_data_dto );


		return $order_and_account_body_data_dto;
	}

	public static function create_order_data_dto(
		WC_Order $order,
		Order_Items_DTO $items_data_dto,
		$payment_method_data,
		$plugin_version,
		$woocommerce_version,
		string $shop_user_email,
		$taxId = null,
		string $simply_billing_id = '',
		bool $placed_during_account_creation = false,
		?Parcel_Locker_Data_DTO $parcel_locker_data_DTO = null
	): Order_Data_DTO {

		$host_addres = gethostname();
		$host_ip     = gethostbyname( $host_addres );

		$order_data_dto = new Order_Data_DTO();
		$order_data_dto->set_payment_method( $payment_method_data['method'] );
		$order_data_dto->set_payment_method_title( $payment_method_data['title'] );
		$order_data_dto->set_shop_order_number( $order->get_order_number() );
		$order_data_dto->set_price( self::normalize_price( $order->get_total() ) );
		$shipping_gross = (float) $order->get_shipping_total() + (float) $order->get_shipping_tax();
		$order_data_dto->set_shipping_price( self::normalize_price( $shipping_gross ) );
		$order_data_dto->set_currency( $order->get_currency() );
		$order_data_dto->set_items( $items_data_dto );
		$order_data_dto->set_placed_during_account_creation( $placed_during_account_creation );
		$order_data_dto->set_billing_data( self::create_billing_data_dto( $order,
			$taxId,
			$simply_billing_id ) );
		$order_data_dto->set_shop_name( addslashes( sanitize_text_field( get_bloginfo( 'name' ) ) ) );
		$order_data_dto->set_plugin_version( $plugin_version );
		$order_data_dto->set_shop_version( $woocommerce_version );
		$order_data_dto->set_shop_user_email( $shop_user_email );
		$order_data_dto->set_user_ip( $order->get_customer_ip_address() );
		$order_data_dto->set_shop_ip( $host_ip );
		$order_data_dto->set_parcel_locker_data_DTO( $parcel_locker_data_DTO );
		$order_data_dto->set_status_info( self::build_status_info( $order ) );

		$date_modified = $order->get_date_modified() ?? $order->get_date_created();
		$order_data_dto->set_updated_at( self::format_api_datetime( $date_modified ) );

		return $order_data_dto;
	}

	public static function build_status_info( WC_Order $order ): Status_Info_DTO {
		$wc_status   = $order->get_status();
		$date_object = $order->get_date_modified() ?? $order->get_date_created();
		$iso_date    = self::format_api_datetime( $date_object );

		$dto = new Status_Info_DTO();
		$dto->set_order_status( $wc_status );
		$dto->set_custom_order_status( '' );
		$dto->set_shipment_status( '' );
		$dto->set_custom_shipment_status( '' );
		$dto->set_status( self::map_wc_status_to_simplyin( $wc_status ) );
		$dto->set_date( $iso_date );
		$dto->set_city( '' );
		$dto->set_courier_slug( '' );
		$dto->set_location( '' );
		$dto->set_description( '' );

		return $dto;
	}

	public static function create_new_account_data_dto(
		string $name,
		string $surname,
		string $phone_number,
		string $email,
		string $unique_id,
		string $language,
		bool $marketing_consent
	): New_Account_Data_DTO {

		$new_account_data_dto = new New_Account_Data_DTO();
		$new_account_data_dto->set_name( $name );
		$new_account_data_dto->set_surname( $surname );
		$new_account_data_dto->set_phone_number( $phone_number );
		$new_account_data_dto->set_email( $email );
		$new_account_data_dto->set_unique_id( $unique_id );
		$new_account_data_dto->set_language( $language );
		$new_account_data_dto->set_marketing_consent( $marketing_consent );

		return $new_account_data_dto;
	}

	/**
	 * @param WC_Order $order
	 *
	 * @return Order_Items_DTO
	 */
	public static function create_order_items( WC_Order $order
	): Order_Items_DTO {
		$simplyin_order_items = new Order_Items_DTO();
		$items                = $order->get_items();
		foreach ( $items as $wc_order_item ) {
			$simplyin_order_item = self::create_order_item( $wc_order_item,
				$order );
			$simplyin_order_items->push( $simplyin_order_item );
		}

		return $simplyin_order_items;
	}

	public static function create_billing_data_dto(
		WC_Order $order,
		$tax_id = null,
		$simply_billing_id = ''
	): Billing_Data_DTO {
		$billing_data_dto = new Billing_Data_DTO();

		$billing_data_dto->set_name( trim( $order->get_billing_first_name() ) );
		$billing_data_dto->set_surname( trim( $order->get_billing_last_name() ) );
		$billing_data_dto->set_street( trim( $order->get_billing_address_1() ) );
		$billing_data_dto->set_appartment_number( trim( $order->get_billing_address_2() ) );
		$billing_data_dto->set_city( trim( $order->get_billing_city() ) );
		$billing_data_dto->set_postal_code( trim( $order->get_billing_postcode() ) );
		$billing_data_dto->set_country( trim( $order->get_billing_country() ) );
		$billing_data_dto->set_state( trim( $order->get_billing_state() ) );
		$billing_data_dto->set_company_name( trim( $order->get_billing_company() ) );
		$billing_data_dto->set_tax_id( is_string( $tax_id ) ? trim( $tax_id ) : '' );


		if ( ! empty( $simply_billing_id ) ) {
			$billing_data_dto->set_id( $simply_billing_id );
		}

		return $billing_data_dto;
	}

	public static function create_shipping_data_dto(
		WC_Order $order,
		$simply_shipping_id = ''
	): Shipping_Data_DTO {
		$shipping_data_dto = new Shipping_Data_DTO();

		$shipping_data_dto->set_icon( '🏡' );
		$shipping_data_dto->set_address_name( '' );
		$shipping_data_dto->set_name( trim( $order->get_shipping_first_name() ) );
		$shipping_data_dto->set_surname( trim( $order->get_shipping_last_name() ) );
		$shipping_data_dto->set_street( trim( $order->get_shipping_address_1() ) );
		$shipping_data_dto->set_appartment_number( trim( $order->get_shipping_address_2() ) );
		$shipping_data_dto->set_city( trim( $order->get_shipping_city() ) );
		$shipping_data_dto->set_postal_code( trim( $order->get_shipping_postcode() ) );
		$shipping_data_dto->set_country( trim( $order->get_shipping_country() ) );
		$shipping_data_dto->set_state( trim( $order->get_shipping_state() ) );
		$shipping_data_dto->set_company_name( trim( $order->get_shipping_company() ) );

		if ( ! empty( $simply_shipping_id ) ) {
			$shipping_data_dto->set_id( $simply_shipping_id );
		}

		return $shipping_data_dto;
	}

	public static function create_parcel_locker_minimal_info_dto(
		string $parcel_machine_id,
		string $provider_name
	): Parcel_Locker_Minimal_Info_DTO {
		$parcel_locker_minimal_info_DTO = new Parcel_Locker_Minimal_Info_DTO();
		$parcel_locker_minimal_info_DTO->set_locker_id( $parcel_machine_id );
		$parcel_locker_minimal_info_DTO->set_provider_name( $provider_name );

		return $parcel_locker_minimal_info_DTO;
	}

	public static function create_parcel_locker_data_dto(
		string $label,
		string $locker_id,
		string $address,
		?string $id = null,
		?string $provider_id = null,
		?string $service_type = null,
		?string $logo_url = null,
		?string $icon = null,
		?string $address_name = null,
		?string $coordinates_lat = null,
		?string $coordinates_long = null,
		?string $desc = null
	): Parcel_Locker_Data_DTO {
		$parcel_locker_data_DTO = new Parcel_Locker_Data_DTO();
		$parcel_locker_data_DTO->set_label( $label );
		$parcel_locker_data_DTO->set_locker_id( $locker_id );
		$parcel_locker_data_DTO->set_address( $address );
		$parcel_locker_data_DTO->set_id( $id );
		$parcel_locker_data_DTO->set_provider_id( $provider_id );
		$parcel_locker_data_DTO->set_service_type( $service_type );
		$parcel_locker_data_DTO->set_logo_url( $logo_url );
		$parcel_locker_data_DTO->set_icon( $icon );
		$parcel_locker_data_DTO->set_address_name( $address_name );
		$parcel_locker_data_DTO->set_coordinates_lat( $coordinates_lat );
		$parcel_locker_data_DTO->set_coordinates_long( $coordinates_long );
		$parcel_locker_data_DTO->set_desc( $desc );

		return $parcel_locker_data_DTO;
	}

	public static function normalize_price( $price ): float {
		$optionPrice = wc_format_decimal( $price,
			2 );

		return abs( ( floatval( $optionPrice ) ) );
	}

	private static function override_shipping_from_session_parcel( Shipping_Data_DTO $shipping_data_dto ): void {
		if ( ! function_exists( 'simplyin' ) ) {
			return;
		}
		$session     = simplyin()->get_simplyin_session();
		$parcel_full = $session->get( 'simplyin_parcel_locker_full', null );
		if ( ! is_array( $parcel_full ) || empty( $parcel_full['address'] ) ) {
			return;
		}
		$address_str = (string) $parcel_full['address'];
		$street      = $address_str;
		$city        = '';
		if ( strpos( $address_str, ',' ) !== false ) {
			$parts  = array_map( 'trim', explode( ',', $address_str ) );
			$city   = (string) array_pop( $parts );
			$street = trim( implode( ', ', $parts ) );
		}
		$address_name = (string) ( $parcel_full['addressName'] ?? ( $parcel_full['label'] ?? '' ) );
		$shipping_data_dto->set_address_name( $address_name );
		if ( $street !== '' ) {
			$shipping_data_dto->set_street( $street );
		}
		if ( $city !== '' ) {
			$shipping_data_dto->set_city( $city );
		}
	}

	private static function attach_parcel_locker_data_from_session( Order_Data_DTO $order_data_dto ): void {

		return;

		if ( ! function_exists( 'simplyin' ) ) {
			return;
		}
		$session     = simplyin()->get_simplyin_session();
		$parcel_full = $session->get( 'simplyin_parcel_locker_full', null );
		if ( ! is_array( $parcel_full ) || empty( $parcel_full['lockerId'] ) ) {
			return;
		}
		$dto = self::create_parcel_locker_data_dto(
			(string) ( $parcel_full['label'] ?? ( $parcel_full['providerName'] ?? '' ) ),
			(string) ( $parcel_full['lockerId'] ?? '' ),
			(string) ( $parcel_full['address'] ?? '' ),
			isset( $parcel_full['_id'] ) ? (string) $parcel_full['_id'] : null,
			isset( $parcel_full['providerId'] ) ? (string) $parcel_full['providerId'] : null,
			isset( $parcel_full['service_type'] ) ? (string) $parcel_full['service_type'] : null,
			isset( $parcel_full['logoUrl'] ) ? (string) $parcel_full['logoUrl'] : null,
			isset( $parcel_full['icon'] ) ? (string) $parcel_full['icon'] : null,
			isset( $parcel_full['addressName'] ) ? (string) $parcel_full['addressName'] : null,
			isset( $parcel_full['coordinates']['latitude'] ) ? (string) $parcel_full['coordinates']['latitude'] : null,
			isset( $parcel_full['coordinates']['longitude'] ) ? (string) $parcel_full['coordinates']['longitude'] : null,
			isset( $parcel_full['desc'] ) ? (string) $parcel_full['desc'] : null
		);
		$order_data_dto->set_parcel_locker_data_DTO( $dto );
	}

	private static function map_wc_status_to_simplyin( string $wc_status ): string {
		$map = [
			'pending'        => 'new',
			'on-hold'        => 'new',
			'processing'     => 'new',
			'completed'      => 'Delivered',
			'refunded'       => 'Exception',
			'cancelled'      => 'Exception',
			'failed'         => 'Exception',
			'checkout-draft' => 'new',
		];

		return $map[ $wc_status ] ?? 'new';
	}

	private static function format_api_datetime( ?\WC_DateTime $date ): ?string {
		if ( ! $date ) {
			return null;
		}

		return gmdate( 'Y-m-d\TH:i:s\Z', $date->getTimestamp() );
	}

	private static function get_product_thumbnail_url( \WC_Order_Item_Product $item, \WC_Product $product ): string {
		$variation_id = $item->get_variation_id();

		if ( $variation_id > 0 ) {
			$variation = wc_get_product( $variation_id );
			if ( $variation ) {
				$image_id = $variation->get_image_id();
				if ( $image_id ) {
					$thumbnail_url = wp_get_attachment_image_url( $image_id, 'thumbnail' );
					if ( $thumbnail_url ) {
						return $thumbnail_url;
					}
				}
			}
		}

		return wp_get_attachment_image_url( $product->get_image_id(), 'thumbnail' ) ?: "";
	}
}
