<?php
/**
 * Enforce minimum order amount on the checkout page (UI + server validation).
 *
 * - Shows a minimal notice on checkout when cart total is below the threshold
 * - Disables the "Place order" button when below threshold
 * - Validates on submission (classic checkout) to prevent bypass
 *
 * @package FoodOrd
 */

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

/**
 * Minimum order amount handler for checkout.
 */
class Foodord_Min_Order_Amount {
	/**
	 * Bootstrap hooks.
	 */
	public function init() {
		// Only do anything on frontend.
		if ( is_admin() ) {
			return;
		}

		// Show a small informational notice at the top of checkout form.
		add_action( 'woocommerce_before_checkout_form', array( $this, 'maybe_show_min_order_notice' ), 5 );

		// Disable the Place order button HTML when below min.
		add_filter( 'woocommerce_order_button_html', array( $this, 'maybe_disable_place_order_button' ), 10, 1 );

		// Server-side validation (classic checkout flow) as a safety net.
		add_action( 'woocommerce_checkout_process', array( $this, 'validate_min_order_on_checkout' ) );
	}

	/**
	 * Display a minimal notice on checkout when below the minimum order amount.
	 */
	public function maybe_show_min_order_notice() {
		if ( ! function_exists( 'is_checkout' ) || ! is_checkout() ) {
			return;
		}
		if ( function_exists( 'is_order_received_page' ) && is_order_received_page() ) {
			return; // Not on the thank you page.
		}
		if ( ! $this->is_wc_ready() ) {
			return;
		}

		$min = $this->get_min_order_amount();
		if ( $min <= 0 ) {
			return; // Feature disabled or not configured.
		}

		if ( $this->cart_total_below_min( $min ) ) {
			$message = sprintf(
				/* translators: %s: minimum order amount. */
				__( 'Minimum order amount is %s. Please add more items to your cart.', 'foodord' ),
				wp_strip_all_tags( wc_price( $min ) )
			);
			// Minimal info notice. Do not use 'error' to keep UX light until submit.
			// Display notice using WooCommerce functions (WC is always available on checkout).
			wc_print_notice( esc_html( $message ), 'notice' );
		}
	}

	/**
	 * Disable the Place order button when the cart is below the minimum.
	 *
	 * @param string $button_html Original button HTML.
	 * @return string Filtered HTML (maybe disabled)
	 */
	public function maybe_disable_place_order_button( $button_html ) {
		if ( empty( $button_html ) ) {
			return $button_html;
		}
		if ( ! function_exists( 'is_checkout' ) || ! is_checkout() ) {
			return $button_html;
		}
		if ( function_exists( 'is_order_received_page' ) && is_order_received_page() ) {
			return $button_html;
		}
		if ( ! $this->is_wc_ready() ) {
			return $button_html;
		}

		$min = $this->get_min_order_amount();
		if ( $min <= 0 ) {
			return $button_html;
		}

		if ( ! $this->cart_total_below_min( $min ) ) {
			return $button_html; // Above min -> no change.
		}

		// Append disabled state and a minimal title for accessibility.
		/* translators: %s: minimum order amount. */
		$title = esc_html( sprintf( __( 'Minimum order amount is %s', 'foodord' ), wp_strip_all_tags( wc_price( $min ) ) ) );

		// Ensure we do not duplicate attributes on repeated fragment refreshes.
		$button_html = $this->ensure_button_has_disabled_attributes( $button_html, $title );

		return $button_html;
	}

	/**
	 * Server-side validation for classic checkout to prevent submission.
	 */
	public function validate_min_order_on_checkout() {
		if ( ! $this->is_wc_ready() ) {
			return;
		}

		$min = $this->get_min_order_amount();
		if ( $min <= 0 ) {
			return;
		}

		if ( $this->cart_total_below_min( $min ) ) {
			$error = sprintf(
				/* translators: %s: minimum order amount. */
				__( 'You need to reach the minimum order amount of %s to place an order.', 'foodord' ),
				wp_strip_all_tags( wc_price( $min ) )
			);
			// Add notice (WC is always available during checkout process).
			wc_add_notice( esc_html( $error ), 'error' );
		}
	}

