<?php
/**
 * Product availability helper.
 *
 * @package FoodOrd
 */

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

/**
 * Class Foodord_Product_Availability
 */
class Foodord_Product_Availability {
	/**
	 * Cached category order map.
	 *
	 * @var array<string,int>
	 */
	private static $category_order_map = null;

	/**
	 * Get category order map keyed by term ID.
	 *
	 * @return array<int,int>
	 */
	private static function get_category_order_map(): array {
		if ( null !== self::$category_order_map ) {
			return self::$category_order_map;
		}

		$map = array();
		$all = get_terms(
			array(
				'taxonomy'   => 'product_cat',
				'hide_empty' => false,
				'orderby'    => 'menu_order',
				'order'      => 'ASC',
			)
		);

		if ( ! is_wp_error( $all ) ) {
			foreach ( (array) $all as $index => $term ) {
				if ( $term instanceof WP_Term ) {
					$map[ (int) $term->term_id ] = (int) $index;
				}
			}
		}

		self::$category_order_map = $map;
		return $map;
	}
	/**
	 * Meta key used to persist availability flag.
	 */
	private const META_KEY = '_foodord_product_available';

	/**
	 * Determine if product is available on FoodOrd surfaces.
	 *
	 * @param int $product_id Product ID.
	 * @return bool
	 */
	public static function is_available( int $product_id ): bool {
		if ( $product_id <= 0 ) {
			return true;
		}

		if ( class_exists( 'Foodord_Timed_Products' ) && ! Foodord_Timed_Products::product_is_allowed( $product_id ) ) {
			return false;
		}

		$stored = get_post_meta( $product_id, self::META_KEY, true );

		if ( '' === $stored ) {
			return true;
		}

		return self::normalize_flag( $stored );
	}

	/**
	 * Persist availability flag for a product.
	 *
	 * @param int  $product_id Product ID.
	 * @param bool $available  Availability state.
	 * @return bool Updated availability state.
	 */
	public static function set_available( int $product_id, bool $available ): bool {
		if ( $product_id <= 0 ) {
			return true;
		}

		update_post_meta( $product_id, self::META_KEY, $available ? 'yes' : 'no' );

		// Bust caches attached to the product object.
		wc_delete_product_transients( $product_id );

		return self::is_available( $product_id );
	}

	/**
	 * Return string status representation.
	 *
	 * @param int $product_id Product ID.
	 * @return string
	 */
	public static function get_status( int $product_id ): string {
		return self::is_available( $product_id ) ? 'available' : 'unavailable';
	}

	/**
	 * Build REST payload for a WooCommerce product.
	 *
	 * @param WC_Product $product Product instance.
	 * @return array<string,mixed>
	 */
	public static function build_rest_payload( WC_Product $product ): array {
		$product_id = $product->get_id();
		$available  = self::is_available( $product_id );

		$image_url = '';
		if ( has_post_thumbnail( $product_id ) ) {
			$thumb = get_the_post_thumbnail_url( $product_id, 'woocommerce_thumbnail' );
			if ( is_string( $thumb ) ) {
				$image_url = $thumb;
			}
		}

		$currency = get_woocommerce_currency();

		$is_variable        = $product->is_type( 'variable' );
		$variations_payload = array();
		$default_attributes = array();

		if ( $is_variable && class_exists( 'WC_Product_Variation' ) ) {
			$default_attributes = method_exists( $product, 'get_default_attributes' )
				? (array) $product->get_default_attributes()
				: array();
			$variations_payload = self::build_variations_payload( $product );
		}

		$category_ids       = $product->get_category_ids();
		$categories         = array();
		$category_order_map = self::get_category_order_map();

		if ( ! empty( $category_ids ) ) {
			$terms = get_terms(
				array(
					'taxonomy'   => 'product_cat',
					'hide_empty' => false,
					'include'    => array_map( 'intval', $category_ids ),
					'orderby'    => 'include',
				)
			);

			if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
				usort(
					$terms,
					function ( WP_Term $a, WP_Term $b ) use ( $category_order_map ) {
						$index_a = $category_order_map[ (int) $a->term_id ] ?? PHP_INT_MAX;
						$index_b = $category_order_map[ (int) $b->term_id ] ?? PHP_INT_MAX;
							return $index_a <=> $index_b;
					}
				);

				foreach ( $terms as $term ) {
					$order        = $category_order_map[ (int) $term->term_id ] ?? PHP_INT_MAX;
					$categories[] = array(
						'id'         => (int) $term->term_id,
						'name'       => $term->name,
						'slug'       => $term->slug,
						'menu_order' => $order,
					);
				}
			}
		}

