<?php
/**
 * PowerPack WooCommerce Module.
 *
 * @package PowerPack
 */

namespace PowerpackElements\Modules\Woocommerce;

use PowerpackElements\Base\Module_Base;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Class Module.
 */
class Module extends Module_Base {

	/**
	 * Module is active or not.
	 *
	 * @since 1.3.3
	 *
	 * @access public
	 *
	 * @return bool true|false.
	 */
	public static function is_active() {
		return class_exists( 'WooCommerce' );
	}

	/**
	 * Get Module Name.
	 *
	 * @since 1.3.3
	 * @access public
	 *
	 * @return string Module name.
	 */
	public function get_name() {
		return 'woocommerce';
	}

	/**
	 * Get Widgets.
	 *
	 * @since 1.3.3
	 * @access public
	 *
	 * @return array Widgets.
	 */
	public function get_widgets() {
		$widgets = [
			'Woo_Add_To_Cart',
			'Woo_Categories',
			'Woo_Cart',
			'Woo_Checkout',
			'Woo_My_Account',
			'Woo_Mini_Cart',
			'Woo_Offcanvas_Cart',
			'Woo_Products',
			'Woo_Single_Product',
		];

		if ( get_option( 'pp_woo_builder_enable' ) ) {
			$widgets[] = 'Woo_Product_Tabs';
			$widgets[] = 'Woo_Product_Title';
			$widgets[] = 'Woo_Product_Meta';
			$widgets[] = 'Woo_Product_Price';
			$widgets[] = 'Woo_Product_Rating';
			$widgets[] = 'Woo_Product_Stock';
			$widgets[] = 'Woo_Product_Short_Description';
			$widgets[] = 'Woo_Product_Content';
			$widgets[] = 'Woo_Product_Images';
			$widgets[] = 'Woo_Product_Reviews';
			$widgets[] = 'Woo_Product_Upsell';
			$widgets[] = 'Woo_Add_To_Cart_Notification';
			$widgets[] = 'Woo_Archive_Description';
		}

		return $widgets;
	}

	/**
	 * WooCommerce hook.
	 *
	 * @since 1.3.3
	 * @access public
	 */
	public function register_wc_hooks() {
		wc()->frontend_includes();
	}

	/**
	 * Load Quick View Product.
	 *
	 * @since 1.3.3
	 * @param array $localize localize.
	 * @access public
	 */
	public function js_localize( $localize ) {

		$localize['is_cart']           = is_cart();
		$localize['is_single_product'] = is_product();
		$localize['view_cart']         = esc_attr__( 'View cart', 'powerpack' );
		$localize['cart_url']          = apply_filters( 'pp_woocommerce_add_to_cart_redirect', wc_get_cart_url() );

		return $localize;
	}

	/**
	 * Load Quick View Product.
	 *
	 * @since 1.3.3
	 * @access public
	 */
	public function load_quick_view_product() {
		check_ajax_referer( 'pp-qv-nonce', 'nonce' );

		$product_id = isset( $_REQUEST['product_id'] ) ? absint( $_REQUEST['product_id'] ) : 0;

		if ( ! $product_id || 'product' !== get_post_type( $product_id ) ) {
			die();
		}

		$this->quick_view_content_actions();

		// set the main wp query for the product.
		wp( 'p=' . $product_id . '&post_type=product' );

		ob_start();

		// load content template.
		include POWERPACK_ELEMENTS_PATH . 'modules/woocommerce/templates/quick-view-product.php';

		echo ob_get_clean(); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

		die();
	}

	/**
	 * Quick view actions
	 */
	public function quick_view_content_actions() {

		add_action( 'pp_woo_quick_view_product_image', 'woocommerce_show_product_sale_flash', 10 );
		// Image.
		add_action( 'pp_woo_quick_view_product_image', [ $this, 'quick_view_product_images_markup' ], 20 );

		// Summary.
		add_action( 'pp_woo_quick_view_product_summary', [ $this, 'quick_view_product_content_structure' ], 10 );
	}

	/**
	 * Quick view product images markup.
	 */
	public function quick_view_product_images_markup() {

		include POWERPACK_ELEMENTS_PATH . 'modules/woocommerce/templates/quick-view-product-image.php';
	}

