<?php

/**
 * Checkout page Elementor widgets (Checkout tab toggles in plugin settings).
 *
 * Split layout: place widgets in this order — Checkout Form → Order Review → Payment Methods.
 * The Payment Methods widget closes the checkout form and `#order_review`. If Order Review is
 * omitted, Payment Methods outputs the full order review table and payment block inside `#order_review`.
 *
 * @package storzen
 */

namespace Elementor;

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

/**
 * Shared state for coordinating checkout fragments across widgets.
 */
class Storzen_Checkout_Widgets_State
{

	/**
	 * HTML id on form.checkout so #place_order can use form="…" when Elementor splits widgets (button must submit this form).
	 */
	const FORM_HTML_ID = 'szp-checkout';

	/** @var bool */
	public static $form_open = false;

	/** @var bool */
	public static $order_review_div_open = false;

	/** @var bool */
	public static $form_closed = false;

	/** @var bool */
	public static $coupon_toggle_inline_printed = false;
}

/**
 * Checkout helpers for Elementor (editor vs front).
 */
trait Storzen_Checkout_Widget_Helpers
{

	/**
	 * Ensure WooCommerce session + cart exist (Elementor editor often renders before the cart is initialized).
	 *
	 * @return bool True when WC()->cart is available.
	 */
	protected function szp_checkout_ensure_cart()
	{
		if (! function_exists('WC')) {
			return false;
		}
		if (WC()->cart) {
			return true;
		}
		// After woocommerce_init, wc_load_cart() attaches session + cart (needed for editor preview / REST-like requests).
		if (function_exists('wc_load_cart') && did_action('woocommerce_init')) {
			wc_load_cart();
		}
		return (bool) WC()->cart;
	}

	/**
	 * @return bool
	 */
	protected function szp_checkout_wc_ready()
	{
		return $this->szp_checkout_ensure_cart();
	}

	/**
	 * @param string $label Editor placeholder label.
	 * @return bool True if placeholder was shown.
	 */
	protected function szp_checkout_maybe_placeholder($label)
	{
		if ($this->szp_checkout_wc_ready()) {
			return false;
		}
		$ed = \Elementor\Plugin::$instance->editor;
		if ($ed && method_exists($ed, 'is_edit_mode') && $ed->is_edit_mode()) {
			printf(
				'<div class="szp-checkout-widget-placeholder" style="padding:16px;border:1px dashed #ccc;border-radius:6px;background:#f9f9f9;font-size:13px;color:#555;">%s</div>',
				esc_html($label)
			);
			return true;
		}
		return false;
	}

	/**
	 * Editor: require checkout context.
	 *
	 * @return bool True if placeholder was shown.
	 */
	protected function szp_checkout_editor_need_checkout_page()
	{
		$ed = \Elementor\Plugin::$instance->editor;
		$is_edit = $ed && method_exists($ed, 'is_edit_mode') && $ed->is_edit_mode();
		$preview = \Elementor\Plugin::$instance->preview;
		$is_preview = $preview && method_exists($preview, 'is_preview_mode') && $preview->is_preview_mode();

		if (! $is_edit && ! $is_preview) {
			return false;
		}

		if (function_exists('is_checkout') && is_checkout()) {
			return false;
		}

		// Theme Builder / Elementor often do not run the main query as `is_checkout()`; if WC + cart are ready, allow preview.
		if ($this->szp_checkout_ensure_cart()) {
			return false;
		}

		printf(
			'<div class="szp-checkout-widget-placeholder" style="padding:16px;border:1px dashed #c77;border-radius:6px;background:#fff8f8;font-size:13px;color:#555;">%s</div>',
			esc_html__('Use this widget on the WooCommerce Checkout page (or a template applied to it).', 'storzen')
		);
		return true;
	}

