<?php
/**
 * Admin rental orders: adds rental date filter inputs to the WooCommerce
 * orders list and filters the query when dates are submitted.
 *
 * Compatible with both HPOS (wc_orders table) and the legacy posts-based order
 * storage by hooking into the appropriate WooCommerce filter chain.
 *
 * @package WooEquipmentRental
 */

declare( strict_types=1 );

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

/**
 * Class WCRent_Admin_Rental_Orders
 */
class WCRent_Admin_Rental_Orders {

	// -----------------------------------------------------------------------
	// Initialisation
	// -----------------------------------------------------------------------

	/**
	 * Registers all hooks.
	 */
	public static function init(): void {
		// Detect HPOS vs legacy and hook accordingly.
		add_action( 'init', array( self::class, 'register_order_list_hooks' ), 20 );
	}

	/**
	 * Registers the correct set of hooks depending on the active order storage.
	 */
	public static function register_order_list_hooks(): void {
		if ( WCRent_Utils::hpos_is_enabled() ) {
			// HPOS orders list (WooCommerce >= 7.1).
			add_action(
				'woocommerce_order_list_table_restrict_manage_orders',
				array( self::class, 'render_filter_inputs' )
			);

			add_filter(
				'woocommerce_order_list_table_prepare_items_query_args',
				array( self::class, 'apply_hpos_filter' )
			);
		} else {
			// Legacy posts list (edit.php?post_type=shop_order).
			add_action(
				'restrict_manage_posts',
				array( self::class, 'render_filter_inputs_legacy' )
			);

			add_filter(
				'request',
				array( self::class, 'apply_legacy_filter' )
			);
		}

		// Enqueue admin assets on the orders page.
		add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_assets' ) );
	}

	// -----------------------------------------------------------------------
	// Filter UI
	// -----------------------------------------------------------------------

	/**
	 * Renders the rental date filter inputs (HPOS orders list).
	 *
	 * @param string $post_type Unused in HPOS context.
	 */
	public static function render_filter_inputs( string $post_type = '' ): void {
		self::output_filter_html();
	}

	/**
	 * Renders the rental date filter inputs (legacy orders list).
	 *
	 * Only outputs on the shop_order post-type screen.
	 *
	 * @param string $post_type Current screen post type.
	 */
	public static function render_filter_inputs_legacy( string $post_type ): void {
		if ( 'shop_order' !== $post_type ) {
			return;
		}

		self::output_filter_html();
	}

	/**
	 * Emits the shared filter HTML.
	 */
	private static function output_filter_html(): void {
		$dates = self::get_filter_dates();
		$from  = $dates['start'];
		$to    = $dates['end'];

		?>
		<div class="wcrent-order-filter-wrap">
			<label for="wcrent_filter_start" class="screen-reader-text">
				<?php esc_html_e( 'Rental start date', 'harbor-equipment-rental-for-woocommerce' ); ?>
			</label>
			<input
				type="date"
				id="wcrent_filter_start"
				name="wcrent_filter_start"
				value="<?php echo esc_attr( $from ); ?>"
				placeholder="<?php esc_attr_e( 'Rental from', 'harbor-equipment-rental-for-woocommerce' ); ?>"
				aria-label="<?php esc_attr_e( 'Rental start date', 'harbor-equipment-rental-for-woocommerce' ); ?>"
			/>
			<span aria-hidden="true">&ndash;</span>
			<label for="wcrent_filter_end" class="screen-reader-text">
				<?php esc_html_e( 'Rental end date', 'harbor-equipment-rental-for-woocommerce' ); ?>
			</label>
			<input
				type="date"
				id="wcrent_filter_end"
				name="wcrent_filter_end"
				value="<?php echo esc_attr( $to ); ?>"
				placeholder="<?php esc_attr_e( 'Rental to', 'harbor-equipment-rental-for-woocommerce' ); ?>"
				aria-label="<?php esc_attr_e( 'Rental end date', 'harbor-equipment-rental-for-woocommerce' ); ?>"
			/>
			<?php if ( $from || $to ) : ?>
				<a
					href="<?php echo esc_url( remove_query_arg( array( 'wcrent_filter_start', 'wcrent_filter_end' ) ) ); ?>"
					class="button"
					aria-label="<?php esc_attr_e( 'Clear rental date filter', 'harbor-equipment-rental-for-woocommerce' ); ?>"
				>
					<span aria-hidden="true">&times;</span>
				</a>
			<?php endif; ?>
		</div>
		<?php
	}

	// -----------------------------------------------------------------------
	// Query filters
	// -----------------------------------------------------------------------

	/**
	 * Filters the HPOS orders list query args based on rental date inputs.
	 *
	 * @param array<string, mixed> $query_args WooCommerce query args.
	 * @return array<string, mixed>
	 */
	public static function apply_hpos_filter( array $query_args ): array {
		if ( ! current_user_can( 'edit_shop_orders' ) ) {
			return $query_args;
		}

		$dates        = self::get_filter_dates();
		$filter_start = $dates['start'];
		$filter_end   = $dates['end'];

		if ( ! $filter_start && ! $filter_end ) {
			return $query_args;
		}

		$order_ids = self::get_order_ids_by_rental_dates( $filter_start, $filter_end );

		// Return an empty result when the query yields nothing.
		if ( empty( $order_ids ) ) {
			$query_args['include'] = array( 0 );
			return $query_args;
		}

		// wc_get_orders() uses 'include' (not 'post__in') to filter by order IDs.
		$query_args['include'] = array_map( 'absint', $order_ids );

		return $query_args;
	}

