<?php
/**
 * SEO local pre-check runner.
 *
 * @package SmartyPress_AI_Engine
 * @since 1.0.0
 */

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

/**
 * Runs deterministic language-neutral checks before AI generation.
 */
class SmartyPress_AI_SEO_Local_Pre_Checks extends SmartyPress_AI_SEO_Controller_Service_Base {

	/**
	 * Content strategy registry.
	 *
	 * @var SmartyPress_AI_SEO_Content_Strategy_Registry
	 */
	private $strategy_registry;

	/**
	 * Constructor.
	 *
	 * @param SmartyPress_AI_SEO_Content_Strategy_Registry $strategy_registry Strategy registry.
	 */
	public function __construct( $strategy_registry ) {
		$this->strategy_registry = $strategy_registry;
	}

	/**
	 * Run deterministic checks before AI generation.
	 *
	 * @param array $payload Payload.
	 * @return array
	 */
	public function run( $payload ) {
		$fixed       = array();
		$manual_only = array();
		$needs_ai    = array();
		$tests       = (array) ( $payload['failed_tests'] ?? array() );
		$seo         = (array) ( $payload['seo'] ?? array() );
		$post        = (array) ( $payload['post'] ?? array() );
		$primary     = (string) ( $seo['focus_keywords'][0] ?? $payload['primaryKeyword'] ?? '' );
		$content     = (string) ( $post['content_html'] ?? $post['content_plain_text'] ?? '' );
		$blocks      = (array) ( $post['blocks'] ?? array() );

		foreach ( $tests as $test ) {
			$id            = (string) ( $test['id'] ?? '' );
			$passed        = false;
			$manual_reason = '';

			switch ( $id ) {
				case 'keywordInTitle':
					$passed = $this->text_contains_keyword( $seo['seo_title'] ?? '', $primary );
					break;
				case 'keywordInMetaDescription':
					$passed = $this->text_contains_keyword( $seo['meta_description'] ?? '', $primary );
					break;
				case 'keywordInPermalink':
					$passed = $this->slug_contains_keyword( $seo['slug'] ?? '', $primary );
					break;
				case 'contentHasAssets':
					$passed = $this->content_has_assets( $blocks );
					if ( ! $passed ) {
						$manual_reason = __( 'No media blocks were found. Add a relevant image or video manually.', 'smartypress-ai-engine' );
					}
					break;
				case 'keywordInImageAlt':
					if ( ! $this->content_has_assets( $blocks ) ) {
						$manual_reason = __( 'No image blocks are available for automatic alt text updates.', 'smartypress-ai-engine' );
					}
					break;
				case 'linksHasInternal':
					$passed = $this->content_has_link_type( $content, true );
					if ( ! $passed ) {
						$manual_reason = __( 'No suitable internal links are available for automatic insertion.', 'smartypress-ai-engine' );
					}
					break;
				case 'linksHasExternals':
					$passed = $this->content_has_link_type( $content, false );
					if ( ! $passed ) {
						$manual_reason = __( 'Trusted external URLs cannot be invented automatically.', 'smartypress-ai-engine' );
					}
					break;
				case 'linksNotAllExternals':
					$manual_reason = __( 'External link balance requires a manual editorial decision.', 'smartypress-ai-engine' );
					break;
				case 'contentHasTOC':
					$passed = $this->content_has_explicit_toc_marker( $content, $blocks );
					break;
				case 'hasContentAI':
				case 'keywordNotUsed':
					$manual_reason = __( 'This test depends on SEO plugin state and must be handled manually.', 'smartypress-ai-engine' );
					break;
				case 'isReadable':
				case 'passiveVoice':
				case 'sentenceBeginnings':
				case 'subheadingDistribution':
				case 'wordComplexity':
				case 'contentHasLists':
				case 'contentStructure':
					$manual_reason = __( 'This readability check requires manual editorial review.', 'smartypress-ai-engine' );
					break;
			}

			if ( $passed ) {
				$fixed[] = $id;
			} elseif ( '' !== $manual_reason ) {
				$manual_only[] = array(
					'testId' => $id,
					'reason' => $manual_reason,
				);
			} elseif ( $this->is_automatable_content_test( $id ) ) {
				$needs_ai[] = $test;
			} else {
				$needs_ai[] = $test;
			}
		}

		return array(
			'fixed_tests' => array_values( array_unique( array_filter( $fixed ) ) ),
			'manual_only' => $manual_only,
			'needs_ai'    => $needs_ai,
		);
	}

