<?php
/**
 * Live Preview functionality for WooCommerce product pages.
 *
 * Renders the Personalize button and modal overlay, injects product
 * data for the React app, and handles AJAX add-to-cart with
 * personalization properties.
 *
 * @package Memwc_Connector
 * @since   1.0.0
 */

defined( 'ABSPATH' ) || exit;

class Memwc_Live_Preview_WooCommerce {

	private static $instance = null;

	private $default_settings = [
		'enable_live_preview'    => 'true',
		'widget_type'            => 'modal',
		'button_text'            => 'PERSONALIZE',
		'button_type'            => 'outline',
		'button_fill_color'      => '#FFFFFF',
		'button_outline_color'   => '#1976D2',
		'button_text_color'      => '#1976D2',
		'button_text_size'       => '16',
		'font_style'             => 'bold',
		'navigate_to_cart'       => 'true',
		'color_variant_as_image' => 'true',
	];

	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		// Store API filter: attach personalization data when using /wp-json/wc/store/v1/cart/add-item
		add_filter( 'woocommerce_store_api_add_to_cart_data', [ $this, 'add_personalization_via_store_api' ], 10, 2 );

		// Cart/Order hooks: display & persist personalization throughout the lifecycle
		add_filter( 'woocommerce_get_item_data', [ $this, 'display_personalization_in_cart' ], 10, 2 );
		add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'save_personalization_to_order' ], 10, 4 );
		add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'remove_variation_attributes_from_order_item' ], 99, 4 );

		// Frontend rendering: only if store is connected AND live preview is enabled
		if ( ! $this->should_render_frontend() ) {
			return;
		}

		$settings    = $this->get_widget_settings();
		$widget_type = $settings['widget_type'] ?? 'modal';

		add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );

		if ( $widget_type === 'embed' ) {
			add_action( 'woocommerce_single_product_summary', [ $this, 'render_embed_form_container' ], 25 );
		} else {
			add_action( 'woocommerce_single_product_summary', [ $this, 'render_personalize_button' ], 25 );
			add_action( 'wp_footer', [ $this, 'render_modal_container' ] );
		}
	}

	// =========================================================================
	// Helpers
	// =========================================================================

	private function should_render_frontend() {
		if ( is_admin() ) {
			return false;
		}
		if ( ! Memwc_Connector::is_connected() ) {
			return false;
		}
		$settings = $this->get_widget_settings();
		return ( $settings['enable_live_preview'] ?? 'false' ) === 'true';
	}

	private function get_current_product() {
		global $product;
		if ( $product instanceof \WC_Product ) {
			return $product;
		}
		$post_id = get_the_ID();
		if ( $post_id ) {
			$local_product = wc_get_product( $post_id );
			return $local_product instanceof \WC_Product ? $local_product : null;
		}
		return null;
	}

	private function product_has_personalization( $product_id ) {
		return get_post_meta( $product_id, '_mem_has_personalization', true ) === '1';
	}

	/**
	 * Whether the current request is a personalizable product page that
	 * should receive the live preview assets and markup.
	 */
	private function is_personalizable_product_page() {
		if ( ! is_product() ) {
			return false;
		}
		$product = $this->get_current_product();
		return $product && $this->product_has_personalization( $product->get_id() );
	}

	public function get_widget_settings() {
		return wp_parse_args( get_option( 'memwc_widget_settings', [] ), $this->default_settings );
	}

	public function get_default_settings() {
		return $this->default_settings;
	}

	// =========================================================================
	// Enqueue Assets (styles, scripts, inline data & behavior)
	// =========================================================================

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

		$product     = $this->get_current_product();
		$settings    = $this->get_widget_settings();
		$widget_type = $settings['widget_type'] ?? 'modal';

		wp_enqueue_style(
			'mem-live-preview',
			MEMWC_CONNECTOR_URL . 'assets/mem-live-preview.css',
			[],
			MEMWC_CONNECTOR_VERSION
		);

		wp_enqueue_script(
			'mem-live-preview',
			MEMWC_CONNECTOR_URL . 'assets/mem-live-preview.js',
			[],
			MEMWC_CONNECTOR_VERSION,
			true
		);

		// Product data + widget config the React app reads from window.* globals.
		wp_add_inline_script( 'mem-live-preview', $this->build_inject_data_js( $product, $settings ), 'before' );

		// Widget-type specific styles and behavior.
		if ( $widget_type === 'embed' ) {
			wp_add_inline_style( 'mem-live-preview', $this->get_embed_inline_css() );
			wp_add_inline_script( 'mem-live-preview', $this->get_embed_inline_js() );
		} else {
			wp_add_inline_style( 'mem-live-preview', $this->get_modal_inline_css() );
			wp_add_inline_script( 'mem-live-preview', $this->get_modal_inline_js() );
		}
	}

	/**
	 * Build the inline JS that exposes product data and widget settings on
	 * window.* globals consumed by the bundled React app. Values are encoded
	 * with wp_json_encode() so they are safely escaped for output.
	 */
	private function build_inject_data_js( $product, $settings ) {
		$money_format    = $this->get_money_format();
		$shop_url        = get_option( 'memwc_shop_url', esc_url_raw( get_site_url() ) );
		$product_data    = $this->build_product_data( $product );
		$product_options = $this->build_product_options( $product );
		$color_images    = $this->build_color_wise_images( $product );

		$widget = [
			'settings' => [
				'main_class'                   => 'mem_widget_ui',
				'mem_widget_type'              => $settings['widget_type'] ?: 'modal',
				'swatchImage'                  => $settings['color_variant_as_image'],
				'customSelectorCompareAtPrice' => '',
				'customSelectorPrice'          => '',
				'personalize_button'           => [
					'text'                       => $settings['button_text'],
					'type'                       => $settings['button_type'],
					'fill_color'                 => $settings['button_fill_color'],
					'outline_color'              => $settings['button_outline_color'],
					'text_color'                 => $settings['button_text_color'],
					'text_size'                  => $settings['button_text_size'],
					'font_style'                 => $settings['font_style'],
					'navigate_to_cart'           => $settings['navigate_to_cart'],
					'personalization_background' => '',
				],
			],
			'jsonMoneyFormat'      => $money_format,
			'productData'          => $product_data,
			'productOptionByValue' => $product_options,
			'colorWiseImage'       => $color_images,
		];

		// JSON_HEX_TAG hex-encodes `<` and `>` so a value containing `</script>`
		// (e.g. inside a product description) cannot break out of the inline tag.
		// JSON_HEX_AMP escapes `&` for safety in mixed HTML/JS contexts.
		// Both flags only change the wire bytes; JS decodes back to the original
		// characters at parse time, so the data the React app sees is identical.
		$flags = JSON_HEX_TAG | JSON_HEX_AMP;

		$js  = 'window.mem_platform = "woocommerce";';
		$js .= 'window.mem_woo_cart_url = ' . wp_json_encode( wc_get_cart_url(), $flags ) . ';';
		$js .= 'window.mem_woo_nonce = ' . wp_json_encode( wp_create_nonce( 'wc_store_api' ), $flags ) . ';';
		$js .= 'window.mem_woo_product_id = ' . wp_json_encode( $product->get_id(), $flags ) . ';';
		$js .= 'window.mem_woo_store_api_url = ' . wp_json_encode( esc_url_raw( rest_url( 'wc/store/v1/' ) ), $flags ) . ';';
		$js .= 'window.mem_widget = ' . wp_json_encode( $widget, $flags ) . ';';
		$js .= 'window.shouldVariantSelectorRender = true;';
		$js .= 'window.memShopUrl = ' . wp_json_encode( $shop_url, $flags ) . ';';

		return $js;
	}

	private function get_modal_inline_css() {
		return '.modal-ui-popup-main-wrapper.is-visible{display:flex !important;}'
			. 'body.no-scroll{overflow:hidden !important;}'
			. '@media screen and (max-width:767px){.modal-ui-popup-main-wrapper{align-items:end;}}';
	}

	private function get_modal_inline_js() {
		return '(function(){'
			. 'var mv=document.querySelector(\'meta[name="viewport"]\');'
			. 'if(mv){mv.setAttribute("content",mv.getAttribute("content")+", user-scalable=0");}'
			. 'var toggleModal=function(){'
			. 'var m=document.querySelector(".modal-ui-popup-main-wrapper");'
			. 'if(!m)return;'
			. 'if(m.classList.contains("is-visible")){'
			. 'm.classList.remove("is-visible");document.body.classList.remove("no-scroll");'
			. 'dispatchEvent(new CustomEvent("modalClose",{detail:{open:false}}));'
			. '}else{m.classList.add("is-visible");document.body.classList.add("no-scroll");}'
			. '};'
			. 'window.toggleModal=toggleModal;'
			. 'var btn=document.querySelector(".mem-personalized-dummy-btn");'
			. 'if(btn){btn.addEventListener("click",function(){toggleModal();});}'
			. 'var modal=document.querySelector(".modal-ui-popup-main-wrapper");'
			. 'if(modal){modal.addEventListener("click",function(e){if(e.target===modal){toggleModal();}});}'
			. '})();';
	}

	private function get_embed_inline_css() {
		return 'img.mem-embed-variant-img{position:absolute !important;top:0 !important;left:0 !important;'
			. 'width:100% !important;height:100% !important;object-fit:contain;z-index:1010;background:#fff;}'
			. '.woocommerce-product-gallery .flex-viewport #mem-canvas-container,'
			. '.woocommerce-product-gallery .flex-viewport [class^="element-svg-"]{'
			. 'position:absolute !important;top:0 !important;left:0 !important;'
			. 'width:100% !important;height:100% !important;z-index:-1;}'
			. 'form.cart{display:none !important;}';
	}

	private function get_embed_inline_js() {
		return '(function(){'
			. 'var meta=document.querySelector(\'meta[name="viewport"]\');'
			. 'if(meta){meta.setAttribute("content","width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no");}'
			. '})();';
	}

	// =========================================================================
	// Render: Personalize Button (markup only; behavior is enqueued inline)
	// =========================================================================

	public function render_personalize_button() {
		if ( ! $this->is_personalizable_product_page() ) {
			return;
		}

		$settings = $this->get_widget_settings();

		$s  = 'outline:0;margin:10px 0;padding:10px 15px;border-radius:4px;line-height:1.75;';
		$s .= 'font-family:Roboto,Helvetica,Arial,sans-serif;cursor:pointer;appearance:none;';
		$s .= 'text-decoration:none;text-transform:uppercase;width:100%;border:1px solid transparent;';
		$s .= 'font-weight:500;background-color:transparent;display:block;text-align:center;';
		$s .= 'color:'     . ( $settings['button_text_color'] ?: 'black' ) . ';';
		$s .= 'font-size:' . ( $settings['button_text_size'] ?: '16' )     . 'px;';
		$s .= 'font-style:'. ( $settings['font_style'] ?: 'normal' )       . ';';

		if ( $settings['button_type'] === 'fill' ) {
			$s .= 'background-color:' . $settings['button_fill_color'] . ';';
		} elseif ( $settings['button_type'] !== 'text' ) {
			$s .= 'border-color:' . ( $settings['button_outline_color'] ?: 'black' ) . ';';
		}
		$s .= 'opacity:0.5;cursor:not-allowed;';

		printf(
			'<button class="mem-personalized-dummy-btn" style="%s" type="button" disabled="disabled">%s</button>',
			esc_attr( $s ),
			esc_html__( 'Loading...', 'my-easy-monogram' )
		);
	}

	// =========================================================================
	// Render: Modal Overlay (markup only; styles/behavior are enqueued inline)
	// =========================================================================

	public function render_modal_container() {
		if ( ! $this->is_personalizable_product_page() ) {
			return;
		}
		?>
		<div class="modal-ui-popup-main-wrapper" style="height:100%;display:none;position:fixed;width:100%;top:0;left:0;background-color:rgba(0,0,0,0.5);z-index:9999999;justify-content:center;align-items:center;">
			<div id="preview-root-app-container"></div>
		</div>
		<?php
	}

	// =========================================================================
	// Embed Mode Container (markup only; styles/behavior are enqueued inline)
	// =========================================================================

	public function render_embed_form_container() {
		if ( ! $this->is_personalizable_product_page() ) {
			return;
		}
		?>
		<div id="preview-root-app-container"></div>
		<?php
	}

	// =========================================================================
	// Store API: Attach personalization when using /wp-json/wc/store/v1/cart/add-item
	// =========================================================================

	public function add_personalization_via_store_api( $add_to_cart_data, \WP_REST_Request $request ) {
		$json_params     = $request->get_json_params();
		$personalization = $json_params['mem_personalization'] ?? null;

		if ( ! empty( $personalization ) && is_array( $personalization ) ) {
			$clean = [];
			foreach ( $personalization as $k => $v ) {
				$clean[ sanitize_text_field( $k ) ] = sanitize_text_field( $v );
			}
			$add_to_cart_data['cart_item_data']['mem_personalization'] = $clean;
		}

		return $add_to_cart_data;
	}

	// =========================================================================
	// Cart / Order: persist personalization data
	// =========================================================================

	public function display_personalization_in_cart( $item_data, $cart_item ) {
		if ( ! isset( $cart_item['mem_personalization'] ) || ! is_array( $cart_item['mem_personalization'] ) ) return $item_data;
		foreach ( $cart_item['mem_personalization'] as $key => $value ) {
			if ( strpos( $key, '_' ) === 0 ) continue;
			$item_data[] = [ 'key' => $key, 'value' => $value ];
		}
		return $item_data;
	}

	public function save_personalization_to_order( $item, $cart_item_key, $values, $order ) {
		if ( isset( $values['mem_personalization'] ) && is_array( $values['mem_personalization'] ) ) {
			foreach ( $values['mem_personalization'] as $key => $value ) {
				$item->add_meta_data( $key, $value );
			}
		}
	}

	/**
	 * Strip WooCommerce's default variation attribute meta (e.g. pa_color, pa_size)
	 * from the order line item. The variation_id already identifies the chosen
	 * combination, so these are redundant and pollute the webhook payload.
	 */
	public function remove_variation_attributes_from_order_item( $item, $cart_item_key, $values, $order ) {
		if ( empty( $values['variation'] ) || ! is_array( $values['variation'] ) ) {
			return;
		}
		foreach ( array_keys( $values['variation'] ) as $attr_key ) {
			// WooCommerce's set_variation() strips "attribute_" before storing,
			// so the actual meta key is e.g. "color" not "attribute_color".
			$meta_key = str_replace( 'attribute_', '', $attr_key );
			$item->delete_meta_data( $meta_key );
		}
	}

	// =========================================================================
	// Product Data Builders (WooCommerce -> Shopify-like format)
	// =========================================================================

	private function build_product_data( $product ) {
		$id = $product->get_id();
		$data = [
			'id'           => $id,
			'title'        => $product->get_name(),
			'body_html'    => $product->get_description(),
			'vendor'       => '',
			'product_type' => implode( ', ', wp_get_post_terms( $id, 'product_cat', [ 'fields' => 'names' ] ) ),
			'handle'       => $product->get_slug(),
			'variants'     => [],
			'options'      => [],
			'images'       => [],
			'image'        => null,
		];

		if ( $product->is_type( 'variable' ) ) {
			$data = $this->build_variable_product_data( $product, $data );
		} else {
			$data['variants'][] = [
				'id'               => $id,
				'product_id'       => $id,
				'available'        => $product->is_in_stock(),
				'title'            => $product->get_name(),
				'price'            => (string)( $product->get_price() ),
				'sku'              => $product->get_sku(),
				'position'         => 1,
				'compare_at_price' => $product->get_regular_price() != $product->get_price()
					? (string)( $product->get_regular_price() ) : null,
				'option1' => null, 'option2' => null, 'option3' => null,
				'quantity' => (string) $product->get_stock_quantity(),
			];
		}

		$img_id = $product->get_image_id();
		if ( $img_id ) {
			$data['image']    = $this->build_image_data( $img_id, $id );
			$data['images'][] = $data['image'];
		}
		foreach ( $product->get_gallery_image_ids() as $i => $gid ) {
			$data['images'][] = $this->build_image_data( $gid, $id, $i + 2 );
		}

		return $data;
	}

	private function build_variable_product_data( $product, $data ) {
		$attributes = $product->get_variation_attributes();
		$option_map = [];
		$opt_idx    = 1;

		foreach ( $attributes as $attr_name => $values ) {
			$label = wc_attribute_label( $attr_name, $product );
			$data['options'][] = $label;
			$option_map[ $attr_name ] = $opt_idx++;
		}

		$position = 1;
		foreach ( $product->get_available_variations() as $v ) {
			$vp = wc_get_product( $v['variation_id'] );
			if ( ! $vp ) continue;

			$vd = [
				'id'               => $v['variation_id'],
				'product_id'       => $product->get_id(),
				'available'        => $v['is_in_stock'],
				'title'            => implode( ' / ', array_values( $v['attributes'] ) ),
				'price'            => (string)( ( $vp->get_price() ?: 0 ) ),
				'sku'              => $vp->get_sku(),
				'position'         => $position++,
				'compare_at_price' => $vp->get_regular_price() != $vp->get_price()
					? (string)( ( $vp->get_regular_price() ?: 0 ) ) : null,
				'option1' => null, 'option2' => null, 'option3' => null,
				'image_id'  => $vp->get_image_id(),
				'quantity'  => (string)( $vp->get_stock_quantity() ?: 0 ),
			];

			foreach ( $option_map as $attr_key => $idx ) {
				$vd[ 'option' . $idx ] = $v['attributes'][ 'attribute_' . sanitize_title( $attr_key ) ] ?? '';
			}

			$data['variants'][] = $vd;
		}

		return $data;
	}

	private function build_product_options( $product ) {
		$options = [];
		if ( ! $product->is_type( 'variable' ) ) return $options;
		foreach ( $product->get_variation_attributes() as $attr_name => $values ) {
			$options[ wc_attribute_label( $attr_name, $product ) ] = array_values( $values );
		}
		return $options;
	}

	private function build_color_wise_images( $product ) {
		$map = [];
		if ( ! $product->is_type( 'variable' ) ) return $map;

		$color_attr = null;
		foreach ( $product->get_variation_attributes() as $attr_name => $vals ) {
			$label = strtolower( wc_attribute_label( $attr_name, $product ) );
			if ( strpos( $label, 'color' ) !== false || strpos( $label, 'colour' ) !== false ) {
				$color_attr = $attr_name;
				break;
			}
		}
		if ( ! $color_attr ) return $map;

		$seen = [];
		foreach ( $product->get_available_variations() as $v ) {
			$color = $v['attributes'][ 'attribute_' . sanitize_title( $color_attr ) ] ?? '';
			if ( $color && ! isset( $seen[ $color ] ) ) {
				$vp     = wc_get_product( $v['variation_id'] );
				$img_id = $vp ? $vp->get_image_id() : null;
				if ( $img_id ) {
					$url = wp_get_attachment_image_url( $img_id, 'full' );
					$map[ $color ] = '//' . preg_replace( '#^https?://#', '', $url );
				}
				$seen[ $color ] = true;
			}
		}
		return $map;
	}

	private function build_image_data( $image_id, $product_id, $position = 1 ) {
		$meta = wp_get_attachment_metadata( $image_id );
		return [
			'id'          => $image_id,
			'product_id'  => $product_id,
			'available'   => true,
			'position'    => $position,
			'alt'         => get_post_meta( $image_id, '_wp_attachment_image_alt', true ),
			'width'       => $meta['width'] ?? 1000,
			'height'      => $meta['height'] ?? 1000,
			'src'         => wp_get_attachment_image_url( $image_id, 'full' ),
			'variant_ids' => [],
		];
	}

	private function get_money_format() {
		$sym = html_entity_decode( get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8' );
		$dec = wc_get_price_decimals();
		$ph  = $dec === 0 ? '{{amount_no_decimals}}' : '{{amount}}';
		$pos = get_option( 'woocommerce_currency_pos', 'left' );

		switch ( $pos ) {
			case 'right':       return $ph . $sym;
			case 'left_space':  return $sym . ' ' . $ph;
			case 'right_space': return $ph . ' ' . $sym;
			default:            return $sym . $ph;
		}
	}
}
