<?php
/**
 * Schema Renderer
 *
 * Resolves dynamic variables (%post.title%, %site.url% etc.) in schema
 * field values and produces the final JSON-LD output.
 *
 * @package SEOvaultSEO\Schema
 */

namespace SEOvaultSEO\Schema;

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

class SchemaRenderer {

	/**
	 * Collected variable data for replacement.
	 *
	 * @var array
	 */
	private $data = array();

	/**
	 * Explicit object being rendered, when building outside the main query.
	 *
	 * @var \WP_Post|\WP_Term|\WP_User|null
	 */
	private $object;

	/**
	 * Constructor — collects all variable data.
	 *
	 * @param \WP_Post|\WP_Term|\WP_User|null $object       Explicit render object.
	 * @param array                           $schema_links Precomputed type => @id references.
	 */
	public function __construct( $object = null, $schema_links = array() ) {
		$this->object = $object;
		$this->data   = $this->collect();
		if ( is_array( $schema_links ) && ! empty( $schema_links ) ) {
			$this->data['schemas'] = array_merge( $this->data['schemas'], $schema_links );
		}
	}

	/**
	 * Collect all dynamic variables available for schema rendering.
	 *
	 * @return array
	 */
	private function collect() {
		$data = array(
			'site'    => array(
				'url'         => home_url( '/' ),
				'title'       => get_bloginfo( 'name' ),
				'description' => get_bloginfo( 'description' ),
				'logo'        => $this->get_site_logo(),
				'language'    => get_bloginfo( 'language' ),
				'search_url'  => home_url( '/?s={search_term_string}' ),
			),
			'current' => array(
				'url'         => '',
				'title'       => '',
				'description' => '',
				'breadcrumbs' => array(),
			),
			'post'    => array(
				'title'          => '',
				'excerpt'        => '',
				'url'            => '',
				'published_date' => '',
				'modified_date'  => '',
				'author_name'    => '',
			),
			'term'    => array(
				'name'        => '',
				'description' => '',
			),
			'author'  => array(
				'name' => '',
			),
			'schemas' => array(
				'organization' => '',
				'person'       => '',
				'searchaction' => '',
			),
		);

		$object = $this->object ? $this->object : get_queried_object();

		if ( $object instanceof \WP_Post ) {
			$data['current']['url']         = get_permalink( $object->ID );
			$data['current']['title']       = get_the_title( $object->ID );
			$data['current']['description'] = wp_strip_all_tags( get_the_excerpt( $object->ID ) );
			$data['current']['breadcrumbs'] = $this->get_breadcrumbs( $object );

			$data['post']['title']          = get_the_title( $object->ID );
			$data['post']['excerpt']        = wp_strip_all_tags( get_the_excerpt( $object->ID ) );
			$data['post']['url']            = get_permalink( $object->ID );
			$data['post']['published_date'] = get_the_date( 'c', $object->ID );
			$data['post']['modified_date']  = get_the_modified_date( 'c', $object->ID );
			$data['post']['author_name']    = get_the_author_meta( 'display_name', $object->post_author );

			$data['author']['name'] = get_the_author_meta( 'display_name', $object->post_author );
		} elseif ( $object instanceof \WP_Term ) {
			$data['current']['url']         = get_term_link( $object );
			$data['current']['title']       = $object->name;
			$data['current']['description'] = $object->description;
			$data['current']['breadcrumbs'] = $this->get_breadcrumbs( $object );

			$data['term']['name']        = $object->name;
			$data['term']['description'] = $object->description;
		} elseif ( $object instanceof \WP_User ) {
			$data['current']['url']   = get_author_posts_url( $object->ID );
			$data['current']['title'] = $object->display_name;
			$data['author']['name']   = $object->display_name;
		}

		$data['current']['time']  = current_time( 'H:i' );
		$data['current']['year']  = gmdate( 'Y' );
		$data['current']['month'] = gmdate( 'F' );
		$data['current']['day']   = gmdate( 'j' );
		$data['current']['date']  = gmdate( 'F j, Y' );

		return apply_filters( 'seovaultseo_schema_variables', $data );
	}