	/**
	 * Check whether a failed test is handled by a content strategy.
	 *
	 * @param string $test_id Test ID.
	 * @return bool
	 */
	private function is_automatable_content_test( $test_id ) {
		return null !== $this->strategy_registry->get( $test_id );
	}

	/**
	 * Check whether text contains keyword.
	 *
	 * @param string $text    Text.
	 * @param string $keyword Keyword.
	 * @return bool
	 */
	private function text_contains_keyword( $text, $keyword ) {
		$text    = $this->normalize_match_text( $text );
		$keyword = $this->normalize_match_text( $keyword );

		return '' !== $keyword && false !== strpos( $text, $keyword );
	}

	/**
	 * Check whether slug contains keyword transliteration.
	 *
	 * @param string $slug    Slug.
	 * @param string $keyword Keyword.
	 * @return bool
	 */
	private function slug_contains_keyword( $slug, $keyword ) {
		if ( '' === (string) $slug || '' === (string) $keyword ) {
			return false;
		}

		$slug_norm    = preg_replace( '/[^a-z0-9]+/i', '', strtolower( (string) $slug ) );
		$keyword_slug = preg_replace( '/[^a-z0-9]+/i', '', strtolower( sanitize_title( $keyword ) ) );

		return '' !== $keyword_slug && false !== strpos( $slug_norm, $keyword_slug );
	}

	/**
	 * Check for media blocks.
	 *
	 * @param array $blocks Blocks.
	 * @return bool
	 */
	private function content_has_assets( $blocks ) {
		foreach ( (array) $blocks as $block ) {
			$name = $block['name'] ?? '';
			if ( in_array( $name, array( 'core/image', 'core/video', 'core/embed' ), true ) ) {
				return true;
			}

			if ( ! empty( $block['innerBlocks'] ) && $this->content_has_assets( $block['innerBlocks'] ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check content links by type.
	 *
	 * @param string $html     HTML.
	 * @param bool   $internal Whether to check internal links.
	 * @return bool
	 */
	private function content_has_link_type( $html, $internal ) {
		if ( ! preg_match_all( '/<a\s+[^>]*href=["\']([^"\']+)["\']/i', (string) $html, $matches ) ) {
			return false;
		}

		$home_host = wp_parse_url( home_url(), PHP_URL_HOST );
		foreach ( $matches[1] as $url ) {
			$host        = wp_parse_url( $url, PHP_URL_HOST );
			$is_internal = empty( $host ) || $host === $home_host;
			if ( $internal === $is_internal ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Check for explicit TOC block/class markers only.
	 *
	 * Semantic TOC detection belongs to the full-context AI analysis.
	 *
	 * @param string $html   Content HTML.
	 * @param array  $blocks Compact blocks.
	 * @return bool
	 */
	private function content_has_explicit_toc_marker( $html, $blocks ) {
		$toc_markers = array(
			'smartypress-toc',
			'rank-math-toc',
			'wp:rank-math/toc-block',
			'wp-block-rank-math-toc-block',
			'id="rank-math-toc"',
			'table-of-contents',
			'wp:yoast-seo/table-of-contents',
			'wp:aioseo/table-of-contents',
		);

		foreach ( $toc_markers as $marker ) {
			if ( false !== stripos( (string) $html, $marker ) ) {
				return true;
			}
		}

		foreach ( (array) $blocks as $block ) {
			$name     = (string) ( $block['name'] ?? '' );
			$haystack = implode( ' ', array( $name, (string) ( $block['htmlSnippet'] ?? '' ) ) );

			if ( in_array( $name, array( 'rank-math/toc-block', 'yoast-seo/table-of-contents', 'aioseo/table-of-contents' ), true ) ) {
				return true;
			}

			foreach ( $toc_markers as $marker ) {
				if ( false !== stripos( $haystack, $marker ) ) {
					return true;
				}
			}

			if ( ! empty( $block['innerBlocks'] ) && $this->content_has_explicit_toc_marker( '', $block['innerBlocks'] ) ) {
				return true;
			}
		}

		return false;
	}
}