	/**
	 * Quick view product content structure.
	 */
	public function quick_view_product_content_structure() {

		global $product;

		$post_id = $product->get_id();

		$single_structure = apply_filters(
			'pp_quick_view_product_structure',
			[
				'title',
				'ratings',
				'price',
				'short_desc',
				'meta',
				'add_cart',
			]
		);

		if ( is_array( $single_structure ) && ! empty( $single_structure ) ) {

			foreach ( $single_structure as $value ) {

				switch ( $value ) {
					case 'title':
						/**
						 * Add Product Title on single product page for all products.
						 */
						do_action( 'pp_quick_view_title_before', $post_id );
						echo '<a href="' . esc_url( apply_filters( 'pp_woo_title_link', get_permalink( $post_id ) ) ) . '">';
						woocommerce_template_single_title();
						echo '</a>';
						do_action( 'pp_quick_view_title_after', $post_id );
						break;
					case 'price':
						/**
						 * Add Product Price on single product page for all products.
						 */
						do_action( 'pp_quick_view_price_before', $post_id );
						woocommerce_template_single_price();
						do_action( 'pp_quick_view_price_after', $post_id );
						break;
					case 'ratings':
						/**
						 * Add rating on single product page for all products.
						 */
						do_action( 'pp_quick_view_rating_before', $post_id );
						woocommerce_template_single_rating();
						do_action( 'pp_quick_view_rating_after', $post_id );
						break;
					case 'short_desc':
						do_action( 'pp_quick_view_short_description_before', $post_id );
						woocommerce_template_single_excerpt();
						do_action( 'pp_quick_view_short_description_after', $post_id );
						break;
					case 'add_cart':
						do_action( 'pp_quick_view_add_to_cart_before', $post_id );
						woocommerce_template_single_add_to_cart();
						do_action( 'pp_quick_view_add_to_cart_after', $post_id );
						break;
					case 'meta':
						do_action( 'pp_quick_view_category_before', $post_id );
						woocommerce_template_single_meta();
						do_action( 'pp_quick_view_category_after', $post_id );
						break;
					default:
						break;
				}
			}
		}

	}

	/**
	 * Single Product add to cart ajax request
	 *
	 * @since 1.1.0
	 *
	 * @return void.
	 */
	public function add_cart_single_product_ajax() {
		check_ajax_referer( 'pp-ac-nonce', 'nonce' );
		$product_id   = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0;
		$variation_id = isset( $_POST['variation_id'] ) ? absint( $_POST['variation_id'] ) : 0;
		$quantity     = isset( $_POST['quantity'] ) ? sanitize_text_field( wp_unslash( $_POST['quantity'] ) ) : 0;

		if ( $variation_id ) {
			add_action( 'wp_loaded', [ 'WC_Form_Handler', 'add_to_cart_action' ], 20 );

			if ( is_callable( [ 'WC_AJAX', 'get_refreshed_fragments' ] ) ) {
				// Sends the fragments as JSON and exits, so nothing below this runs.
				\WC_AJAX::get_refreshed_fragments();
			}
		} else {
			WC()->cart->add_to_cart( $product_id, $quantity );
		}
		die();
	}

	/**
	 * Refresh the cart counter and subtotal after an AJAX cart update.
	 *
	 * One key per node: WooCommerce runs replaceWith() for every fragment, so two
	 * keys matching the same element would rebuild it twice per add to cart.
	 *
	 * @param array $fragments Cart fragments keyed by jQuery selector.
	 * @return array
	 */
	public function pp_cart_count_fragments( $fragments ) {
		$count = WC()->cart->get_cart_contents_count();

		$fragments['.pp-cart-counter'] = '<span class="pp-cart-counter" data-counter="' . esc_attr( $count ) . '">' . esc_html( $count ) . '</span>';
		$fragments['span.pp-cart-subtotal'] = '<span class="pp-cart-subtotal">' . WC()->cart->get_cart_subtotal() . '</span>';

		return $fragments;
	}

	/**
	 * Get Widget Setting data.
	 *
	 * @since 1.7.0
	 * @access public
	 * @param array  $elements Element array.
	 * @param string $form_id Element ID.
	 * @return Boolean True/False.
	 */
	public function find_element_recursive( $elements, $form_id ) {

		foreach ( $elements as $element ) {
			if ( $form_id === $element['id'] ) {
				return $element;
			}

			if ( ! empty( $element['elements'] ) ) {
				$element = $this->find_element_recursive( $element['elements'], $form_id );

				if ( $element ) {
					return $element;
				}
			}
		}

		return false;
	}

