<?php

namespace Blocksy;

class WooCommerceAddToCart {
	use WordPressActionsManager;

	private $finalize_action_name = '';

	private $handled_product_ids = [];

	private $actions = [
		[
			'action' => 'woocommerce_before_add_to_cart_form',
			'priority' => 10
		],

		[
			'action' => 'woocommerce_before_add_to_cart_quantity',
			'priority' => PHP_INT_MAX
		],

		[
			'action' => 'woocommerce_before_add_to_cart_button',
			'priority' => PHP_INT_MAX
		],

		[
			'action' => 'woocommerce_after_add_to_cart_button',
			'priority' => 100
		],

		[
			'action' => 'woocommerce_post_class',
			'priority' => 10
		]
	];

	public function __construct() {
		$this->attach_hooks([
			'exclude' => [
				'woocommerce_after_add_to_cart_button'
			]
		]);

		if (isset($_REQUEST['blocksy_add_to_cart'])) {
			add_filter(
				'woocommerce_add_to_cart_redirect',
				'__return_false'
			);
		}

		// TODO: maybe extract block watcher in a separate class and use from one
		// single place, in case we will need to detect more of them.
		add_filter('pre_render_block', function ($pre_render, $parsed_block, $parent_block) {
			if ($parsed_block['blockName'] === 'woocommerce/add-to-cart-form') {
				global $blocksy_detect_woo_block_render;

				$blocksy_detect_woo_block_render = true;
			}

			return $pre_render;
		}, 10, 3);

		// Make sure we process response exactly same way as Woo does. In the
		// exact point of time.
		//
		// https://github.com/woocommerce/woocommerce/blob/9f26128dc2a223d5b9e5482e6a41b5052bfc91c4/plugins/woocommerce/includes/class-wc-ajax.php#L30
		add_action('template_redirect', [$this, 'do_wc_ajax'], 0);
	}

	public function do_wc_ajax() {
		if (! isset($_REQUEST['blocksy_add_to_cart'])) {
			return;
		}

		// Signal that this is an AJAX request for plugins that check wp_doing_ajax().
		// Since this request doesn't go through admin-ajax.php, the DOING_AJAX constant
		// is not defined. Plugins like "Advanced Product Fields for WooCommerce" use
		// wp_doing_ajax() to detect mini-cart context and display custom field data.
		//
		// https://wordpress.org/plugins/advanced-product-fields-for-woocommerce/
		add_filter('wp_doing_ajax', '__return_true');

		ob_start();
		wc_print_notices();
		$notices = ob_get_clean();

		ob_start();
		woocommerce_mini_cart();
		$mini_cart = ob_get_clean();

		$data = [
			'notices' => $notices,
			'fragments' => apply_filters(
				'woocommerce_add_to_cart_fragments',
				[
					'div.widget_shopping_cart_content' => '<div class="widget_shopping_cart_content">' . $mini_cart . '</div>',
				]
			),
			'cart_hash' => WC()->cart->get_cart_hash()
		];

		wp_send_json_success($data);
	}

	private function product_was_handled($product) {
		if (! $product instanceof \WC_Product) {
			return false;
		}

		return in_array($product->get_id(), $this->handled_product_ids);
	}

	private function output_cart_action_open() {
		if (
			(
				blocksy_manager()->screen->is_product()
				||
				wp_doing_ajax()
			)
			&&
			! blocksy_manager()->screen->uses_woo_default_template()
		) {
			return;
		}

		global $blocksy_detect_woo_block_render;

		if (
			isset($blocksy_detect_woo_block_render)
			&&
			$blocksy_detect_woo_block_render
		) {
			return;
		}

		$attr = apply_filters('blocksy:woocommerce:cart-actions:attr', [
			'class' => 'ct-cart-actions'
		]);

		echo '<div ' . blocksy_attr_to_html($attr) . '>';

		$this->attach_hooks([
			'only' => [
				'woocommerce_after_add_to_cart_button'
			]
		]);
	}

	public function finalize_add_to_cart() {
		global $product;

		if ($this->product_was_handled($product)) {
			return;
		}

		if (! $product) {
			return;
		}

		// On single product pages we know for sure that there's only one
		// product that needs handling. On other pages or during AJAX requests,
		// we need to make sure that we don't handle the same product twice.
		if (blocksy_manager()->screen->is_product()) {
			$this->detach_hooks();
		} else {
			$this->handled_product_ids[] = $product->get_id();
		}

		remove_action(
			$this->finalize_action_name,
			[$this, 'finalize_add_to_cart'],
			50
		);
	}

