<?php
/**
 * Class FlexibleShippingIntegration
 *
 * @package DhlPolska\Integration\FlexibleShipping
 */

namespace DhlPolska\Integration\FlexibleShipping;

use DhlPolska\Shipment\DhlServices;
use DhlPolska\Shipment\ShipmentData;
use DhlPolska\ShippingMethod\DhlInstanceSettings;
use DhlPolska\ShippingMethod\LogoVariants;
use DhlPolska\Shipment\ShipmentTypes;
use DhlPolska\WooCommerceSettings\Templates\TemplatesRepository;
use DhlPolska\WooCommerceSettings\WooCommerceDHLPolskaSettings;
use DhlPolskaVendor\WPDesk\PluginBuilder\Plugin\Hookable;

/**
 * Integration with Flexible Shipping.
 */
class FlexibleShippingIntegration implements Hookable {

	public const INTEGRATION_ID = 'dhl_polska';

	private const FIELD_SERVICE       = self::INTEGRATION_ID . '_' . ShipmentData::OPTION_SERVICE;
	private const FIELD_POINT_TYPE    = self::INTEGRATION_ID . '_' . ShipmentData::OPTION_POINT_TYPE;
	private const FIELD_POINT_TYPE_PL = self::INTEGRATION_ID . '_' . ShipmentData::OPTION_POINT_TYPE_PL;
	private const FIELD_TEMPLATE      = self::INTEGRATION_ID . '_' . ShipmentData::OPTION_TEMPLATE;
	private const FIELD_SHOW_LOGO     = self::INTEGRATION_ID . '_show_logo';
	private const FIELD_LOGO_VARIANT  = self::INTEGRATION_ID . '_' . DhlInstanceSettings::OPTION_LOGO_VARIANT;

	/**
	 * @return void
	 */
	public function hooks() {
		add_filter( 'flexible_shipping_integration_options', [ $this, 'add_integration' ] );
		add_filter( 'flexible_shipping_method_settings', [ $this, 'add_integration_fields' ], 10, 2 );
	}

	/**
	 * @param array<string, string> $options .
	 *
	 * @return array<string, string>
	 */
	public function add_integration( array $options ): array {
		$options[ self::INTEGRATION_ID ] = __( 'DHL Polska', 'dhl-polska' );

		return $options;
	}

	/**
	 * @param array<string, array<string, mixed>> $fields                   .
	 * @param array<string, string>               $shipping_method_settings .
	 *
	 * @return array<string, array<string, mixed>>
	 */
	public function add_integration_fields( array $fields, array $shipping_method_settings ): array {
		$shipment_types = new ShipmentTypes();
		$template_field = ( new DhlInstanceSettings(
			new TemplatesRepository( new WooCommerceDHLPolskaSettings(), $shipment_types ),
			$shipment_types
		) )->get_settings_fields()[ DhlInstanceSettings::OPTION_TEMPLATE ];

		$integration_fields = [
			self::FIELD_SHOW_LOGO => [
				'title'   => __( 'Logo DHL', 'dhl-polska' ),
				'type'    => 'checkbox',
				'label'   => __( 'Wyświetl logo DHL pod nazwą metody wysyłki.', 'dhl-polska' ),
				'default' => 'no',
				'class'   => 'js--field-dhl_polska',
			],
			self::FIELD_LOGO_VARIANT => [
				'title'    => __( 'Wariant logo DHL', 'dhl-polska' ),
				'type'     => 'select',
				'options'  => LogoVariants::get_variant_options(),
				'default'  => LogoVariants::get_default_variant(),
				'desc_tip' => __( 'Wybierz wariant logo wyświetlany pod nazwą metody wysyłki.', 'dhl-polska' ),
				'class'    => 'js--field-dhl_polska js--field-dhl_polska-logo-variant',
			],
			self::FIELD_SERVICE => [
				'title'   => __( 'Usługa', 'dhl-polska' ),
				'type'    => 'select',
				'options' => ( new DhlServices() )->get_all_services(),
				'class'   => 'js--field-dhl_polska',
			],
		];

		if ( $this->should_show_point_type_field_for_non_pl() ) {
			$integration_fields[ self::FIELD_POINT_TYPE ] = [
				'title'   => __( 'Typ punktu odbioru (zagranica)', 'dhl-polska' ),
				'type'    => 'select',
				'options' => [
					ShipmentData::POINT_TYPE_PACKSTATION => __( 'DHL BOX', 'dhl-polska' ),
					ShipmentData::POINT_TYPE_PARCELSHOP  => __( 'DHL POP', 'dhl-polska' ),
				],
				'default' => ShipmentData::POINT_TYPE_PARCELSHOP,
				'class'   => 'js--field-dhl_polska js--field-dhl_polska-point-type',
			];
		}

		if ( $this->should_show_point_type_field_for_pl() ) {
			$integration_fields[ self::FIELD_POINT_TYPE_PL ] = [
				'title'   => __( 'Typ punktu odbioru (Polska)', 'dhl-polska' ),
				'type'    => 'select',
				'options' => [
					ShipmentData::POINT_TYPE_PL_ALL => __( 'Wszystkie punkty', 'dhl-polska' ),
					ShipmentData::POINT_TYPE_PL_BOX => __( 'DHL BOX', 'dhl-polska' ),
					ShipmentData::POINT_TYPE_PL_POP => __( 'DHL POP', 'dhl-polska' ),
				],
				'default' => ShipmentData::POINT_TYPE_PL_ALL,
				'class'   => 'js--field-dhl_polska js--field-dhl_polska-point-type-pl',
			];
		}

		$integration_fields[ self::FIELD_TEMPLATE ] = [
			'title'    => $template_field['title'],
			'type'     => $template_field['type'],
			'options'  => $template_field['options'],
			'desc_tip' => $template_field['desc_tip'],
			'class'    => 'js--field-dhl_polska',
		];

		return array_merge( $fields, $integration_fields );
	}