	/**
	 * Get Woo Data via AJAX call.
	 *
	 * @since 2.4.0
	 * @access public
	 */
	public function pp_get_products() {
		check_ajax_referer( 'pp-product-nonce', 'nonce' );

		$post_id   = isset( $_POST['page_id'] ) ? absint( $_POST['page_id'] ) : 0;
		$widget_id = isset( $_POST['widget_id'] ) ? sanitize_key( wp_unslash( $_POST['widget_id'] ) ) : '';

		if ( ! $post_id || '' === $widget_id ) {
			wp_send_json_error( esc_html__( 'Invalid request.', 'powerpack' ) );
		}

		// Only serve documents the visitor is allowed to see. A nonce is printed
		// in public markup, so it is not on its own proof of any capability.
		if ( 'publish' !== get_post_status( $post_id ) && ! current_user_can( 'edit_post', $post_id ) ) {
			wp_send_json_error( esc_html__( 'Invalid request.', 'powerpack' ) );
		}

		$elementor = \Elementor\Plugin::$instance;
		$document  = $elementor->documents->get( $post_id );

		if ( ! $document ) {
			wp_send_json_error( esc_html__( 'Invalid request.', 'powerpack' ) );
		}

		$widget_data = $this->find_element_recursive( $document->get_elements_data(), $widget_id );

		$data = [
			'message'    => esc_html__( 'Saved', 'powerpack' ),
			'ID'         => '',
			'skin_id'    => '',
			'html'       => '',
			'pagination' => '',
		];

		// find_element_recursive() returns false, not null, when nothing matches.
		if ( $widget_data ) {
			$widget = $elementor->elements_manager->create_element_instance( $widget_data );
			$skin   = $widget ? $widget->get_current_skin() : false;

			// The requested element is not necessarily a products widget.
			if ( $skin && is_callable( [ $skin, 'render_ajax_product_body' ] ) && is_callable( [ $skin, 'render_ajax_pagination' ] ) ) {
				$data['ID']         = $widget->get_id();
				$data['skin_id']    = $widget->get_current_skin_id();
				$data['html']       = $skin->render_ajax_product_body( $widget );
				$data['pagination'] = $skin->render_ajax_pagination( $widget );
			}
		}

		wp_send_json_success( $data );
	}

