<?php
/**
 * Rank Math integration — extends Rank Math's schema graph with the plugin's
 * vehicle/dealer pieces (per ADR-19, mode 3).
 *
 * Rank Math fires `rank_math/json_ld` as a filter (registered through its
 * `$this->action()` helper, but the handlers return modified data, so it
 * behaves as a filter). The payload is an associative array keyed by entity
 * name — e.g. `$data['WebSite']`, `$data['BlogPosting']`, `$data['BreadcrumbList']`.
 * Rank Math wraps the final array in `@graph` before output.
 *
 * We add `Car`/`Vehicle`/`Motorcycle` + `Offer` on single vehicles, `AutoDealer`
 * on single dealers, and `FAQPage` if FAQ entries exist. Rank Math's
 * BreadcrumbList stays — we don't double up.
 *
 * @package ThemeREX\AutoDealer
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Integrations\RankMath;

defined( 'ABSPATH' ) || exit;
use ThemeREX\AutoDealer\Integrations\Schema\SchemaGenerator;

class RankMathIntegration {

	public function register(): void {
		// Priority 11 — after Rank Math's own `add_schema` (10) and before
		// `connect_schema_entities` (99) which post-processes references.
		add_filter( 'rank_math/json_ld', array( $this, 'extendGraph' ), 11, 2 );
	}

	/**
	 * Append plugin pieces to Rank Math's `$data` map.
	 *
	 * Source of pieces is `SchemaGenerator::buildGraph()` so anything
	 * registered through the `trx_ad_schema_graph_pieces` filter (Phase 18
	 * AEO/GEO modules add WebSite, ItemList, Dataset, Speakable, etc.
	 * through that hook) flows through automatically. We strip
	 * `BreadcrumbList` because Rank Math emits its own.
	 *
	 * @param array<string, mixed>|mixed $data    Rank Math's keyed-by-entity map.
	 * @param mixed                      $jsonld  Rank Math's JsonLD orchestrator (unused).
	 * @return array<string, mixed>
	 */
	public function extendGraph( $data, $jsonld = null ): array {
		unset( $jsonld );
		if ( ! is_array( $data ) ) {
			$data = array();
		}

		$index = 0;
		foreach ( SchemaGenerator::buildGraph() as $piece ) {
			if ( ! is_array( $piece ) ) {
				continue;
			}
			$type = $piece['@type'] ?? '';
			if ( 'BreadcrumbList' === $type ) {
				continue;
			}
			if ( self::dataHasId( $data, $piece['@id'] ?? '' ) ) {
				continue;
			}
			$type_str = is_array( $type ) ? ( $type[0] ?? 'piece' ) : (string) $type;
			$slug     = strtolower( preg_replace( '/[^a-z0-9]+/i', '_', $type_str ) ?: 'piece' );
			$key      = 'trx_ad_' . $slug;
			// Avoid clobbering if multiple pieces share a @type slug.
			while ( isset( $data[ $key ] ) ) {
				$key = 'trx_ad_' . $slug . '_' . ( ++$index );
			}
			$data[ $key ] = $piece;
		}

		return $data;
	}

	/**
	 * Avoid clobbering or duplicating an entity that already lives in
	 * Rank Math's `$data` map under a different key (some Rank Math add-ons
	 * stash custom snippets).
	 *
	 * @param array<string, mixed> $data
	 */
	private static function dataHasId( array $data, string $id ): bool {
		if ( '' === $id ) {
			return false;
		}
		foreach ( $data as $node ) {
			if ( is_array( $node ) && isset( $node['@id'] ) && $node['@id'] === $id ) {
				return true;
			}
		}
		return false;
	}
}