	private function should_show_point_type_field_for_non_pl(): bool {
		$instance_id = $this->get_instance_id_from_request();
		if ( null === $instance_id || 0 === $instance_id ) {
			return true;
		}

		return $this->zone_has_non_pl_countries( $instance_id );
	}

	private function should_show_point_type_field_for_pl(): bool {
		$instance_id = $this->get_instance_id_from_request();
		if ( null === $instance_id || 0 === $instance_id ) {
			return true;
		}

		return $this->zone_has_pl_countries( $instance_id );
	}

	private function get_instance_id_from_request(): ?int {
		$instance_id = filter_input( INPUT_GET, 'instance_id', FILTER_VALIDATE_INT );
		if ( false === $instance_id || null === $instance_id ) {
			return null;
		}

		$instance_id = absint( $instance_id );

		return 0 === $instance_id ? null : $instance_id;
	}

	private function zone_has_non_pl_countries( int $instance_id ): bool {
		$countries = $this->get_countries_for_instance( $instance_id );
		if ( null === $countries ) {
			return true;
		}

		return $this->countries_include_non_pl( $countries );
	}

	private function zone_has_pl_countries( int $instance_id ): bool {
		$countries = $this->get_countries_for_instance( $instance_id );
		if ( null === $countries ) {
			return true;
		}

		return $this->countries_include_pl( $countries );
	}

	/**
	 * @param int $instance_id .
	 *
	 * @return string[]|null
	 */
	private function get_countries_for_instance( int $instance_id ): ?array {
		$zone = $this->get_zone_by_instance_id( $instance_id );
		if ( null === $zone ) {
			return null;
		}

		if ( 0 === (int) $zone->get_id() ) {
			return null;
		}

		$locations = $zone->get_zone_locations();
		if ( empty( $locations ) ) {
			return null;
		}

		if ( $this->locations_include_continent( $locations ) ) {
			return null;
		}

		$countries = $this->get_countries_from_locations( $locations );
		if ( empty( $countries ) ) {
			return null;
		}

		return array_values( $countries );
	}

	private function get_zone_by_instance_id( int $instance_id ): ?\WC_Shipping_Zone {
		if ( ! class_exists( 'WC_Shipping_Zones' ) ) {
			return null;
		}

		$zone = \WC_Shipping_Zones::get_zone_by( 'instance_id', $instance_id );
		if ( ! $zone instanceof \WC_Shipping_Zone ) {
			return null;
		}

		return $zone;
	}

	/**
	 * @param array<int, object> $locations .
	 */
	private function locations_include_continent( array $locations ): bool {
		foreach ( $locations as $location ) {
			if ( isset( $location->type ) && 'continent' === $location->type ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param array<int, object> $locations .
	 *
	 * @return string[]
	 */
	private function get_countries_from_locations( array $locations ): array {
		$countries = [];
		foreach ( $locations as $location ) {
			if ( ! isset( $location->type, $location->code ) ) {
				continue;
			}

			if ( 'country' === $location->type ) {
				$countries[] = (string) $location->code;
				continue;
			}

			if ( 'state' === $location->type ) {
				$parts       = explode( ':', (string) $location->code, 2 );
				$countries[] = (string) $parts[0];
			}
		}

		return array_filter( $countries, static fn ( $country ) => '' !== $country );
	}

	/**
	 * @param string[] $countries .
	 */
	private function countries_include_non_pl( array $countries ): bool {
		foreach ( $countries as $country ) {
			if ( 'PL' !== $country ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param string[] $countries .
	 */
	private function countries_include_pl( array $countries ): bool {
		return in_array( 'PL', $countries, true );
	}
}
