<?php

namespace Quicker\Core\Frontend;

defined( 'ABSPATH' ) || exit;

use Quicker;
use Quicker\Utils\Singleton;

/**
 * Frontend Class
 *
 * @since 1.0.0
 */
class Frontend {

	use Singleton;

	public function init() {
		add_action( 'init', array( $this, 'frontend_actions' ) );
	}

	public function frontend_actions() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return;
		}

		\Quicker\Core\Frontend\DirectCheckout::instance()->init();

		$settings               = \Quicker\Utils\Helper::get_settings();
		$enable_mini_cart       = $settings['enable_mini_cart'] ?? 'no';
		$stick_add_to_cart_btn  = $settings['stick_add_to_cart_btn'] ?? 'no'; // new setting (renamed from stick_add_to_cart_btn)
		$enable_quickview       = ( isset( $settings['quicker_view'] ) && $settings['quicker_view'] === 'yes' ) || ( $stick_add_to_cart_btn === 'yes' && class_exists( 'QuickerPro' ) );

		if ( $enable_mini_cart === 'yes' ) {
			add_action( 'wp_footer', array( $this, 'side_cart' ) );
			add_filter( 'woocommerce_add_to_cart_fragments', array( $this, 'refresh_cart_fragments' ), 50, 1 );
			add_action( 'wp_head', array( $this, 'hide_minicart_on_cart_checkout_pages' ) );
			add_action( 'quicker_mini_cart_after_coupons', array( $this, 'show_fees_in_mini_cart' ), 15 );
		}
		if ( $enable_quickview ) {
			// Avoid duplicate modal markup if QuickerPro also outputs it.
			if ( ! class_exists( 'QuickerPro' ) ) {
				add_action( 'wp_head', array( $this, 'variation_popup_markup' ) );
			}
			// Register BOTH hooks; output function will self-limit based on setting and current_filter().
			add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'add_quick_view_button' ), 25 );
			add_action( 'woocommerce_after_shop_loop_item', array( $this, 'add_quick_view_button' ), 99 );
			// Astra specific: its add to cart/button group hook.
			if ( function_exists( 'astra_theme_version' ) || defined( 'ASTRA_THEME_VERSION' ) ) {
				add_action( 'astra_woo_shop_add_to_cart', array( $this, 'add_quick_view_button' ), 35 );
			}
			// Add extra class to product link wrapper for within-image positioning.
			if ( isset( $settings['quicker_view_btn_postion'] ) && $settings['quicker_view_btn_postion'] === 'within_image' ) {
				// Replace default WooCommerce link open with our own adding class.
				remove_action( 'woocommerce_before_shop_loop_item', 'woocommerce_template_loop_product_link_open', 10 );
				add_action( 'woocommerce_before_shop_loop_item', array( $this, 'quicker_loop_product_link_open' ), 10 );
			}
			// If within image, also filter the thumbnail HTML to ensure overlay (some themes restructure hooks).
			if ( isset( $settings['quicker_view_btn_postion'] ) && $settings['quicker_view_btn_postion'] === 'within_image' ) {
				add_filter( 'woocommerce_get_product_thumbnail', array( $this, 'inject_button_in_thumbnail' ), 99, 2 );
			}
			add_action( 'wp_head', array( $this, 'output_quickview_custom_styles' ), 101 );

		}

		// Default Quantity on single product page (global or min/max-qty specific).
		$has_global_default = ! empty( $settings['default_quantity'] ) && (int) $settings['default_quantity'] > 0;
		$has_qty_default    = ( $settings['mix_max_order_qty'] ?? '' ) === 'yes' && ! empty( $settings['default_order_quantity'] ) && (int) $settings['default_order_quantity'] > 0;
		if ( $has_global_default || $has_qty_default ) {
			add_filter( 'woocommerce_quantity_input_args', array( $this, 'set_default_quantity' ), 10, 2 );
		}

		// Min/Max order quantity validation message.
		if ( isset( $settings['mix_max_order_qty'] ) && $settings['mix_max_order_qty'] === 'yes' ) {
			add_action( 'woocommerce_check_cart_items', array( $this, 'check_min_max_order_quantity' ) );
			add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'validate_qty_on_add_to_cart' ), 10, 3 );
			add_filter( 'woocommerce_quantity_input_args', array( $this, 'set_qty_input_max' ), 20, 2 );
		}

		// Basic Extra Fee (free plugin) if enabled.
		if ( isset( $settings['enable_extra_fees'] ) && $settings['enable_extra_fees'] === 'yes' ) {
			add_action( 'woocommerce_cart_calculate_fees', array( $this, 'apply_extra_fee' ), 28 );
			add_filter( 'woocommerce_cart_fee_label', array( $this, 'style_extra_fee_label' ), 10, 2 );
			add_filter( 'woocommerce_cart_totals_fee_html', array( $this, 'style_extra_fee_amount_html' ), 10, 2 );
			// Print inline styles so the fee line stands out
			add_action( 'wp_head', array( $this, 'print_extra_fee_styles' ) );
		}
	}

	/**
	 * Variation popup modal markup
	 */
	public static function variation_popup_markup() {
		?>
		<div class="quicker-popup-modal quicker_popup_content" role="dialog" aria-modal="true" aria-label="Quick View"></div>
		<?php
	}

	/**
	 * Output dynamic styles for Quick View customization settings.
	 */
	public function output_quickview_custom_styles() {
		$settings = \Quicker\Utils\Helper::get_settings();
		$stick_add_to_cart_btn = $settings['stick_add_to_cart_btn'] ?? 'no';
		// Bail out early if neither quick view nor stick flag enabled to avoid unnecessary style tag.
		if ( ( $settings['quicker_view'] ?? 'no' ) !== 'yes' && ! ( $stick_add_to_cart_btn === 'yes' && class_exists( 'QuickerPro' ) ) ) {
			return;
		}
		// Collect settings
		$content_bg  = isset( $settings['quickview_content_bg'] ) ? trim( $settings['quickview_content_bg'] ) : '';
		$overlay_col = isset( $settings['quickview_overlay'] ) ? trim( $settings['quickview_overlay'] ) : '';
		$btn_color   = isset( $settings['quickview_btn_color'] ) ? trim( $settings['quickview_btn_color'] ) : '';
		$btn_hover   = isset( $settings['quickview_btn_hover'] ) ? trim( $settings['quickview_btn_hover'] ) : '';
		$text_color  = isset( $settings['quickview_text_color'] ) ? trim( $settings['quickview_text_color'] ) : '';
		$text_hover  = isset( $settings['quickview_text_hover'] ) ? trim( $settings['quickview_text_hover'] ) : '';
		$icon_color  = isset( $settings['quickview_icon'] ) ? trim( $settings['quickview_icon'] ) : '';
		$icon_hover  = isset( $settings['quickview_icon_hover'] ) ? trim( $settings['quickview_icon_hover'] ) : '';
		// Elements to hide
		$hide_opts   = isset( $settings['quicker_view_options'] ) ? (array) $settings['quicker_view_options'] : array();

		$css = '';
		if ( $overlay_col !== '' ) {
			$css .= ".quicker-popup-modal{background: {$overlay_col}!important;}";
		}
		if ( $content_bg !== '' ) {
			$css .= ".quicker-popup-modal .modal-content{background: {$content_bg}!important;}";
		}
		if ( $btn_color !== '' ) {
			$css .= ".quicker_q_view{background: {$btn_color}!important;border-color: {$btn_color}!important;}";
		}
		if ( $btn_hover !== '' ) {
			$css .= ".quicker_q_view:hover{background: {$btn_hover}!important;border-color: {$btn_hover}!important;}";
		}
		if ( $text_color !== '' ) {
			$css .= ".quicker_q_view{color: {$text_color}!important;}";
		}
		if ( $text_hover !== '' ) {
			$css .= ".quicker_q_view:hover{color: {$text_hover}!important;}";
		}
		if ( $icon_color !== '' ) {
			$css .= ".modal-content .quicker-popup-close svg circle{fill: {$icon_color}!important;opacity:1;}";
		}
		if ( $icon_hover !== '' ) {
			$css .= ".modal-content .quicker-popup-close:hover svg circle{fill: {$icon_hover}!important;}";
		}
		// Hide selected pieces within quick view modal
		if ( ! empty( $hide_opts ) ) {
			$map = array(
				'image'    => '.modal-content .woocommerce-product-gallery',
				'title'    => '.modal-content .product_title',
				'rating'   => '.modal-content .woocommerce-product-rating',
				'price'    => '.modal-content p.price',
				'meta'     => '.modal-content .product_meta',
				'add_cart' => '.modal-content form.cart',
			);
			$selectors = array();
			$hide_image = false;
			foreach ( $hide_opts as $opt ) {
				if ( isset( $map[ $opt ] ) ) { $selectors[] = $map[ $opt ]; }
				if ( $opt === 'image' ) { $hide_image = true; }
			}
			if ( ! empty( $selectors ) ) {
				$css .= implode( ',', $selectors ) . '{display:none !important;}';
			}
			if ( $hide_image ) {
				// Common WooCommerce structure: .product div.images + .summary; account for themes using columns.
				$css .= '.quicker-popup-modal .modal-content .product,.quicker-popup-modal .modal-content .product .summary{width:100%!important;max-width:100%!important;}'
					. '.quicker-popup-modal .modal-content .product .summary{float:none!important;margin:0!important;padding:0!important;}'
					. '.quicker-popup-modal .modal-content .product .summary:before,.quicker-popup-modal .modal-content .product .summary:after{display:none!important;}'
					. '.quicker-popup-modal .modal-content .product{display:block!important;}'
					. '.quicker-popup-modal .modal-content .woocommerce-product-gallery{display:none!important;}';
			}
		}
		// Constrain long descriptions only when feature toggle is enabled.
		if ( $stick_add_to_cart_btn === 'yes' ) {
			$css .= '.quicker-popup-modal .modal-content .woocommerce-product-details__short-description{max-height:220px;overflow:auto;scrollbar-width:thin;}' .
				'.quicker-popup-modal .modal-content .woocommerce-product-details__short-description::-webkit-scrollbar{width:6px;}' .
				'.quicker-popup-modal .modal-content .woocommerce-product-details__short-description::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.25);border-radius:3px;}';
		}
		if ( $css === '' ) { return; }
		echo '<style id="quicker-quickview-dynamic-css">' . esc_html( $css ) . '</style>';
	}

	private static function build_quick_view_button( $product ) {
		$settings     = \Quicker\Utils\Helper::get_settings();
		$position     = isset( $settings['quicker_view_btn_postion'] ) ? $settings['quicker_view_btn_postion'] : '';
		$mobile_class = $settings['hide_quicker_view_mobile'] == 'yes' ? ' quicker-hide-mobile' : '';
		$label    = isset( $settings['quicker_btn_label'] ) ? $settings['quicker_btn_label'] : __( 'Quick View', 'quicker' );
		$icon_url = isset( $settings['qview_icon'] ) ? trim( $settings['qview_icon'] ) : '';
		$classes  = 'quicker_q_view product_type_' . $product->get_type() . $mobile_class;
		$classes .= ( $position === 'after_cart_btn' ) ? ' quicker-qv-after-cart' : ' quicker-qv-in-image';
		$label     = apply_filters( 'quicker_quick_view_button_label', $label, $product );
		$icon_html = '';
		if ( $icon_url ) {
			$icon_html = \Quicker\Utils\Helper::inline_svg_or_img( $icon_url, array( 'class' => 'quicker-qv-icon is-svg' ) );
			if ( $icon_html === '' ) {
				$icon_html = '<img class="quicker-qv-icon" src="' . esc_url( $icon_url ) . '" alt="" />';
			}
		}
		if ( $icon_html === '' ) {
			$icon_html = '<span class="quicker-qv-icon quicker-icon is-svg" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span>';
		}
		$icon_html = apply_filters( 'quicker_quick_view_button_icon_html', $icon_html, $product );
		$classes   = apply_filters( 'quicker_quick_view_button_classes', $classes, $product );
		// Use anchor for outside image, div (role=button) when inside image to avoid nested anchors.
		if ( $position === 'within_image' ) {
			$button_html = '<div class="' . esc_attr( $classes ) . ' quicker-in-thumb" role="button" tabindex="0" data-product_id="' . esc_attr( $product->get_id() ) . '">' . $icon_html . '<span class="quicker-qv-label">' . esc_html( $label ) . '</span></div>';
		} else {
			$button_html = '<a href="#" class="' . esc_attr( $classes ) . '" data-product_id="' . esc_attr( $product->get_id() ) . '">' . $icon_html . '<span class="quicker-qv-label">' . esc_html( $label ) . '</span></a>';
		}
		return apply_filters( 'quicker_quick_view_button_html', $button_html, $product, $label, $icon_html );
	}

	public static function add_quick_view_button() {
		global $product;
		$settings     = \Quicker\Utils\Helper::get_settings();
		$position     = isset( $settings['quicker_view_btn_postion'] ) ? $settings['quicker_view_btn_postion'] : '';
		$current_hook = current_filter();
		// Only render once on the correct hook based on position setting.
		if ( $position === 'after_cart_btn' ) {
			$allowed_after_hooks = array( 'woocommerce_after_shop_loop_item', 'astra_woo_shop_add_to_cart' );
			if ( ! in_array( $current_hook, $allowed_after_hooks, true ) ) { return; }
			// Prevent duplicate output if both hooks fire for same product: mark via static.
			static $printed_for_product = array();
			$pid = $product->get_id();
			if ( isset( $printed_for_product[ $pid ] ) ) { return; }
			$printed_for_product[ $pid ] = true;
		} else { // within image: let thumbnail filter inject; skip here unless fallback
			if ( $current_hook !== 'woocommerce_before_shop_loop_item_title' ) { return; }
		}
		if ( $product->get_type() === 'external' ) { return; }
		echo self::build_quick_view_button( $product ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	public function inject_button_in_thumbnail( $html, $size ) {
		$settings = \Quicker\Utils\Helper::get_settings();
		if ( ! isset( $settings['quicker_view_btn_postion'] ) || $settings['quicker_view_btn_postion'] !== 'within_image' ) { return $html; }
		global $product;
		if ( ! $product || $product->get_type() === 'external' ) { return $html; }
		$button = self::build_quick_view_button( $product );
		return '<div class="quicker-thumb-wrapper">' . $html . $button . '</div>';
	}

	/**
	 * Open product link with extra class when within-image quick view active.
	 */
	public function quicker_loop_product_link_open() {
		global $product;
		if ( empty( $product ) ) { return; }
		$link = apply_filters( 'woocommerce_loop_product_link', get_the_permalink(), $product );
		echo '<a class="woocommerce-LoopProduct-link woocommerce-loop-product__link quicker-has-quickview" href="' . esc_url( $link ) . '">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Side cart data
	 */
	public static function side_cart_data( $cart_item, $cart_item_key ) {
		$product       = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
		$product_id    = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
		$product_name  = apply_filters( 'woocommerce_cart_item_name', $product->get_name(), $cart_item, $cart_item_key );
		$thumbnail     = apply_filters( 'woocommerce_cart_item_thumbnail', $product->get_image( array( 80, 80 ) ), $cart_item, $cart_item_key );
		$product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $product ), $cart_item, $cart_item_key );
		$link          = apply_filters( 'woocommerce_cart_item_permalink', $product->is_visible() ? $product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );

		// DISCOUNTIFY INTEGRATION: Add discount info directly to product name
		// Skip for BOGO free items — the FREE tag is shown in the template.
		if ( class_exists( 'Discountify\Core\Frontend\Coupon' ) &&
			empty( $cart_item['bogo_x_x_free'] ) && empty( $cart_item['bogo_x_y_free'] ) ) {
			$check_product_id = ! empty( $cart_item['variation_id'] ) ? $cart_item['variation_id'] : $cart_item['product_id'];
			$discount_data = \Discountify\Core\Frontend\Coupon::instance()->item_discount_check( $check_product_id, $cart_item['quantity'], $cart_item );

			if ( !empty( $discount_data ) && !empty($discount_data['message']) ) {
				$product_name .= '<div class="discountify-cart-meta">';
				$label_prefix  = ! empty( $discount_data['msg_label'] ) ? esc_html( $discount_data['msg_label'] ) . ' ' : '';
					$product_name .= '<small class="discountify-label">' . $label_prefix . wp_kses_post( $discount_data['message'] ) . '</small>';
				$product_name .= '</div>';
			}
		}

		return array(
			'product'       => $product,
			'product_id'    => $product_id,
			'product_name'  => $product_name,
			'thumbnail'     => $thumbnail,
			'product_price' => $product_price,
			'link'          => $link,
		);
	}

	public function get_all_bump_items( $bump_product_type, $cross_sell_products ) {
		$bump_items = array();
		if ( ! empty( WC()->cart ) && ! WC()->cart->is_empty() ) :
			foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) :
				$product = wc_get_product( $cart_item['product_id'] );

				if ( $bump_product_type == 'upsell' ) {
					foreach ( $product->get_upsell_ids() as $item ) {
						$bump_items[] = $item;
					}
				} elseif ( $bump_product_type == 'cross_sell' ) {
					foreach ( $product->get_cross_sell_ids() as $item ) {
						$bump_items[] = $item;
					}
				} else {
					$all_items = array_unique( array_merge( $product->get_upsell_ids(), $product->get_cross_sell_ids() ) );
					foreach ( $all_items as $item ) {
						$bump_items[] = $item;
					}
				}
			endforeach;

		endif;

		if ( count( $bump_items ) == 0 ) {
			$bump_items = $cross_sell_products;
		}

		return $bump_items;
	}

	/**
	 * Hide mini cart on cart and checkout pages with CSS
	 */
	public function hide_minicart_on_cart_checkout_pages() {
		if ( is_cart() || is_checkout() ) {
			echo '<style>.quicker-container, .side-cart-icon { display: none !important; }</style>';
		}
	}

	/**
	 * Show fees in mini cart (fires via quicker_mini_cart_after_coupons hook)
	 */
	public function show_fees_in_mini_cart() {
		if ( ! WC()->cart || ! WC()->cart->get_fees() ) {
			return;
		}
		foreach ( WC()->cart->get_fees() as $fee ) {
			if ( 0 !== $fee->amount ) {
				?>
				<p class="mini-cart-fee">
					<span class="fee-label"><?php echo esc_html( $fee->name ); ?></span>
					<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wc_price returns safe HTML ?>
					<span class="fee-amount"><?php echo wc_price( $fee->total ); ?></span>
				</p>
				<?php
			}
		}
	}

	/**
	 * Load mini cart in footer
	 */
	public function side_cart() {
		// Don't show mini cart on cart and checkout pages
		if ( is_cart() || is_checkout() ) {
			return;
		}

		$settings              = \Quicker\Utils\Helper::get_settings();
		$bump_product_type     = $settings['bump_product_type'];
		$upsell_heading        = $settings['upsell_heading'];
		$enable_cart_upsell    = $settings['enable_cart_upsell'] ?? '';
		$side_cart_style       = $settings['side_cart_style'] ?? '1';
		$cart_order_bump_style = $settings['cart_order_bump_style'] ?? '1';
		$show_coupon_form      = $settings['show_coupon_form'];
		$up_sell_products      = $settings['up_sell_products'] ?? array();
		$bump_section          = ( class_exists( 'QuickerPro' ) && $enable_cart_upsell == 'yes' ) ? 'quicker-up-sells' : '';

		$template_path  = \Quicker::template_dir() . 'side-cart/template-' . $side_cart_style . '.php';
		$cart_count     = WC()->cart->get_cart_contents_count();
		$all_bump_items = $this->get_all_bump_items( $bump_product_type, $up_sell_products );

		if ( file_exists( $template_path ) ) {
			echo '<div class="quicker-container">';
			?>
			<div class="added-message" data-added_message="<?php esc_attr_e( 'Product has been added', 'quicker' ); ?>"></div>
			<span class="side-cart-icon" onClick="cart_open()">
				<?php echo \Quicker\Utils\Helper::get_cart_icon_svg( $settings['sc_icon_type'] ?? 'cart-1' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
				<div class="quicker-cart-count"><?php echo intval( $cart_count ); ?></div>
			</span>
			<?php
			include $template_path;
			echo '</div>';
		}
	}

	/**
	 * AJAX fragment updates
	 */
	public function refresh_cart_fragments( $fragments ) {
		if ( ! WC()->cart ) {
			return $fragments;
		}

		// Don't update fragments on cart and checkout pages
		if ( is_cart() || is_checkout() ) {
			return $fragments;
		}

		// Force session reload to ensure we have the latest cart data
		if ( WC()->session ) {
			WC()->cart->get_cart_from_session();
		}

		// Capture notices BEFORE anything else, as they may be cleared during processing
		$all_notices = wc_get_notices();

		// Trigger cart loaded from session to ensure all hooks fire properly
		do_action( 'woocommerce_cart_loaded_from_session', WC()->cart );

		// Force cart totals recalculation to ensure discount hooks fire
		// This triggers woocommerce_before_calculate_totals where discounts are applied

		// Manually apply Discountify coupons before calculating totals during fragment refresh
		if ( class_exists( 'Discountify\Core\Frontend\Coupon' ) ) {
			$coupons_to_apply = array();
			foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
				$product_id = ! empty( $cart_item['variation_id'] ) ? $cart_item['variation_id'] : $cart_item['product_id'];
				$quantity   = $cart_item['quantity'];
				$price      = $cart_item['data']->get_price();

				$result = \Discountify\Core\Frontend\Coupon::instance()->get_coupon_discount( $product_id, $quantity, $price );
				if ( ! empty( $result ) && ! empty( $result['rules_type'] ) ) {
					$allowed_coupons = array( 'bogo', 'discount', 'total_spend' );
					// Add the coupon if it's an allowed type
					if ( in_array( $result['rules_type'], $allowed_coupons ) ) {
						// Check bogo_discount_type only if it exists
						if ( ! isset( $result['bogo_discount_type'] ) || $result['bogo_discount_type'] !== 'buy_x_get_y' ) {
							$coupons_to_apply[] = $result['rules_type'];
						}
					}
				}
			}

			// Apply unique coupons
			if ( ! empty( $coupons_to_apply ) ) {
				$coupons_to_apply = array_unique( $coupons_to_apply );
				foreach ( $coupons_to_apply as $coupon_code ) {
					if ( ! in_array( $coupon_code, WC()->cart->applied_coupons ) ) {
						WC()->cart->applied_coupons[] = $coupon_code;
					}
				}
				// Save the applied coupons to session
				if ( WC()->session ) {
					WC()->session->set( 'applied_coupons', WC()->cart->applied_coupons );
				}
			}
		}

		WC()->cart->calculate_totals();

		$settings              = \Quicker\Utils\Helper::get_settings();
		$bump_product_type     = $settings['bump_product_type'];
		$upsell_heading        = $settings['upsell_heading'];
		$side_cart_style       = $settings['side_cart_style'] ?? '1';
		$cart_order_bump_style = $settings['cart_order_bump_style'] ?? '1';
		$show_coupon_form      = $settings['show_coupon_form'];
		$up_sell_products      = $settings['up_sell_products'] ?? array();
		$enable_cart_upsell    = $settings['enable_cart_upsell'] ?? '';
		$bump_section          = ( class_exists( 'QuickerPro' ) && $enable_cart_upsell == 'yes' ) ? 'quicker-up-sells' : '';
		$all_bump_items        = $this->get_all_bump_items( $bump_product_type, $up_sell_products );
		$settings              = \Quicker\Utils\Helper::get_settings();
		$side_cart_style       = $settings['side_cart_style'] ?? '1';

		$template_path = \Quicker::template_dir() . 'side-cart/template-' . $side_cart_style . '.php';
		$cart_count    = WC()->cart->get_cart_contents_count();
		$cart_total    = WC()->cart->get_cart_total();

		ob_start();

		?>
		<div class="quicker-container">
			<div class="added-message" data-added_message="<?php esc_attr_e( 'Product has been added', 'quicker' ); ?>"></div>
			<span class="side-cart-icon" onClick="cart_open()">
				<?php echo \Quicker\Utils\Helper::get_cart_icon_svg( $settings['sc_icon_type'] ?? 'cart-1' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
				<div class="quicker-cart-count"><?php echo intval( $cart_count ); ?></div>
			</span>
			<?php
			if ( file_exists( $template_path ) ) {
				include $template_path;
			}
			?>
		</div>
		<?php

		$fragments['div.quicker-container']   = ob_get_clean();
		$fragments['div.quicker-cart-count']  = '<div class="quicker-cart-count">' . intval( $cart_count ) . '</div>';
		$fragments['span.quicker-cart-total'] = $cart_total;

		// Add WooCommerce notices fragment - use captured notices or current ones
		$notice_html = '';
		if ( ! empty( $all_notices ) ) {
			foreach ( $all_notices as $type => $messages ) {
				foreach ( $messages as $message ) {
					$notice_html .= '<div class="woocommerce-message" role="alert">' . wp_kses_post( $message['notice'] ) . '</div>';
				}
			}
		}

		if ( ! empty( $notice_html ) ) {
			$fragments['div.woocommerce-notices-wrapper'] = '<div class="woocommerce-notices-wrapper">' . $notice_html . '</div>';
		}

		return $fragments;
	}

	/**
	 * Apply simplified extra fee (mirrors Pro logic subset) on checkout/cart.
	 */
	public function apply_extra_fee( $cart ) {
		if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
		if ( ! $cart || $cart->is_empty() ) return;
		$settings = \Quicker\Utils\Helper::get_settings();
		if ( empty( $settings['enable_extra_fees'] ) || $settings['enable_extra_fees'] !== 'yes' ) return;
		$amount = isset( $settings['extra_fees_amount'] ) ? (float) $settings['extra_fees_amount'] : 0;
		if ( $amount == 0 ) return; // nothing to add
		$label = isset( $settings['extra_fees_label'] ) && $settings['extra_fees_label'] !== '' ? $settings['extra_fees_label'] : __( 'Extra Fee', 'quicker' );
		$desc  = isset( $settings['extra_fees_description'] ) ? trim( $settings['extra_fees_description'] ) : '';
		$scope = isset( $settings['extra_fees_scope'] ) ? $settings['extra_fees_scope'] : 'all';
		$role_scope = isset( $settings['extra_fees_user_roles'] ) ? (array) $settings['extra_fees_user_roles'] : array();

		// Role filtering (ignore if empty or contains 'all').
		if ( ! empty( $role_scope ) && ! in_array( 'all', $role_scope, true ) ) {
			$current_user = wp_get_current_user();
			if ( $current_user && ! empty( $current_user->roles ) ) {
				if ( empty( array_intersect( $current_user->roles, $role_scope ) ) ) return;
			} else {
				return; // no user roles, treat as mismatch
			}
		}

		$apply = false;
		if ( $scope === 'all' ) {
			$apply = true;
		} elseif ( $scope === 'products' ) {
			$targets = isset( $settings['extra_fees_products'] ) ? (array) $settings['extra_fees_products'] : array();
			if ( ! empty( $targets ) ) {
				foreach ( $cart->get_cart() as $item ) {
					if ( in_array( (string) $item['product_id'], array_map( 'strval', $targets ), true ) ) { $apply = true; break; }
				}
			}
		} elseif ( $scope === 'categories' ) {
			$targets = isset( $settings['extra_fees_categories'] ) ? (array) $settings['extra_fees_categories'] : array();
			if ( ! empty( $targets ) ) {
				foreach ( $cart->get_cart() as $item ) {
					$prod_cats = wp_get_post_terms( $item['product_id'], 'product_cat', array( 'fields' => 'ids' ) );
					if ( array_intersect( $targets, $prod_cats ) ) { $apply = true; break; }
				}
			}
		}

		// Cart total scope: check subtotal threshold (global only)
		if ( $scope === 'cart_total' ) {
			$threshold = isset( $settings['extra_fees_cart_total'] ) && $settings['extra_fees_cart_total'] !== '' ? (float) $settings['extra_fees_cart_total'] : 0;
			if ( $threshold > 0 ) {
				$cart_total_val = (float) WC()->cart->get_cart_contents_total();
				if ( $cart_total_val >= $threshold ) { $apply = true; }
			}
		}

		// Cart quantity scope: check quantity threshold (global only)
		if ( $scope === 'cart_quantity' ) {
			$threshold_qty = isset( $settings['extra_fees_cart_quantity'] ) && $settings['extra_fees_cart_quantity'] !== '' ? (int) $settings['extra_fees_cart_quantity'] : 0;
			if ( $threshold_qty > 0 ) {
				$cart_qty = (int) WC()->cart->get_cart_contents_count();
				if ( $cart_qty >= $threshold_qty ) { $apply = true; }
			}
		}

		if ( ! $apply ) return;
		if ( $desc !== '' ) {
			$label .= ' - ' . wp_strip_all_tags( $desc );
		}
		$cart->add_fee( $label, $amount );
	}

	/**
	 * Wrap the fee label for styling when it's the plugin extra fee
	 */
	public function style_extra_fee_label( $label, $fee ) {
		$settings = \Quicker\Utils\Helper::get_settings();
		$expected = isset( $settings['extra_fees_label'] ) && $settings['extra_fees_label'] !== '' ? $settings['extra_fees_label'] : __( 'Extra Fee', 'quicker' );
		// If the configured label is present in the rendered label, mark it
		if ( false !== stripos( $label, $expected ) ) {
			return '<span class="quicker-extra-fee-label">' . wp_kses_post( $label ) . '</span>';
		}
		// Fallback: inspect the fee object (some themes/filters alter the rendered label)
		$fee_name = '';
		if ( is_object( $fee ) ) {
			if ( method_exists( $fee, 'get_name' ) ) {
				$fee_name = $fee->get_name();
			} elseif ( isset( $fee->name ) ) {
				$fee_name = $fee->name;
			}
		}
		if ( $fee_name && false !== stripos( $fee_name, $expected ) ) {
			return '<span class="quicker-extra-fee-label">' . wp_kses_post( $label ) . '</span>';
		}
		return $label;
	}

	/**
	 * Wrap the fee amount HTML (cart totals) so we can style the amount too
	 */
	public function style_extra_fee_amount_html( $html, $fee ) {
		$settings = \Quicker\Utils\Helper::get_settings();
		$expected = isset( $settings['extra_fees_label'] ) && $settings['extra_fees_label'] !== '' ? $settings['extra_fees_label'] : __( 'Extra Fee', 'quicker' );
		// $fee may be WC_Order_Item_Fee or WC_Fee; handle both by checking name
		$fee_name = '';
		if ( is_object( $fee ) ) {
			if ( method_exists( $fee, 'get_name' ) ) {
				$fee_name = $fee->get_name();
			} elseif ( isset( $fee->name ) ) {
				$fee_name = $fee->name;
			}
		}
		if ( false !== stripos( $fee_name, $expected ) ) {
			return '<span class="quicker-extra-fee-amount">' . wp_kses_post( $html ) . '</span>';
		}
		return $html;
	}

	/**
	 * Print minimal inline styles for the extra fee line
	 */
	public function print_extra_fee_styles() {
		$settings = \Quicker\Utils\Helper::get_settings();
		if ( empty( $settings['enable_extra_fees'] ) || $settings['enable_extra_fees'] !== 'yes' ) { return; }
		$color = '#27ae60'; // accent color for fee (green)

		// Build the expected (possibly suffixed) label exactly as we add the fee so we can target block markup too.
		$label = isset( $settings['extra_fees_label'] ) && $settings['extra_fees_label'] !== '' ? $settings['extra_fees_label'] : __( 'Extra Fee', 'quicker' );
		$desc  = isset( $settings['extra_fees_description'] ) && $settings['extra_fees_description'] !== '' ? wp_strip_all_tags( $settings['extra_fees_description'] ) : '';
		$full_label = $label;
		if ( $desc !== '' ) {
			$full_label .= ' - ' . $desc;
		}
		// Slugify to match the block class (wc-block uses sanitize_title on fee name)
		$slug = sanitize_title( $full_label );

		$css  = ".quicker-extra-fee-label{color:{$color};font-weight:600;}";
		$css .= ".quicker-extra-fee-amount{color:{$color};font-weight:600;}";
		$css .= ".quicker-extra-fee-label .quicker-extra-fee-amount, .quicker-extra-fee-amount { display:inline-block; }";
		// WC Blocks output adds a fee-specific class using the fee slug e.g. .wc-block-components-totals-fees__extra-fee-this-is-additional-fees
		$css .= ".wc-block-components-totals-fees__{$slug} .wc-block-components-totals-item__label, .wc-block-components-totals-fees__{$slug} .wc-block-components-totals-item__value { color:{$color};font-weight:600; }";

		echo '<style id="quicker-extra-fee-style">' . esc_html( $css ) . '</style>';
	}

	/**
	 * Check if a product belongs to the configured qty restriction scope.
	 */
	private static function is_in_qty_scope( string $scope, int $product_id, array $target_products, array $target_categories ): bool {
		if ( $scope === 'all' ) {
			return true;
		}
		if ( $scope === 'products' ) {
			// Cast both sides to int to avoid string/int mismatch from saved settings.
			return in_array( $product_id, array_map( 'intval', $target_products ), true );
		}
		if ( $scope === 'categories' ) {
			// has_term() is reliable in all contexts including AJAX (wp_get_post_terms can miss taxonomy cache).
			foreach ( $target_categories as $cat_id ) {
				if ( has_term( (int) $cat_id, 'product_cat', $product_id ) ) {
					return true;
				}
			}
			return false;
		}
		return false;
	}

	/**
	 * Count cart items that fall within the configured scope.
	 * Pass $exclude_key + $replace_qty to simulate updating an existing cart row.
	 * Pass $extra_product_id + $extra_qty to simulate adding a new product.
	 */
	public static function get_scoped_cart_qty( array $settings, string $exclude_key = '', int $replace_qty = 0, int $extra_product_id = 0, int $extra_qty = 0 ): int {
		$scope             = $settings['mix_max_qty_scope'] ?? 'all';
		$target_products   = array_map( 'intval', (array) ( $settings['mix_max_qty_products']  ?? array() ) );
		$target_categories = array_map( 'intval', (array) ( $settings['mix_max_qty_categories'] ?? array() ) );
		$cart_items        = WC()->cart ? WC()->cart->get_cart() : array();

		if ( $scope === 'all' ) {
			$total = WC()->cart ? (int) WC()->cart->get_cart_contents_count() : 0;
			if ( $exclude_key !== '' && isset( $cart_items[ $exclude_key ] ) ) {
				$total = $total - (int) $cart_items[ $exclude_key ]['quantity'] + $replace_qty;
			}
			return $total + $extra_qty;
		}

		$scoped = 0;
		foreach ( $cart_items as $key => $item ) {
			$pid = (int) $item['product_id'];
			$qty = ( $exclude_key !== '' && $key === $exclude_key ) ? $replace_qty : (int) $item['quantity'];
			if ( $qty > 0 && self::is_in_qty_scope( $scope, $pid, $target_products, $target_categories ) ) {
				$scoped += $qty;
			}
		}

		if ( $extra_product_id > 0 && $extra_qty > 0 && self::is_in_qty_scope( $scope, $extra_product_id, $target_products, $target_categories ) ) {
			$scoped += $extra_qty;
		}

		return $scoped;
	}

	/**
	 * Block adding to cart when it would exceed the max order quantity.
	 */
	public function validate_qty_on_add_to_cart( $passed, $product_id, $quantity ) {
		$settings = \Quicker\Utils\Helper::get_settings();
		$max      = isset( $settings['max_order_quantity'] ) && $settings['max_order_quantity'] !== '' ? (int) $settings['max_order_quantity'] : 0;

		if ( $max > 0 ) {
			$scoped_qty = self::get_scoped_cart_qty( $settings, '', 0, (int) $product_id, (int) $quantity );
			if ( $scoped_qty > $max ) {
				/* translators: %d: maximum allowed quantity */
				wc_add_notice( sprintf( __( 'You cannot add more than %d items to your cart.', 'quicker' ), $max ), 'error' );
				return false;
			}
		}

		return $passed;
	}

	/**
	 * Validate cart scoped quantity against min/max settings and show a notice.
	 */
	public function check_min_max_order_quantity() {
		$settings = \Quicker\Utils\Helper::get_settings();
		$min      = isset( $settings['min_order_quantity'] ) && $settings['min_order_quantity'] !== '' ? (int) $settings['min_order_quantity'] : 0;
		$max      = isset( $settings['max_order_quantity'] ) && $settings['max_order_quantity'] !== '' ? (int) $settings['max_order_quantity'] : 0;

		if ( $min === 0 && $max === 0 ) {
			return;
		}

		$cart_qty = self::get_scoped_cart_qty( $settings );
		$msg_tpl  = isset( $settings['mix_max_order_qty_msg'] ) ? trim( $settings['mix_max_order_qty_msg'] ) : '';

		if ( $min > 0 && $cart_qty > 0 && $cart_qty < $min ) {
			$message = $msg_tpl !== ''
				? str_replace( array( '{min}', '{max}', '{qty}' ), array( $min, $max ?: '∞', $cart_qty ), $msg_tpl )
				/* translators: 1: minimum quantity, 2: current quantity */
				: sprintf( __( 'Your order must have at least %1$s items. Current: %2$s.', 'quicker' ), $min, $cart_qty );
			wc_add_notice( $message, 'error' );
		} elseif ( $max > 0 && $cart_qty > $max ) {
			$message = $msg_tpl !== ''
				? str_replace( array( '{min}', '{max}', '{qty}' ), array( $min ?: '0', $max, $cart_qty ), $msg_tpl )
				/* translators: 1: maximum quantity, 2: current quantity */
				: sprintf( __( 'Your order cannot exceed %1$s items. Current: %2$s.', 'quicker' ), $max, $cart_qty );
			wc_add_notice( $message, 'error' );
		}
	}

	/**
	 * Set max_value on the quantity input so the browser enforces the cart max on product pages.
	 */
	public function set_qty_input_max( $args, $product ) {
		if ( ! is_product() ) {
			return $args;
		}
		$settings          = \Quicker\Utils\Helper::get_settings();
		$max               = isset( $settings['max_order_quantity'] ) && $settings['max_order_quantity'] !== '' ? (int) $settings['max_order_quantity'] : 0;
		$scope             = $settings['mix_max_qty_scope'] ?? 'all';
		$target_products   = array_map( 'intval', (array) ( $settings['mix_max_qty_products']  ?? array() ) );
		$target_categories = array_map( 'intval', (array) ( $settings['mix_max_qty_categories'] ?? array() ) );

		if ( $max > 0 && self::is_in_qty_scope( $scope, (int) $product->get_id(), $target_products, $target_categories ) ) {
			$current_scoped  = self::get_scoped_cart_qty( $settings );
			$remaining       = $max - $current_scoped;
			// Only restrict the input when there is meaningful headroom; avoid hiding the field (WC hides when min===max).
			if ( $remaining > 1 ) {
				$args['max_value'] = $remaining;
			}
		}

		return $args;
	}

	/**
	 * Sets the default quantity value on the single product page quantity input.
	 * Priority: default_order_quantity (when min/max qty enabled) > default_quantity (global).
	 */
	public function set_default_quantity( $args, $product ) {
		if ( ! is_product() ) {
			return $args;
		}
		$settings = \Quicker\Utils\Helper::get_settings();
		$default  = (int) ( $settings['default_quantity'] ?? 0 );
		// Override with the qty-restriction-specific default when that feature is on.
		if ( ( $settings['mix_max_order_qty'] ?? '' ) === 'yes' ) {
			$order_qty_default = (int) ( $settings['default_order_quantity'] ?? 0 );
			if ( $order_qty_default > 0 ) {
				$default = $order_qty_default;
			}
		}
		if ( $default > 0 ) {
			$args['input_value'] = $default;
		}
		return $args;
	}
}
