<?php
/**
 * Discount Rules — Free
 *
 * Handles:
 *  - Product price modification (percentage / fixed_product)
 *  - Strikethrough original price on product page
 *  - Cart item strikethrough pricing
 *  - Cart / checkout notices
 *
 * Same DB structure as pro (woowsn_rules table).
 *
 * @package WholesaleNode_For_WooCommerce
 */

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

// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment

class WOOWSNFree_Discount {

	private static $_instance = null;

	/** @var array|null Request-scoped rule cache. */
	private $rules_cache = null;

	/** @var bool Recursion guard (instance var, not static). */
	private $modifying = false;

	/** @var bool Whether cart notices have already been rendered. */
	private $notices_rendered = false;

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

	private function __construct() {
		// Price filters.
		add_filter( 'woocommerce_product_get_price',                array( $this, 'modify_product_price' ), 10, 2 );
		add_filter( 'woocommerce_product_get_sale_price',           array( $this, 'modify_product_price' ), 10, 2 );
		add_filter( 'woocommerce_product_variation_get_price',      array( $this, 'modify_product_price' ), 10, 2 );
		add_filter( 'woocommerce_product_variation_get_sale_price', array( $this, 'modify_product_price' ), 10, 2 );

		// Strikethrough price on product page — hooks after WC renders price HTML.
		add_filter( 'woocommerce_get_price_html', array( $this, 'maybe_show_original_price' ), 10, 2 );

		// Cart item strikethrough.
		add_filter( 'woocommerce_cart_item_price', array( $this, 'display_cart_item_discount' ), 10, 3 );

		// Cart / checkout notices.
		add_action( 'woocommerce_before_cart',            array( $this, 'display_cart_notices' ), 5 );
		add_action( 'woocommerce_before_checkout_form',   array( $this, 'display_cart_notices' ), 5 );

		// CSS on product / cart / checkout pages.
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
	}

	// ─────────────────────────────────────────────────────────────────
	// Rule loading
	// ─────────────────────────────────────────────────────────────────

	private function get_active_rules() {
		if ( null !== $this->rules_cache ) {
			return $this->rules_cache;
		}

		global $wpdb;
		$table = $wpdb->prefix . 'woowsn_rules';

		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
			return $this->rules_cache = array();
		}

		$rows = $wpdb->get_results(
			"SELECT * FROM {$table} WHERE rule_type = 'discount' AND status = 'active' ORDER BY priority ASC, id ASC",
			ARRAY_A
		);

		$rules = array();
		foreach ( (array) $rows as $row ) {
			$rules[] = $this->hydrate( $row );
		}

