<?php

use FS\InPost\PickupPoints\PickupPointDetails;
use FS\InPost\PickupPoints\PickupPointsManager;

/**
 * Class WPDesk_Paczkomaty_Checkout
 */
class WPDesk_Paczkomaty_Checkout {

	const ACTION_AFTER_SHIPPING = 'woocommerce_review_order_after_shipping';

	const SHIPPING_METOD_SERVICE = 'paczkomaty_usluga';

	const FIELD_PACZKOMAT_ID                      = 'paczkomat_id';
	const SUBSCRIPTIONS_RECURRING_TOTALS_SHIPPING = 'woocommerce_subscriptions_recurring_totals_shipping';
	const PRIORITY_FIRST                          = 1;

	/**
	 * Plugin.
	 *
	 * @var WPDesk_Paczkomaty_Plugin
	 */
	private $plugin;


	/**
	 * @var array
	 */
	public $machines = [];

	/**
	 * @var array
	 */
	public $machinesInfo = [];

	/**
	 * @var array
	 */
	public $allMachines = [];

	/**
	 * @var array
	 */
	public $adminMachines = [];

	/**
	 * @var PickupPointsManager
	 */
	private $pickup_points_manager;

	/**
	 * WPDesk_Paczkomaty_Checkout constructor.
	 *
	 * @param WPDesk_Paczkomaty_Plugin $plugin                .
	 * @param PickupPointsManager      $pickup_points_manager .
	 */
	public function __construct( WPDesk_Paczkomaty_Plugin $plugin, PickupPointsManager $pickup_points_manager ) {
		$this->plugin                = $plugin;
		$this->pickup_points_manager = $pickup_points_manager;
	}

	/**
	 *
	 */
	public function hooks() {
		add_action( 'woocommerce_after_checkout_validation', [
			$this,
			'validate_selected_point_on_paczkomaty_shipping_method',
		], self::PRIORITY_FIRST );
		add_action( 'woocommerce_checkout_process', [ $this, 'save_paczkomat_id_in_session_on_checkout' ] );
		add_action( 'woocommerce_checkout_update_order_review', [
			$this,
			'woocommerce_checkout_update_order_review',
		] );

		add_action( self::ACTION_AFTER_SHIPPING, [ $this, 'display_choose_machine_field_after_shipping' ] );

		add_action( self::SUBSCRIPTIONS_RECURRING_TOTALS_SHIPPING, [ $this, 'display_choose_machine_field_subscription_after_shipping' ] );

		add_filter( 'paczkomaty_shipping_method_admin_machines', [
			$this,
			'paczkomaty_shipping_method_admin_machines',
		], 10, 2 );
		add_filter( 'paczkomaty_paczkomaty_options_admin', [ $this, 'paczkomaty_paczkomaty_options_admin' ] );
	}

	/**
	 * @param array $paczkomaty_options
	 *
	 * @return array
	 */
	public function paczkomaty_paczkomaty_options_admin( $paczkomaty_options ) {
		$this->init_machine_list();
		$paczkomaty_options = $this->adminMachines;

		return $paczkomaty_options;
	}


	/**
	 * Validate selected point and how notice on failure.
	 *
	 * @throws Exception .
	 * @throws WPDesk_Paczkomaty_ShipX_Exception .
	 */
	public function validate_selected_point_on_paczkomaty_shipping_method() {
		if ( WC()->cart->needs_shipping() ) {
			$shipping_methods = $this->flexible_shipping_methods_selected_in_cart( WPDesk_Paczkomaty_FS::SHIPPING_METHOD_INTEGRATION );
			foreach ( $shipping_methods as $shipping_method ) {
				if ( isset( $shipping_method[ self::SHIPPING_METOD_SERVICE ] )
				     && WC_Paczkomaty_Shipping_Method::USLUGA_PACZKOMATY === $shipping_method[ self::SHIPPING_METOD_SERVICE ]
				) {
					if ( empty( $_POST[ self::FIELD_PACZKOMAT_ID ] ) ) { // phpcs:ignore
						if ( ! wc_has_notice( __( 'Proszę wybrać paczkomat', 'woocommerce-paczkomaty-inpost' ), 'error' ) ) {
							wc_add_notice( __( 'Proszę wybrać paczkomat', 'woocommerce-paczkomaty-inpost' ), 'error' );
						}
					} else {
						$paczkomat_id = $_POST[ self::FIELD_PACZKOMAT_ID ]; // phpcs:ignore
						$this->validate_point_and_add_notice( sanitize_text_field( $paczkomat_id ), $shipping_method );
					}
				}
			}
		}
	}

