<?php
/**
 * AEO / Quick Answer content injection helpers.
 *
 * Prefers native seovault/quick-answer Gutenberg blocks. Legacy plain
 * paragraph insertion remains only when the Quick Answer block is unavailable,
 * and is marked deprecated in the API response.
 *
 * @package SEOvaultSEO\Api
 */

namespace SEOvaultSEO\Api;

use SEOvaultSEO\Blocks\QuickAnswerBlock;
use SEOvaultSEO\Blocks\QuickAnswerBlockAttributeSanitizer;

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

/**
 * Insert or update Quick Answer content for AEO inject endpoints.
 */
class QuickAnswerAeoInjector {

	const MODE_QUICK_ANSWER_BLOCK = 'quick_answer_block';
	const MODE_LEGACY_PLAIN       = 'legacy_plain';
	const DEPRECATION_LEGACY      = 'legacy_plain_quick_answer_insertion';

	/**
	 * Whether the native Quick Answer block is available for injection.
	 *
	 * @return bool
	 */
	public static function supports_quick_answer_block() {
		if ( ! function_exists( 'register_block_type' ) ) {
			return false;
		}
		if ( class_exists( '\WP_Block_Type_Registry' ) ) {
			$registry = \WP_Block_Type_Registry::get_instance();
			if ( $registry && $registry->is_registered( QuickAnswerBlock::BLOCK_NAME ) ) {
				return true;
			}
		}
		// Block class present is enough for serialization even before registry warm-up.
		return class_exists( QuickAnswerBlock::class ) && class_exists( QuickAnswerBlockAttributeSanitizer::class );
	}

	/**
	 * Build serialized Gutenberg block markup for a snippet.
	 *
	 * @param string $snippet Plain answer text.
	 * @param string $source  Diagnostic source token.
	 * @return string Empty when answer sanitizes to empty.
	 */
	public static function build_block_markup( $snippet, $source = 'aeo' ) {
		$sanitized = QuickAnswerBlockAttributeSanitizer::sanitize(
			array(
				'label'  => 'Quick answer',
				'answer' => $snippet,
				'source' => $source,
			)
		);
		$attrs = $sanitized['attributes'];
		if ( '' === trim( (string) $attrs['answer'] ) ) {
			return '';
		}

		$visible = QuickAnswerBlock::build_static_markup( $attrs );
		if ( '' === trim( $visible ) ) {
			return '';
		}

		$json = wp_json_encode( $attrs );
		if ( ! is_string( $json ) || '' === $json ) {
			$json = '{}';
		}

		return '<!-- wp:seovault/quick-answer ' . $json . ' -->' . "\n" . $visible . "\n" . '<!-- /wp:seovault/quick-answer -->';
	}

	/**
	 * Find the first seovault/quick-answer block range in post content.
	 *
	 * @param string $content Post content.
	 * @return array{start:int,end:int}|null
	 */
	public static function find_existing_block_range( $content ) {
		$source = (string) $content;
		$open   = '<!-- wp:seovault/quick-answer';
		$close  = '<!-- /wp:seovault/quick-answer -->';
		$start  = strpos( $source, $open );
		if ( false === $start ) {
			return null;
		}
		$close_at = strpos( $source, $close, $start );
		if ( false === $close_at ) {
			return null;
		}
		return array(
			'start' => $start,
			'end'   => $close_at + strlen( $close ),
		);
	}

	/**
	 * Index after the first </p>, else 0 (prepend).
	 *
	 * @param string $content Post content.
	 * @return int
	 */
	public static function find_insertion_index( $content ) {
		$source   = (string) $content;
		$first_p  = stripos( $source, '</p>' );
		if ( false !== $first_p ) {
			return $first_p + strlen( '</p>' );
		}
		return 0;
	}

