<?php
/**
 * Shipping functions for Multi-account sync.
 *
 * @package flo-sync
 */

namespace NMFS\Sync\Items\Articles\Stock;

use NMFS\Shared;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Models\Hub_Account_Model;
use Northmill_Online_Shared_Sync\Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Hub_Account_Setup;
use WC_Shipping_Flat_Rate;
use WC_Shipping_Free_Shipping;
use WC_Shipping_Local_Pickup;
use WC_Shipping_Method;
use WC_Shipping_Rate;
use WC_Shipping_Zone;
use WC_Shipping_Zones;

defined( 'ABSPATH' ) || exit;

/**
 * Shipping class.
 */
class Shipping {
	/**
	 * Setup shipping functions.
	 */
	public static function setup(): void {
		add_action( 'woocommerce_after_shipping_rate', array( __CLASS__, 'maybe_show_shipping_meta' ), 10, 2 );

		add_filter( 'woocommerce_shipping_packages', array( __CLASS__, 'shipping_packages' ), 50 ); // Shouldn't be done early.
		add_filter( 'woocommerce_cart_no_shipping_available_html', array( __CLASS__, 'no_shipping_available_html' ) );

		Shared::instance()->ajax->add_action(
			'update_shipping_store',
			array( __CLASS__, 'ajax_update_shipping_store' ),
			true
		);
	}


	/**
	 * Get shipping method by shipping rate.
	 *
	 * @param WC_Shipping_Rate $shipping_rate The shipping rate.
	 *
	 * @return WC_Shipping_Method|null Returns the shipping method if found, otherwise null.
	 */
	public static function get_shipping_method_by_shipping_rate( WC_Shipping_Rate $shipping_rate ): ?WC_Shipping_Method {
		/**
		 * Magic okay for WC().
		 *
		 * @noinspection PhpUndefinedFieldInspection
		 */
		$shipping_methods = WC()->shipping->get_shipping_methods();

		foreach ( $shipping_methods as $shipping_method ) {
			if ( $shipping_method->id === $shipping_rate->get_method_id() ) {
				return $shipping_method;
			}
		}

		return null;
	}

	/**
	 * Get the shipping method type of WC_Shipping_Rate object.
	 *
	 * @param WC_Shipping_Rate $shipping_rate       Shipping method object.
	 * @param bool             $fallback_to_default Fallback to the default shipping type if not found.
	 *
	 * @return string
	 */
	public static function get_shipping_method_type_by_rate( WC_Shipping_Rate $shipping_rate, bool $fallback_to_default = true ): string {
		$shipping_method = self::get_shipping_method_by_shipping_rate( $shipping_rate );

		return self::get_shipping_method_type_by_shipping_method( $shipping_method, $fallback_to_default );
	}

	/**
	 * Get the shipping method type by the shipping method.
	 *
	 * @param WC_Shipping_Method $method              The shipping method.
	 * @param bool               $fallback_to_default Whether to fall back to default if the method doesn't match any type.
	 *
	 * @return string The shipping method type.
	 */
	public static function get_shipping_method_type_by_shipping_method( WC_Shipping_Method $method, bool $fallback_to_default = true ): string {
		if ( is_a( $method, WC_Shipping_Free_Shipping::class ) ) {
			return 'delivery';
		} elseif ( is_a( $method, WC_Shipping_Flat_Rate::class ) ) {
			return 'delivery';
		} elseif ( is_a( $method, WC_Shipping_Local_Pickup::class ) ) {
			return 'pickup';
		} else {
			return $fallback_to_default ? self::get_default_shipping_type_id() : '';
		}
	}


	/**
	 * Get default shipping type id.
	 *
	 * @return string
	 */
	public static function get_default_shipping_type_id(): string {
		return 'pickup';
	}


	/**
	 * Check if shipping method is allowed for a Hub account.
	 *
	 * @param Hub_Account_Model $backoffice_account Hub account.
	 * @param WC_Shipping_Zone  $package_zone Shipping zone.
	 * @param WC_Shipping_Rate  $shipping_method Shipping method.
	 *
	 * @return bool True if shipping method is allowed for the Hub account.
	 */
	public static function is_shipping_method_allowed_for_backoffice_account(
		Hub_Account_Model $backoffice_account,
		WC_Shipping_Zone $package_zone,
		WC_Shipping_Rate $shipping_method
	): bool {
		$package_shipping_method = $package_zone->get_id() . ':' . $shipping_method->get_id();
		return in_array( $package_shipping_method, $backoffice_account->allowed_shipping_methods, true );
	}

