<?php
/**
 * Postnummer field for checkout blocks.
 *
 * @package DhlPolska\CheckoutBlocks
 */

namespace DhlPolska\CheckoutBlocks;

use Automattic\WooCommerce\StoreApi\Schemas\V1\CheckoutSchema;
use DhlPolska\Point\CheckoutSavePoint;
use DhlPolska\Point\SessionStorage;
use DhlPolska\Shipment\DhlServices;
use DhlPolska\Shipment\ShipmentData;
use DhlPolskaVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use WP_Error;
use WP_REST_Request;

class PostnummerField implements Hookable {
	public const EXTENSION_NAMESPACE      = 'dhl-polska-postnummer';
	public const EXTENSION_POSTNUMMER_KEY = 'postnummer';

	/**
	 * Hooks.
	 */
	public function hooks(): void {
		add_action( 'woocommerce_blocks_loaded', [ $this, 'register_store_api_data' ] );
		add_action( 'woocommerce_store_api_checkout_update_order_from_request', [ $this, 'update_order_meta_from_request' ], 10, 2 );
		add_action( 'woocommerce_checkout_validate_order_before_payment', [ $this, 'validate_order_before_payment' ], 10, 2 );
	}

	/**
	 * @return void
	 */
	public function register_store_api_data(): void {
		if ( ! function_exists( 'woocommerce_store_api_register_endpoint_data' ) ) {
			return;
		}

		woocommerce_store_api_register_endpoint_data(
			[
				'endpoint'        => CheckoutSchema::IDENTIFIER,
				'namespace'       => self::EXTENSION_NAMESPACE,
				'schema_callback' => [ $this, 'schema_callback' ],
				'data_callback'   => [ $this, 'data_callback' ],
				'schema_type'     => ARRAY_A,
			]
		);
	}

	/**
	 * @return array<string, array<string, mixed>>
	 */
	public function schema_callback(): array {
		return [
			self::EXTENSION_POSTNUMMER_KEY => [
				'description' => __( 'Postnummer dla Packstation w Niemczech.', 'dhl-polska' ),
				'type'        => [ 'string' ],
				'context'     => [ 'view', 'edit' ],
				'readonly'    => false,
				'optional'    => true,
			],
		];
	}

	/**
	 * @return array<string, string>
	 */
	public function data_callback(): array {
		$session_storage = new SessionStorage();
		$parcelshop_data = $session_storage->get_parcelshop_data();

		return [
			self::EXTENSION_POSTNUMMER_KEY => (string) ( $parcelshop_data['postnummer'] ?? '' ),
		];
	}

	/**
	 * @param \WC_Order        $order   .
	 * @param WP_REST_Request  $request .
	 *
	 * @return void
	 */
	public function update_order_meta_from_request( \WC_Order $order, WP_REST_Request $request ): void {
		$extensions = $request->get_param( 'extensions' );
		if ( ! is_array( $extensions ) ) {
			$extensions = [];
		}

		$integration_data = $extensions[ self::EXTENSION_NAMESPACE ] ?? [];
		if ( ! is_array( $integration_data ) ) {
			$integration_data = [];
		}

		$postnummer = trim( (string) ( $integration_data[ self::EXTENSION_POSTNUMMER_KEY ] ?? '' ) );
		$postnummer = sanitize_text_field( $postnummer );

		$order->update_meta_data( CheckoutSavePoint::ORDER_META_PARCELSHOP_POSTNUMMER, $postnummer );

		if ( '' !== $postnummer ) {
			( new SessionStorage() )->set_parcelshop_data_from_fields(
				[
					ShipmentData::OPTION_PARCELSHOP_POSTNUMMER => $postnummer,
				]
			);
		}
	}

	/**
	 * @param \WC_Order $order  .
	 * @param WP_Error  $errors .
	 *
	 * @return void
	 */
	public function validate_order_before_payment( \WC_Order $order, WP_Error $errors ): void {
		if ( ! $this->is_postnummer_required_for_order( $order ) ) {
			return;
		}

		$postnummer = trim( (string) $order->get_meta( CheckoutSavePoint::ORDER_META_PARCELSHOP_POSTNUMMER ) );
		if ( '' !== $postnummer ) {
			return;
		}

		$errors->add(
			'dhl-polska-postnummer-required',
			__( 'Postnummer jest wymagany dla odbioru w automacie paczkowym (Packstation) w Niemczech.', 'dhl-polska' )
		);
	}

	/**
	 * @param \WC_Order $order .
	 *
	 * @return bool
	 */
	private function is_postnummer_required_for_order( \WC_Order $order ): bool {
		$shipping_country = strtoupper( (string) $order->get_shipping_country() );
		if ( '' === $shipping_country ) {
			$shipping_country = strtoupper( (string) $order->get_billing_country() );
		}

		if ( 'DE' !== $shipping_country ) {
			return false;
		}

		foreach ( $order->get_shipping_methods() as $shipping_method ) {
			if ( $this->is_packstation_shipping_method( $shipping_method ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param \WC_Order_Item_Shipping $shipping_method .
	 *
	 * @return bool
	 */
	private function is_packstation_shipping_method( $shipping_method ): bool {
		if ( ! $shipping_method instanceof \WC_Order_Item_Shipping ) { /** @phpstan-ignore-line */
			return false;
		}

		$fs_method = $shipping_method->get_meta( '_fs_method' );
		if ( ! is_array( $fs_method ) ) {
			return false;
		}

		$service = (string) ( $fs_method[ ShipmentData::OPTION_SERVICE ] ?? $fs_method['dhl_polska_service'] ?? '' );
		if ( DhlServices::SERVICE_POINT !== $service ) {
			return false;
		}

		$point_type = (string) ( $fs_method[ ShipmentData::OPTION_POINT_TYPE ] ?? $fs_method['dhl_polska_point_type'] ?? '' );
		$point_type = ShipmentData::normalize_point_type( $point_type );

		return ShipmentData::POINT_TYPE_PACKSTATION === $point_type;
	}
}