	/**
	 * Returns FS methods selected in cart.
	 *
	 * @param string $shipping_method_integration .
	 *
	 * @return array
	 */
	private function flexible_shipping_methods_selected_in_cart( $shipping_method_integration ) {
		$shipping_methods = [];
		$chosen_shipping_methods = $this->get_filtered_chosen_shipping_methods();
		if ( is_array( $chosen_shipping_methods ) ) {
			$all_shipping_methods    = flexible_shipping_get_all_shipping_methods();
			$flexible_shipping       = $all_shipping_methods['flexible_shipping'];
			$flexible_shipping_rates = $flexible_shipping->get_all_rates();
			foreach ( $chosen_shipping_methods as $id => $shipping ) {
				if ( isset( $flexible_shipping_rates[ $shipping ] ) ) {
					$shipping_method = $flexible_shipping_rates[ $shipping ];
					if ( $shipping_method['method_integration'] === $shipping_method_integration ) {
						$shipping_methods[] = $shipping_method;
					}
				}
			}
		}

		return $shipping_methods;
	}

	/**
	 * Saves paczkomat id in session on checkout process.
	 */
	public function save_paczkomat_id_in_session_on_checkout() {
		if ( isset( $_POST[ self::FIELD_PACZKOMAT_ID ] ) ) { // phpcs:ignore
			WC()->session->set( self::FIELD_PACZKOMAT_ID, sanitize_text_field( $_POST[ self::FIELD_PACZKOMAT_ID ] ) ); // phpcs:ignore
		}
	}

	/**
	 * .
	 * @param string $parcel_machine_id .
	 * @param array  $shipping_method   .
	 *
	 * @throws Exception .
	 * @throws WPDesk_Paczkomaty_ShipX_Exception .
	 */
	private function validate_point_and_add_notice( $parcel_machine_id, array $shipping_method ) {
		try {
			$point = $this->pickup_points_manager->get_point_details( $parcel_machine_id );
			$this->validate_point_cod_if_enabled_and_add_notice( $point, $shipping_method );
			$this->validate_point_end_of_week_collection_if_enabled_and_add_notice( $point, $shipping_method );
		} catch ( Exception $e ) {
			wc_add_notice( $e->getMessage(), 'error' );
		}
	}