	/**
	 * Add store select or other metas to cart shipping methods.
	 *
	 * @param WC_Shipping_Rate $method Shipping method object.
	 * @param int              $index Shipping package index.
	 */
	public static function maybe_show_shipping_meta( WC_Shipping_Rate $method, int $index ): void {
		if ( ! Hub_Account_Setup::is_multiple_stocks_enabled() ) {
			return;
		}

		$packages     = WC()->shipping()->get_packages();
		$package      = $packages[ $index ];
		$package_zone = WC_Shipping_Zones::get_zone_matching_package( $package );

		$cart_shipping_type = self::get_shipping_method_type_by_rate( $method );

		/**
		 * If the shipping method is pickup, show store select.
		 */
		if ( 'pickup' === $cart_shipping_type ) {
			$accounts = Checkout::get_pickup_backoffice_accounts_with_stock( $package_zone, $method );

			$shipping_method_id = $method->get_id();
			$shipping_method_id = str_replace( ':', '_', $shipping_method_id );
			$select_id          = 'nmf_backoffice_account_id_' . $shipping_method_id;

			$chosen_store = WC()->session->get( 'nmf_cart_chosen_store', array() );
			if ( ( $chosen_store['method'] ?? '' ) === $shipping_method_id ) {
				$chosen_store_id = $chosen_store['store'] ?? '';
			} else {
				$chosen_store_id = '';
			}

			Shared::instance()->template_loader->get_template(
				'cart/pickup-shipping-meta',
				array(
					'accounts'           => $accounts,
					'select_id'          => $select_id,
					'shipping_method_id' => $shipping_method_id,
					'chosen_store_id'    => $chosen_store_id,
				),
				false
			);

			/**
			 * If the shipping method is delivery, only check if there is any store with stock.
			 */
		} elseif ( 'delivery' === $cart_shipping_type ) {
			$accounts    = Checkout::get_delivery_backoffice_account_with_stock( $package_zone, $method );
			$has_account = ! empty( $accounts );

			Shared::instance()->template_loader->get_template(
				'cart/delivery-shipping-meta',
				array(
					'has_account' => $has_account,
				),
				false
			);
		}
	}

	/**
	 * Filter shipping packages.
	 *
	 * @param array $packages Shipping packages.
	 *
	 * @return array
	 */
	public static function shipping_packages( array $packages ): array {
		if ( ! Hub_Account_Setup::is_multiple_stocks_enabled() ) {
			return $packages;
		}

		if ( ! isset( $packages[0]['rates'] ) ) {
			return $packages;
		}

		$package_zone = WC_Shipping_Zones::get_zone_matching_package( $packages[0] );

		foreach ( $packages[0]['rates'] as $method_key => $method ) {
			$shipping_method_type = self::get_shipping_method_type_by_rate( $method );

			switch ( $shipping_method_type ) {
				case 'pickup':
					$accounts = Checkout::get_pickup_backoffice_accounts_with_stock( $package_zone, $method );
					if ( empty( $accounts ) ) {
						unset( $packages[0]['rates'][ $method_key ] );
					}
					break;
				case 'delivery':
					$account = Checkout::get_delivery_backoffice_account_with_stock( $package_zone, $method );
					if ( ! $account ) {
						unset( $packages[0]['rates'][ $method_key ] );
					}
					break;
			}
		}

		return $packages;
	}

	/**
	 * Filter no shipping available html.
	 *
	 * @param string $html No shipping available html.
	 *
	 * @return string
	 */
	public static function no_shipping_available_html( string $html ): string {
		if ( ! Hub_Account_Setup::is_multiple_stocks_enabled() ) {
			return $html;
		}

		return $html . '<br>' .
			esc_html__(
				'Please make sure all cart items are available in the same store.',
				'flo-sync'
			);
	}

	/**
	 * Ajax update shipping store.
	 *
	 * @phpcs:disable WordPress.Security.NonceVerification -- Nonce verification is done in the parent class.
	 */
	public static function ajax_update_shipping_store(): never {

		$posted_store  = isset( $_POST['shipping_store'] ) ? wc_clean( wp_unslash( $_POST['shipping_store'] ) ) : array();
		$posted_method = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array();

		WC()->session->set(
			'nmf_cart_chosen_store',
			array(
				'store'  => $posted_store,
				'method' => $posted_method,
			)
		);

		wp_send_json_success();
		exit();
	}
	// phpcs:enable WordPress.Security.NonceVerification
}