	/**
	 * Mirrors WC_Shortcode_Checkout::checkout() setup before templates (runs once per request).
	 *
	 * @return bool False if cart empty / errors-only template was output — skip widget markup.
	 */
	protected function szp_checkout_bootstrap()
	{
		static $boot_done = false;
		if ($boot_done) {
			return true;
		}
		$boot_done = true;

		if (! $this->szp_checkout_wc_ready()) {
			return false;
		}

		do_action('woocommerce_before_checkout_form_cart_notices');

		$ed = \Elementor\Plugin::$instance->editor;
		$elementor_editing = $ed && method_exists($ed, 'is_edit_mode') && $ed->is_edit_mode();
		$preview = \Elementor\Plugin::$instance->preview;
		$elementor_preview = $preview && method_exists($preview, 'is_preview_mode') && $preview->is_preview_mode();

		if (WC()->cart->is_empty() && ! is_customize_preview() && apply_filters('woocommerce_checkout_redirect_empty_cart', true)) {
			// Front: same as WC shortcode (no output). Editor / preview: still show markup for layout.
			if (! $elementor_editing && ! $elementor_preview) {
				return false;
			}
		}

		do_action('woocommerce_check_cart_items');
		WC()->cart->calculate_totals();

		$checkout = WC()->checkout();

		if (empty($_POST) && wc_notice_count('error') > 0) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			wc_get_template('checkout/cart-errors.php', array('checkout' => $checkout));
			wc_clear_notices();
			return false;
		}

		$non_js_checkout = ! empty($_POST['woocommerce_checkout_update_totals']); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		if (wc_notice_count('error') === 0 && $non_js_checkout) {
			// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Using WooCommerce's own translations
			wc_add_notice(__('The order totals have been updated. Please confirm your order by pressing the "Place order" button at the bottom of the page.', 'woocommerce'));
		}

		return true;
	}
}

/**
 * Opens the checkout form and outputs billing / shipping fields only.
 */
class Storzen_Checkout_Form_Widget extends Widget_Base
{
	use Storzen_Checkout_Widget_Helpers;

	public function get_name()
	{
		return 'storzen-checkout-form';
	}

	public function get_title()
	{
		return esc_html__('Checkout Form', 'storzen');
	}

	public function get_icon()
	{
		return 'eicon-form-horizontal';
	}

	public function get_categories()
	{
		return array('storzen-checkout');
	}

	/**
	 * Ensure Elementor loads checkout widget CSS when optimizing assets.
	 *
	 * @return string[]
	 */
	public function get_style_depends()
	{
		return array('storzen-checkout-widgets');
	}

	protected function register_controls() {}