	/**
	 * Render a value by resolving all %variable% placeholders.
	 *
	 * @param mixed $value Value to render.
	 * @return void
	 */
	public function render( &$value ) {
		if ( is_array( $value ) ) {
			foreach ( $value as &$item ) {
				$this->render( $item );
			}
			return;
		}

		if ( ! is_string( $value ) || ! str_contains( $value, '%' ) ) {
			return;
		}

		preg_match_all( '/%([^%\s]+)%/', $value, $matches );

		foreach ( $matches[1] as $match ) {
			$keys        = explode( '.', $match );
			$replacement = $this->resolve_variable( $keys, $this->data );

			if ( is_array( $replacement ) ) {
				$value = $replacement;
			} else {
				$value = str_replace( "%{$match}%", $replacement ?? '', $value );
			}

			$this->render( $value );
		}
	}

	/**
	 * Resolve a variable from data using dot notation.
	 *
	 * @param array $keys Keys to traverse.
	 * @param array $data Data array.
	 * @return mixed|null
	 */
	private function resolve_variable( array $keys, array $data ) {
		$current = $data;
		foreach ( $keys as $key ) {
			if ( is_array( $current ) && isset( $current[ $key ] ) ) {
				$current = $current[ $key ];
			} elseif ( is_object( $current ) && isset( $current->$key ) ) {
				$current = $current->$key;
			} else {
				return null;
			}
		}
		return $current;
	}

	/**
	 * Get site logo URL.
	 *
	 * @return string
	 */
	private function get_site_logo() {
		$logo_id = get_theme_mod( 'custom_logo' );
		if ( $logo_id ) {
			$logo = wp_get_attachment_image_url( $logo_id, 'full' );
			if ( $logo ) {
				return $logo;
			}
		}
		return '';
	}

	/**
	 * Get breadcrumbs for the current page.
	 *
	 * Delegates to the Frontend\Breadcrumbs class for a full trail
	 * with taxonomy hierarchy, post type archives, and ancestors.
	 * Falls back to a simple trail if breadcrumbs are disabled.
	 *
	 * @param \WP_Post|\WP_Term|\WP_User|null $object Explicit render object.
	 * @return array
	 */
	private function get_breadcrumbs( $object = null ) {
		if ( is_front_page() ) {
			return array();
		}

		$crumb_instance = \SEOvaultSEO\Frontend\Breadcrumbs::get();
		if ( $crumb_instance ) {
			$canonical = '';
			$object    = $object ? $object : get_queried_object();
			if ( $object instanceof \WP_Post ) {
				$canonical = get_permalink( $object->ID );
			} elseif ( $object instanceof \WP_Term ) {
				$canonical = get_term_link( $object );
			} elseif ( $object instanceof \WP_User ) {
				$canonical = get_author_posts_url( $object->ID );
			}

			$schema = $crumb_instance->get_schema( $canonical );
			if ( $schema && ! empty( $schema['itemListElement'] ) ) {
				return $schema['itemListElement'];
			}
		}

		// Fallback: simple two-level trail.
		$breadcrumbs = array();

		$breadcrumbs[] = array(
			'@type'    => 'ListItem',
			'position' => 1,
			'name'     => get_bloginfo( 'name' ),
			'item'     => home_url( '/' ),
		);

		$position = 2;

		if ( $object instanceof \WP_Post || is_singular() ) {
			$post_id = $object instanceof \WP_Post ? $object->ID : get_queried_object_id();

			$breadcrumbs[] = array(
				'@type'    => 'ListItem',
				'position' => $position,
				'name'     => get_the_title( $post_id ),
				'item'     => get_permalink( $post_id ),
			);
		} elseif ( $object instanceof \WP_Term || is_category() || is_tag() || is_tax() ) {
			$term          = $object instanceof \WP_Term ? $object : get_queried_object();
			$breadcrumbs[] = array(
				'@type'    => 'ListItem',
				'position' => $position,
				'name'     => $term->name,
				'item'     => get_term_link( $term ),
			);
		} elseif ( $object instanceof \WP_User || is_author() ) {
			$user          = $object instanceof \WP_User ? $object : get_queried_object();
			$breadcrumbs[] = array(
				'@type'    => 'ListItem',
				'position' => $position,
				'name'     => $user->display_name,
				'item'     => get_author_posts_url( $user->ID ),
			);
		}

		return $breadcrumbs;
	}
}
