<?php
/**
 * HBFWC Core Functions
 *
 * General core functions available on both the front-end and admin.
 *
 * @package HBFWC
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use function HBFWC\HBF_WC;

/**
 * Include the classes to register custom product type.
 *
 * @return void
 */
function hbfwc_product_type_register() {
	include_once dirname( HBF_WC()->plugin_file ) . '/includes/wc/class-hbfwc-wc-product-accommodation.php';
}

/**
 * Filtering the WC product type selector.
 *
 * @param array $types Current product type selector.
 *
 * @return Array new product type selector set.
 */
add_filter(
	'product_type_selector',
	function ( $types ) {
		$types['accommodation'] = esc_html__( 'Accommodation', 'hotel-booking-for-woocommerce' );

		return $types;
	},
	10,
	1
);

/**
 * Get default rate id.
 */
function hbfwc_get_default_rate() {
	$rate_id = (int) get_option( 'hbfwc_default_rate_id', 0 );

	if ( $rate_id ) {
		$default_rate = get_term_by( 'term_id', $rate_id, 'rate_plan' );

		if ( $default_rate ) {
			return $default_rate;
		}
	}

	return false;
}

/**
 * Get rate plans.
 */
function hbfwc_get_rateplans() {
	$plans = get_terms(
		array(
			'taxonomy'   => 'rate_plan',
			'hide_empty' => false,
		)
	);
	return $plans;
}

/**
 * Is the PRO version activated?
 *
 * @return mixed
 */
function hbfwc_is_pro() {
	return HBF_WC()->pro_v;
}

/**
 * Get amenities group.
 *
 * @return array
 */
function hbfwc_get_amenities_group(): array {
	$default = array(
		'features'   => __( 'Room Features', 'hotel-booking-for-woocommerce' ),
		'facilities' => __( 'Room Facilities', 'hotel-booking-for-woocommerce' ),
	);

	return apply_filters( 'hbfwc_get_amenities_group', $default );
}

/**
 * Get amenities list by group.
 *
 * @param string $group The group.
 *
 * @return mixed
 */
function hbfwc_get_amenities_by_group( $group ) {
	$amenities = get_terms(
		array(
			'taxonomy'   => 'amenities',
			'hide_empty' => false,
			'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
			array(
				'key'     => 'group',
				'value'   => $group,
				'compare' => '=',
			),
			),
		)
	);
	return $amenities;
}

/**
 * Get all amenities list.
 *
 * @return mixed
 */
function hbfwc_get_amenities() {
	$amenities = get_terms(
		array(
			'taxonomy'   => 'amenities',
			'hide_empty' => false,
		)
	);
	return $amenities;
}

/**
 * Get amenities assigned to a post by group.
 *
 * @param int    $post_id The post ID.
 * @param string $group The group.
 *
 * @return mixed
 */
function hbfwc_get_post_amenities_by_group( $post_id, $group = '' ) {
	$amenities = wp_get_post_terms(
		$post_id,
		'amenities',
		array(
			'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
				array(
					'key'     => 'group',
					'value'   => $group,
					'compare' => '=',
				),
			),
		)
	);
	return $amenities;
}


/**
 * Get the max children age.
 *
 * @return string
 */
function hbfwc_get_max_children_age() {
	$settings = get_option( 'woocommerce_hbfwc_settings', array() );
	return isset( $settings['max_child_age'] ) ? $settings['max_child_age'] : '16';
}

/**
 * Get accommodation visiblity.
 *
 * @return bool
 */
function hbfwc_accommodation_is_hidden() {
	$settings = get_option( 'woocommerce_hbfwc_settings', array() );
	$hidden   = isset( $settings['exclude_shop'] ) ? $settings['exclude_shop'] : 'no';
	return 'no' === $hidden ? false : true;
}

/**
 * Check if same day booking are enabled.
 */
function hbfwc_sameday_booking_enabled() {
	$settings = get_option( 'woocommerce_hbfwc_settings', array() );
	$enabled  = isset( $settings['sameday_enabled'] ) ? $settings['sameday_enabled'] : 'no';
	return 'no' === $enabled ? false : true;
}

