<?php
/**
 * Hide Price class file.
 *
 * @package WholesaleNode_For_WooCommerce
 */

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

/**
 * Hides product prices for guest users.
 */
class WOOWSNFree_Hide_Price {

	private static $_instance = null;

	/** @var array Settings. */
	private $s = array();

	/**
	 * @return self
	 */
	public static function instance() {
		if ( is_null( self::$_instance ) ) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}

	private function __construct() {
		$this->s = $this->load_settings();
		$this->hooks();
	}

	/**
	 * Merge stored settings with defaults.
	 *
	 * @return array
	 */
	private function load_settings() {
		$stored   = get_option( 'woowsn_hide_price_settings', null );
		$defaults = array(
			'hide_for_guests'  => false,
			'replacement_text' => __( 'Login to see prices', 'wholesalenode-for-woocommerce' ),
			'show_button'      => false,
			'button_text'      => __( 'Request Quote', 'wholesalenode-for-woocommerce' ),
			'button_url'       => '',
			'hide_add_to_cart' => false,
		);
		if ( null === $stored ) {
			return $defaults;
		}
		$merged = wp_parse_args( (array) $stored, $defaults );
		unset( $merged['login_action'] );
		return $merged;
	}

	/**
	 * Attach WooCommerce price / cart hooks.
	 */
	private function hooks() {
		$has_global = ! empty( $this->s['hide_for_guests'] );
		$has_rules  = $this->has_active_rules();

		if ( $has_global || $has_rules ) {
			add_filter( 'woocommerce_get_price_html',               array( $this, 'maybe_hide' ), 100, 2 );
			add_filter( 'woocommerce_variable_price_html',          array( $this, 'maybe_hide' ), 100, 2 );
			add_filter( 'woocommerce_grouped_price_html',           array( $this, 'maybe_hide' ), 100, 2 );

			if ( ! empty( $this->s['hide_add_to_cart'] ) ) {
				add_filter( 'woocommerce_is_purchasable',           array( $this, 'disable_purchase' ), 10, 2 );
				add_filter( 'woocommerce_variation_is_purchasable', array( $this, 'disable_purchase' ), 10, 2 );
				add_filter( 'woocommerce_loop_add_to_cart_link',   array( $this, 'replace_cart_link' ), 10, 2 );
				add_filter( 'woocommerce_add_to_cart_validation',  array( $this, 'block_add_to_cart' ), 10, 3 );
			}
		}

		add_action( 'add_meta_boxes',    array( $this, 'register_meta_box' ) );
		add_action( 'save_post_product', array( $this, 'save_meta_box' ), 20 );
	}

