<?php
defined( 'ABSPATH' ) || exit;

/**
 * Injects Buy Now buttons on product pages and the shop loop.
 *
 * Handles:
 * - Single product page button (after/before Add to Cart)
 * - Shop/archive loop button
 * - AJAX add-to-cart + redirect handler
 * - Optional WooCommerce Add to Cart button hiding
 */
class TFDC_Buy_Now {

	/** @var self|null */
	private static $instance = null;

	/**
	 * Get the single instance.
	 *
	 * @return self
	 */
	public static function get_instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor — registers hooks.
	 */
	private function __construct() {
		$settings = TFDC_Admin::get_settings();

		// Single product page.
		if ( ! empty( $settings['buy_now_enabled'] ) ) {
			$position = $settings['buy_now_position'];
			if ( 'before_cart' === $position ) {
				add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'render_button' ) );
			} else {
				// Default: after_cart.
				add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'render_button' ) );
			}
		}

		// Shop/archive loop.
		if ( ! empty( $settings['buy_now_on_loop'] ) ) {
			add_filter( 'woocommerce_loop_add_to_cart_link', array( $this, 'filter_loop_add_to_cart_link' ), 10, 3 );
		}

		if ( ! empty( $settings['buy_now_enabled'] ) || ! empty( $settings['buy_now_on_loop'] ) ) {
			// AJAX handlers.
			add_action( 'wp_ajax_tfdc_add_and_redirect', array( $this, 'ajax_add_and_redirect' ) );
			add_action( 'wp_ajax_nopriv_tfdc_add_and_redirect', array( $this, 'ajax_add_and_redirect' ) );

			// Enqueue inline JS.
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
		}

		if ( ! empty( $settings['hide_add_to_cart'] ) ) {
			add_action( 'wp_head', array( $this, 'print_hide_add_to_cart_css' ) );
		}
		add_action( 'wp', array( $this, 'setup_single_product_add_to_cart_css' ) );
	}

	/**
	 * Render Buy Now button on single product pages.
	 */
	public function render_button() {
		global $product;
		if ( ! $product || ! $this->can_render_button( $product ) ) {
			return;
		}
		echo wp_kses_post( $this->button_html( $product, 'single' ) );
	}

	/**
	 * Add Buy Now buttons wherever WooCommerce prints loop Add to Cart links.
	 *
	 * @param string     $html    Existing Add to Cart HTML.
	 * @param WC_Product $product Product instance.
	 * @param array      $_args   WooCommerce loop button args.
	 * @return string
	 */
	public function filter_loop_add_to_cart_link( $html, $product, $_args = array() ) {
		if ( ! $this->can_render_button( $product ) || ( ! $product->is_type( 'simple' ) && ! $product->is_type( 'variable' ) ) ) {
			return $html;
		}

		$button   = $this->button_html( $product, 'loop' );
		$settings = TFDC_Admin::get_settings();

		if ( ! empty( $settings['hide_add_to_cart'] ) ) {
			return $button;
		}

		return $html . $button;
	}

	/**
	 * Whether the Buy Now button should render for a product.
	 *
	 * @param WC_Product $product Product instance.
	 * @return bool
	 */
	private function can_render_button( $product ) {
		return $product instanceof WC_Product
			&& $product->is_purchasable()
			&& $product->is_in_stock()
			&& ! TFDC_Product_Settings::is_buy_now_disabled( $product->get_id() );
	}

	/**
	 * Build the Buy Now button HTML.
	 *
	 * @param WC_Product $product WooCommerce product.
	 * @return string
	 */
	private function button_html( $product, $context = 'single' ) {
		$product_id       = $product->get_id();
		$settings         = TFDC_Admin::get_settings();
		$product_text     = TFDC_Product_Settings::get_button_text( $product_id );
		$default_quantity = TFDC_Product_Settings::get_default_quantity( $product_id );
		$text             = '' !== $product_text ? $product_text : ( ! empty( $settings['buy_now_text'] ) ? $settings['buy_now_text'] : __( 'Buy Now', 'tukiflow-direct-checkout-woocommerce' ) );
		$product_url      = '';

		if ( 'loop' === $context && $product->is_type( 'variable' ) ) {
			$product_url = add_query_arg( 'tfdc_buy_now', '1', get_permalink( $product_id ) );
		}

		/**
		 * Filter the CSS classes applied to the Buy Now button.
		 *
		 * Defaults mirror WooCommerce's single-product Add to Cart button so the
		 * button inherits the active theme's button styles automatically.
		 *
		 * Example — add a Divi-compatible class:
		 *   add_filter( 'tfdc_buy_now_button_classes', function( $classes ) {
		 *       $classes[] = 'et_pb_button';
		 *       return $classes;
		 *   } );
		 *
		 * @param string[] $classes CSS class list.
		 * @param int      $product_id Product ID.
		 */
		$classes = apply_filters(
			'tfdc_buy_now_button_classes',
			array( 'tfdc-buy-now-btn', 'button', 'alt' ),
			absint( $product_id )
		);

		if ( $product_url ) {
			$classes[] = 'tfdc-buy-now-select-options';
		}

		return sprintf(
			'<a href="%s" class="%s" data-product-id="%d" data-default-quantity="%d" data-nonce="%s" data-product-url="%s">%s</a>',
			esc_url( $product_url ? $product_url : '#' ),
			esc_attr( implode( ' ', array_map( 'sanitize_html_class', $classes ) ) ),
			absint( $product_id ),
			absint( $default_quantity ),
			esc_attr( wp_create_nonce( 'tfdc_buy_now' ) ),
			esc_url( $product_url ),
			esc_html( $text )
		);
	}

	/**
	 * AJAX handler: add product to cart and return a redirect URL.
	 */
	public function ajax_add_and_redirect() {
		check_ajax_referer( 'tfdc_buy_now', 'nonce' );

		$product_id   = absint( $_POST['product_id'] ?? 0 );
		$qty          = max( 1, absint( $_POST['quantity'] ?? 1 ) );
		$variation_id = absint( $_POST['variation_id'] ?? 0 );

		if ( ! $product_id ) {
			wp_send_json_error( array( 'message' => __( 'Invalid product.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		if ( TFDC_Product_Settings::is_buy_now_disabled( $product_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Buy Now is disabled for this product.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		$product = $variation_id ? wc_get_product( $variation_id ) : wc_get_product( $product_id );
		if ( ! $product || ! $product->is_purchasable() ) {
			wp_send_json_error( array( 'message' => __( 'This product is not available for purchase.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		if ( ! $variation_id && $product->is_type( 'variable' ) ) {
			wp_send_json_error( array( 'message' => __( 'Please choose product options before buying now.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		if ( ! $product->is_in_stock() ) {
			wp_send_json_error( array( 'message' => __( 'This product is out of stock.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		// Collect variation attributes when a variation is selected.
		$attributes = array();
		if ( $variation_id ) {
			$posted_data = wp_unslash( $_POST );
			foreach ( $posted_data as $key => $value ) {
				if ( 0 === strpos( $key, 'attribute_' ) ) {
					$attributes[ sanitize_text_field( $key ) ] = sanitize_text_field( $value );
				}
			}
		}

		$settings = TFDC_Admin::get_settings();

		if ( ! empty( $settings['buy_now_clear_cart'] ) ) {
			WC()->cart->empty_cart();
		}

		$added = WC()->cart->add_to_cart( $product_id, $qty, $variation_id, $attributes );
		if ( ! $added ) {
			wp_send_json_error( array( 'message' => __( 'Unable to add this product to the cart.', 'tukiflow-direct-checkout-woocommerce' ) ) );
		}

		$redirect = wc_get_checkout_url();

		wp_send_json_success( array( 'redirect' => $redirect ) );
	}

	/**
	 * Enqueue the lightweight inline JS for Buy Now click handling.
	 */
	public function enqueue_scripts() {
		$js = "
(function() {
	if (window.location.search.indexOf('tfdc_buy_now=1') !== -1) {
		var cartForm = document.querySelector('form.cart');
		if (cartForm) {
			cartForm.scrollIntoView({ behavior: 'smooth', block: 'center' });
			var firstChoice = cartForm.querySelector('select[name^=\"attribute_\"], input[name^=\"attribute_\"]');
			if (firstChoice) firstChoice.focus();
		}
	}

	document.addEventListener('click', function(e) {
		var btn = e.target.closest('.tfdc-buy-now-btn');
		if (!btn) return;
		e.preventDefault();

		var productId = btn.dataset.productId;
		var nonce     = btn.dataset.nonce;
		var productUrl = btn.dataset.productUrl;

		// Find the enclosing product form (single product page).
		var form = btn.closest('form.cart');
		if (productUrl && !form) {
			window.location.href = productUrl;
			return;
		}

		// For variable products, ensure a variation is selected.
		var variationId = 0;
		if (form) {
			var varInput = form.querySelector('input[name=\"variation_id\"]');
			if (varInput) {
				variationId = parseInt(varInput.value, 10) || 0;
				if (!variationId) {
					// Trigger WooCommerce's own validation notice.
					var addBtn = form.querySelector('.single_add_to_cart_button');
					if (addBtn) addBtn.click();
					return;
				}
			}
		}

		// Collect quantity from the form if available.
		var qty = parseInt(btn.dataset.defaultQuantity, 10) || 1;
		if (form) {
			var qtyInput = form.querySelector('input[name=\"quantity\"]');
			if (qtyInput) qty = parseInt(qtyInput.value, 10) || 1;
		}

		btn.classList.add('loading');

		var formData = new FormData();
		formData.append('action', 'tfdc_add_and_redirect');
		formData.append('product_id', productId);
		formData.append('quantity', qty);
		formData.append('nonce', nonce);

		// Pass variation data if this is a variable product.
		if (variationId) {
			formData.append('variation_id', variationId);
			if (form) {
				form.querySelectorAll('select[name^=\"attribute_\"], input[name^=\"attribute_\"]').forEach(function(el) {
					formData.append(el.name, el.value);
				});
			}
		}

		fetch('" . esc_url( admin_url( 'admin-ajax.php' ) ) . "', {
			method: 'POST',
			body: formData,
		})
		.then(function(r) { return r.json(); })
		.then(function(res) {
			if (res.success && res.data && res.data.redirect) {
				window.location.href = res.data.redirect;
			} else {
				btn.classList.remove('loading');
			}
		})
		.catch(function() {
			btn.classList.remove('loading');
		});
	});
})();
		";

		wp_register_script( 'tfdc-buy-now', false, array(), TFDC_VER, true );
		wp_enqueue_script( 'tfdc-buy-now' );
		wp_add_inline_script( 'tfdc-buy-now', $js );

		// Inline CSS from Button Style settings.
		$css = $this->build_button_css( TFDC_Admin::get_settings() );
		if ( $css ) {
			wp_register_style( 'tfdc-buy-now', false, array(), TFDC_VER );
			wp_enqueue_style( 'tfdc-buy-now' );
			wp_add_inline_style( 'tfdc-buy-now', $css );
		}
	}

	/**
	 * Hide WooCommerce's Add to Cart button while leaving TukiFlow buttons visible.
	 */
	public function print_hide_add_to_cart_css() {
		if ( ! is_product() && ! is_shop() && ! is_product_category() && ! is_product_tag() ) {
			return;
		}
		?>
		<style>
			.single_add_to_cart_button:not(.tfdc-buy-now-btn),
			.add_to_cart_button:not(.tfdc-buy-now-btn) {
				display: none !important;
			}
		</style>
		<?php
	}

	/**
	 * Attach the single-product Add to Cart hide CSS only when the current product needs it.
	 */
	public function setup_single_product_add_to_cart_css() {
		if ( ! is_product() ) {
			return;
		}

		$product_id = absint( get_queried_object_id() );
		$product    = $product_id ? wc_get_product( $product_id ) : null;

		if ( ! $product instanceof WC_Product || ! TFDC_Product_Settings::is_single_add_to_cart_disabled( $product->get_id() ) ) {
			return;
		}

		add_action( 'wp_head', array( $this, 'print_single_product_add_to_cart_css' ) );
	}

	/**
	 * Hide the Add to Cart button on single product pages for flagged products.
	 */
	public function print_single_product_add_to_cart_css() {
		?>
		<style>
			.single_add_to_cart_button:not(.tfdc-buy-now-btn) {
				display: none !important;
			}
		</style>
		<?php
	}

	/**
	 * Build CSS for both button contexts from saved settings.
	 *
	 * @param array $settings Plugin settings.
	 * @return string CSS string (may be empty).
	 */
	private function build_button_css( $settings ) {
		$css  = $this->build_context_css( $settings, 'single', '.woocommerce div.product .tfdc-buy-now-btn' );
		$css .= $this->build_context_css( $settings, 'loop', 'body:not(.single-product) .tfdc-buy-now-btn' );
		return $css;
	}

	/**
	 * Build CSS rules for one button context.
	 *
	 * Only outputs declarations for properties that are explicitly set —
	 * empty values are skipped so the theme styles remain in effect.
	 *
	 * @param array  $settings Plugin settings.
	 * @param string $ctx      'single' or 'loop'.
	 * @param string $selector CSS selector for this context.
	 * @return string CSS string (may be empty).
	 */
	private function build_context_css( $settings, $ctx, $selector ) {
		$p     = "btn_{$ctx}_";
		$rules = array();
		$hover = array();

		$rules[] = 'text-decoration:none';
		$hover[] = 'text-decoration:none';

		if ( ! empty( $settings["{$p}bg"] ) ) {
			$rules[] = 'background-color:' . $settings["{$p}bg"];
		}
		if ( ! empty( $settings["{$p}bg_hover"] ) ) {
			$hover[] = 'background-color:' . $settings["{$p}bg_hover"];
		}
		if ( ! empty( $settings["{$p}color"] ) ) {
			$rules[] = 'color:' . $settings["{$p}color"];
		}
		if ( ! empty( $settings["{$p}color_hover"] ) ) {
			$hover[] = 'color:' . $settings["{$p}color_hover"];
		}
		if ( ! empty( $settings["{$p}border_color"] ) ) {
			$rules[] = 'border-color:' . $settings["{$p}border_color"];
		}
		if ( ! empty( $settings["{$p}border_color_hover"] ) ) {
			$hover[] = 'border-color:' . $settings["{$p}border_color_hover"];
		}
		if ( '' !== $settings["{$p}border_width"] ) {
			$rules[] = 'border-width:' . absint( $settings["{$p}border_width"] ) . 'px;border-style:solid';
		}
		if ( '' !== $settings["{$p}border_radius"] ) {
			$rules[] = 'border-radius:' . absint( $settings["{$p}border_radius"] ) . 'px';
		}
		if ( '' !== $settings["{$p}font_size"] ) {
			$rules[] = 'font-size:' . absint( $settings["{$p}font_size"] ) . 'px';
		}
		if ( ! empty( $settings["{$p}font_weight"] ) ) {
			$rules[] = 'font-weight:' . $settings["{$p}font_weight"];
		}
		if ( '' !== $settings["{$p}padding_y"] || '' !== $settings["{$p}padding_x"] ) {
			$py      = '' !== $settings["{$p}padding_y"] ? absint( $settings["{$p}padding_y"] ) . 'px' : '0';
			$px      = '' !== $settings["{$p}padding_x"] ? absint( $settings["{$p}padding_x"] ) . 'px' : '0';
			$rules[] = "padding:{$py} {$px}";
		}

		// For the single product context, always prevent flex-child stretching
		// (e.g. WoodMart's `form.cart > * { flex: 1 1 100% }`). Full-width
		// overrides this intentionally; other themes are unaffected.
		if ( 'single' === $ctx ) {
			$rules[] = 'flex:0 0 auto';
		}

		$width = $settings["{$p}width"];
		if ( 'full' === $width ) {
			$rules[] = 'width:100%;display:block;flex:1 1 100%';
		} elseif ( 'auto' === $width ) {
			$rules[] = 'width:auto;display:inline-block';
		} elseif ( 'fit-content' === $width ) {
			$rules[] = 'width:fit-content;display:inline-block';
		} elseif ( 'custom' === $width && ! empty( $settings["{$p}width_custom"] ) ) {
			$rules[] = 'width:' . $settings["{$p}width_custom"];
		}

		$css = '';
		if ( $rules ) {
			$css .= "{$selector}{" . implode( ';', $rules ) . "}\n";
		}
		if ( $hover ) {
			$css .= "{$selector}:hover{" . implode( ';', $hover ) . "}\n";
		}

		return $css;
	}
}