/**
 * Get the setting for hide unavailable items in search.
 */
function hbfwc_hide_unavailable_items_in_search() {
	$settings = get_option( 'woocommerce_hbfwc_settings', array() );
	$status   = isset( $settings['availability_visibility'] ) ? $settings['availability_visibility'] : 'hide';
	return 'hide' === $status ? true : false;	
}

function hbfwc_show_rooms_field() {
	$settings = get_option( 'woocommerce_hbfwc_settings', array() );
	$enabled  = isset( $settings['show_rooms_field'] ) ? $settings['show_rooms_field'] : 'no';
	return 'no' === $enabled ? false : true;
}

function hbfwc_default_checkin_date() {
	return hbfwc_sameday_booking_enabled() ? strtotime( wp_date('Y-m-d') . ' 00:00:00' ) : strtotime( 'tomorrow' );
}

/**
 * Get maximum adults.
 * Use within the loop/single template.
 */
function hbfwc_get_max_adults() {
	global $product;
	return $product->get_max_adults() ? $product->get_max_adults() : '';
}

/**
 * Get maximum children.
 * Use within the loop/single template.
 */
function hbfwc_get_max_child() {
	global $product;
	return $product->get_max_children() ? $product->get_max_children() : '';
}

/**
 * Live check before add to cart.
 *
 * @param int    $product_id The Product ID.
 * @param string $from_micro The start date.
 * @param string $end_micro The end date.
 *
 * @return int
 */
function hbfwc_get_min_qty_available( $product_id, $from_micro, $end_micro ) {
	$from = wp_date( 'Y-m-d', (int) $from_micro );
	$end  = wp_date( 'Y-m-d', (int) $end_micro );

	$data = hbfwc_get_availability( $product_id, $from, $end );

	$min = wp_list_pluck( $data, 'qty' );
	$min = array_map( 'absint', $min );

	$min = min( $min );

	if ( 1 > $min ) {
		foreach ( $data as $dt ) {
			if ( 1 > absint( $dt['qty'] ) ) {
				if ( ! is_array( $min ) ) {
					$min = array();
				}

				$min[] = array(
					'min'  => 0,
					'date' => wp_date( hbfwc_get_date_format(), strtotime( $dt['booking_date'] ) ),
				);
			}
		}
	}

	return $min;
}

/**
 * Get availability data based on date range.
 *
 * @param int    $product_id The Product ID.
 * @param string $from The start date.
 * @param string $to The end date.
 * @param bool   $for_admin For admin area ussage.
 *
 * @return array
 */
function hbfwc_get_availability( $product_id, $from, $to, $for_admin = false ) {
	$product_id = absint( $product_id );
	if ( $for_admin ) {
		$to = wp_date( 'Y-m-d', strtotime( $to . ' +1 day' ) );
	}
	$db_data = HBF_WC()->db->get_date_range_entries( $product_id, $from, $to, 'ARRAY_A' );

	$start_ts = strtotime( $from );
	$end_ts   = strtotime( $to );

	// Return empty data for invalid date input instead of throwing DateTime errors.
	if ( false === $start_ts || false === $end_ts || $start_ts > $end_ts ) {
		return array();
	}

	$default_qty  = absint( get_post_meta( $product_id, '_allotment', true ) );
	$end_date_str = wp_date( 'Y-m-d', $end_ts );

	// Build O(1) lookup map by date; preserve current behavior by skipping end date entries.
	$db_data_by_date = array();
	if ( ! empty( $db_data ) ) {
		foreach ( $db_data as $item ) {
			if ( ! isset( $item['booking_date'] ) ) {
				continue;
			}

			$booking_date = trim( (string) $item['booking_date'] );
			if ( $booking_date === $end_date_str || isset( $db_data_by_date[ $booking_date ] ) ) {
				continue;
			}

			$db_data_by_date[ $booking_date ] = $item;
		}
	}

	$results = array();
	for ( $cursor = $start_ts; $cursor < $end_ts; $cursor = strtotime( '+1 day', $cursor ) ) {
		$date_str = wp_date( 'Y-m-d', $cursor );

		if ( isset( $db_data_by_date[ $date_str ] ) ) {
			$the_date_data = $db_data_by_date[ $date_str ];

			if ( intval( $the_date_data['qty'] ) > $default_qty ) {
				$the_date_data['qty'] = $default_qty;
			}

			if ( ! $for_admin && 1 > absint( $the_date_data['status'] ) ) {
				$the_date_data['qty'] = 0;
			}

			$results[] = $the_date_data;
			continue;
		}

		$results[] = array(
			'post_id'      => $product_id,
			'booking_date' => $date_str,
			'qty'          => $default_qty,
			'status'       => 1,
		);
	}

	return $results;
}

