<?php

declare( strict_types=1 );

namespace Automattic\WooCommerce\Internal\Utilities;

use Automattic\WooCommerce\Caches\ProductCountCache;

/**
 * Class with general utility methods related to products.
 */
class ProductUtil {
	/**
	 * Delete all product transients for a set of products.
	 *
	 * Fixed-name transients are deleted once for the whole set, and the
	 * woocommerce_delete_product_transients action fires once per product ID.
	 *
	 * @param array $product_ids Product IDs whose transients are being deleted.
	 * @return void
	 */
	public function delete_product_transients_for_products( array $product_ids ): void {
		$product_ids = array_unique( array_map( 'absint', $product_ids ) );

		// Transient data to clear with a fixed name which may be stale after product updates.
		$transients_to_clear = array(
			'wc_products_onsale',
			'wc_featured_products',
			'wc_outofstock_count',
			'wc_low_stock_count',
		);

		foreach ( $transients_to_clear as $transient ) {
			delete_transient( $transient );
		}

		$product_ids_to_clear = array_filter( $product_ids );
		if ( ! empty( $product_ids_to_clear ) ) {
			$this->delete_product_specific_transients_for_products( $product_ids_to_clear );
		}

		// Kept for compatibility, WooCommerce core doesn't use product transient versions anymore.
		\WC_Cache_Helper::get_transient_version( 'product', true );

		foreach ( $product_ids as $product_id ) {
			/**
			 * Fires after product transients are deleted.
			 *
			 * @since 2.3.0
			 *
			 * @param int $product_id Product ID whose transients were deleted.
			 */
			do_action( 'woocommerce_delete_product_transients', $product_id );
		}
	}

	/**
	 * Delete the transients related to a specific product.
	 * If the product is a variation, delete the transients for the parent too.
	 *
	 * @param WC_Product|int $product_or_id The product or the product id.
	 * @return void
	 */
	public function delete_product_specific_transients( $product_or_id ) {
		$this->delete_product_specific_transients_for_products( array( $product_or_id ) );
	}

	/**
	 * Delete the transients related to a set of products.
	 * If a product is a variation, delete the transients for the parent too.
	 *
	 * @param array $products_or_ids Products or product ids.
	 * @return void
	 */
	public function delete_product_specific_transients_for_products( array $products_or_ids ) {
		$product_ids = array();

		foreach ( $products_or_ids as $product_or_id ) {
			$parent_id = 0;
			if ( $product_or_id instanceof \WC_Product ) {
				$product    = $product_or_id;
				$product_id = $product->get_id();
			} else {
				$product_id = $product_or_id;
				$product    = wc_get_product( $product_id );
			}

			if ( $product instanceof \WC_Product_Variation ) {
				$parent_id = $product->get_parent_id();
			}

			$product_ids[] = $product_id;
			if ( $parent_id ) {
				$product_ids[] = $parent_id;
			}
		}

		$product_ids = array_unique( array_filter( array_map( 'absint', $product_ids ) ) );

		$product_specific_transient_names = array(
			'wc_product_children_',
			'wc_var_prices_',
			'wc_related_',
			'wc_child_has_weight_',
			'wc_child_has_dimensions_',
		);

		foreach ( $product_ids as $product_id ) {
			foreach ( $product_specific_transient_names as $transient ) {
				delete_transient( $transient . $product_id );
			}
		}
	}

	/**
	 * Prime featured and gallery image attachment caches for a collection of products in a single
	 * batched query, instead of priming each product's images separately.
	 *
	 * @param array $products Products whose image attachments should be primed. Non-product items are ignored.
	 * @return void
	 */
	public function prime_image_caches( array $products ): void {
		$products  = array_filter( $products, static fn( $product ) => $product instanceof \WC_Product );
		$featured  = array_map( static fn( $product ) => $product->get_image_id(), $products );
		$gallery   = array_map( static fn( $product ) => $product->get_gallery_image_ids(), $products );
		$image_ids = array_filter( array_unique( array_map( 'intval', array_merge( $featured, ...$gallery ) ) ) );
		if ( ! empty( $image_ids ) ) {
			// Prime caches to reduce future queries.
			_prime_post_caches( $image_ids );
		}
	}

	/**
	 * Counts per-status number of products.
	 *
	 * @since 11.0.0
	 *
	 * @param string $post_type Post type (e.g. 'product', 'product_variation').
	 * @return array<string,int>
	 */
	public function get_counts_for_type( string $post_type ): array {
		// Until persistent counters reactivated, switch back to WordPress API for accessing post status counters.
		return array_map( 'intval', (array) wp_count_posts( $post_type ) );
	}
}