	/**
	 * Idempotently add disabled/aria-disabled/class/title attributes to the button HTML.
	 *
	 * @param string $html  Original button HTML.
	 * @param string $title Title attribute to add.
	 * @return string
	 */
	private function ensure_button_has_disabled_attributes( $html, $title ) {
		$disabled_added = strpos( $html, 'disabled="disabled"' ) !== false || strpos( $html, 'disabled' ) !== false;

		// Add disabled and aria-disabled attributes right after the opening <button ...
		if ( ! $disabled_added ) {
			$html = preg_replace( '/<button(\s+)/i', '<button$1disabled="disabled" aria-disabled="true" ', $html, 1 );
		}

		// Ensure a disabled CSS class is present (non-visual change if theme styles already cover it).
		if ( strpos( $html, 'class="' ) !== false && strpos( $html, ' foodord-min-order-disabled' ) === false ) {
			$html = preg_replace( '/class="([^"]*)"/i', 'class="$1 foodord-min-order-disabled disabled"', $html, 1 );
		}

		// Add/replace title attribute as a minimal hint.
		if ( strpos( $html, 'title=' ) !== false ) {
			// Replace any existing title content (keep first attribute only).
			$html = preg_replace( '/title="[^"]*"/i', 'title="' . esc_attr( $title ) . '"', $html, 1 );
		} else {
			$html = preg_replace( '/<button(\s+)/i', '<button$1title="' . esc_attr( $title ) . '" ', $html, 1 );
		}

		return $html;
	}

	/**
	 * Determine if WooCommerce cart APIs are available.
	 *
	 * @return bool
	 */
	private function is_wc_ready() {
		if ( ! function_exists( 'WC' ) ) {
			return false;
		}
		$wc = WC();
		return isset( $wc->cart ) && is_object( $wc->cart );
	}

	/**
	 * Get configured minimum order amount.
	 *
	 * Value can be provided by option or filtered programmatically.
	 *
	 * @return float
	 */
	private function get_min_order_amount() {
		// Table ordering (dine-in) should not enforce delivery minimum amounts.
		if ( $this->is_table_order_context() ) {
			return 0.0;
		}

		// If pickup mode is active, disable minimum order enforcement entirely.
		$session    = ( function_exists( 'WC' ) && WC()->session ) ? WC()->session : null;
		$order_type = $session ? (string) $session->get( 'foodord_order_type', 'delivery' ) : 'delivery';
		$order_mode = 'delivery';
		if ( function_exists( 'foodord_get_order_mode' ) ) {
			$order_mode = foodord_get_order_mode();
		} elseif ( $session ) {
			$order_mode = (string) $session->get( 'foodord_order_mode', $order_type );
		} else {
			$order_mode = $order_type;
		}
		if ( 'pickup' === $order_type || 'pickup' === $order_mode ) {
			return 0.0;
		}

		$raw = get_option( 'foodord_min_order_amount', '0' );
		if ( function_exists( 'wc_format_decimal' ) ) {
			$amount = (float) wc_format_decimal( $raw );
		} else {
			$amount = (float) $raw;
		}
		/**
		 * Filter: allow overriding minimum order amount programmatically.
		 *
		 * @param float $amount Current minimum.
		 */
		$amount = (float) apply_filters( 'foodord_min_order_amount', $amount );

		return max( 0.0, $amount );
	}

	/**
	 * Compare current cart total (items only, after discounts) against minimum.
	 *
	 * @param float $min Minimum order amount.
	 * @return bool True when below minimum, false otherwise.
	 */
	private function cart_total_below_min( $min ) {
		if ( ! $this->is_wc_ready() ) {
			return false;
		}

		$cart = WC()->cart;
		// Use cart contents total (after coupons), exclude shipping; common expectation for min-order rules.
		$total = (float) $cart->get_cart_contents_total();

		return $total > 0 ? ( $total < (float) $min ) : ( 0.0 < (float) $min );
	}

	/**
	 * Determine if current visitor is in table ordering (dine-in) context.
	 *
	 * @return bool
	 */
	private function is_table_order_context() {
		$source = '';
		if ( function_exists( 'WC' ) && WC()->session ) {
			$source = (string) WC()->session->get( 'foodord_order_source', '' );
		}
		if ( 'table' === $source ) {
			return true;
		}

		$cookie = filter_input( INPUT_COOKIE, 'foodord_table_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$cookie = is_string( $cookie ) ? sanitize_text_field( wp_unslash( $cookie ) ) : '';
		return '' !== $cookie;
	}
}