/**
 * Backward-compatible wrapper for misspelled function name.
 *
 * @deprecated 1.1.0 Use hbfwc_get_availability() instead.
 *
 * @param int    $product_id The Product ID.
 * @param string $from The start date.
 * @param string $to The end date.
 * @param bool   $for_admin For admin area usage.
 *
 * @return array
 */
function hbfwc_get_availibility( $product_id, $from, $to, $for_admin = false ) {
	return hbfwc_get_availability( $product_id, $from, $to, $for_admin );
}

/**
 * Check the "room left" for given Product id and dates.
 * 
 * @param int    $product_id The Product ID.
 * @param string $start      The start date.
 * @param string $end        The end date.
 *
 * @return array
 */
function hbfwc_get_quantity_data_from_cart_order( $product_id, $start, $end ): array {
	// Current cart is empty.
	if ( is_null( WC()->cart ) || WC()->cart->is_empty() ) {
		return [];
	}

	$check_start_date = wp_date( 'Y-m-d', (int) $start );
	$check_end_date   = wp_date( 'Y-m-d', (int) $end );

	$room_range_datas = hbfwc_get_availability( absint( $product_id ), $check_start_date, $check_end_date );
	$room_range_valid = [];
	foreach( $room_range_datas as $rrd ) {
		$room_range_valid[ $rrd['booking_date'] ] = [
			'qty'    => $rrd['qty'] ?? 0,
			'status' => $rrd['status'] ?? 1,
		];
	}

	$in_cart = [];
	foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
		if ( absint( $product_id ) !== absint( $cart_item['product_id'] ) ) {
			continue;
		}

		$cart_start_date = wp_date( 'Y-m-d', $cart_item['check_in'] );
		$cart_end_date   = wp_date( 'Y-m-d', $cart_item['check_out'] );

		// Prepare the dates to check.
		$dates_to_check = array( $cart_start_date );
		while ( end( $dates_to_check ) < $cart_end_date ) {
			$dates_to_check[] = wp_date( 'Y-m-d', strtotime( end( $dates_to_check ) . ' +1 day' ) );
		}

		foreach( $room_range_valid as $booking_date => $qty_info ) {
			if ( 
				! in_array( $booking_date, $dates_to_check, true ) 
				|| $booking_date === $cart_end_date 
			) {
				continue;
			}
			
			if ( ! isset( $in_cart[$booking_date] ) ) {
				$in_cart[$booking_date] = absint( $qty_info['qty'] );
			}

			$in_cart[$booking_date] -= intval( $cart_item['quantity'] ?? 0 );
		}
	}

	return $in_cart;
}

/**
 * Validate quantity in cart.
 *
 * @param int    $product_id The product ID.
 * @param string $start The start date.
 * @param string $end The end date.
 * @param int    $rooms The room number.
 * @param bool   $get_only_min Only get the minimum data.
 *
 * @return mixed
 */