	/**
	 * Constructer.
	 *
	 * @since 1.3.3
	 * @access public
	 */
	public function __construct() {
		parent::__construct();

		add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] );

		// In Editor Woocommerce frontend hooks before the Editor init.
		add_action( 'admin_action_elementor', [ $this, 'register_wc_hooks' ], 9 );

		add_filter( 'pp_woo_elements_js_localize', [ $this, 'js_localize' ] );

		// quick view ajax.
		add_action( 'wp_ajax_pp_woo_quick_view', [ $this, 'load_quick_view_product' ] );
		add_action( 'wp_ajax_nopriv_pp_woo_quick_view', [ $this, 'load_quick_view_product' ] );

		add_action( 'wp_ajax_pp_add_cart_single_product', [ $this, 'add_cart_single_product_ajax' ] );
		add_action( 'wp_ajax_nopriv_pp_add_cart_single_product', [ $this, 'add_cart_single_product_ajax' ] );

		add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'pp_cart_count_fragments' ] );

		// Pagination ajax
		add_action( 'wp_ajax_pp_get_products', [ $this, 'pp_get_products' ] );
		add_action( 'wp_ajax_nopriv_pp_get_products', [ $this, 'pp_get_products' ] );

		// Add hook for recently viewed products (only when tracking is enabled in settings).
		if ( get_option( 'pp_woo_recently_viewed_tracking' ) ) {
			add_action( 'template_redirect', [ $this, 'pp_recently_viewed_product_data' ] );
			add_action( 'wp_login', [ $this, 'pp_sync_viewed_products_cookie_to_user_meta' ], 10, 2 );
		}
	}

	/**
	 * Set recently viewed product into db & cookies.
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function pp_recently_viewed_product_data() {
		if ( ! is_product() ) {
			return;
		}

		global $post;
		$product_id = (int) $post->ID;

		$cookie_usermeta_name = 'pp_viewed_products_ids';
		$max_stored_ids       = apply_filters( 'pp_woo_recently_viewed_max_ids', 50 );

		if ( is_user_logged_in() ) {
			$user_id = get_current_user_id();
			$viewed = get_user_meta( $user_id, $cookie_usermeta_name, true );

			if ( empty( $viewed ) || ! is_array( $viewed ) ) {
				$viewed = [];
			}

			if ( ! in_array( $product_id, $viewed, true ) ) {
				$viewed[] = $product_id;
				$viewed   = array_slice( $viewed, -$max_stored_ids );
				update_user_meta( $user_id, $cookie_usermeta_name, $viewed );
			}
		} else {
			$cookie_expire = time() + ( 30 * DAY_IN_SECONDS ); // 30 days.

			$viewed = isset( $_COOKIE[ $cookie_usermeta_name ] ) ? array_map( 'intval', explode( ',', sanitize_text_field( wp_unslash( $_COOKIE[ $cookie_usermeta_name ] ) ) ) ) : [];

			if ( ! in_array( $product_id, $viewed, true ) ) {
				$viewed[] = $product_id;
				$viewed   = array_slice( $viewed, -$max_stored_ids );
				setcookie( $cookie_usermeta_name, implode( ',', $viewed ), $cookie_expire, '/' );
			}
		}
	}

	/**
	 * Sync pp_viewed_products_ids cookies while logged in.
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function pp_sync_viewed_products_cookie_to_user_meta( $user_login, $user ) {
		$sync_cookies = apply_filters( 'pp_woo_sync_product_ids_cookie', false );
		if ( ! $sync_cookies ) {
			return;
		}
		$cookie_usermeta_name = 'pp_viewed_products_ids';

		if ( isset( $_COOKIE[ $cookie_usermeta_name ] ) ) {

			$cookie_ids = array_filter( array_map( 'absint', explode( ',', sanitize_text_field( wp_unslash( $_COOKIE[ $cookie_usermeta_name ] ) ) ) ) );

			// Check existing user meta.
			$user_ids = get_user_meta( $user->ID, $cookie_usermeta_name, true );
			if ( empty( $user_ids ) || ! is_array( $user_ids ) ) {
				$user_ids = [];
			}

			// Merge & remove duplicates.
			$merged_ids = array_unique( array_merge( array_map( 'intval', $user_ids ), $cookie_ids ) );

			update_user_meta( $user->ID, $cookie_usermeta_name, $merged_ids );

			setcookie( $cookie_usermeta_name, '', time() - 3600, '/' );
		}
	}

	/**
	 * Get All Taxonomies
	 *
	 * @since 2.7.0
	 * @return array $options Array of taxonomies
	 */
	public static function get_taxonomies_list() {
		$options = [];

		$post_type = 'product';
		$taxonomies = get_object_taxonomies( $post_type, 'objects' );
		$exclude = apply_filters( 'woopack_grid_filters_exclude_taxonomies', [ 'product_type', 'product_shipping_class' ] );

		foreach ( $taxonomies as $tax_slug => $tax ) {

			// $exclude is already an array; wrapping it in another one compared
			// each slug against a single array element and so never matched,
			// leaving product_type and product_shipping_class in the list.
			if ( in_array( $tax_slug, $exclude, true ) ) {
				continue;
			}

			$options[ $tax_slug ] = $tax->label;
		}

		return apply_filters( 'woopack_grid_filters_taxonomies', $options, $taxonomies, $exclude );
	}

	/**
	 * Register styles.
	 *
	 * @return void
	 */
	public function register_styles() {
		wp_register_style(
			'widget-pp-offcanvas-content',
			$this->get_css_assets_url( 'widget-offcanvas-content', null, true, true ),
			[],
			POWERPACK_ELEMENTS_VER
		);

		wp_register_style(
			'widget-pp-woo-product-images',
			$this->get_css_assets_url( 'widget-woo-product-images', null, true, true ),
			[],
			POWERPACK_ELEMENTS_VER
		);

		wp_register_style(
			'widget-pp-woo-checkout',
			$this->get_css_assets_url( 'widget-woo-checkout', null, true, true ),
			[],
			POWERPACK_ELEMENTS_VER
		);

		wp_register_style(
			'widget-pp-woo-single-product',
			$this->get_css_assets_url( 'widget-woo-single-product', null, true, true ),
			[],
			POWERPACK_ELEMENTS_VER
		);
	}
}
