<?php
/**
 * Storzen Module — Floating Cart
 *
 * A persistent floating cart icon with live item count, optional running total,
 * and 3 reveal modes (mini-cart drawer, go to cart page, hover preview).
 *
 * Hooks into WooCommerce's existing fragment system so the count badge,
 * total, and drawer contents auto-refresh on add-to-cart without us writing
 * custom AJAX — WC does that work for free via `woocommerce_add_to_cart_fragments`.
 *
 * Settings honored (all 12):
 *   position, icon_style, reveal_action, hide_when_empty,
 *   show_count_badge, show_total, animation,
 *   bg_color, icon_color, show_on_mobile, show_on_desktop, hide_on_pages
 *
 * Compatibility:
 *   • Classic + Block themes
 *   • HPOS (display-only, no order data touched)
 *   • Loads on every front-end page when enabled (drawer is empty until needed)
 *   • Mobile-first responsive (icon ≥ 48px tap target; drawer is full-screen on mobile)
 *
 * @package storzen
 * @since   1.4.0
 */

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

if ( ! class_exists( 'Storzen_Module_Floating_Cart' ) ) {

	class Storzen_Module_Floating_Cart {

		/** @var array|null Resolved settings cache (per request) */
		private $settings = null;

		public function __construct() {
			if ( ! function_exists( 'WC' ) ) {
				return;
			}

			// Frontend rendering.
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
			add_action( 'wp_footer',          array( $this, 'render_button' ), 20 );

			// Hook our cart-count + total spans into WC's fragment refresh so
			// they update automatically when items are added/removed.
			add_filter( 'woocommerce_add_to_cart_fragments', array( $this, 'register_fragments' ) );
		}

		// ─────────────────────────────────────────────────────────────────
		// Settings helper
		// ─────────────────────────────────────────────────────────────────

		private function get_settings() {
			if ( null !== $this->settings ) {
				return $this->settings;
			}

			$valid_positions  = array( 'bottom-right', 'bottom-left', 'middle-right', 'middle-left' );
			$valid_icons      = array( 'cart', 'bag', 'basket' );
			$valid_reveals    = array( 'mini_cart', 'go_to_cart', 'preview' );
			$valid_animations = array( 'bounce', 'shake', 'pulse', 'none' );

			$position = (string) storzen_module_setting( 'floating_cart', 'position' );
			if ( ! in_array( $position, $valid_positions, true ) ) {
				$position = 'bottom-right';
			}

			$icon_style = (string) storzen_module_setting( 'floating_cart', 'icon_style' );
			if ( ! in_array( $icon_style, $valid_icons, true ) ) {
				$icon_style = 'cart';
			}

			$reveal = (string) storzen_module_setting( 'floating_cart', 'reveal_action' );
			if ( ! in_array( $reveal, $valid_reveals, true ) ) {
				$reveal = 'mini_cart';
			}

			$animation = (string) storzen_module_setting( 'floating_cart', 'animation' );
			if ( ! in_array( $animation, $valid_animations, true ) ) {
				$animation = 'bounce';
			}

			$bg_color   = $this->sanitize_color( storzen_module_setting( 'floating_cart', 'bg_color' ),   '#FF3636' );
			$icon_color = $this->sanitize_color( storzen_module_setting( 'floating_cart', 'icon_color' ), '#ffffff' );

			$hide_on_pages_raw = (string) storzen_module_setting( 'floating_cart', 'hide_on_pages' );
			$hide_on_pages     = $this->parse_id_list( $hide_on_pages_raw );

			$this->settings = array(
				'position'        => $position,
				'icon_style'      => $icon_style,
				'reveal_action'   => $reveal,
				'hide_when_empty' => (bool) storzen_module_setting( 'floating_cart', 'hide_when_empty' ),
				'show_count_badge'=> (bool) storzen_module_setting( 'floating_cart', 'show_count_badge' ),
				'show_total'      => (bool) storzen_module_setting( 'floating_cart', 'show_total' ),
				'animation'       => $animation,
				'bg_color'        => $bg_color,
				'icon_color'      => $icon_color,
				'show_on_mobile'  => (bool) storzen_module_setting( 'floating_cart', 'show_on_mobile' ),
				'show_on_desktop' => (bool) storzen_module_setting( 'floating_cart', 'show_on_desktop' ),
				'hide_on_pages'   => $hide_on_pages,
			);

			return $this->settings;
		}

		/**
		 * Parse a comma-separated list of page IDs into a clean int array.
		 *
		 * @param string $raw
		 * @return int[]
		 */
		private function parse_id_list( $raw ) {
			if ( '' === $raw ) {
				return array();
			}
			$parts = array_map( 'trim', explode( ',', $raw ) );
			$ids   = array();
			foreach ( $parts as $p ) {
				if ( '' === $p ) continue;
				$id = (int) $p;
				if ( $id > 0 ) {
					$ids[] = $id;
				}
			}
			return array_unique( $ids );
		}

		/**
		 * Sanitize a hex color with a fallback default.
		 */
		private function sanitize_color( $raw, $fallback ) {
			$clean = sanitize_hex_color( (string) $raw );
			return $clean ? $clean : $fallback;
		}

		/**
		 * Decide whether to render the button on the current request at all.
		 *
		 * @return bool
		 */
		private function should_render() {
			$s = $this->get_settings();

			// Both device toggles off → never render.
			if ( ! $s['show_on_mobile'] && ! $s['show_on_desktop'] ) {
				return false;
			}

			// Never render on the cart or checkout pages — redundant + visually noisy.
			if ( function_exists( 'is_cart' ) && is_cart() ) {
				return false;
			}
			if ( function_exists( 'is_checkout' ) && is_checkout() ) {
				return false;
			}

			// Explicit page-ID blocklist from settings.
			if ( ! empty( $s['hide_on_pages'] ) ) {
				$current_id = (int) ( get_queried_object_id() ?: get_the_ID() );
				if ( $current_id && in_array( $current_id, $s['hide_on_pages'], true ) ) {
					return false;
				}
			}

			return true;
		}

		// ─────────────────────────────────────────────────────────────────
		// Asset enqueue
		// ─────────────────────────────────────────────────────────────────

		public function enqueue_assets() {
			if ( is_admin() ) {
				return;
			}
			if ( ! $this->should_render() ) {
				return;
			}

			$s = $this->get_settings();

			wp_enqueue_style(
				'storzen-floating-cart',
				STORZEN_ROOT_URL . 'assets/css/modules/floating-cart.css',
				array(),
				STORZEN_VERSION
			);

			// Inline CSS for user-tunable values — keeps the .css file static-cacheable.
			$inline = sprintf(
				':root{--storzen-fc-bg:%s;--storzen-fc-icon:%s;}',
				$s['bg_color'],
				$s['icon_color']
			);
			wp_add_inline_style( 'storzen-floating-cart', $inline );

			wp_enqueue_script(
				'storzen-floating-cart',
				STORZEN_ROOT_URL . 'assets/js/modules/floating-cart.js',
				array( 'jquery' ),
				STORZEN_VERSION,
				true
			);

			wp_localize_script(
				'storzen-floating-cart',
				'szFloatingCart',
				array(
					'cartUrl'      => function_exists( 'wc_get_cart_url' ) ? wc_get_cart_url() : '',
					'checkoutUrl'  => function_exists( 'wc_get_checkout_url' ) ? wc_get_checkout_url() : '',
					'animation'    => $s['animation'],
					'revealAction' => $s['reveal_action'],
					'hideWhenEmpty'=> (bool) $s['hide_when_empty'],
					'i18n'         => array(
						'openCart'    => __( 'Open cart', 'storzen' ),
						'closeCart'   => __( 'Close cart', 'storzen' ),
						'cartEmpty'   => __( 'Your cart is empty.', 'storzen' ),
					),
				)
			);
		}

		// ─────────────────────────────────────────────────────────────────
		// Fragment registration — keeps badge + total live
		// ─────────────────────────────────────────────────────────────────

		/**
		 * Register count badge and total amount as WC fragments so they refresh
		 * automatically on every cart change without us writing AJAX glue.
		 *
		 * @param array $fragments
		 * @return array
		 */
		public function register_fragments( $fragments ) {
			$count = WC()->cart ? (int) WC()->cart->get_cart_contents_count() : 0;

			ob_start();
			$this->render_count_badge( $count );
			$fragments['span.storzen-fc__count'] = ob_get_clean();

			if ( function_exists( 'wc_price' ) && WC()->cart ) {
				ob_start();
				?>
				<span class="storzen-fc__total" data-empty="<?php echo $count > 0 ? '0' : '1'; ?>">
					<?php echo wp_kses_post( WC()->cart->get_cart_subtotal() ); ?>
				</span>
				<?php
				$fragments['span.storzen-fc__total'] = ob_get_clean();
			}

			return $fragments;
		}

		/**
		 * Render the cart count badge.
		 *
		 * @param int $count
		 */
		private function render_count_badge( $count ) {
			?>
			<span class="storzen-fc__count"
				data-count="<?php echo (int) $count; ?>"
				data-empty="<?php echo $count > 0 ? '0' : '1'; ?>"
				aria-hidden="<?php echo $count > 0 ? 'false' : 'true'; ?>"
			><?php echo (int) $count; ?></span>
			<?php
		}

		// ─────────────────────────────────────────────────────────────────
		// Button + drawer rendering
		// ─────────────────────────────────────────────────────────────────

		public function render_button() {
			if ( is_admin() ) {
				return;
			}
			if ( ! $this->should_render() ) {
				return;
			}

			$s = $this->get_settings();

			// Read cart state (cheap if WC session is already initialized,
			// otherwise initializes the session — but at footer time the cart
			// has usually been touched already by mini-cart widgets/themes).
			$count = WC()->cart ? (int) WC()->cart->get_cart_contents_count() : 0;

			// Build classes.
			$classes = array(
				'storzen-fc',
				'storzen-fc--' . $s['position'],
				'storzen-fc--icon-' . $s['icon_style'],
				'storzen-fc--reveal-' . str_replace( '_', '-', $s['reveal_action'] ),
				'storzen-fc--anim-' . $s['animation'],
			);
			if ( ! $s['show_on_mobile'] )  { $classes[] = 'storzen-fc--hide-mobile'; }
			if ( ! $s['show_on_desktop'] ) { $classes[] = 'storzen-fc--hide-desktop'; }
			if ( 0 === $count && $s['hide_when_empty'] ) { $classes[] = 'storzen-fc--empty'; }

			$is_link = ( 'go_to_cart' === $s['reveal_action'] );
			$tag     = $is_link ? 'a' : 'button';
			$cart_url = function_exists( 'wc_get_cart_url' ) ? wc_get_cart_url() : '#';
			?>
			<div
				class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
				data-storzen-floating-cart="1"
				role="region"
				aria-label="<?php esc_attr_e( 'Floating cart', 'storzen' ); ?>"
			>
				<<?php echo esc_attr( $tag ); ?>
					class="storzen-fc__btn"
					<?php if ( $is_link ) : ?>
						href="<?php echo esc_url( $cart_url ); ?>"
					<?php else : ?>
						type="button"
						aria-expanded="false"
						aria-controls="storzen-fc-drawer"
					<?php endif; ?>
					aria-label="<?php esc_attr_e( 'Open cart', 'storzen' ); ?>"
				>
					<span class="storzen-fc__icon" aria-hidden="true">
						<?php echo $this->get_icon_svg( $s['icon_style'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static, allowlisted SVG ?>
					</span>

					<?php if ( $s['show_count_badge'] ) :
						$this->render_count_badge( $count );
					endif; ?>

					<?php if ( $s['show_total'] ) : ?>
						<span class="storzen-fc__total" data-empty="<?php echo $count > 0 ? '0' : '1'; ?>">
							<?php echo WC()->cart ? wp_kses_post( WC()->cart->get_cart_subtotal() ) : ''; ?>
						</span>
					<?php endif; ?>
				</<?php echo esc_attr( $tag ); ?>>

				<?php if ( 'mini_cart' === $s['reveal_action'] ) {
					$this->render_drawer();
				} elseif ( 'preview' === $s['reveal_action'] ) {
					$this->render_preview();
				} ?>
			</div>
			<?php
		}

		/**
		 * Mini-cart drawer — slides in from the side. Contents come from
		 * WooCommerce's standard mini-cart template, which means it auto-
		 * refreshes through the same WC fragment system that keeps the
		 * theme's header mini-cart in sync.
		 */
		private function render_drawer() {
			?>
			<div
				class="storzen-fc__drawer"
				id="storzen-fc-drawer"
				role="dialog"
				aria-modal="true"
				aria-label="<?php esc_attr_e( 'Cart', 'storzen' ); ?>"
				aria-hidden="true"
			>
				<div class="storzen-fc__drawer-overlay" data-storzen-fc-close="1"></div>
				<aside class="storzen-fc__drawer-panel">
					<header class="storzen-fc__drawer-header">
						<h2 class="storzen-fc__drawer-title"><?php esc_html_e( 'Your cart', 'storzen' ); ?></h2>
						<button
							type="button"
							class="storzen-fc__drawer-close"
							data-storzen-fc-close="1"
							aria-label="<?php esc_attr_e( 'Close cart', 'storzen' ); ?>"
						>&times;</button>
					</header>

					<div class="storzen-fc__drawer-body widget_shopping_cart_content">
						<?php
						// Render WC's standard mini-cart template. Wrapping this in
						// .widget_shopping_cart_content makes WC's own fragment
						// refresh logic update it automatically on add-to-cart.
						if ( function_exists( 'woocommerce_mini_cart' ) ) {
							woocommerce_mini_cart();
						}
						?>
					</div>
				</aside>
			</div>
			<?php
		}

		/**
		 * Hover preview — a smaller popover with the first 3 items. Used when
		 * `reveal_action` is 'preview'. Same fragment hook as the drawer.
		 */
		private function render_preview() {
			?>
			<div
				class="storzen-fc__preview widget_shopping_cart_content"
				role="region"
				aria-label="<?php esc_attr_e( 'Cart preview', 'storzen' ); ?>"
				aria-hidden="true"
			>
				<?php
				if ( function_exists( 'woocommerce_mini_cart' ) ) {
					woocommerce_mini_cart();
				}
				?>
			</div>
			<?php
		}

		// ─────────────────────────────────────────────────────────────────
		// Icon SVGs — minimal, single-color via currentColor
		// ─────────────────────────────────────────────────────────────────

		/**
		 * Return inline SVG for the chosen icon style. SVG uses `currentColor`
		 * so the user's `icon_color` setting applies via the CSS custom prop.
		 *
		 * @param string $style cart|bag|basket
		 * @return string
		 */
		private function get_icon_svg( $style ) {
			$paths = array(
				'cart'   => '<path d="M3 3h2l.4 2M7 13h12l3-8H5.4M7 13l-2-8M7 13l-2.293 2.293a1 1 0 0 0 .707 1.707H17M17 17a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm-9 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
				'bag'    => '<path d="M6 7h12l-1 13H7L6 7Z M9 7V5a3 3 0 0 1 6 0v2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
				'basket' => '<path d="M5 9h14l-1.5 9h-11L5 9Z M9 9 12 4l3 5 M3 9h18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
			);
			$path = isset( $paths[ $style ] ) ? $paths[ $style ] : $paths['cart'];

			return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">' . $path . '</svg>';
		}

	} // end class

} // end class_exists