	/**
	 * Return true if at least one active hide_price rule exists in the DB.
	 *
	 * @return bool
	 */
	private function has_active_rules() {
		global $wpdb;
		$table = $wpdb->prefix . 'woowsn_rules';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
			return false;
		}
		$count = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE rule_type = %s AND status = %s', $table, 'hide_price', 'active' )
		);
		return $count > 0;
	}

	/**
	 * Determine whether price should be hidden for the current user + product.
	 *
	 * @param WC_Product|int|null $product Product or ID.
	 * @return bool
	 */
	public function should_hide( $product = null ) {
		$product_id = null;
		if ( $product instanceof WC_Product ) {
			$product_id = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id();
		} elseif ( is_numeric( $product ) ) {
			$product_id = intval( $product );
		}

		if ( $product_id ) {
			$override = get_post_meta( $product_id, '_woowsn_hide_price', true );
			if ( $override === 'show' ) {
				return false;
			}
			if ( $override === 'hide' ) {
				return true;
			}
		}

		if ( is_user_logged_in() ) {
			return $this->rule_applies_to_user( $product_id );
		}

		if ( ! empty( $this->s['hide_for_guests'] ) ) {
			return true;
		}

		return $this->rule_applies_to_guests( $product_id );
	}

	/**
	 * Check whether any active hide_price rule targets the current logged-in user.
	 *
	 * @param int|null $product_id Product ID.
	 * @return bool
	 */
	private function rule_applies_to_user( $product_id ) {
		global $wpdb;
		$table = $wpdb->prefix . 'woowsn_rules';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
			return false;
		}
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$rows = $wpdb->get_results(
			$wpdb->prepare( 'SELECT user_roles, product_ids, product_list_ids, config FROM %i WHERE rule_type = %s AND status = %s', $table, 'hide_price', 'active' ),
			ARRAY_A
		);
		if ( empty( $rows ) ) {
			return false;
		}
		$user  = wp_get_current_user();
		foreach ( $rows as $rule ) {
			$cfg          = json_decode( $rule['config'] ?? '{}', true ) ?: array();
			$allowed_roles = json_decode( $rule['user_roles'] ?? '[]', true ) ?: array();
			if ( ! empty( $allowed_roles ) && empty( array_intersect( (array) $user->roles, $allowed_roles ) ) ) {
				continue;
			}
			$scope = $cfg['product_scope'] ?? 'all';
			if ( 'all' !== $scope && $product_id && ! $this->product_in_rule_scope( $rule, $product_id ) ) {
				continue;
			}
			return true;
		}
		return false;
	}

	/**
	 * Check whether any active hide_price rule applies to guests.
	 *
	 * @param int|null $product_id Product ID.
	 * @return bool
	 */
	private function rule_applies_to_guests( $product_id ) {
		global $wpdb;
		$table = $wpdb->prefix . 'woowsn_rules';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
			return false;
		}
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$rows = $wpdb->get_results(
			$wpdb->prepare( 'SELECT user_roles, product_ids, product_list_ids, config FROM %i WHERE rule_type = %s AND status = %s', $table, 'hide_price', 'active' ),
			ARRAY_A
		);
		if ( empty( $rows ) ) {
			return false;
		}
		foreach ( $rows as $rule ) {
			$cfg          = json_decode( $rule['config'] ?? '{}', true ) ?: array();
			$allowed_roles = json_decode( $rule['user_roles'] ?? '[]', true ) ?: array();
			if ( ! empty( $allowed_roles ) ) {
				continue;
			}
			$scope = $cfg['product_scope'] ?? 'all';
			if ( $scope !== 'all' && $product_id && ! $this->product_in_rule_scope( $rule, $product_id ) ) {
				continue;
			}
			return true;
		}
		return false;
	}

	/**
	 * Check whether a product falls within a rule's product scope.
	 *
	 * @param array    $rule       Rule row.
	 * @param int|null $product_id Product ID.
	 * @return bool
	 */
	private function product_in_rule_scope( $rule, $product_id ) {
		if ( ! $product_id ) {
			return true;
		}
		$cfg   = json_decode( $rule['config'] ?? '{}', true ) ?: array();
		$scope = $cfg['product_scope'] ?? 'all';
		if ( $scope === 'specific' ) {
			$ids = json_decode( $rule['product_ids'] ?? '[]', true ) ?: array();
			return in_array( (string) $product_id, array_map( 'strval', (array) $ids ), true );
		}
		return true;
	}

	/**
	 * Replace price HTML when hiding is required.
	 *
	 * @param string     $html    Original price HTML.
	 * @param WC_Product $product Product.
	 * @return string
	 */
	public function maybe_hide( $html, $product ) {
		if ( ! $this->should_hide( $product ) ) {
			return $html;
		}

		static $rendered = array();
		$product_id = ( $product instanceof WC_Product )
			? ( $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id() )
			: 0;

		if ( $product_id && ! empty( $rendered[ $product_id ] ) ) {
			return '<span class="woowsn-hidden-price">' . esc_html( $this->s['replacement_text'] ) . '</span>';
		}
		if ( $product_id ) {
			$rendered[ $product_id ] = true;
		}

		return $this->replacement_html();
	}

	/**
	 * Build the replacement HTML shown instead of the price.
	 *
	 * @return string
	 */
	private function replacement_html() {
		$text = esc_html( $this->s['replacement_text'] );

		if ( ! empty( $this->s['show_button'] ) ) {
			$url = $this->s['button_url'] ?: wc_get_page_permalink( 'myaccount' );
			$btn = esc_html( $this->s['button_text'] ?: __( 'Request Quote', 'wholesalenode-for-woocommerce' ) );
			return '<span class="woowsn-hidden-price">' . $text . '</span> '
				. '<a href="' . esc_url( $url ) . '" class="button woowsn-hidden-price-btn">' . $btn . '</a>';
		}

		if ( ! is_user_logged_in() ) {
			return '<span class="woowsn-hidden-price">' . $text . '</span> '
				. '<a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '" class="button woowsn-login-to-view-btn">'
				. esc_html__( 'Login to View Price', 'wholesalenode-for-woocommerce' ) . '</a>';
		}

		return '<span class="woowsn-hidden-price">' . $text . '</span>';
	}

	/**
	 * Disable purchasing when price is hidden.
	 *
	 * @param bool       $purchasable Current purchasable state.
	 * @param WC_Product $product     Product.
	 * @return bool
	 */
	public function disable_purchase( $purchasable, $product ) {
		return $this->should_hide( $product ) ? false : $purchasable;
	}

	/**
	 * Replace the add-to-cart loop button when price is hidden.
	 *
	 * @param string     $html    Original button HTML.
	 * @param WC_Product $product Product.
	 * @return string
	 */
	public function replace_cart_link( $html, $product ) {
		if ( ! $this->should_hide( $product ) ) {
			return $html;
		}
		if ( ! empty( $this->s['show_button'] ) ) {
			$url = $this->s['button_url'] ?: wc_get_page_permalink( 'myaccount' );
			$btn = esc_html( $this->s['button_text'] ?: __( 'Request Quote', 'wholesalenode-for-woocommerce' ) );
			return '<a href="' . esc_url( $url ) . '" class="button woowsn-hidden-price-btn">' . $btn . '</a>';
		}
		$url = wc_get_page_permalink( 'myaccount' );
		return '<a href="' . esc_url( $url ) . '" class="button woowsn-login-to-view-btn">'
			. esc_html__( 'Login to View Price', 'wholesalenode-for-woocommerce' ) . '</a>';
	}

	/**
	 * Block add-to-cart if price is hidden.
	 *
	 * @param bool $passed     Validation passed.
	 * @param int  $product_id Product ID.
	 * @param int  $qty        Quantity.
	 * @return bool
	 */
	public function block_add_to_cart( $passed, $product_id, $qty ) {
		$product = wc_get_product( $product_id );
		if ( $product && $this->should_hide( $product ) ) {
			wc_add_notice(
				__( 'Please log in to purchase this product.', 'wholesalenode-for-woocommerce' ),
				'error'
			);
			return false;
		}
		return $passed;
	}

	/**
	 * Register product-level meta box.
	 */
	public function register_meta_box() {
		add_meta_box(
			'woowsn_free_hide_price',
			__( 'B2B Price Visibility', 'wholesalenode-for-woocommerce' ),
			array( $this, 'render_meta_box' ),
			'product',
			'side',
			'default'
		);
	}

	/**
	 * Render the per-product override meta box.
	 *
	 * @param WP_Post $post Post object.
	 */
	public function render_meta_box( $post ) {
		$value = get_post_meta( $post->ID, '_woowsn_hide_price', true );
		wp_nonce_field( 'woowsn_free_hide_price_product', 'woowsn_free_hide_price_nonce' );
		?>
		<p><label for="woowsn_free_hide_price"><?php esc_html_e( 'Price Visibility Override:', 'wholesalenode-for-woocommerce' ); ?></label></p>
		<select name="woowsn_free_hide_price" id="woowsn_free_hide_price" class="widefat">
			<option value=""       <?php selected( $value, '' ); ?>><?php esc_html_e( 'Use Global Settings', 'wholesalenode-for-woocommerce' ); ?></option>
			<option value="show"   <?php selected( $value, 'show' ); ?>><?php esc_html_e( 'Always Show Price', 'wholesalenode-for-woocommerce' ); ?></option>
			<option value="hide"   <?php selected( $value, 'hide' ); ?>><?php esc_html_e( 'Always Hide Price', 'wholesalenode-for-woocommerce' ); ?></option>
		</select>
		<p class="description"><?php esc_html_e( 'Override the global hide-price setting for this product.', 'wholesalenode-for-woocommerce' ); ?></p>
		<?php
	}

	/**
	 * Save the per-product meta box value.
	 *
	 * @param int $post_id Post ID.
	 */
	public function save_meta_box( $post_id ) {
		if (
			wp_is_post_autosave( $post_id ) ||
			wp_is_post_revision( $post_id ) ||
			get_post_type( $post_id ) !== 'product'
		) {
			return;
		}

		if (
			! isset( $_POST['woowsn_free_hide_price_nonce'] ) ||
			! wp_verify_nonce(
				sanitize_text_field( wp_unslash( $_POST['woowsn_free_hide_price_nonce'] ) ),
				'woowsn_free_hide_price_product'
			)
		) {
			return;
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		$allowed = array( '', 'show', 'hide' );
		$raw     = isset( $_POST['woowsn_free_hide_price'] ) ? sanitize_text_field( wp_unslash( $_POST['woowsn_free_hide_price'] ) ) : '';
		$value   = in_array( $raw, $allowed, true ) ? $raw : '';

		update_post_meta( $post_id, '_woowsn_hide_price', $value );
	}
}
