<?php
/**
 * Collection Page Schema Generator — Free Tier
 *
 * @package Voidek_SERP_Schema_Builder
 * @since   1.3.0
 */

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

class Vserpb_Collection_Schema {

	// -----------------------------------------------------------------------
	// Public entry-point
	// -----------------------------------------------------------------------

	public static function vserpb_output_schema() {

		if ( ! is_product_category() ) {
			return;
		}

		$term = get_queried_object();
		if ( ! $term || ! isset( $term->term_id ) ) {
			return;
		}

		$schema = self::vserpb_generate_schema( $term );
		if ( empty( $schema ) ) {
			return;
		}

		$schema = apply_filters( 'vserpb_extend_collection_schema', $schema, $term );

		echo '<script type="application/ld+json" class="Vserpb-collection-schema">' . "\n"
			. wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT )
			. "\n</script>\n";
	}

	// -----------------------------------------------------------------------
	// Main schema builder
	// -----------------------------------------------------------------------

	public static function vserpb_generate_schema( WP_Term $term ) {

		$term_id   = (int) $term->term_id;
		$term_name = sanitize_text_field( $term->name );
		$term_url  = get_term_link( $term );
		$term_desc = wp_strip_all_tags( $term->description );

		$thumbnail_id  = get_term_meta( $term_id, 'thumbnail_id', true );
		$thumbnail_url = $thumbnail_id ? wp_get_attachment_url( (int) $thumbnail_id ) : '';

		$paged    = max( 1, get_query_var( 'paged' ) );
		$per_page = (int) get_option( 'posts_per_page', 12 );
		$args     = array(
			'post_type'      => 'product',
			'post_status'    => 'publish',
			'posts_per_page' => $per_page,
			'paged'          => $paged,
			'tax_query'      => array(
				array(
					'taxonomy' => 'product_cat',
					'field'    => 'term_id',
					'terms'    => $term_id,
				),
			),
		);
		$products_query = new WP_Query( $args );
		$total_products = (int) $products_query->found_posts;

		// ---- CollectionPage root ------------------------------------------
		// FIX 1: Removed 'publisher' (invalid on CollectionPage).
		// FIX 2: Moved 'potentialAction' (SearchAction) inside isPartOf WebSite.
		$schema = array(
			'@context'   => 'https://schema.org',
			'@type'      => 'CollectionPage',
			'@id'        => esc_url( $term_url ) . '#collection',
			'name'       => $term_name,
			'url'        => esc_url( $term_url ),
			'inLanguage' => get_bloginfo( 'language' ),
			'isPartOf'   => array(
				'@type'           => 'WebSite',
				'@id'             => esc_url( home_url( '/' ) ) . '#website',
				'name'            => sanitize_text_field( get_bloginfo( 'name' ) ),
				'url'             => esc_url( home_url( '/' ) ),
				'potentialAction' => array(
					'@type'       => 'SearchAction',
					'target'      => array(
						'@type'       => 'EntryPoint',
						'urlTemplate' => esc_url( home_url( '/' ) ) . '?s={search_term_string}&post_type=product',
					),
					'query-input' => 'required name=search_term_string',
				),
			),
		);

		if ( ! empty( $term_desc ) ) {
			$schema['description'] = $term_desc;
		}

		if ( $thumbnail_url ) {
			$schema['image'] = self::vserpb_get_image_object_schema( $thumbnail_id, $thumbnail_url, $term_name );
		}

		$breadcrumb = self::vserpb_get_breadcrumb_schema( $term );
		if ( ! empty( $breadcrumb ) ) {
			$schema['breadcrumb'] = $breadcrumb;
		}

		$item_list = self::vserpb_get_item_list_schema( $products_query, $paged, $per_page, $term_url, $term_name, $total_products );
		if ( ! empty( $item_list ) ) {
			$schema['mainEntity'] = $item_list;
		}

		$agg_offer = self::vserpb_get_aggregate_offer_schema( $term_id, $term_url );
		if ( ! empty( $agg_offer ) ) {
			$schema['offers'] = $agg_offer;
		}

		// FIX 4: Removed 'speakable' (unsupported on CollectionPage by Google).
		// FIX 5: Removed 'about' with internal WP term identifiers (non-standard).
		// FIX 6: Removed pagination SiteNavigationElement block (use rel prev/next instead).

		wp_reset_postdata();

		return $schema;
	}

	// -----------------------------------------------------------------------
	// Sub-schema builders
	// -----------------------------------------------------------------------

	private static function vserpb_get_item_list_schema(
		WP_Query $query,
		int $paged,
		int $per_page,
		string $collection_url,
		string $collection_name,
		int $total_products
	) {
		if ( ! $query->have_posts() ) {
			return array();
		}

		$item_list = array(
			'@type'           => 'ItemList',
			'@id'             => esc_url( $collection_url ) . '#itemlist',
			'name'            => $collection_name . ' — Product List',
			'numberOfItems'   => $total_products,
			'itemListOrder'   => 'https://schema.org/ItemListOrderAscending',
			'description'     => sprintf( 'Showing products in the %s collection.', $collection_name ),
			'itemListElement' => array(),
		);

		$position_offset = ( $paged - 1 ) * $per_page;
		$position        = $position_offset + 1;

		while ( $query->have_posts() ) {
			$query->the_post();
			$post_id = get_the_ID();
			$product = wc_get_product( $post_id );

			if ( ! $product || ! ( $product instanceof WC_Product ) ) {
				$position++;
				continue;
			}

			$product_url = get_permalink( $post_id );

			$item_product = array(
				'@type' => $product->is_type( 'variable' ) ? 'ProductGroup' : 'Product',
				'@id'   => esc_url( $product_url ) . '#product',
				'name'  => sanitize_text_field( $product->get_name() ),
				'url'   => esc_url( $product_url ),
			);

			// FIX 8: Only output sku if non-empty.
			$sku = sanitize_text_field( $product->get_sku() );
			if ( ! empty( $sku ) ) {
				$item_product['sku'] = $sku;
			}

			$desc = wp_strip_all_tags( do_shortcode( $product->get_short_description() ?: $product->get_description() ) );
			if ( ! empty( $desc ) ) {
				$item_product['description'] = $desc;
			}

			// GTIN.
			$gtin = '';
			if ( method_exists( $product, 'get_global_unique_id' ) ) {
				$gtin = $product->get_global_unique_id();
			}
			if ( empty( $gtin ) ) {
				$gtin = get_post_meta( $post_id, '_global_unique_id', true );
			}
			if ( empty( $gtin ) ) {
				$gtin = get_post_meta( $post_id, '_gtin', true );
			}
			if ( ! empty( $gtin ) ) {
				$item_product['gtin'] = sanitize_text_field( $gtin );
			}

			// Image.
			$img_id = $product->get_image_id();
			if ( $img_id ) {
				$img_url = wp_get_attachment_url( $img_id );
				if ( $img_url ) {
					$item_product['image'] = self::vserpb_get_image_object_schema(
						$img_id,
						$img_url,
						sanitize_text_field( $product->get_name() )
					);
				}
			}

			// Brand.
			$brand_terms = get_the_terms( $post_id, 'product_brand' );
			if ( ! $brand_terms || is_wp_error( $brand_terms ) ) {
				$brand_terms = get_the_terms( $post_id, 'pa_brand' );
			}
			if ( $brand_terms && ! is_wp_error( $brand_terms ) ) {
				$item_product['brand'] = array(
					'@type' => 'Brand',
					'name'  => sanitize_text_field( $brand_terms[0]->name ),
				);
			}

			// Offer.
			$item_product['offers'] = self::vserpb_get_list_item_offer( $product );

			// AggregateRating.
			$avg_rating   = (float) $product->get_average_rating();
			$review_count = (int) $product->get_review_count();
			if ( $avg_rating > 0 && $review_count > 0 ) {
				$item_product['aggregateRating'] = array(
					'@type'       => 'AggregateRating',
					'ratingValue' => $avg_rating,
					'reviewCount' => $review_count,
					'bestRating'  => apply_filters( 'vserpb_best_rating', 5 ),
					'worstRating' => apply_filters( 'vserpb_worst_rating', 1 ),
				);
			}

			$item_list['itemListElement'][] = array(
				'@type'    => 'ListItem',
				'position' => $position,
				'url'      => esc_url( $product_url ),
				'name'     => sanitize_text_field( $product->get_name() ),
				'item'     => $item_product,
			);

			$position++;
		}

		return $item_list;
	}

	/**
	 * Offer block for a single product inside ItemList.
	 *
	 * FIX 3: Variable products now use AggregateOffer with lowPrice/highPrice
	 * instead of Offer + priceSpecification range.
	 */
	private static function vserpb_get_list_item_offer( WC_Product $product ) {

		$currency        = get_woocommerce_currency();
		$availability_ns = $product->is_in_stock()
			? 'https://schema.org/InStock'
			: 'https://schema.org/OutOfStock';

		$product_url = esc_url( get_permalink( $product->get_id() ) );

		if ( $product->is_type( 'variable' ) ) {

			// FIX 3: AggregateOffer for variable/ProductGroup products.
			$min = $product->get_variation_price( 'min', true );
			$max = $product->get_variation_price( 'max', true );

			$offer = array(
				'@type'          => 'AggregateOffer',
				'priceCurrency'  => $currency,
				'availability'   => $availability_ns,
				'url'            => $product_url,
				'itemCondition'  => 'https://schema.org/NewCondition',
				'seller'         => self::vserpb_get_organization_schema(),
			);

			if ( $min !== '' && $min !== null ) {
				$offer['lowPrice']  = wc_format_decimal( $min, wc_get_price_decimals() );
				$offer['highPrice'] = wc_format_decimal( $max, wc_get_price_decimals() );
			}

			$variations = $product->get_children();
			if ( ! empty( $variations ) ) {
				$offer['offerCount'] = count( $variations );
			}

		} else {

			$price = $product->is_on_sale() ? $product->get_sale_price() : $product->get_regular_price();

			$offer = array(
				'@type'          => 'Offer',
				'priceCurrency'  => $currency,
				'availability'   => $availability_ns,
				'url'            => $product_url,
				'priceValidUntil'=> gmdate( 'Y-m-d', strtotime( '+30 days' ) ),
				'itemCondition'  => 'https://schema.org/NewCondition',
				'seller'         => self::vserpb_get_organization_schema(),
			);

			if ( $price !== '' && $price !== null ) {
				$offer['price'] = wc_format_decimal( $price, wc_get_price_decimals() );
			}

			// FIX 7: $sale_end is WC_DateTime — use getTimestamp(), not strtotime().
			$sale_end = $product->get_date_on_sale_to();
			if ( $sale_end instanceof WC_DateTime ) {
				$offer['priceValidUntil'] = gmdate( 'Y-m-d', $sale_end->getTimestamp() );
			}
		}

		return $offer;
	}

	/**
	 * AggregateOffer — price range for the entire collection.
	 */
	private static function vserpb_get_aggregate_offer_schema( int $term_id, string $collection_url ) {

		$meta_query = new WP_Query(
			array(
				'post_type'      => 'product',
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'tax_query'      => array(
					array(
						'taxonomy' => 'product_cat',
						'field'    => 'term_id',
						'terms'    => $term_id,
					),
				),
				'meta_query'     => array(
					array(
						'key'     => '_price',
						'value'   => '',
						'compare' => '!=',
					),
				),
			)
		);

		if ( ! $meta_query->have_posts() ) {
			wp_reset_postdata();
			return null;
		}

		$prices      = array();
		$in_stock    = 0;
		$offer_count = 0;

		foreach ( $meta_query->posts as $pid ) {
			$price = get_post_meta( (int) $pid, '_price', true );
			if ( $price !== '' && is_numeric( $price ) ) {
				$prices[]     = (float) $price;
				$offer_count++;
				$stock_status = get_post_meta( (int) $pid, '_stock_status', true );
				if ( $stock_status === 'instock' ) {
					$in_stock++;
				}
			}
		}

		wp_reset_postdata();

		if ( empty( $prices ) ) {
			return null;
		}

		return array(
			'@type'         => 'AggregateOffer',
			'@id'           => esc_url( $collection_url ) . '#aggregateoffer',
			'priceCurrency' => get_woocommerce_currency(),
			'lowPrice'      => wc_format_decimal( min( $prices ), wc_get_price_decimals() ),
			'highPrice'     => wc_format_decimal( max( $prices ), wc_get_price_decimals() ),
			'offerCount'    => $offer_count,
			'availability'  => $in_stock > 0
				? 'https://schema.org/InStock'
				: 'https://schema.org/OutOfStock',
			'seller'        => self::vserpb_get_organization_schema(),
		);
	}

	/**
	 * BreadcrumbList — Home → Shop → [ancestors] → current term.
	 */
	private static function vserpb_get_breadcrumb_schema( WP_Term $term ) {

		$items = array();
		$pos   = 1;

		$items[] = array(
			'@type'    => 'ListItem',
			'position' => $pos++,
			'name'     => sanitize_text_field( get_bloginfo( 'name' ) ),
			'item'     => esc_url( home_url( '/' ) ),
		);

		$shop_id = wc_get_page_id( 'shop' );
		if ( $shop_id > 0 ) {
			$items[] = array(
				'@type'    => 'ListItem',
				'position' => $pos++,
				'name'     => sanitize_text_field( get_the_title( $shop_id ) ),
				'item'     => esc_url( get_permalink( $shop_id ) ),
			);
		}

		$ancestors = get_ancestors( $term->term_id, 'product_cat', 'taxonomy' );
		foreach ( array_reverse( $ancestors ) as $ancestor_id ) {
			$ancestor = get_term( $ancestor_id, 'product_cat' );
			if ( $ancestor && ! is_wp_error( $ancestor ) ) {
				$items[] = array(
					'@type'    => 'ListItem',
					'position' => $pos++,
					'name'     => sanitize_text_field( $ancestor->name ),
					'item'     => esc_url( get_term_link( $ancestor ) ),
				);
			}
		}

		$items[] = array(
			'@type'    => 'ListItem',
			'position' => $pos,
			'name'     => sanitize_text_field( $term->name ),
			'item'     => esc_url( get_term_link( $term ) ),
		);

		return array(
			'@type'           => 'BreadcrumbList',
			'@id'             => esc_url( get_term_link( $term ) ) . '#breadcrumb',
			'itemListElement' => $items,
		);
	}

	/**
	 * ImageObject — rich image node with dimensions.
	 */
	private static function vserpb_get_image_object_schema( $attachment_id, string $url, string $caption ) {

		$image = array(
			'@type'      => 'ImageObject',
			'url'        => esc_url( $url ),
			'contentUrl' => esc_url( $url ),
			'caption'    => sanitize_text_field( $caption ),
		);

		if ( $attachment_id ) {
			$meta = wp_get_attachment_metadata( (int) $attachment_id );
			if ( $meta ) {
				if ( ! empty( $meta['width'] ) ) {
					$image['width'] = (int) $meta['width'];
				}
				if ( ! empty( $meta['height'] ) ) {
					$image['height'] = (int) $meta['height'];
				}
				if ( ! empty( $meta['image_meta']['credit'] ) ) {
					$image['creditText'] = sanitize_text_field( $meta['image_meta']['credit'] );
				}
			}

			$alt = get_post_meta( (int) $attachment_id, '_wp_attachment_image_alt', true );
			if ( $alt ) {
				$image['description'] = sanitize_text_field( $alt );
			}
		}

		return $image;
	}

	/**
	 * Organization node for seller / publisher fields.
	 */
	private static function vserpb_get_organization_schema() {

		$schema = array(
			'@type' => 'Organization',
			'@id'   => esc_url( home_url( '/' ) ) . '#organization',
			'name'  => sanitize_text_field( get_bloginfo( 'name' ) ),
			'url'   => esc_url( home_url( '/' ) ),
		);

		$logo_id = get_option( 'site_logo' );
		if ( ! $logo_id ) {
			$logo_id = get_theme_mod( 'custom_logo' );
		}
		if ( $logo_id ) {
			$logo_url = wp_get_attachment_url( (int) $logo_id );
			if ( $logo_url ) {
				$schema['logo'] = array(
					'@type' => 'ImageObject',
					'url'   => esc_url( $logo_url ),
				);
			}
		}

		return $schema;
	}
}