<?php
/**
 * Order meta updates for checkout blocks pickup point selection.
 *
 * @package DhlPolska\CheckoutBlocks
 */

namespace DhlPolska\CheckoutBlocks;

use DhlPolska\Point\CheckoutSavePoint;
use DhlPolska\Point\CheckoutChoosePointField;
use DhlPolska\Point\SessionStorage;
use DhlPolskaVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use WC_Order;

class OrderMetaUpdater implements Hookable {

	/**
	 * @var string
	 */
	private string $integration_name;

	/**
	 * @param string $integration_name .
	 */
	public function __construct( string $integration_name ) {
		$this->integration_name = $integration_name;
	}

	/**
	 * @return void
	 */
	public function hooks(): void {
		add_action( 'octolize-pickup-point-blocks-' . $this->integration_name . '-before_update_order_meta', [ $this, 'update_order_meta' ], 10, 2 );
	}

	/**
	 * @param string   $point_id .
	 * @param WC_Order $order    .
	 *
	 * @return void
	 */
	public function update_order_meta( $point_id, $order ): void {
		if ( ! $order instanceof WC_Order ) { /** @phpstan-ignore-line */
			return;
		}

		if ( ! $this->is_service_point_selected() ) {
			$this->clear_point_meta( $order );
			( new SessionStorage() )->clear_parcelshop_data();

			return;
		}

		$point_id = trim( (string) $point_id );
		if ( '' === $point_id ) {
			$this->clear_point_meta( $order );
			( new SessionStorage() )->clear_parcelshop_data();

			return;
		}

		$meta_map = $this->get_meta_map( (string) $point_id );

		foreach ( $meta_map as $meta_key => $value ) {
			if ( '' === $value ) {
				continue;
			}

			$order->update_meta_data( $meta_key, $value );
		}
	}

	/**
	 * @param string $point_id .
	 *
	 * @return array<string, string>
	 */
	private function get_meta_map( string $point_id ): array {
		$session_storage = new SessionStorage();
		$parcelshop_data = $session_storage->get_parcelshop_data();
		$point_id        = trim( $point_id );
		$point_data      = $this->decode_point_data( $point_id );
		$is_json_payload = $this->is_json_payload( $point_id );

		if ( ! empty( $point_data ) ) {
			$parcelshop_data = array_merge( $parcelshop_data, $point_data );
		}

		$fallback_id = $point_data['id'] ?? '';
		if ( '' === $fallback_id && ! $is_json_payload ) {
			$fallback_id = $point_id;
		}

		$parcelshop_data = $this->get_parcelshop_data_with_fallback( $parcelshop_data, $fallback_id );

		return [
			CheckoutSavePoint::ORDER_META_PARCELSHOP_ID   => (string) ( $parcelshop_data['id'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_SAP  => (string) ( $parcelshop_data['sap'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_NAME => (string) ( $parcelshop_data['name'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_ZIP  => (string) ( $parcelshop_data['zip'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_STREET => (string) ( $parcelshop_data['street'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_CITY => (string) ( $parcelshop_data['city'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_STREET_NO => (string) ( $parcelshop_data['streetNo'] ?? '' ),
			CheckoutSavePoint::ORDER_META_PARCELSHOP_HOUSE_NO => (string) ( $parcelshop_data['houseNo'] ?? '' ),
		];
	}

	/**
	 * @param WC_Order $order .
	 *
	 * @return void
	 */
	private function clear_point_meta( WC_Order $order ): void {
		foreach ( $this->get_order_meta_fields_map() as $meta_key ) {
			$order->delete_meta_data( $meta_key );
		}
	}

	/**
	 * @return string[]
	 */
	private function get_order_meta_fields_map(): array {
		return [
			CheckoutSavePoint::ORDER_META_PARCELSHOP_ID,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_SAP,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_NAME,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_ZIP,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_STREET,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_CITY,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_STREET_NO,
			CheckoutSavePoint::ORDER_META_PARCELSHOP_HOUSE_NO,
		];
	}

	/**
	 * @return bool
	 */
	private function is_service_point_selected(): bool {
		$session = WC()->session;

		$chosen_methods      = wp_parse_list( $session->get( 'chosen_shipping_methods', [] ) );
		$service_point_rates = wp_parse_list( $session->get( CheckoutChoosePointField::SESSION_SERVICE_POINT_RATES, [] ) );
		if ( empty( $chosen_methods ) || empty( $service_point_rates ) ) {
			return false;
		}

		foreach ( $chosen_methods as $chosen_method ) {
			if ( in_array( $chosen_method, $service_point_rates, true ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param array<string, string> $parcelshop_data .
	 * @param string                $point_id        .
	 *
	 * @return array<string, string>
	 */
	private function get_parcelshop_data_with_fallback( array $parcelshop_data, string $point_id ): array {
		if ( '' === (string) ( $parcelshop_data['id'] ?? '' ) && '' !== $point_id ) {
			$parcelshop_data['id'] = $point_id;
		}

		if ( '' === (string) ( $parcelshop_data['sap'] ?? '' ) && '' !== (string) ( $parcelshop_data['id'] ?? '' ) ) {
			$parcelshop_data['sap'] = (string) $parcelshop_data['id'];
		}

		return $parcelshop_data;
	}

	/**
	 * @param string $point_id .
	 *
	 * @return array<string, string>
	 */
	private function decode_point_data( string $point_id ): array {
		if ( '' === $point_id ) {
			return [];
		}

		$decoded = json_decode( $point_id, true );
		if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $decoded ) ) {
			return [];
		}

		$data = [
			'id'       => (string) ( $decoded['id'] ?? '' ),
			'sap'      => (string) ( $decoded['sap'] ?? '' ),
			'name'     => (string) ( $decoded['name'] ?? '' ),
			'zip'      => (string) ( $decoded['zip'] ?? ( $decoded['postal_code'] ?? '' ) ),
			'street'   => (string) ( $decoded['street'] ?? '' ),
			'city'     => (string) ( $decoded['city'] ?? '' ),
			'streetNo' => (string) ( $decoded['streetNo'] ?? ( $decoded['street_no'] ?? '' ) ),
			'houseNo'  => (string) ( $decoded['houseNo'] ?? ( $decoded['house_no'] ?? '' ) ),
		];

		return array_filter(
			$data,
			static fn( $value ) => '' !== $value
		);
	}

	/**
	 * @param string $point_id .
	 *
	 * @return bool
	 */
	private function is_json_payload( string $point_id ): bool {
		if ( '' === $point_id ) {
			return false;
		}

		$first_char = $point_id[0];

		return '{' === $first_char || '[' === $first_char;
	}
}