	public function woocommerce_before_add_to_cart_form() {
		global $product;
		global $root_product;

		if ($this->product_was_handled($product)) {
			return;
		}

		$root_product = $product;
	}

	public function woocommerce_before_add_to_cart_quantity() {
		global $product;
		global $root_product;

		if ($this->product_was_handled($product)) {
			return;
		}

		if (! $root_product) {
			return;
		}

		if (
			! $root_product->is_type('simple')
			&&
			! $root_product->is_type('variation')
			&&
			! $root_product->is_type('variable')
			&&
			! $this->check_product_type($root_product)
		) {
			return;
		}

		$this->output_cart_action_open();

		// If we will encounter an woocommerce_before_add_to_cart_button action before the actual next woocommerce_post_class,
		// this means that the add to cart form for the current product is rendered
		// one more time. So, we need to ignore it and finalize logic in this case.
		//
		// This check is needed only on single product pages. The archive pages logic is covered
		// in finalize logic already.
		//
		// This happened for Free Downloads WooCommerce Pro plugin.
		if (blocksy_manager()->screen->is_product()) {
			$this->finalize_action_name = 'woocommerce_before_add_to_cart_button';
			add_action($this->finalize_action_name, [$this, 'finalize_add_to_cart'], 50);
		}
	}

	public function woocommerce_before_add_to_cart_button() {
		global $product;
		global $root_product;

		if ($this->product_was_handled($product)) {
			return;
		}

		if (! $root_product) {
			return;
		}

		if (
			! $root_product->is_type('grouped')
			&&
			! $root_product->is_type('external')
		) {
			return;
		}

		$this->output_cart_action_open();
	}

	public function woocommerce_after_add_to_cart_button() {
		global $product;

		if ($this->product_was_handled($product)) {
			return;
		}

		if (! $product) {
			return;
		}

		if (
			! $product->is_type('simple')
			&&
			! $product->is_type('variable')
			&&
			! $product->is_type('grouped')
			&&
			! $product->is_type('external')
			&&
			! $this->check_product_type($product)
		) {
			return;
		}

		if (
			(
				$product->is_type('simple')
				||
				$product->is_type('variable')
				||
				$this->check_product_type($product)
			)
			&&
			! did_action('woocommerce_before_add_to_cart_quantity')
		) {
			return;
		}

		global $blocksy_detect_woo_block_render;

		if (
			isset($blocksy_detect_woo_block_render)
			&&
			$blocksy_detect_woo_block_render
		) {
			return;
		}

		echo '</div>';

	}

	public function woocommerce_post_class($classes) {
		global $product;
		global $woocommerce_loop;

		if ($this->product_was_handled($product)) {
			return $classes;
		}

		$classes = apply_filters(
			'blocksy:woocommerce:single-product:post-class',
			$classes
		);

		$default_product_layout = blocksy_get_woo_single_layout_defaults();

		$layout = blocksy_get_theme_mod(
			'woo_single_layout',
			blocksy_get_woo_single_layout_defaults()
		);

		if (
			(
				function_exists('blocksy_has_product_specific_layer')
				&&
				! blocksy_has_product_specific_layer('product_add_to_cart')
			)
			||
			! $product
			||
			$product->is_type('external')
			||
			(
				! blocksy_manager()->screen->is_product()
				&&
				! wp_doing_ajax()
			)
		) {
			return $classes;
		}

		$has_ajax_add_to_cart = blocksy_get_theme_mod(
			'has_ajax_add_to_cart',
			'yes'
		);

		if (
			$has_ajax_add_to_cart === 'yes'
			&&
			get_option('woocommerce_cart_redirect_after_add', 'no') === 'no'
		) {
			$classes[] = 'ct-ajax-add-to-cart';
		}

		if (! empty($this->finalize_action_name)) {
			remove_action(
				$this->finalize_action_name,
				[$this, 'finalize_add_to_cart'],
				50
			);
		}

		// https://github.com/woocommerce/woocommerce/blob/701d9341dde6fa8861684fb161cdca9ec94a7a4d/plugins/woocommerce/includes/wc-template-functions.php#L1784
		$this->finalize_action_name = 'woocommerce_' . $product->get_type() . '_add_to_cart';

		add_action(
			$this->finalize_action_name,
			[$this, 'finalize_add_to_cart'],
			50
		);

		return $classes;
	}

	public function check_product_type($product) {
		$allowed_custom_product_types = [
			'subscription',
			'variable-subscription',
			'woosb'
		];

		return in_array($product->get_type(), $allowed_custom_product_types);
	}
}