	/**
	 * Validate point cod if enabled.
	 *
	 * @param PickupPointDetails $point           .
	 * @param array              $shipping_method .
	 */
	private function validate_point_cod_if_enabled_and_add_notice( $point, $shipping_method ) {
		if ( '1' === $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_COD ] ) {
			if ( ! $point->is_payment_available() ) {
				wc_add_notice( __( 'Wybrany punkt nie obsługuje płatności.', 'woocommerce-paczkomaty-inpost' ), 'error' );
			}
		}
	}

	/**
	 * Validate end of week collection if enabled.
	 * End of week collection is available only on parcel machines.
	 *
	 * @param PickupPointDetails $point           .
	 * @param array              $shipping_method .
	 */
	private function validate_point_end_of_week_collection_if_enabled_and_add_notice( $point, $shipping_method ) {
		if ( isset( $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_END_OF_WEEK_COLLECTION ] )
		     && '1' === $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_END_OF_WEEK_COLLECTION ]
		) {
			if ( ! in_array( 'parcel_locker', $point->get_type(), true ) ) {
				wc_add_notice( __( 'Paczka w weekend dostępna jest wyłącznie dla przesyłek do paczkomatów.', 'woocommerce-paczkomaty-inpost' ), 'error' );
			}
		}
	}

	/**
	 * @param $post_data
	 */
	public function woocommerce_checkout_update_order_review( $post_data ) {
		parse_str( $post_data, $data );
		if ( isset( $data[ self::FIELD_PACZKOMAT_ID ] ) ) {
			WC()->session->set( self::FIELD_PACZKOMAT_ID, $data[ self::FIELD_PACZKOMAT_ID ] );
		}
	}

	/**
	 * @return array
	 */
	public function is_paczkomaty_and_cod_and_eowc_in_selected_shipping_methods() {
		$paczkomaty                = false;
		$cod                       = false;
		$end_of_weekend_collection = false;

		$shippings = $this->get_filtered_chosen_shipping_methods();

		$all_shipping_methods = WC()->shipping()->get_shipping_methods();
		if ( empty( $all_shipping_methods ) ) {
			$all_shipping_methods = WC()->shipping()->load_shipping_methods();
		}
		/** @var WPDesk_Flexible_Shipping $flexible_shipping */
		$flexible_shipping       = $all_shipping_methods['flexible_shipping'];
		$flexible_shipping_rates = $flexible_shipping->get_all_rates();

		$shipping_methods = [];

		foreach ( $shippings as $shipping_method_id ) {
			if ( isset( $flexible_shipping_rates[ $shipping_method_id ] ) ) {
				$shipping_method = $flexible_shipping_rates[ $shipping_method_id ];
				if ( isset( $shipping_method['method_integration'] ) && $shipping_method['method_integration'] == WPDesk_Paczkomaty_FS::SHIPPING_METHOD_INTEGRATION ) {
					$shipping_methods[] = $shipping_method;
				}
			}
		}

		foreach ( $shipping_methods as $shipping_method ) {
			if ( isset( $shipping_method[ self::SHIPPING_METOD_SERVICE ] ) && $shipping_method[ self::SHIPPING_METOD_SERVICE ] == WC_Paczkomaty_Shipping_Method::USLUGA_PACZKOMATY ) {
				$paczkomaty = true;
				if ( isset( $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_COD ] ) && $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_COD ] == '1' ) {
					$cod = true;
				}
				if ( isset( $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_END_OF_WEEK_COLLECTION ] ) && $shipping_method[ WPDesk_Paczkomaty_FS::SETTING_END_OF_WEEK_COLLECTION ] == '1' ) {
					$end_of_weekend_collection = true;
				}
			}
		}

		return [
			'paczkomaty'             => $paczkomaty,
			'cod'                    => $cod,
			'end_of_week_collection' => $end_of_weekend_collection,
		];
	}

	public function display_choose_machine_field_after_shipping() {
		if ( is_cart() || ! WC()->cart->needs_shipping() || ! WC()->cart->show_shipping() ) {
			return;
		}

		// If has any subscription product then disable displaying field for woocommerce_review_order_after_shipping action.
		if ( class_exists( 'WC_Subscriptions_Cart' ) && WC_Subscriptions_Cart::cart_contains_subscriptions_needing_shipping() && current_action() === self::ACTION_AFTER_SHIPPING ) {
			return;
		}

		$this->render_choose_machine_field();
	}

	/**
	 * @param array<string, WC_Cart> $recurring_carts
	 *
	 * @return void
	 */
	public function display_choose_machine_field_subscription_after_shipping( $recurring_carts ) {
		$should_display = false;
		foreach ( $recurring_carts as $cart ) {
			if ( is_checkout() && $cart->needs_shipping() && $cart->show_shipping() ) {
				$should_display = true;
			}
		}
		if ( $should_display ) {
			$this->render_choose_machine_field();
		}
	}

	private function render_choose_machine_field() {
		$args = [];

		$paczkomaty_and_cod_and_eowc = $this->is_paczkomaty_and_cod_and_eowc_in_selected_shipping_methods();

		if ( $paczkomaty_and_cod_and_eowc['paczkomaty'] ) {

			$settings            = $this->plugin->get_shipping_method_settings();
			$args['select_type'] = isset( $settings['select_type'] ) ? $settings['select_type'] : 'select2';

			$this->init_machine_list( $paczkomaty_and_cod_and_eowc['cod'], $paczkomaty_and_cod_and_eowc['end_of_week_collection'] );

			//create two arrays
			$machniesNearly = [];

			$args['nearly']  = $machniesNearly;
			$args['other']   = $this->allMachines;
			$args['select2'] = apply_filters( 'woocommerce_paczkomaty_inpost_checkout_select2', true );
			if ( ! $args['select2'] ) {
				$args['select_type'] = 'select';
			}

			$args['selected'] = WC()->session->get( self::FIELD_PACZKOMAT_ID, '' );

			$args['cod'] = 'FALSE';
			if ( $paczkomaty_and_cod_and_eowc['cod'] ) {
				$args['cod'] = 'TRUE';
			}

			$args['end_of_week_collection'] = 'FALSE';
			if ( $paczkomaty_and_cod_and_eowc['end_of_week_collection'] ) {
				$args['end_of_week_collection'] = 'TRUE';
			}
			echo $this->plugin->load_template( 'shipping-method-after', '', $args );
		}
	}

	/**
	 * @param bool $cod
	 */
	public function init_machine_list( $cod = false, $end_of_week_collection = false ) {
		$paczkomaty_shipping_method = $this->plugin->paczkomaty->get_shipping_method();
		if ( $paczkomaty_shipping_method->get_parcel_lockers_api() === $paczkomaty_shipping_method::API_TYPE_XML ) {
			if ( empty( $this->allMachines ) ) {
				$machines          = inpost_get_machine_list();
				$this->machines    = [ '' => '-- ' . __( 'wybierz', 'woocommerce-paczkomaty-inpost' ) . ' --' ];
				$this->allMachines = [];
				foreach ( $machines as $machine ) {
					if ( $cod === true && $machine['paymentavailable'] != 1 ) {
						continue;
					}
					$this->machines[ $machine['name'] ]      = $machine['town'] . ", " . $machine['street'] . " " . $machine['buildingnumber'];
					$this->adminMachines[ $machine['name'] ] = "[" . $machine['name'] . "] " . $machine['town'] . ", " . $machine['street'] . " " . $machine['buildingnumber'];
					$this->allMachines[ $machine['name'] ]   = $machine['town'] . ", " . $machine['street'] . " " . $machine['buildingnumber'];
					$this->machinesInfo[ $machine['name'] ]  = $machine;
				}
			}
		} else {
			$points_type = WPDesk_Paczkomaty_ShipX_Cache::PARCEL_LOCKER_AND_POP;
			if ( $cod || $end_of_week_collection ) {
				$points_type = WPDesk_Paczkomaty_ShipX_Cache::PARCEL_LOCKER_ONLY;
			}
			$this->machines      = $this->pickup_points_manager->get_points( $points_type );
			$this->allMachines   = $this->machines;
			$this->adminMachines = $this->machines;
		}
	}

	/**
	 * Returns list of parcel lockers.
	 *
	 * @param array $admin_machines   List of saved options (keys are values).
	 * @param bool  $show_all_options Whether to return all options or only selected.
	 *
	 * @return array Updated list of options.
	 */
	public function paczkomaty_shipping_method_admin_machines( $admin_machines, $show_all_options = true ) {
		$default_values  = [ '' => __( '-- wybierz --', 'woocommerce-paczkomaty-inpost' ) ];
		$selected_values = $admin_machines;
		try {
			$this->init_machine_list();
			if ( count( $this->adminMachines ) ) {
				foreach ( $admin_machines as $id => $name ) {
					if ( ! isset( $this->adminMachines[ $id ] ) ) {
						$this->adminMachines[ $id ] = $name;
					} else {
						$selected_values[ $id ] = $this->adminMachines[ $id ];
					}
				}
				if ( $show_all_options ) {
					return $default_values + $this->adminMachines;
				} else {
					return $default_values + $selected_values;
				}
			} else {
				return $default_values + $admin_machines;
			}
		} catch ( Exception $e ) {
			return $default_values + $admin_machines;
		}
	}

	/**
	 * @return string[]
	 */
	private function get_filtered_chosen_shipping_methods(): array {
		$chosen_shipping_methods = $this->get_chosen_shipping_methods();
		$cart = WC()->cart;
		$packages_ids = array_keys( WC()->shipping()->get_packages() );
		$packages_ids = $this->get_recurring_carts_package_ids( $cart, $packages_ids );
		foreach ( $chosen_shipping_methods as $key=>$method ) {
			if ( ! in_array( $key, $packages_ids, true ) ) {
				unset( $chosen_shipping_methods[ $key ] );
			}
		}

		return $chosen_shipping_methods;
	}

	/**
	 * @param WC_Cart $cart
	 * @param array $package_ids
	 *
	 * @return array
	 */
	private function get_recurring_carts_package_ids( $cart, $packages_ids ) {
		if ( isset( $cart->recurring_carts ) && is_array( $cart->recurring_carts ) ) {
			foreach ( $cart->recurring_carts as $cart_key => $recurring_cart ) {
				if ( $recurring_cart instanceof WC_Cart ) {
					$should_add_packages = false;
					foreach ( $cart->cart_contents as $cart_content_key => $cart_content ) {
						if ( ! isset( $recurring_cart->cart_contents[ $cart_content_key ] ) || empty( $packages_ids ) ) {
							$should_add_packages = true;
						}
					}
					if ( $should_add_packages ) {
						foreach ( $recurring_cart->get_shipping_packages() as $package_id => $package ) {
							$packages_ids[] = $cart_key . '_' . $package_id;
						}
					}
				}
			}
		}

		return $packages_ids;
	}

	/**
	 * @return string[]
	 */
	private function get_chosen_shipping_methods(): array {
		return array_filter( wp_parse_list( WC()->session->get( 'chosen_shipping_methods', [] ) ) );
	}
}
