<?php
/**
 * Defines the public-facing rendering and asset loading for the inline upsell widget shown on product pages.
 *
 * @link       https://nudgesells.com
 * @since      1.0.0
 *
 * @package    NudgeSells
 * @subpackage NudgeSells/public
 */

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

/**
 * NudgeSells_Public
 *
 * Enqueues frontend assets and builds the widget markup shown above the Add to Cart button.
 *
 * @since      1.0.0
 * @package    NudgeSells
 * @subpackage NudgeSells/public
 * @author     NudgeSells <support@nudgesells.com>
 */
class NudgeSells_Public {

	/**
	 * Assets.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function assets() {
		if ( is_cart() || is_checkout() ) {
			wp_enqueue_style( 'nudgesells-cart', NUDGESELLS_PLUGIN_URL . 'public/css/nudgesells-cart.css', array(), NUDGESELLS_VERSION );
			wp_add_inline_style( 'nudgesells-cart', self::build_color_variables_css( 'body' ) );
		}

		if ( ! is_product() ) {
			return;
		}
		wp_enqueue_style( 'nudgesells-public', NUDGESELLS_PLUGIN_URL . 'public/css/nudgesells-public.css', array(), NUDGESELLS_VERSION );
		wp_add_inline_style( 'nudgesells-public', self::build_color_variables_css() );
		wp_enqueue_script( 'nudgesells-public', NUDGESELLS_PLUGIN_URL . 'public/js/nudgesells-public.js', array( 'jquery' ), NUDGESELLS_VERSION, true );
		wp_localize_script( 'nudgesells-public', 'NudgeSells_Public_Vars', array(
			'add_to_cart_url'    => WC_AJAX::get_endpoint( 'nudgesells_add_to_cart' ),
			'price_preview_url'  => WC_AJAX::get_endpoint( 'nudgesells_price_preview' ),
			'render_widget_url'  => WC_AJAX::get_endpoint( 'nudgesells_render_widget' ),
			'nonce'    => wp_create_nonce( 'nudgesells_frontend' ),
			'i18n'     => array(
				'add_to_cart_error' => __( 'Something went wrong adding these to your cart. Please try again.', 'nudgesells' ),
				'select_option'     => __( 'Please choose an option', 'nudgesells' ),
			),
		) );
	}

	/**
	 * Build color variables css.
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function build_color_variables_css( $selector = '.nudgesells-upsell-wrap' ) {
		$declarations = array();
		foreach ( NudgeSells_Admin_Settings::color_fields() as $setting_key => $css_var ) {
			$value = NudgeSells_Admin_Settings::get( $setting_key );
			if ( $value && preg_match( '/^#[0-9a-fA-F]{3,6}$/', $value ) ) {
				$declarations[] = $css_var . ': ' . $value . ';';
			}
		}

		$radius_style = NudgeSells_Admin_Settings::get( 'border_radius_style', 'rounded' );
		foreach ( NudgeSells_Admin_Settings::radius_values( $radius_style ) as $css_var => $px_value ) {
			$declarations[] = $css_var . ': ' . $px_value . ';';
		}

		if ( empty( $declarations ) ) {
			return '';
		}
		return $selector . ' { ' . implode( ' ', $declarations ) . ' }';
	}

	/**
	 * Render.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function render() {
		global $product;

		if ( ! $product instanceof WC_Product ) {
			return;
		}

		$rules = NudgeSells_Rules_Repository::get_rules( $product->get_id(), 0 );
		if ( empty( $rules['enabled'] ) || ( empty( $rules['addons'] ) && empty( $rules['upgrades'] ) ) ) {
			return;
		}

		$is_variable_host = $product->is_type( array( 'variable', 'variable-subscription' ) );
		$has_qty_filter    = $rules['offer_id'] && NudgeSells_Offer_Repository::get_min_qty( $rules['offer_id'] ) > 1;

		if ( ! $is_variable_host && ! $has_qty_filter ) {
			echo self::get_widget_markup( $product->get_id(), 0, 1, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- template output is escaped field-by-field in nudgesells-public-display.php.
			return;
		}

		printf(
			'<div class="nudgesells-upsell-wrap nudgesells-upsell-wrap--pending" data-product-id="%d" data-is-variable-host="%d" data-has-qty-filter="%d" data-needs-fetch="1"></div>',
			esc_attr( $product->get_id() ),
			esc_attr( $is_variable_host ? '1' : '0' ),
			esc_attr( $has_qty_filter ? '1' : '0' )
		);
	}

	/**
	 * Get widget markup.
	 *
	 * @param mixed $host_id
	 * @param int $variation_id
	 * @param int $host_qty
	 * @param bool $count_impression
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_widget_markup( $host_id, $variation_id = 0, $host_qty = 1, $count_impression = false ) {
		$product = wc_get_product( $host_id );
		if ( ! $product instanceof WC_Product ) {
			return '';
		}

		$host_qty = max( 1, (int) $host_qty );

		$price_source = $product;
		if ( $variation_id ) {
			$variation = wc_get_product( $variation_id );
			if ( $variation && (int) $variation->get_parent_id() === (int) $host_id ) {
				$price_source = $variation;
			} else {
				$variation_id = 0; 
			}
		}

		$rules = NudgeSells_Rules_Repository::get_rules( $host_id, $variation_id );

		if ( empty( $rules['enabled'] ) || ( empty( $rules['addons'] ) && empty( $rules['upgrades'] ) ) ) {
			return '';
		}

		if ( $rules['offer_id'] && ! NudgeSells_Offer_Repository::meets_min_qty( $rules['offer_id'], $host_qty ) ) {
			return self::render_locked_teaser( $rules['offer_id'], $host_qty );
		}

		$has_qty_filter = $rules['offer_id'] && NudgeSells_Offer_Repository::get_min_qty( $rules['offer_id'] ) > 1;

		$addon_data = array();
		$addons_use_qty_input = 'qty' === ( $rules['addons_selection_type'] ?? 'checkbox' );
		foreach ( $rules['addons'] as $addon_rule ) {
			$addon_product = wc_get_product( $addon_rule['product_id'] );
			if ( ! $addon_product || ! $addon_product->is_purchasable() || ! $addon_product->is_in_stock() ) {
				continue;
			}
			$view = self::build_variable_aware_view_data( $addon_product, $addon_rule, $addons_use_qty_input );
			if ( $view['is_variable'] && empty( $view['variations'] ) ) {
				// Every variation was out of stock -- nothing left to offer.
				continue;
			}
			$addon_data[] = $view;
		}

		$upgrade_data = array();
		foreach ( $rules['upgrades'] as $upgrade_rule ) {
			$upgrade_product = wc_get_product( $upgrade_rule['product_id'] );
			if ( ! $upgrade_product || ! $upgrade_product->is_purchasable() || ! $upgrade_product->is_in_stock() ) {
				continue;
			}
			$view = self::build_variable_aware_view_data( $upgrade_product, $upgrade_rule );
			if ( $view['is_variable'] && empty( $view['variations'] ) ) {
				continue;
			}
			$upgrade_data[] = $view;
		}

		if ( empty( $addon_data ) && empty( $upgrade_data ) ) {
			return '';
		}

		if ( $count_impression && $rules['offer_id'] ) {
			NudgeSells_Analytics_Data::record_impression( $rules['offer_id'] );
		}

		$host_price_source = $price_source->is_purchasable() || $variation_id ? $price_source : $product;
		$host_price        = $host_price_source->get_price();

		$countdown_end_timestamp = 0;
		if ( 'yes' === NudgeSells_Admin_Settings::get( 'show_flash_sale_countdown', 'no' ) && ! empty( $rules['condition_end'] ) ) {
			$end_timestamp = NudgeSells_Offer_Repository::local_date_to_timestamp( $rules['condition_end'], '23:59:59' );
			if ( $end_timestamp && $end_timestamp > time() ) {
				$countdown_end_timestamp = $end_timestamp;
			}
		}

		ob_start();
		wc_get_template(
			'nudgesells-public-display.php',
			array(
				'product'       => $product,
				'host_price'    => $host_price,
				'host_price_display' => self::format_price_for_product( $host_price, $host_price_source ),
				'addons'        => $addon_data,
				'upgrades'      => $upgrade_data,
				'bundle'                => $rules['bundle_discount'],
				'tiers'                 => $rules['tiers'],
				'display_style'         => $rules['display_style'],
				'addons_title'          => $rules['addons_title'],
				'addons_subline'        => $rules['addons_subline'],
				'addons_icon_url'       => $rules['addons_icon_url'],
				'addons_readmore_text'  => $rules['addons_readmore_text'],
				'addons_readmore_url'   => $rules['addons_readmore_url'],
				'upgrades_title'        => $rules['upgrades_title'],
				'upgrades_subline'      => $rules['upgrades_subline'],
				'upgrades_icon_url'     => $rules['upgrades_icon_url'],
				'upgrades_readmore_text' => $rules['upgrades_readmore_text'],
				'upgrades_readmore_url'  => $rules['upgrades_readmore_url'],
				'upgrades_original_label'    => $rules['upgrades_original_label'],
				'upgrades_original_sublabel' => $rules['upgrades_original_sublabel'],
				'countdown_end_timestamp'    => $countdown_end_timestamp,
				'countdown_format'           => NudgeSells_Admin_Settings::get( 'countdown_format', 'hms' ),
				'has_qty_filter'         => $has_qty_filter,
				'show_qty_stepper'       => 'yes' === NudgeSells_Admin_Settings::get( 'show_qty_stepper', 'yes' ),
				'show_upgrade_thumbnails' => 'yes' === NudgeSells_Admin_Settings::get( 'show_upgrade_thumbnails', 'no' ),
				'host_image'             => wp_get_attachment_image_url( $product->get_image_id(), NudgeSells_Admin_Settings::get( 'thumbnail_image_size', 'thumbnail' ) ),
				'title_link_mode'        => NudgeSells_Admin_Settings::get( 'product_title_link_mode', 'none' ),

				'quick_view_css_class'   => apply_filters(
					'nudgesells_quick_view_css_class',
					NudgeSells_Admin_Settings::get( 'quick_view_css_class', '' ),
					$host_id
				),
			),
			'nudgesells/',
			NUDGESELLS_PLUGIN_DIR . 'public/partials/'
		);
		return ob_get_clean();
	}

	/**
	 * Render locked teaser.
	 *
	 * @param mixed $offer_id
	 * @param mixed $host_qty
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function render_locked_teaser( $offer_id, $host_qty ) {
		$min_qty = NudgeSells_Offer_Repository::get_min_qty( $offer_id );
		$needed  = max( 0, $min_qty - $host_qty );

		$message = sprintf(
			/* translators: %d: number of additional units needed to reach the minimum quantity condition */
			_n( 'Add %d more to unlock an exclusive offer!', 'Add %d more to unlock an exclusive offer!', $needed, 'nudgesells' ),
			$needed
		);

		return sprintf(
			'<div class="nudgesells-upsell-wrap nudgesells-upsell-wrap--locked" data-has-qty-filter="1" data-min-qty="%d" role="status" aria-live="polite"><p class="nudgesells-locked-note">%s</p></div>',
			esc_attr( $min_qty ),
			esc_html( $message )
		);
	}

	/**
	 * Format price for product.
	 *
	 * @param mixed $amount
	 * @param WC_Product $product
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function format_price_for_product( $amount, WC_Product $product ) {
		if ( class_exists( 'WC_Subscriptions_Product' ) && WC_Subscriptions_Product::is_subscription( $product ) ) {
			return WC_Subscriptions_Product::get_price_string( $product, array( 'price' => wc_price( $amount ) ) );
		}
		return wc_price( $amount );
	}

	/**
	 * Build variable aware view data.
	 *
	 * @param WC_Product $offer_product
	 * @param array $rule
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function build_variable_aware_view_data( WC_Product $offer_product, array $rule, $use_qty_input = false ) {
		$is_variable = $offer_product->is_type( array( 'variable', 'variable-subscription' ) );

		$link_product_id = $offer_product->is_type( 'variation' ) ? $offer_product->get_parent_id() : $offer_product->get_id();

		$view = array(
			'product_id'      => $offer_product->get_id(),
			'link_product_id' => $link_product_id,
			'name'            => $offer_product->get_name(),
			'label'           => $offer_product->get_name(),
			'badge_label'     => isset( $rule['label'] ) ? $rule['label'] : '',
			'sublabel'        => isset( $rule['sublabel'] ) ? $rule['sublabel'] : ( isset( $rule['subline'] ) ? $rule['subline'] : '' ),
			'permalink'       => get_permalink( $link_product_id ),
			'image'           => wp_get_attachment_image_url( $offer_product->get_image_id(), NudgeSells_Admin_Settings::get( 'thumbnail_image_size', 'thumbnail' ) ),
			'is_variable'     => $is_variable,
			'allow_qty'       => $use_qty_input,
			'default_value'   => isset( $rule['allow_qty'] ) ? absint( $rule['allow_qty'] ) : 0,
			'discount_type'   => $rule['discount_type'],
			'discount_value'  => $rule['discount_value'],
			'savings_label'   => NudgeSells_Pricing_Engine::format_savings_label( $rule['discount_type'], $rule['discount_value'] ),
			'variations'      => array(),
			'variation_attributes' => array(),
			'price_original'  => 0,
			'price_discounted'=> 0,
			'price_display_original'   => '',
			'price_display_discounted' => '',
		);

		if ( $is_variable ) {
			
			$prices = array();
			foreach ( $offer_product->get_available_variations() as $variation_raw ) {
				$variation = wc_get_product( $variation_raw['variation_id'] );
				if ( ! $variation || ! $variation->is_purchasable() || ! $variation->is_in_stock() ) {
					// Hide out-of-stock variations from the widget rather
					// than offering a selection the customer can't actually
					// add to cart.
					continue;
				}
				$resolved   = NudgeSells_Pricing_Engine::resolve_effective_prices( $variation, $rule['discount_type'], $rule['discount_value'] );
				$price      = $resolved['original'];
				$discounted = $resolved['final'];
				$prices[]   = $discounted;
				$own_image_id = $variation->get_image_id( 'edit' );
				$view['variations'][] = array(
					'variation_id'     => $variation->get_id(),
					'attributes'       => $variation_raw['attributes'], 
					'label'            => wc_get_formatted_variation( $variation, true, false ),
					'price_original'   => $price,
					'price_display_original'   => self::format_price_for_product( $price, $variation ),
					'price_discounted' => $discounted,
					'price_display_discounted' => self::format_price_for_product( $discounted, $variation ),
					'in_stock'         => $variation->is_in_stock(),
					'image'            => $own_image_id ? wp_get_attachment_image_url( $own_image_id, NudgeSells_Admin_Settings::get( 'thumbnail_image_size', 'thumbnail' ) ) : $view['image'],
				);
			}
			$view['variation_attributes'] = $offer_product->get_variation_attributes();

			if ( ! empty( $prices ) ) {
				$from_price = min( $prices );
				$view['price_discounted']         = $from_price;
				$view['price_display_discounted'] = self::format_price_for_product( $from_price, $offer_product );
			}
		} else {
			$resolved   = NudgeSells_Pricing_Engine::resolve_effective_prices( $offer_product, $rule['discount_type'], $rule['discount_value'] );
			$price      = $resolved['original'];
			$discounted = $resolved['final'];
			$view['price_original']   = $price;
			$view['price_display_original']   = self::format_price_for_product( $price, $offer_product );
			$view['price_discounted'] = $discounted;
			$view['price_display_discounted'] = self::format_price_for_product( $discounted, $offer_product );
		}

		return $view;
	}
}
