<?php
/**
 * Product JSON-LD emitter.
 *
 * Emits schema.org/Product on single-product pages using the built-in
 * WooCommerce data (name, description, image, SKU, GTIN when set, price,
 * availability, aggregateRating, review count). Coexists with SEO plugins:
 * if Yoast/RankMath/AIOSEO is active it steps aside to avoid duplicate
 * Product schema output.
 *
 * @package CatalogPilot\AiStudio
 */

namespace CatalogPilot\AiStudio;

defined( 'ABSPATH' ) || exit;

final class Product_Schema {

	private static $instance = null;

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
			add_action( 'wp_head', array( self::$instance, 'emit' ), 40 );
			add_filter( 'woocommerce_structured_data_product', array( self::$instance, 'suppress_woo_duplicate' ), 99 );
		}
		return self::$instance;
	}

	/**
	 * When this plugin emits its own richer Product schema, drop WooCommerce's
	 * native Product structured data so a product page never carries two Product nodes.
	 *
	 * @param array $markup WooCommerce product structured-data markup.
	 * @return array
	 */
	public function suppress_woo_duplicate( $markup ) {
		return self::should_emit() ? array() : $markup;
	}

	/**
	 * If another SEO plugin is emitting Product schema, stand down.
	 * Site owners can force-enable via the "cpais_emit_product_schema" filter.
	 */
	private static function should_emit() {
		$force = (bool) apply_filters( 'cpais_emit_product_schema', null );
		if ( null !== apply_filters( 'cpais_emit_product_schema', null ) ) {
			return $force;
		}
		$id = SEO::detect();
		if ( SEO::INT_CORE !== $id ) {
			return false;
		}
		return true;
	}

	public function emit() {
		if ( ! function_exists( 'is_product' ) || ! is_product() ) {
			return;
		}
		if ( ! self::should_emit() ) {
			return;
		}
		global $post;
		if ( ! $post ) {
			return;
		}
		$product = function_exists( 'wc_get_product' ) ? wc_get_product( $post->ID ) : null;
		if ( ! $product ) {
			return;
		}

		$data = self::build_schema( $product );
		if ( empty( $data ) ) {
			return;
		}
		$json = wp_json_encode( $data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
		if ( false === $json ) {
			return;
		}
		echo '<script type="application/ld+json">' . wp_kses( $json, array() ) . '</script>' . "\n";
	}

	/**
	 * Build a schema.org/Product array for the given product.
	 * Public so REST/preview endpoints can reuse it.
	 *
	 * @param \WC_Product $product
	 * @return array
	 */
	public static function build_schema( $product ) {
		$id = $product->get_id();

		$name        = $product->get_name();
		$description = wp_strip_all_tags( $product->get_short_description() ?: $product->get_description() );
		$description = trim( wp_trim_words( $description, 55, '' ) );
		$sku         = $product->get_sku();
		$permalink   = get_permalink( $id );

		$image_urls = array();
		if ( has_post_thumbnail( $id ) ) {
			$feat = wp_get_attachment_image_url( get_post_thumbnail_id( $id ), 'full' );
			if ( $feat ) {
				$image_urls[] = $feat;
			}
		}
		foreach ( $product->get_gallery_image_ids() as $img_id ) {
			$u = wp_get_attachment_image_url( (int) $img_id, 'full' );
			if ( $u ) {
				$image_urls[] = $u;
			}
		}
		$image_urls = array_values( array_unique( $image_urls ) );

		$schema = array(
			'@context'    => 'https://schema.org/',
			'@type'       => 'Product',
			'name'        => $name,
			'description' => $description,
			'url'         => $permalink,
		);
		if ( $sku ) {
			$schema['sku'] = $sku;
		}
		$gtin = get_post_meta( $id, '_cpais_gtin', true );
		if ( ! $gtin ) {
			$gtin = get_post_meta( $id, '_wc_gla_gtin', true );
		}
		if ( ! $gtin ) {
			$gtin = get_post_meta( $id, '_gtin', true );
		}
		if ( $gtin ) {
			$schema['gtin'] = (string) $gtin;
		}
		if ( ! empty( $image_urls ) ) {
			$schema['image'] = count( $image_urls ) === 1 ? $image_urls[0] : $image_urls;
		}

		// Brand — from a "brand" attribute if the merchant has one.
		$brand = $product->get_attribute( 'brand' );
		if ( $brand ) {
			$schema['brand'] = array( '@type' => 'Brand', 'name' => (string) $brand );
		}

		// Offers.
		$currency  = function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : '';
		$price     = $product->get_price();
		$in_stock  = $product->is_in_stock();
		$availability = $in_stock ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock';

		if ( $product->is_type( 'variable' ) ) {
			$prices  = method_exists( $product, 'get_variation_prices' ) ? $product->get_variation_prices( true ) : array();
			$lo      = $prices && ! empty( $prices['price'] ) ? min( $prices['price'] ) : $price;
			$hi      = $prices && ! empty( $prices['price'] ) ? max( $prices['price'] ) : $price;
			$offers  = array(
				'@type'         => 'AggregateOffer',
				'priceCurrency' => $currency,
				'lowPrice'      => (string) wc_format_decimal( $lo, wc_get_price_decimals() ),
				'highPrice'     => (string) wc_format_decimal( $hi, wc_get_price_decimals() ),
				'offerCount'    => count( $product->get_children() ),
				'availability'  => $availability,
			);
			$schema['offers'] = $offers;
		} elseif ( '' !== $price ) {
			$schema['offers'] = array(
				'@type'         => 'Offer',
				'priceCurrency' => $currency,
				'price'         => (string) wc_format_decimal( $price, wc_get_price_decimals() ),
				'availability'  => $availability,
				'url'           => $permalink,
			);
		}

		// Aggregate rating.
		$rating_count = (int) $product->get_rating_count();
		$review_count = (int) $product->get_review_count();
		if ( $rating_count > 0 ) {
			$schema['aggregateRating'] = array(
				'@type'       => 'AggregateRating',
				'ratingValue' => (string) $product->get_average_rating(),
				'reviewCount' => $review_count > 0 ? $review_count : $rating_count,
			);
		}

		return apply_filters( 'cpais_product_schema', $schema, $product );
	}
}