	/**
	 * Filters the legacy WP_Query request based on rental date inputs.
	 *
	 * @param array<string, mixed> $query_vars WP_Query vars.
	 * @return array<string, mixed>
	 */
	public static function apply_legacy_filter( array $query_vars ): array {
		if ( ! current_user_can( 'edit_shop_orders' ) ) {
			return $query_vars;
		}

		if ( ! is_admin()
			|| ! isset( $query_vars['post_type'] )
			|| 'shop_order' !== $query_vars['post_type'] ) {
			return $query_vars;
		}

		$dates        = self::get_filter_dates();
		$filter_start = $dates['start'];
		$filter_end   = $dates['end'];

		if ( ! $filter_start && ! $filter_end ) {
			return $query_vars;
		}

		$order_ids = self::get_order_ids_by_rental_dates( $filter_start, $filter_end );

		if ( empty( $order_ids ) ) {
			$query_vars['post__in'] = array( 0 );
			return $query_vars;
		}

		$query_vars['post__in'] = array_map( 'absint', $order_ids );

		return $query_vars;
	}

	// -----------------------------------------------------------------------
	// DB query
	// -----------------------------------------------------------------------

	/**
	 * Returns order IDs that have at least one rental line item whose rental
	 * period overlaps with the requested filter window.
	 *
	 * When only one date is supplied the range is open-ended on that side.
	 *
	 * @param string $filter_start YYYY-MM-DD or empty string.
	 * @param string $filter_end   YYYY-MM-DD or empty string.
	 * @return int[]
	 */
	private static function get_order_ids_by_rental_dates(
		string $filter_start,
		string $filter_end
	): array {
		global $wpdb;

		// Validate supplied dates.
		if ( $filter_start && ! WCRent_Utils::is_valid_date( $filter_start ) ) {
			$filter_start = '';
		}

		if ( $filter_end && ! WCRent_Utils::is_valid_date( $filter_end ) ) {
			$filter_end = '';
		}

		if ( ! $filter_start && ! $filter_end ) {
			return array();
		}

		$conditions = array();
		$args       = array();

		// Rental period overlaps with the filter window when:
		// rental_start <= filter_end AND rental_end >= filter_start
		// (standard interval overlap test — both bounds are inclusive)

		if ( $filter_start ) {
			// Rental must end on or after the filter start date.
			$conditions[] = 'im_end.meta_value >= %s';
			$args[]       = $filter_start;
		}

		if ( $filter_end ) {
			// Rental must start on or before the filter end date.
			$conditions[] = 'im_start.meta_value <= %s';
			$args[]       = $filter_end;
		}

		$where = implode( ' AND ', $conditions );

		// Build active-status placeholders for the IN clause.
		$active_statuses     = WCRent_Utils::active_statuses();
		$status_placeholders = WCRent_Utils::status_placeholders( $active_statuses );
		$orders_info         = WCRent_Utils::orders_table();
		$orders_table        = $orders_info['table'];
		$status_col          = $orders_info['status_col'];

		// Merge status args before date args (status IN placeholders come first in SQL).
		$args = array_merge( $active_statuses, $args );

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- spread operator args confuse static analysis
		// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter -- $where and $status_placeholders are built from hardcoded safe fragments
		$order_ids = $wpdb->get_col(
			$wpdb->prepare(
				"
				SELECT DISTINCT oi.order_id
				FROM {$wpdb->prefix}woocommerce_order_items AS oi
				INNER JOIN {$orders_table} AS o
					ON o.id = oi.order_id
					AND {$status_col} IN ({$status_placeholders})
				INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im_start
					ON im_start.order_item_id = oi.order_item_id
					AND im_start.meta_key = '_rental_start'
				INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im_end
					ON im_end.order_item_id = oi.order_item_id
					AND im_end.meta_key = '_rental_end'
				WHERE oi.order_item_type = 'line_item'
				  AND {$where}
				",
				...$args
			)
		);
		// phpcs:enable

		return array_map( 'intval', (array) $order_ids );
	}

	// -----------------------------------------------------------------------
	// Assets
	// -----------------------------------------------------------------------

	/**
	 * Enqueues a small stylesheet on the WooCommerce orders list page.
	 *
	 * @param string $hook Current admin page hook.
	 */
	public static function enqueue_assets( string $hook ): void {
		$is_orders_page = ( 'edit.php' === $hook || 'woocommerce_page_wc-orders' === $hook );

		if ( ! $is_orders_page ) {
			return;
		}

		wp_enqueue_style(
			'wcrent-admin-orders',
			WCRENT_PLUGIN_URL . 'assets/admin-rental.css',
			array(),
			WCRENT_VERSION
		);
	}

	// -----------------------------------------------------------------------
	// Helpers
	// -----------------------------------------------------------------------

	/**
	 * Returns sanitized rental filter dates from the current GET request.
	 *
	 * @return array{start: string, end: string}
	 */
	private static function get_filter_dates(): array {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		return array(
			'start' => isset( $_GET['wcrent_filter_start'] )
				? sanitize_text_field( wp_unslash( $_GET['wcrent_filter_start'] ) )
				: '',
			'end'   => isset( $_GET['wcrent_filter_end'] )
				? sanitize_text_field( wp_unslash( $_GET['wcrent_filter_end'] ) )
				: '',
		);
		// phpcs:enable
	}
}