function hbfwc_validate_quantity_in_cart( $product_id, $start, $end, $rooms, $get_only_min = false ) {
	// Current cart is empty.
	if ( is_null( WC()->cart ) || WC()->cart->is_empty() ) {
		return true;
	}

	$check_start_date = wp_date( 'Y-m-d', (int) $start );
	$check_end_date   = wp_date( 'Y-m-d', (int) $end );

	// Prepare the dates to check.
	$dates_to_check = array( $check_start_date );
	while ( end( $dates_to_check ) < $check_end_date ) {
		$dates_to_check[] = wp_date( 'Y-m-d', strtotime( end( $dates_to_check ) . ' +1 day' ) );
	}

	$error_msg           = array();
	$room_available_left = array();
	foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
		if ( absint( $product_id ) !== absint( $cart_item['product_id'] ) ) {
			continue;
		}

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

		if ( ! $product instanceof \HBFWC_WC_Product_Accommodation ) continue;

		$allotment = $product->get_allotment();

		// Allotment not set. Don't bother.
		if ( empty( $allotment ) ) {
			continue;
		}

		$cart_start_date = wp_date( 'Y-m-d', $cart_item['check_in'] );
		$cart_end_date   = wp_date( 'Y-m-d', $cart_item['check_out'] );

		$room_datas = hbfwc_get_availability( absint( $product_id ), $cart_start_date, $cart_end_date );
		if ( $room_datas ) {
			foreach ( $room_datas as $room_data ) {
				if ( 
					! in_array( $room_data['booking_date'], $dates_to_check, true ) 
					|| ( $room_data['booking_date'] === $cart_end_date || $room_data['booking_date'] === $check_end_date ) )
				{
					continue;
				}

				$bk = $room_data['booking_date'];

				if ( 1 > absint( $room_data['status'] ) ) {
					$room_available_left[ $bk ] = 0;
				} elseif ( isset( $room_available_left[ $bk ] ) ) {
					$room_available_left[ $bk ] = absint( $room_available_left[ $bk ] ) - absint( $cart_item['quantity'] );
				} else {
					$room_available_left[ $bk ] = absint( $room_data['qty'] ) - absint( $cart_item['quantity'] );
				}
			}
		}
	}

	$val = array();
	$multi_days = [];
	if ( ! empty( $room_available_left ) ) {
		foreach ( $room_available_left as $date_ral => $ral ) {
			if ( 1 > absint( $ral ) ) {
				$multi_days[] = wp_date( hbfwc_get_date_format(), strtotime( $date_ral ) );
			} elseif ( absint( $rooms ) > absint( $ral ) ) {
				$allotment_string = sprintf(
					/* translators: Number of rooms */
					_n( '%s room is', '%s rooms are', $room_data['qty'], 'hotel-booking-for-woocommerce' ),
					number_format_i18n( $room_data['qty'] )
				);
				$on_cart_string = sprintf(
					/* translators: Number of rooms */
					_n( '%s room is', '%s rooms are', $cart_item['quantity'], 'hotel-booking-for-woocommerce' ),
					number_format_i18n( $cart_item['quantity'] )
				);

				$error_msg[] = sprintf(
					/* translators: Booking date */
					__( 'Only %1$s available for %2$s, and %3$s already in your cart.', 'hotel-booking-for-woocommerce' ),
					$allotment_string,
					wp_date( hbfwc_get_date_format(), strtotime( $room_data['booking_date'] ) ),
					$on_cart_string
				);
			}
		}
	}

	if ( $get_only_min ) {
		if ( ! empty( $val ) ) {
			return min( $val );
		} else {
			return false;
		}
	}

	if ( ! empty($multi_days) ) {
		$print_date = \hbfwc_natural_language_join( $multi_days, __( 'and', 'hotel-booking-for-woocommerce' ) );
		$error_msg[] = sprintf(
			/* translators: Booking date */
			__( 'No additional rooms are available for %s The last room is already in your cart.', 'hotel-booking-for-woocommerce' ),
			"**{$print_date}**"
		);
	}
	
	$error_msg = array_unique($error_msg);

	if ( ! empty( $error_msg ) ) {
		return new \WP_Error( 'error', implode( ' <br/> ', $error_msg ) );
	} else {
		return true;
	}
}

