<?php
/**
 * SEO & Schema Injector
 *
 * @package WPAgenticWriter
 */

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

class WP_Agentic_Writer_SEO_Schema {

	/**
	 * Instance of this class.
	 *
	 * @var WP_Agentic_Writer_SEO_Schema
	 */
	private static $instance = null;

	/**
	 * Get the singleton instance.
	 *
	 * @return WP_Agentic_Writer_SEO_Schema
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor.
	 */
	private function __construct() {
		add_action( 'save_post', array( $this, 'extract_and_save_faq_schema' ), 20, 3 );
		add_action( 'wp_head', array( $this, 'output_faq_schema_in_head' ) );
		
		// Integrate with Yoast SEO schema graph
		add_filter( 'wpseo_schema_graph', array( $this, 'inject_into_yoast_schema' ) );

		// Integrate with RankMath SEO schema graph
		add_filter( 'rank_math/json_ld', array( $this, 'inject_into_rankmath_schema' ), 99, 2 );
	}

	/**
	 * Parse post content when saved, looking for Q&A structures
	 * to build an FAQPage JSON-LD array.
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated.
	 */
	public function extract_and_save_faq_schema( $post_id, $post, $update ) {
		// Don't run on autosaves or revisions.
		if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
			return;
		}

		// Check if FAQ schema is enabled in settings
		$settings = get_option( 'wp_agentic_writer_settings', array() );
		$enable_faq_schema = $settings['enable_faq_schema'] ?? false;
		if ( ! $enable_faq_schema ) {
			delete_post_meta( $post_id, '_wpaw_faq_schema' );
			return;
		}

		// Only run for active post types (e.g. post, page).
		$allowed_types = array( 'post', 'page' );
		if ( ! in_array( $post->post_type, $allowed_types, true ) ) {
			return;
		}

		$content = $post->post_content;
		if ( empty( $content ) ) {
			delete_post_meta( $post_id, '_wpaw_faq_schema' );
			return;
		}

		// Strip Gutenberg block HTML comments to make regex matching easier.
		$clean_content = preg_replace( '/<!-- wp:[^>]*-->/s', '', $content );
		$clean_content = preg_replace( '/<!-- \/wp:[^>]*-->/s', '', $clean_content );

		// Regex to find H2, H3, or H4 that contain a question mark, immediately followed by a paragraph.
		// Matches: <hX>Question?</hX> <p>Answer</p>
		$pattern = '/<h([2-4])[^>]*>(.*?\?)<\/h\1>[\s]*<p[^>]*>(.*?)<\/p>/is';
		
		$faqs = array();

		if ( preg_match_all( $pattern, $clean_content, $matches, PREG_SET_ORDER ) ) {
			foreach ( $matches as $match ) {
				$question = wp_strip_all_tags( $match[2] );
				$answer   = wp_strip_all_tags( $match[3] );

				// Basic validation: question must not be too short, answer must have some length.
				if ( strlen( $question ) > 10 && strlen( $answer ) > 15 ) {
					$faqs[] = array(
						'@type'          => 'Question',
						'name'           => $question,
						'acceptedAnswer' => array(
							'@type' => 'Answer',
							'text'  => $answer,
						),
					);
				}
			}
		}

		// Only save schema if we actually detected 1 or more valid FAQ pairs.
		if ( ! empty( $faqs ) ) {
			$schema = array(
				'@context'   => 'https://schema.org',
				'@type'      => 'FAQPage',
				'mainEntity' => $faqs,
			);
			update_post_meta( $post_id, '_wpaw_faq_schema', wp_json_encode( $schema, JSON_UNESCAPED_UNICODE ) );
		} else {
			delete_post_meta( $post_id, '_wpaw_faq_schema' );
		}
	}

	/**
	 * Output the JSON-LD schema script in the frontend <head>.
	 * This serves as a fallback for sites without a primary SEO plugin.
	 */
	public function output_faq_schema_in_head() {
		if ( ! is_singular() ) {
			return;
		}

		// Abort if feature is manually disabled in Settings
		$settings = get_option( 'wp_agentic_writer_settings', array() );
		if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) {
			return;
		}

		// Prevent duplicate output if a major SEO plugin is active and handling our schema
		if ( defined( 'WPSEO_VERSION' ) || class_exists( 'RankMath' ) ) {
			return;
		}

		$post_id = get_the_ID();
		
		// Attempt to fetch schema generated by WP Agentic Writer.
		$schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true );

		if ( ! empty( $schema_json ) ) {
			echo "\n<!-- WP Agentic Writer: Automated FAQ Schema -->\n";
			echo '<script type="application/ld+json">' . $schema_json . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			echo "\n<!-- /WP Agentic Writer Schema -->\n";
		}
	}

	/**
	 * Inject the FAQ schema directly into Yoast SEO's JSON-LD graph.
	 * This prevents disjointed schema and consolidates everything into Yoast's payload.
	 *
	 * @param array $graph The Yoast schema graph array.
	 * @return array
	 */
	public function inject_into_yoast_schema( $graph ) {
		if ( ! is_singular() ) {
			return $graph;
		}

		// Abort if feature is manually disabled in Settings
		$settings = get_option( 'wp_agentic_writer_settings', array() );
		if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) {
			return $graph;
		}

		$post_id = get_the_ID();
		
		// Fetch our pre-computed schema
		$schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true );

		if ( ! empty( $schema_json ) ) {
			$schema_array = json_decode( $schema_json, true );
			if ( is_array( $schema_array ) ) {
				// Yoast specifically expects nodes with an @id property
				$schema_array['@id'] = get_permalink( $post_id ) . '#wpaw-faq';
				
				// Yoast wraps all schemas in a global @context, so we can unset our local one to remain clean
				if ( isset( $schema_array['@context'] ) ) {
					unset( $schema_array['@context'] );
				}
				
				// Append our FAQ schema snippet to Yoast's massive graph
				$graph[] = $schema_array;
			}
		}

		return $graph;
	}

	/**
	 * Inject the FAQ schema directly into RankMath's JSON-LD output.
	 * This prevents disjointed schema and consolidates everything into RankMath's payload.
	 *
	 * @param array  $data   The RankMath JSON-LD data array.
	 * @param object $jsonld The RankMath JsonLd object.
	 * @return array
	 */
	public function inject_into_rankmath_schema( $data, $jsonld ) {
		if ( ! is_singular() ) {
			return $data;
		}

		// Abort if feature is manually disabled in Settings
		$settings = get_option( 'wp_agentic_writer_settings', array() );
		if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) {
			return $data;
		}

		$post_id = get_the_ID();

		// Fetch our pre-computed schema
		$schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true );

		if ( ! empty( $schema_json ) ) {
			$schema_array = json_decode( $schema_json, true );
			if ( is_array( $schema_array ) && ! empty( $schema_array['mainEntity'] ) ) {
				// RankMath expects a keyed array for each schema type
				$data['WPAWFaqPage'] = array(
					'@type'      => 'FAQPage',
					'mainEntity' => $schema_array['mainEntity'],
				);
			}
		}

		return $data;
	}
}