		$primary_category       = count( $categories ) > 0
			? $categories[0]['name']
			: __( 'Uncategorized', 'foodord' );
		$primary_category_slug  = count( $categories ) > 0
			? $categories[0]['slug']
			: 'uncategorized';
		$primary_category_order = count( $categories ) > 0
			? ( $categories[0]['menu_order'] ?? PHP_INT_MAX )
			: PHP_INT_MAX;

		return array(
			'id'                           => $product_id,
			'name'                         => $product->get_name(),
			'title'                        => $product->get_name(),
			'product_title'                => $product->get_name(),
			'sku'                          => $product->get_sku(),
			'product_sku'                  => $product->get_sku(),
			'price'                        => $product->get_price(),
			'current_price'                => $product->get_price(),
			'prices'                       => array(
				'price'         => $product->get_price(),
				'regular_price' => $product->get_regular_price(),
				'sale_price'    => $product->get_sale_price(),
				'currency'      => $currency,
				'price_html'    => $product->get_price_html(),
			),
			'price_html'                   => $product->get_price_html(),
			'currency'                     => $currency,
			'type'                         => $product->get_type(),
			'product_type'                 => $product->get_type(),
			'is_variable'                  => $is_variable,
			'has_variations'               => $is_variable && count( $variations_payload ) > 0,
			'variations'                   => $variations_payload,
			'default_variation_attributes' => $default_attributes,
			'available'                    => $available,
			'is_available'                 => $available,
			'isAvailable'                  => $available,
			'availability'                 => self::get_status( $product_id ),
			'status'                       => $product->get_status(),
			'stock_status'                 => $product->get_stock_status(),
			'manage_stock'                 => $product->managing_stock(),
			'stock_quantity'               => $product->get_stock_quantity(),
			'backorders'                   => $product->get_backorders(),
			'backorders_allowed'           => $product->backorders_allowed(),
			'sold_individually'            => $product->is_sold_individually(),
			'tax_status'                   => $product->get_tax_status(),
			'tax_class'                    => $product->get_tax_class(),
			'image'                        => $image_url,
			'thumbnail'                    => $image_url,
			'image_urls'                   => array_filter( array( $image_url ) ),
			'category_ids'                 => array_map( 'intval', $category_ids ),
			'categories'                   => $categories,
			'primary_category'             => $primary_category,
			'primary_category_slug'        => $primary_category_slug,
			'primary_category_order'       => $primary_category_order,
		);
	}

	/**
	 * Build payload for variations of a variable product.
	 *
	 * @param WC_Product $product Product instance.
	 * @return array<int,array<string,mixed>>
	 */
	private static function build_variations_payload( WC_Product $product ): array {
		if ( ! class_exists( 'WC_Product_Variable' ) || ! class_exists( 'WC_Product_Variation' ) ) {
			return array();
		}

		if ( ! $product instanceof WC_Product_Variable ) {
			return array();
		}

		$children = $product->get_children();
		if ( empty( $children ) ) {
			return array();
		}

		$currency   = get_woocommerce_currency();
		$variations = array();

		foreach ( $children as $child_id ) {
			$variation = wc_get_product( (int) $child_id );
			if ( ! $variation instanceof WC_Product_Variation ) {
				continue;
			}

			$attributes_payload = self::build_variation_attributes_payload( $variation );
			$price_payload      = array(
				'price'         => $variation->get_price(),
				'regular_price' => $variation->get_regular_price(),
				'sale_price'    => $variation->get_sale_price(),
				'currency'      => $currency,
				'price_html'    => $variation->get_price_html(),
			);

			$variations[] = array(
				'id'                 => $variation->get_id(),
				'variation_id'       => $variation->get_id(),
				'sku'                => $variation->get_sku(),
				'name'               => $variation->get_name(),
				'status'             => $variation->get_status(),
				'stock_status'       => $variation->get_stock_status(),
				'in_stock'           => $variation->is_in_stock(),
				'manage_stock'       => $variation->managing_stock(),
				'stock_quantity'     => $variation->get_stock_quantity(),
				'backorders'         => $variation->get_backorders(),
				'backorders_allowed' => $variation->backorders_allowed(),
				'tax_status'         => $variation->get_tax_status(),
				'tax_class'          => $variation->get_tax_class(),
				'attribute_summary'  => $attributes_payload['summary'],
				'attributes'         => $attributes_payload['attributes'],
				'prices'             => $price_payload,
				'price'              => $price_payload['price'],
				'regular_price'      => $price_payload['regular_price'],
				'sale_price'         => $price_payload['sale_price'],
			);
		}

		return $variations;
	}

	/**
	 * Build formatted attributes payload for a variation.
	 *
	 * @param WC_Product_Variation $variation Variation instance.
	 * @return array{attributes: array<int,array<string,string>>, summary: string}
	 */
	private static function build_variation_attributes_payload( WC_Product_Variation $variation ): array {
		$attributes     = array();
		$summary_parts  = array();
		$raw_attributes = $variation->get_attributes();

		foreach ( (array) $raw_attributes as $raw_key => $raw_value ) {
			if ( '' === $raw_key ) {
				continue;
			}

			$attribute_key = is_string( $raw_key ) ? $raw_key : '';
			$taxonomy      = 0 === strpos( $attribute_key, 'attribute_' ) ? substr( $attribute_key, 10 ) : $attribute_key;
			$label         = wc_attribute_label( $taxonomy );
			$value         = self::resolve_variation_attribute_value( $taxonomy, $raw_value );

			$attributes[] = array(
				'key'   => $taxonomy,
				'slug'  => $taxonomy,
				'label' => $label,
				'name'  => $label,
				'value' => $value,
				'raw'   => is_scalar( $raw_value ) ? (string) $raw_value : '',
			);

			if ( '' !== $value ) {
				$summary_parts[] = trim( sprintf( '%s: %s', $label, $value ) );
			}
		}

		return array(
			'attributes' => $attributes,
			'summary'    => implode( ', ', array_filter( $summary_parts ) ),
		);
	}

	/**
	 * Resolve a variation attribute value into a human readable label.
	 *
	 * @param string $taxonomy Attribute taxonomy or key.
	 * @param mixed  $value    Raw value.
	 * @return string
	 */
	private static function resolve_variation_attribute_value( string $taxonomy, $value ): string {
		if ( ! is_scalar( $value ) ) {
			return '';
		}

		$clean_value = function_exists( 'wc_clean' ) ? wc_clean( (string) $value ) : sanitize_text_field( (string) $value );
		if ( '' === $clean_value ) {
			return '';
		}

		if ( taxonomy_exists( $taxonomy ) ) {
			$term = get_term_by( 'slug', $clean_value, $taxonomy );
			if ( $term instanceof WP_Term ) {
				return $term->name;
			}
		}

		return $clean_value;
	}

	/**
	 * Convert incoming flag to boolean.
	 *
	 * @param mixed $value Raw flag value.
	 * @return bool
	 */
	public static function normalize_flag( $value ): bool {
		if ( is_bool( $value ) ) {
			return $value;
		}

		$normalized = strtolower( trim( (string) $value ) );

		if ( '' === $normalized ) {
			return true;
		}

		if ( in_array( $normalized, array( '0', 'no', 'false', 'off' ), true ) ) {
			return false;
		}

		return true;
	}

	// Intentionally no camelCase alias: WordPress coding standards enforce snake_case.
}