/**
 * After stock change events, triggers adds order notes.
 *
 * @param WC_Order $order order object.
 * @param array    $changes Array of changes.
 */
function hbfwc_trigger_stock_change_notifications( $order, $changes ) {
	if ( empty( $changes ) ) {
		return;
	}

	$order_notes = array();

	foreach ( $changes as $change ) {
		$product_name = '';
		$inner_change = array();
		foreach ( $change as $date_change ) {
			$product_name = $date_change['product']->get_formatted_name();

			$inner_change[] = '[' . $date_change['date'] . '] ' . $date_change['from'] . '&rarr;' . $date_change['to'];
		}

		$order_notes[] = $product_name . ' ' . implode( ', ', $inner_change );
	}

	$order->add_order_note( __( 'Room value reduced:', 'hotel-booking-for-woocommerce' ) . ' ' . implode( ', ', $order_notes ) );
}

/**
 * Check if rate available for given date and product.
 *
 * @param int    $product_id Product ID.
 * @param int    $rate_id Rate ID.
 * @param string $date_from Date from.
 * @param string $date_end Date end.
 *
 * @return mixed
 */
function hbfwc_rate_is_active( $product_id, $rate_id, $date_from, $date_end ) {
	$product = wc_get_product( absint( $product_id ) );

	if ( ! $product || ! $product instanceof HBFWC_WC_Product_Accommodation ) {
		return new WP_Error( 'error', __( 'Invalid product.', 'hotel-booking-for-woocommerce' ) );
	}

	if ( ! in_array( (int) $rate_id, $product->has_active_rates_by_date( $date_from, $date_end ), true ) ) {
		return new WP_Error( 'error', __( 'Rate is not available. Please refresh your browser to get the latest room data.', 'hotel-booking-for-woocommerce' ) );
	}

	return true;
}

/**
 * Get the diff between 2 dates
 * @param mixed $from Date from; can be string|int
 * @param mixed $end Date end; can be string|int
 *
 * @return int
 */
function hbfwc_get_stay_len( $from, $end ) {
	$ts_from = is_numeric( $from )
			? new \Datetime( '@' . $from )
			: new \Datetime( $from );

	$ts_end = is_numeric( $end )
			? new \Datetime( '@' . $end )
			: new \Datetime( $end );

	$stay_len = intval( $ts_end->diff( $ts_from )->format( '%a' ) );
	return $stay_len;
}

/**
 * Conditional check if current product is part of our plugin.
 */
function hbfwc_is_page_post() {
	global $post, $product;

	// Ensure $post valid.
	if ( empty( $post ) || ! $post instanceof WP_Post ) {
		return false;
	}

	// Ensure we're on a product page and $product is set.
	if ( is_product() && ! is_object( $product ) ) {
		$product = wc_get_product( get_the_ID() );
	}

	// Check if current product is a HBFWC accommodation product.
	$single_accommodation = is_product() && $product && $product instanceof HBFWC_WC_Product_Accommodation;

	// Check if current page/post contains any HBFWC shortcode.
	$page_with_shortcode  = has_shortcode( $post->post_content, 'hbfwc_availability' );
	
	/**
	 * @since 1.1.0
	 * Check if page contain block from plugin.
	 */
	$page_with_block = has_block( 'hbfwc/availability', $post );

	return $single_accommodation || $page_with_shortcode || $page_with_block;
}

/**
 * Check if cart contain our accommodation.
 * @return bool
 */
function hbfwc_cart_has_accommodation() {
	if ( null === \WC()->cart ) {
		return false;
	}

	if ( empty( \WC()->cart->get_cart() ) ) {
		return false;
	}

	$found = false;
	foreach ( \WC()->cart->get_cart() as $item => $values ) {
		$product = $values['data'];

		if ( $product instanceof HBFWC_WC_Product_Accommodation ) {
			$found = true;
			break;
		}
	}

	return $found;
}
