<?php
/**
 * Class to handle forms.
 *
 * @package HBFWC
 */

namespace HBFWC\WC;

defined( 'ABSPATH' ) || exit;

use function HBFWC\HBF_WC;
use HBFWC\Utils\Constants;
use HBFWC_WC_Product_Accommodation;

/**
 * Class HBFWC\WC\Form_Handler.
 */
class Form_Handler {
	/**
	 * Accommodation variables.
	 *
	 * @var array
	 */
	private static $accommodation_variables;

	/**
	 * Hook in method.
	 */
	public static function init() {
		add_action( 'woocommerce_add_to_cart_handler_accommodation', array( __CLASS__, 'add_to_cart' ), 5, 1 );
		add_action( 'woocommerce_before_calculate_totals', array( __CLASS__, 'calculate_booking_price' ) );
		add_action( 'woocommerce_checkout_create_order_line_item', array( __CLASS__, 'add_order_line_metadata' ), 10, 3 );
		add_action( 'woocommerce_after_checkout_validation', array( __CLASS__, 'check_availability_before_checkout' ), 1, 2 );
		add_action( 'woocommerce_store_api_checkout_update_order_meta',  array( __CLASS__, 'validate_block_availability' ), 10 );
		add_action( 'woocommerce_store_api_checkout_order_processed',  array( __CLASS__, 'validate_block_availability' ), 10 );
	}