	/**
	 * Apply Quick Answer block upsert or legacy plain insert.
	 *
	 * @param string $content Existing post content.
	 * @param string $snippet Snippet text.
	 * @param array  $options {
	 *     @type bool   $force_legacy Force legacy plain path (tests / old connectors).
	 *     @type string $source       Diagnostic source.
	 * }
	 * @return array{
	 *   ok:bool,
	 *   content:string,
	 *   mode:string,
	 *   deprecated:bool,
	 *   deprecation:?string,
	 *   updated:bool,
	 *   code?:string,
	 *   message:string
	 * }
	 */
	public static function apply( $content, $snippet, array $options = array() ) {
		$snippet = is_string( $snippet ) ? trim( $snippet ) : '';
		if ( '' === $snippet ) {
			return array(
				'ok'          => false,
				'content'     => (string) $content,
				'mode'        => '',
				'deprecated'  => false,
				'deprecation' => null,
				'updated'     => false,
				'code'        => 'missing_snippet',
				'message'     => 'Snippet text is required.',
			);
		}

		$force_legacy = ! empty( $options['force_legacy'] );
		$source       = isset( $options['source'] ) ? (string) $options['source'] : 'aeo';
		$use_block    = ! $force_legacy && self::supports_quick_answer_block();

		if ( $use_block ) {
			$markup = self::build_block_markup( $snippet, $source );
			if ( '' === $markup ) {
				return array(
					'ok'          => false,
					'content'     => (string) $content,
					'mode'        => self::MODE_QUICK_ANSWER_BLOCK,
					'deprecated'  => false,
					'deprecation' => null,
					'updated'     => false,
					'code'        => 'empty_after_sanitize',
					'message'     => 'Snippet was empty after sanitization.',
				);
			}

			$existing = self::find_existing_block_range( $content );
			if ( null !== $existing ) {
				$next = substr( $content, 0, $existing['start'] ) . $markup . substr( $content, $existing['end'] );
				return array(
					'ok'          => true,
					'content'     => $next,
					'mode'        => self::MODE_QUICK_ANSWER_BLOCK,
					'deprecated'  => false,
					'deprecation' => null,
					'updated'     => true,
					'message'     => 'Quick Answer block updated.',
				);
			}

			$at   = self::find_insertion_index( $content );
			$next = substr( $content, 0, $at ) . "\n" . $markup . "\n" . substr( $content, $at );
			return array(
				'ok'          => true,
				'content'     => $next,
				'mode'        => self::MODE_QUICK_ANSWER_BLOCK,
				'deprecated'  => false,
				'deprecation' => null,
				'updated'     => false,
				'message'     => 'Quick Answer block injected successfully.',
			);
		}

		// Legacy plain paragraph path (unsupported old connectors).
		$first_words_raw  = implode( ' ', array_slice( preg_split( '/\s+/', wp_strip_all_tags( (string) $content ), -1, PREG_SPLIT_NO_EMPTY ), 0, 300 ) );
		$snippet_stripped = wp_strip_all_tags( $snippet );
		if ( '' !== $first_words_raw && '' !== $snippet_stripped ) {
			similar_text( mb_strtolower( $first_words_raw ), mb_strtolower( $snippet_stripped ), $pct );
			if ( $pct > 55 ) {
				return array(
					'ok'          => false,
					'content'     => (string) $content,
					'mode'        => self::MODE_LEGACY_PLAIN,
					'deprecated'  => true,
					'deprecation' => self::DEPRECATION_LEGACY,
					'updated'     => false,
					'code'        => 'ALREADY_EXISTS',
					'message'     => 'A similar snippet already exists near the top of the content.',
				);
			}
		}

		$wrapped = "\n<p>" . esc_html( $snippet ) . "</p>\n";
		$at      = self::find_insertion_index( $content );
		$next    = substr( $content, 0, $at ) . $wrapped . substr( $content, $at );

		return array(
			'ok'          => true,
			'content'     => $next,
			'mode'        => self::MODE_LEGACY_PLAIN,
			'deprecated'  => true,
			'deprecation' => self::DEPRECATION_LEGACY,
			'updated'     => false,
			'message'     => 'AEO snippet injected with legacy plain insertion (deprecated).',
		);
	}
}