		return $this->rules_cache = $rules;
	}

	private function hydrate( $row ) {
		$rule           = new stdClass();
		$rule->id       = (int) $row['id'];
		$rule->title    = $row['title']    ?? '';
		$rule->status   = $row['status']   ?? 'inactive';
		$rule->priority = (int) ( $row['priority'] ?? 0 );

		foreach ( array( 'user_roles', 'user_ids', 'user_list_ids', 'product_ids', 'product_list_ids', 'category_ids', 'tag_ids' ) as $col ) {
			$raw = $row[ $col ] ?? '';
			if ( is_array( $raw ) ) {
				$rule->$col = $raw;
			} elseif ( ! empty( $raw ) ) {
				$uns        = maybe_unserialize( $raw );
				$rule->$col = is_array( $uns ) ? $uns : ( is_array( json_decode( $raw, true ) ) ? json_decode( $raw, true ) : array() );
			} else {
				$rule->$col = array();
			}
		}

		$cfg = ! empty( $row['config'] ) ? ( json_decode( $row['config'], true ) ?: array() ) : array();

		$rule->discount_type      = $cfg['discount_type']      ?? 'percentage';
		$rule->discount_value     = (float) ( $cfg['discount_value'] ?? 0 );
		$rule->apply_to           = $cfg['apply_to']           ?? 'product';
		$rule->product_scope      = $cfg['product_scope']      ?? 'all';
		$rule->message            = $cfg['message']            ?? '';
		$rule->show_cart_notice   = isset( $cfg['show_cart_notice'] ) ? (bool) $cfg['show_cart_notice'] : true;
		$rule->user_targeting_type = $cfg['user_targeting_type'] ?? 'all';

		return $rule;
	}

	// ─────────────────────────────────────────────────────────────────
	// Qualification
	// ─────────────────────────────────────────────────────────────────

	private function user_qualifies( stdClass $rule, int $user_id ): bool {
		$has_roles = ! empty( $rule->user_roles );
		$has_ids   = ! empty( $rule->user_ids );

		if ( ! $has_roles && ! $has_ids ) {
			return $user_id > 0;
		}
		if ( ! $user_id ) {
			return false;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return false;
		}
		if ( $has_roles && ! empty( array_intersect( (array) $user->roles, (array) $rule->user_roles ) ) ) {
			return true;
		}
		if ( $has_ids && in_array( $user_id, array_map( 'intval', (array) $rule->user_ids ), true ) ) {
			return true;
		}
		return false;
	}

	private function product_qualifies( stdClass $rule, int $product_id ): bool {
		$scope = $rule->product_scope;

		if ( $scope === 'all' ) {
			return true;
		}
		if ( $scope === 'selected' || $scope === 'specific' ) {
			return ! empty( $rule->product_ids )
				&& in_array( $product_id, array_map( 'intval', (array) $rule->product_ids ), true );
		}
		if ( in_array( $scope, array( 'lists', 'product_lists', 'product_list' ), true ) ) {
			return ! empty( $rule->product_list_ids )
				&& $this->product_in_lists( $product_id, (array) $rule->product_list_ids );
		}
		if ( $scope === 'categories' ) {
			if ( empty( $rule->category_ids ) ) {
				return false;
			}
			$cats = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) );
			return ! empty( array_intersect( array_map( 'intval', (array) $rule->category_ids ), $cats ) );
		}
		return false;
	}

	private function product_in_lists( int $product_id, array $list_ids ): bool {
		global $wpdb;
		$table = $wpdb->prefix . 'woowsn_product_lists';
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
			return false;
		}
		foreach ( $list_ids as $lid ) {
			$lid = (int) $lid;
			if ( ! $lid ) continue;
			$row = $wpdb->get_row( $wpdb->prepare( "SELECT product_ids FROM {$table} WHERE id = %d AND status = 'active'", $lid ), ARRAY_A );
			if ( ! $row ) continue;
			$ids = json_decode( $row['product_ids'] ?? '[]', true );
			if ( is_array( $ids ) && in_array( $product_id, array_map( 'intval', $ids ), true ) ) {
				return true;
			}
		}
		return false;
	}

	// ─────────────────────────────────────────────────────────────────
	// Price modification
	// ─────────────────────────────────────────────────────────────────

	public function modify_product_price( $price, $product ) {
		if ( is_admin() ) {
			if ( ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
				return $price;
			}
			$action  = isset( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$allowed = array( 'woocommerce_update_order_review', 'woocommerce_apply_coupon', 'woocommerce_remove_coupon', 'woocommerce_update_shipping_method', 'woocommerce_get_refreshed_fragments' );
			if ( ! in_array( $action, $allowed, true ) ) {
				return $price;
			}
		}

		$price_float = (float) $price;
		if ( $price_float <= 0 ) {
			return $price;
		}
		if ( $this->modifying ) {
			return $price;
		}
		$this->modifying = true;

		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			$this->modifying = false;
			return $price;
		}

		$product_id = $product->get_id();
		if ( $product->is_type( 'variation' ) ) {
			$product_id = $product->get_parent_id();
		}

		foreach ( $this->get_active_rules() as $rule ) {
			if ( ! in_array( $rule->discount_type, array( 'percentage', 'fixed_product' ), true ) ) {
				continue;
			}
			if ( $rule->apply_to === 'cart' ) {
				continue;
			}
			if ( ! $this->user_qualifies( $rule, $user_id ) ) {
				continue;
			}
			if ( ! $this->product_qualifies( $rule, $product_id ) ) {
				continue;
			}
			if ( $rule->discount_value <= 0 ) {
				continue;
			}

			$discount = ( $rule->discount_type === 'percentage' )
				? round( $price_float * min( $rule->discount_value, 100 ) / 100, wc_get_price_decimals() )
				: round( min( $rule->discount_value, $price_float ), wc_get_price_decimals() );

			if ( $discount > 0 ) {
				$this->modifying = false;
				return max( 0.0, $price_float - $discount );
			}
		}

		$this->modifying = false;
		return $price;
	}

	// ─────────────────────────────────────────────────────────────────
	// Strikethrough price on product page
	// Mirrors pro's maybe_show_original_price()
	// ─────────────────────────────────────────────────────────────────

	public function maybe_show_original_price( $price_html, $product ) {
		if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
			return $price_html;
		}
		// Variable products show a range — skip, WC handles it.
		if ( $product->is_type( 'variable' ) ) {
			return $price_html;
		}

		$user_id    = get_current_user_id();
		$product_id = $product->get_id();
		if ( $product->is_type( 'variation' ) ) {
			$product_id = $product->get_parent_id();
		}

		// Check if any product-level rule applies to this user + product.
		$has_discount = false;
		foreach ( $this->get_active_rules() as $rule ) {
			if ( ! in_array( $rule->discount_type, array( 'percentage', 'fixed_product' ), true ) ) {
				continue;
			}
			if ( $rule->apply_to === 'cart' ) {
				continue;
			}
			if ( ! $this->user_qualifies( $rule, $user_id ) ) {
				continue;
			}
			if ( ! $this->product_qualifies( $rule, $product_id ) ) {
				continue;
			}
			$has_discount = true;
			break;
		}

		if ( ! $has_discount ) {
			return $price_html;
		}

		// Read raw DB meta to get original price — bypasses our own price filter.
		$pid            = $product->get_id();
		$raw_sale       = get_post_meta( $pid, '_sale_price', true );
		$raw_reg        = get_post_meta( $pid, '_regular_price', true );
		$original_price = ( $raw_sale !== '' && $raw_sale !== null && (float) $raw_sale > 0 )
			? (float) $raw_sale
			: (float) $raw_reg;

		if ( $original_price <= 0 ) {
			return $price_html;
		}

		$current_price = (float) $product->get_price();
		if ( $current_price >= $original_price || abs( $original_price - $current_price ) < 0.01 ) {
			return $price_html;
		}

		return '<del>' . wc_price( $original_price ) . '</del> <ins>' . wc_price( $current_price ) . '</ins>';
	}

	// ─────────────────────────────────────────────────────────────────
	// Cart item strikethrough
	// Mirrors pro's display_cart_item_discount()
	// ─────────────────────────────────────────────────────────────────

	public function display_cart_item_discount( $price_html, $cart_item, $cart_item_key ) {
		$product        = $cart_item['data'];
		$original_price = (float) $product->get_regular_price();
		$current_price  = (float) $product->get_price();

		if ( $original_price > 0 && $current_price > 0 && $original_price > $current_price ) {
			$price_html = '<del>' . wp_kses_post( wc_price( $original_price ) ) . '</del> ' . $price_html;
		}

		return $price_html;
	}

	// ─────────────────────────────────────────────────────────────────
	// Cart notices
	// Mirrors pro's display_cart_notices() / collect_cart_notices()
	// ─────────────────────────────────────────────────────────────────

	public function display_cart_notices() {
		if ( $this->notices_rendered ) {
			return;
		}
		$this->notices_rendered = true;

		if ( ! function_exists( 'WC' ) || ! WC()->cart || WC()->cart->is_empty() ) {
			return;
		}

		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			return;
		}

		$notices = $this->collect_cart_notices( $user_id );
		if ( empty( $notices ) ) {
			return;
		}

		echo '<div class="woowsn-discount-notices">';
		foreach ( $notices as $notice ) {
			$cls = ( $notice['type'] === 'success' ) ? 'woowsn-notice-success' : 'woowsn-notice-info';
			echo '<div class="woowsn-discount-notice ' . esc_attr( $cls ) . '">';
			echo wp_kses_post( $notice['message'] );
			echo '</div>';
		}
		echo '</div>';
	}

	/**
	 * Build cart notices for all qualifying rules.
	 * Free supports: percentage and fixed_product notices only.
	 *
	 * @param int $user_id Current user ID.
	 * @return array  Array of ['type' => string, 'message' => string]
	 */
	private function collect_cart_notices( int $user_id ): array {
		$notices = array();

		foreach ( $this->get_active_rules() as $rule ) {
			if ( ! $rule->show_cart_notice ) {
				continue;
			}
			if ( ! in_array( $rule->discount_type, array( 'percentage', 'fixed_product' ), true ) ) {
				continue;
			}
			if ( ! $this->user_qualifies( $rule, $user_id ) ) {
				continue;
			}

			// Check if at least one cart item qualifies for this rule.
			$applies = false;
			foreach ( WC()->cart->get_cart() as $item ) {
				$pid = ! empty( $item['variation_id'] ) ? (int) $item['variation_id'] : (int) $item['product_id'];
				// product_qualifies expects the parent ID for variations.
				$product = $item['data'];
				$check_id = $product && $product->is_type( 'variation' )
					? $product->get_parent_id()
					: $pid;
				if ( $this->product_qualifies( $rule, $check_id ) ) {
					$applies = true;
					break;
				}
			}

			if ( ! $applies ) {
				continue;
			}

			// Build the message — mirrors pro's percentage/fixed_product handler text.
			if ( $rule->discount_type === 'percentage' ) {
				/* translators: %1$g: percentage value, %2$s: rule title */
				$message = sprintf(
					__( '<strong>%1$g%% discount</strong> applied — %2$s', 'wholesalenode-for-woocommerce' ),
					$rule->discount_value,
					$rule->title
				);
			} else {
				/* translators: %1$s: formatted price, %2$s: rule title */
				$message = sprintf(
					__( '<strong>%1$s off</strong> per item applied — %2$s', 'wholesalenode-for-woocommerce' ),
					wc_price( $rule->discount_value ),
					$rule->title
				);
			}

			// Use custom message if set.
			if ( ! empty( $rule->message ) ) {
				$message = $rule->message;
			}

			$notices[] = array(
				'type'    => 'success',
				'message' => $message,
			);
		}

		// Deduplicate.
		$seen   = array();
		$unique = array();
		foreach ( $notices as $n ) {
			$key = md5( $n['message'] );
			if ( ! isset( $seen[ $key ] ) ) {
				$seen[ $key ] = true;
				$unique[]     = $n;
			}
		}

		return $unique;
	}

	// ─────────────────────────────────────────────────────────────────
	// Assets
	// ─────────────────────────────────────────────────────────────────

	public function enqueue_styles() {
		if ( is_product() || is_cart() || is_checkout() ) {
			wp_enqueue_style(
				'woowsn-free-discount-notices',
				WOOWSN_FREE_URL . 'assets/css/discount-notices.css',
				array(),
				WOOWSN_FREE_VERSION
			);
		}
	}
}