	/**
	 * Add to cart.
	 *
	 * @param string $url The url redirection.
	 *
	 * @return mixed
	 */
	public static function add_to_cart( $url ) {
		$product_id = isset( $_REQUEST['add-to-cart'] ) ? absint( wp_unslash( $_REQUEST['add-to-cart'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		// Get all required variables.
		$check_in_date  = empty( $_REQUEST['check-in'] ) ? '' : sanitize_text_field( wp_unslash( $_REQUEST['check-in'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$check_out_date = empty( $_REQUEST['check-out'] ) ? '' : sanitize_text_field( wp_unslash( $_REQUEST['check-out'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$adults         = empty( $_REQUEST['adults'] ) ? '' : absint( wp_unslash( $_REQUEST['adults'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$children       = empty( $_REQUEST['children'] ) ? '0' : absint( wp_unslash( $_REQUEST['children'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$quantity       = empty( $_REQUEST['qty'] ) ? '' : absint( wp_unslash( $_REQUEST['qty'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$rate_id        = empty( $_REQUEST['rate'] ) ? '' : absint( wp_unslash( $_REQUEST['rate'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		// One or more empty. Bail.
		if ( empty( $check_in_date ) || empty( $check_out_date ) || empty( $adults ) || empty( $quantity ) || empty( $rate_id ) ) {
			wc_add_notice( __( 'Some data are missing. Please try again.', 'hotel-booking-for-woocommerce' ), 'error' );
			return false;
		}

		if ( $check_in_date === $check_out_date ) {
			wc_add_notice( __( 'Kindly select an appropriate date for your visit.', 'hotel-booking-for-woocommerce' ), 'error' );
			return false;
		}

		$check_in_date  = strtotime( $check_in_date );
		$check_out_date = strtotime( $check_out_date );

		if ( wp_date( 'Y-m-d', $check_in_date ) < wp_date( 'Y-m-d' ) ) {
			wc_add_notice( __( 'Booking for a past date is not allowed.', 'hotel-booking-for-woocommerce' ), 'error' );
			return false;
		}

		$adding_to_cart = \wc_get_product( $product_id );

		$rate_is_active = hbfwc_rate_is_active( $product_id, $rate_id, $check_in_date, $check_out_date );

		if ( is_wp_error( $rate_is_active ) ) {
			wc_add_notice( $rate_is_active->get_error_message(), 'error' );
			return false;
		}

		$is_available = $adding_to_cart->check_availability( $check_in_date, $check_out_date, $quantity );

		if ( is_wp_error( $is_available ) ) {
			$formattedError = \hbfwc_parse_stars_to_html( $is_available->get_error_message() );
			wc_add_notice( $formattedError, 'error' );
			return false;
		}

		$check_cart_items = self::check_cart_items( $product_id, $check_in_date, $check_out_date, $quantity );

		if ( is_wp_error( $check_cart_items ) ) {
			$formattedError = \hbfwc_parse_stars_to_html( $check_cart_items->get_error_message() );
			wc_add_notice( $formattedError, 'error' );
			return false;
		}

		$acommodation_cart_vars = array(
			'rate_id'   => $rate_id,
			'check_in'  => $check_in_date,
			'check_out' => $check_out_date,
			'adults'    => $adults,
		);

		if ( absint( $children ) > 0 && isset( $_REQUEST['children_age'] ) && ! empty( $_REQUEST['children_age'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$acommodation_cart_vars['children'] = $children;
			$acommodation_cart_vars['children_ages'] = array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['children_age'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}

		if ( isset( $_REQUEST['addon'] ) && is_array( $_REQUEST['addon'] ) ) {
			$addon_ids = map_deep( wp_unslash( $_REQUEST['addon'] ), 'absint' );
			$addon_qty = isset( $_REQUEST['addon_qty'] ) ? map_deep( wp_unslash( $_REQUEST['addon_qty'] ), 'intval' ) : [];

			$addon_datas = [];
			foreach ( array_keys( $addon_ids ) as $addon_id ) {
				$qty = isset( $addon_qty[ $addon_id ] ) ? $addon_qty[ $addon_id ] : 1;

				if ( $qty < 1 ) {
					$qty = 1;
				}

				$addon_datas[] = [
					'id'    => $addon_id,
					'price' => \hbfwc_price_to_float( get_post_meta( (int) $addon_id, 'price', true ) ),
					'qty'   => $qty,
				];
			}
			$acommodation_cart_vars['addons'] = $addon_datas;
		}

		$cart_id_item_keys = [
			'room_id'       => $product_id,
			'rate_id'       => $rate_id,
			'check_in'      => $check_in_date,
			'check_out'     => $check_out_date,
			'adults'        => $adults,
			'children'      => $acommodation_cart_vars['children'] ?? 0,
			'children_ages' => $acommodation_cart_vars['children_ages'] ?? [],
		];

		$acommodation_cart_vars = apply_filters( 'hbfwc_add_to_cart_vars', $acommodation_cart_vars, $product_id, $cart_id_item_keys );
		
		if ( ! isset( $acommodation_cart_vars['_hbfwc_cart_id'] ) ) {
			$acommodation_cart_vars['_hbfwc_cart_id'] = hbfwc_generate_booking_cart_key( $cart_id_item_keys );
		}

		self::$accommodation_variables = $acommodation_cart_vars;

		add_filter( 'woocommerce_add_cart_item_data', array( __CLASS__, 'inject_accommodation_data' ), 30 );

		WC()->cart->add_to_cart( $product_id, $quantity );

		remove_filter( 'woocommerce_add_cart_item_data', array( __CLASS__, 'inject_accommodation_data' ), 30 );

		if ( 0 === wc_notice_count( 'error' ) ) {
			$url = apply_filters( 'woocommerce_add_to_cart_redirect', $url, $adding_to_cart );

			if ( $url ) {
				wp_safe_redirect( $url );
				exit;
			} elseif ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
				wp_safe_redirect( wc_get_cart_url() );
				exit;
			}
		}

		wc_add_to_cart_message( array( $product_id => $quantity ), true );
	}

	/**
	 * Check the current cart item, before we add it to the cart.
	 *
	 * @param int    $product_id The product ID.
	 * @param string $start The start date.
	 * @param string $end The end date.
	 * @param int    $rooms Quantity or room number.
	 *
	 * @return mixed. Object of WP_Error if not possible to add the item to the cart.
	 */
	public static function check_cart_items( $product_id, $start, $end, $rooms ) {
		return hbfwc_validate_quantity_in_cart( $product_id, $start, $end, $rooms );
	}

	/**
	 * Calculate item price in cart.
	 *
	 * @param object $cart The cart.
	 *
	 * @return void
	 */
	public static function calculate_booking_price( $cart ) {
		foreach ( $cart->get_cart() as $item => $values ) {
			// Check if checkin and checkout dates are set.
			if (
				! isset( $values['check_in'] ) || empty( $values['check_in'] )
				|| ! isset( $values['check_out'] ) || empty( $values['check_out'] )
				|| ! isset( $values['rate_id'] ) || empty( $values['rate_id'] )
			) {
				continue;
			}

			$start_date = $values['check_in'];
			$end_date   = $values['check_out'];
			$rate_id    = $values['rate_id'];
			$product_id = $values['product_id'];

			$product = \wc_get_product( absint( $product_id ) );

			if ( ! $product instanceof HBFWC_WC_Product_Accommodation ) {
				continue;
			}

			$total_cost = $product->get_cart_price( $start_date, $end_date, $rate_id );

			if ( isset( $values['addons'] ) ) {
				$addon_costs = '0';
				foreach( $values['addons'] as $addon ) {
					$addon_id  = (int) $addon['id'];
					// Re-fetch price from source to prevent session-tampering attacks.
					$raw_price = \hbfwc_price_to_float( get_post_meta( $addon_id, 'price', true ) );

					$addon_costs = bcadd(
						$addon_costs,
						bcmul( 
							wc_format_decimal($raw_price, Constants::MATH_SCALE),
							$addon['qty'],
							Constants::MATH_SCALE 
						),
						Constants::MATH_SCALE
					);
				}

				$addon_total_costs = apply_filters( 'hbfwc_register_addon_cart_price', $addon_costs, $values, $values['addons'] );

				$total_cost = bcadd( $total_cost, $addon_total_costs, Constants::MATH_SCALE );
			}

			// Update the item price in the cart.
			$values['data']->set_price( $total_cost );
		}
	}

	/**
	 * Adds custom cart item data.
	 *
	 * @param array $cart_item_data The cart item data.
	 *
	 * @return array
	 */
	public static function inject_accommodation_data( $cart_item_data ) {
		if ( ! empty( self::$accommodation_variables ) ) {
			$custom_data    = self::$accommodation_variables;
			$cart_item_data = array_merge( $cart_item_data, $custom_data );

			self::$accommodation_variables = null;
		}

		return apply_filters( 'hbfwc_add_cart_item_data', $cart_item_data );
	}

	/**
	 * Adds order line meta data.
	 *
	 * @param object $item The cart item.
	 * @param string $cart_item_key The cart item key.
	 * @param array  $values The cart item values.
	 *
	 * @return void
	 */
	public static function add_order_line_metadata( $item, $cart_item_key, $values ) {
		$product = $item->get_product();

		if ( $product->is_type( 'accommodation' ) ) {
			if ( isset( $values['rate_id'] ) ) {
				$item->update_meta_data( 'rate_id', $values['rate_id'] );
			}
			if ( isset( $values['check_in'] ) && isset( $values['check_out'] ) ) {
				$dates = array(
					'check_in'  => $values['check_in'],
					'check_out' => $values['check_out'],
				);
				$item->update_meta_data( 'booking_date', wp_json_encode( $dates ) );
			}
			if ( isset( $values['adults'] ) ) {
				$guests = array(
					'adults'   => $values['adults'],
				);

				isset( $values['children'] ) ? $guests['children'] = $values['children'] : '';

				$item->update_meta_data( 'booking_guests', wp_json_encode( $guests ) );
			}
			if ( isset( $values['children_ages'] ) ) {
				$item->update_meta_data( 'booking_children_ages', wp_json_encode( $values['children_ages'] ) );
			}
			if ( isset( $values['addons'] ) ) {
				if ( ! empty( $values['addons'] ) ) {
					$addons_breaks = [];
					foreach( $values['addons'] as $addon ) {
						$abreaks = [
							'id'    => $addon['id'],
							'price' => $addon['price'],
							'qty'   => $addon['qty'],
							'dates' => [
								'checkin' => $values['check_in'] ?? 0,
								'checkout' => $values['check_out'] ?? 0,
							],
							'guests' => [
								'adults' => $values['adults'] ?? 0,
								'child'  => $values['children'] ?? 0,
							]
						];

						$addons_breaks[] = apply_filters( 'hbfwc_order_addon_breaks', $abreaks, $addon );
					}
					$item->update_meta_data( 'booking_line_addons', wp_json_encode( $addons_breaks ) );
				}
			}
		}
	}

	/**
	 * Check availability before checkout.
	 */
	public static function check_availability_before_checkout($data, $errors) {
		if ( isset( $_POST['woocommerce_checkout_update_totals'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			return;
		}

		foreach ( \WC()->cart->get_cart() as $item => $values ) {
			// Check if checkin and checkout dates are set.
			if (
				! isset( $values['check_in'] ) || empty( $values['check_in'] )
				|| ! isset( $values['check_out'] ) || empty( $values['check_out'] )
				|| ! isset( $values['rate_id'] ) || empty( $values['rate_id'] )
			) {
				continue;
			}

			$start_date = $values['check_in'];
			$end_date   = $values['check_out'];
			$product_id = $values['product_id'];

			$product = \wc_get_product( absint( $product_id ) );

			if ( ! $product instanceof HBFWC_WC_Product_Accommodation ) {
				continue;
			}

			$is_available = \hbfwc_get_min_qty_available( $product_id, $start_date, $end_date );

			if ( is_array( $is_available ) ) {
				$the_date   = wp_list_pluck( $is_available, 'date' );
				$print_date = \hbfwc_natural_language_join( $the_date, __( 'and', 'hotel-booking-for-woocommerce' ) );

				$error_message = sprintf(
						/* translators: Booking date */
						esc_html__( 'The room is not available on %s.', 'hotel-booking-for-woocommerce' ),
						esc_html( $print_date )
					);

				$errors->add(
					'room',
					$error_message
				);
			}
		}
	}

	public static function validate_block_availability($order) {
		foreach ($order->get_items() as $item) {
			$product_id    = $item->get_product_id();
			$booking_dates = $item->get_meta( 'booking_date' );

			$product = \wc_get_product( absint( $product_id ) );

			if ( ! $product instanceof HBFWC_WC_Product_Accommodation ) {
				continue;
			}

			if ( empty( $booking_dates ) ) {
				continue;
			}

			$booking_dates = json_decode( $booking_dates, true );

			$is_available = \hbfwc_get_min_qty_available( $product_id, $booking_dates['check_in'], $booking_dates['check_out'] );

			if ( is_array( $is_available ) ) {
				$the_date   = wp_list_pluck( $is_available, 'date' );
				$print_date = \hbfwc_natural_language_join( $the_date, __( 'and', 'hotel-booking-for-woocommerce' ) );

				$error_message = sprintf(
						/* translators: Booking date */
						esc_html__( 'The room is no longer available on %s.', 'hotel-booking-for-woocommerce' ),
						esc_html( $print_date )
					);

				wp_die( $error_message, 400);
			}
		}
	}
}

Form_Handler::init();