	protected function render()
	{
		if ($this->szp_checkout_maybe_placeholder(esc_html__('WooCommerce checkout (install & activate WooCommerce).', 'storzen'))) {
			return;
		}
		if ($this->szp_checkout_editor_need_checkout_page()) {
			return;
		}

		if (! $this->szp_checkout_bootstrap()) {
			return;
		}

		$checkout = WC()->checkout();

		if (! $checkout->is_registration_enabled() && $checkout->is_registration_required() && ! is_user_logged_in()) {
			// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Using WooCommerce's own translations
			echo esc_html(apply_filters('woocommerce_checkout_must_be_logged_in_message', __('You must be logged in to checkout.', 'woocommerce')));
			return;
		}

		wc_maybe_define_constant('WOOCOMMERCE_CHECKOUT', true);

		ob_start();
		do_action('woocommerce_before_checkout_form', $checkout);
		$szp_before_checkout_form = ob_get_clean();
		if ('' !== trim($szp_before_checkout_form)) {
			echo '<div class="szp-checkout-before-form szp-checkout-skin">' . $szp_before_checkout_form . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WooCommerce core HTML.
		}

		// Inline toggle: Elementor preview/edit often sets is_checkout() false, so checkout-elementor.js may not load.
		if (
			! Storzen_Checkout_Widgets_State::$coupon_toggle_inline_printed
			&& false !== strpos($szp_before_checkout_form, 'woocommerce-form-coupon-toggle')
			&& false !== strpos($szp_before_checkout_form, 'woocommerce-checkout-form-coupon')
		) {
			Storzen_Checkout_Widgets_State::$coupon_toggle_inline_printed = true;
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Minimal inline handler; no user input.
			echo '<script>(function(){if(window.__szpCouponInlineBound)return;window.__szpCouponInlineBound=1;document.addEventListener("click",function(e){var t=e.target;if(t&&t.nodeType!==1&&t.parentElement)t=t.parentElement;if(!t||!t.closest)return;var a=t.closest("a.showcoupon");if(!a||!a.closest(".woocommerce-form-coupon-toggle"))return;var f=document.getElementById("woocommerce-checkout-form-coupon");if(!f)return;e.preventDefault();e.stopImmediatePropagation();e.stopPropagation();var h=function(el){return el.style.display==="none"||window.getComputedStyle(el).display==="none";};var openNow=h(f);f.style.display=openNow?"block":"none";a.setAttribute("aria-expanded",openNow?"true":"false");if(openNow){var inp=f.querySelector("#coupon_code");if(inp)inp.focus();}},true);})();</script>';
		}

		// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Using WooCommerce's own translations
		echo '<form id="' . esc_attr(Storzen_Checkout_Widgets_State::FORM_HTML_ID) . '" name="checkout" method="post" class="checkout woocommerce-checkout szp-checkout-widget szp-checkout-form szp-checkout-skin" action="' . esc_url(wc_get_checkout_url()) . '" enctype="multipart/form-data" aria-label="' . esc_attr__('Checkout', 'woocommerce') . '">';

		Storzen_Checkout_Widgets_State::$form_open = true;

		if ($checkout->get_checkout_fields()) {
			do_action('woocommerce_checkout_before_customer_details');
			echo '<div class="col2-set" id="customer_details">';
			echo '<div class="col-12">';
			do_action('woocommerce_checkout_billing');
			echo '</div>';
			echo '<div class="col-12">';
			do_action('woocommerce_checkout_shipping');
			echo '</div>';
			echo '</div>';
			do_action('woocommerce_checkout_after_customer_details');
		}

		// Trigger update_checkout to ensure shipping methods are calculated after form loads.
		// This is especially important when using Elementor widgets where the form may load
		// dynamically and WooCommerce needs to recalculate shipping based on the address.
		?>
		<script type="text/javascript">
		jQuery(function($) {
			// Wait for WooCommerce checkout to initialize, then trigger update
			var triggerUpdate = function() {
				if (typeof wc_checkout_form !== 'undefined' && $('#<?php echo esc_js(Storzen_Checkout_Widgets_State::FORM_HTML_ID); ?>').length) {
					$(document.body).trigger('update_checkout');
				}
			};
			// Try immediately and with delays to catch different load scenarios
			setTimeout(triggerUpdate, 100);
			setTimeout(triggerUpdate, 500);
			setTimeout(triggerUpdate, 1000);
			// Also trigger on initial checkout update
			$(document.body).one('updated_checkout', function() {
				// Ensure shipping address uses billing address when not shipping to different address
				if ($('#ship-to-different-address input').length && !$('#ship-to-different-address input').is(':checked')) {
					setTimeout(triggerUpdate, 100);
				}
			});
		});
		</script>
		<?php
	}
}

/**
 * Order review table only (inside `#order_review`; payment is rendered by the Payment Methods widget).
 */
class Storzen_Checkout_Order_Review_Widget extends Widget_Base
{
	use Storzen_Checkout_Widget_Helpers;

	public function get_name()
	{
		return 'storzen-checkout-order-review';
	}

	public function get_title()
	{
		return esc_html__('Order Review', 'storzen');
	}

	public function get_icon()
	{
		return 'eicon-product-list';
	}

	public function get_categories()
	{
		return array('storzen-checkout');
	}

	public function get_style_depends()
	{
		return array('storzen-checkout-widgets');
	}

	protected function register_controls() {}

	protected function render()
	{
		if ($this->szp_checkout_maybe_placeholder(esc_html__('WooCommerce order review (requires WooCommerce).', 'storzen'))) {
			return;
		}
		if ($this->szp_checkout_editor_need_checkout_page()) {
			return;
		}

		if (! $this->szp_checkout_bootstrap()) {
			return;
		}

		if (! Storzen_Checkout_Widgets_State::$form_open) {
			$ed = \Elementor\Plugin::$instance->editor;
			if ($ed && method_exists($ed, 'is_edit_mode') && $ed->is_edit_mode()) {
				echo '<div class="szp-checkout-widget-placeholder" style="padding:16px;border:1px dashed #c77;border-radius:6px;background:#fff8f8;font-size:13px;color:#555;">';
				echo esc_html__('Place the “Checkout Form” widget above this one so the order review sits inside the same checkout form.', 'storzen');
				echo '</div>';
			}
			return;
		}

		wc_maybe_define_constant('WOOCOMMERCE_CHECKOUT', true);

		do_action('woocommerce_checkout_before_order_review_heading');

		// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Using WooCommerce's own translations
		echo '<h3 id="order_review_heading" class="szp-checkout-section-title">' . esc_html__('Your order', 'woocommerce') . '</h3>';

		do_action('woocommerce_checkout_before_order_review');

		echo '<div id="order_review" class="woocommerce-checkout-review-order szp-checkout-order-review szp-checkout-skin">';

		remove_action('woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20);
		if (function_exists('woocommerce_order_review')) {
			woocommerce_order_review();
		}
		add_action('woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20);

		Storzen_Checkout_Widgets_State::$order_review_div_open = true;
	}
}

/**
 * Payment gateways + place order; closes `#order_review` and the checkout form.
 */
class Storzen_Checkout_Payment_Widget extends Widget_Base
{
	use Storzen_Checkout_Widget_Helpers;

	public function get_name()
	{
		return 'storzen-checkout-payment';
	}

	public function get_title()
	{
		return esc_html__('Payment Methods', 'storzen');
	}

	public function get_icon()
	{
		return 'eicon-payment';
	}

	public function get_categories()
	{
		return array('storzen-checkout');
	}

	public function get_style_depends()
	{
		return array('storzen-checkout-widgets');
	}

	protected function register_controls() {}

	protected function render()
	{
		if ($this->szp_checkout_maybe_placeholder(esc_html__('WooCommerce payment methods (requires WooCommerce).', 'storzen'))) {
			return;
		}
		if ($this->szp_checkout_editor_need_checkout_page()) {
			return;
		}

		if (! $this->szp_checkout_bootstrap()) {
			return;
		}

		if (! Storzen_Checkout_Widgets_State::$form_open) {
			$ed = \Elementor\Plugin::$instance->editor;
			if ($ed && method_exists($ed, 'is_edit_mode') && $ed->is_edit_mode()) {
				echo '<div class="szp-checkout-widget-placeholder" style="padding:16px;border:1px dashed #c77;border-radius:6px;background:#fff8f8;font-size:13px;color:#555;">';
				echo esc_html__('Place the “Checkout Form” widget first, then Order Review (optional) and this widget, so billing and payment stay in one checkout form.', 'storzen');
				echo '</div>';
			}
			return;
		}

		wc_maybe_define_constant('WOOCOMMERCE_CHECKOUT', true);

		$checkout = WC()->checkout();

		add_filter('woocommerce_order_button_html', array($this, 'szp_inject_place_order_form_attribute'), 5, 1);

		try {
			// Order Review widget omitted: full #order_review (table + payment) like core form-checkout.php.
			if (! Storzen_Checkout_Widgets_State::$order_review_div_open) {
				do_action('woocommerce_checkout_before_order_review_heading');
				// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Using WooCommerce's own translations
				echo '<h3 id="order_review_heading" class="szp-checkout-section-title">' . esc_html__('Your order', 'woocommerce') . '</h3>';
				do_action('woocommerce_checkout_before_order_review');
				echo '<div id="order_review" class="woocommerce-checkout-review-order szp-checkout-widget szp-checkout-payment szp-checkout-skin">';
				do_action('woocommerce_checkout_order_review');
			} elseif (function_exists('woocommerce_checkout_payment')) {
				echo '<div class="szp-checkout-widget szp-checkout-payment szp-checkout-skin">';
				woocommerce_checkout_payment();
				echo '</div>';
			}
		} finally {
			remove_filter('woocommerce_order_button_html', array($this, 'szp_inject_place_order_form_attribute'), 5);
		}

		echo '</div>';

		do_action('woocommerce_checkout_after_order_review');

		echo '</form>';

		do_action('woocommerce_after_checkout_form', $checkout);

		Storzen_Checkout_Widgets_State::$form_closed = true;
		Storzen_Checkout_Widgets_State::$form_open     = false;
	}

	/**
	 * Tie #place_order to form#szp-checkout when Elementor wraps fragments so the button may sit outside the form node.
	 *
	 * @param string $html Button HTML.
	 * @return string
	 */
	public function szp_inject_place_order_form_attribute($html)
	{
		$fid = Storzen_Checkout_Widgets_State::FORM_HTML_ID;
		if (strpos($html, 'form="' . $fid . '"') !== false || strpos($html, "form='" . $fid . "'") !== false) {
			return $html;
		}
		return (string) preg_replace('/<button\s/i', '<button form="' . esc_attr($fid) . '" ', $html, 1);
	}
}
