<?php
/**
 * AI Readiness Engine — structure & entity pillars for pre-publish editorial scoring.
 *
 * @package Newsroom_AI_Assistant
 */

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

if ( ! function_exists( 'nros_readiness_count_words' ) ) {
	/**
	 * Unicode-safe word count (Greek/multibyte). Prefer schema helper when loaded.
	 *
	 * @param string $text Plain text.
	 * @return int
	 */
	function nros_readiness_count_words( $text ) {
		if ( function_exists( 'nros_schema_count_plain_words' ) ) {
			return nros_schema_count_plain_words( $text );
		}
		$text = trim( (string) $text );
		if ( $text === '' ) {
			return 0;
		}
		$parts = preg_split( '/\s+/u', $text, -1, PREG_SPLIT_NO_EMPTY );
		return is_array( $parts ) ? count( $parts ) : 0;
	}
}

if ( ! function_exists( 'nros_readiness_count_answer_blocks' ) ) {
	/**
	 * Heuristic "answer blocks": headings, lists, FAQ markup.
	 *
	 * @param string $content Post HTML.
	 * @return int
	 */
	function nros_readiness_count_answer_blocks( $content ) {
		$content = (string) $content;
		if ( $content === '' ) {
			return 0;
		}
		$headings = (int) preg_match_all( '/<h[23]\b[^>]*>/i', $content );
		$lists    = (int) preg_match_all( '/<(ul|ol)\b[^>]*>/i', $content );
		$faq      = (bool) preg_match( '/(newsai-faq|schema\.org\/FAQPage|itemtype=["\']https?:\/\/schema\.org\/FAQPage)/i', $content );
		$geo      = (bool) preg_match( '/nros-geo-summary/i', $content );
		$blocks   = $headings + $lists;
		if ( $faq ) {
			$blocks += 2;
		}
		if ( $geo ) {
			++$blocks;
		}
		return max( 0, $blocks );
	}
}

if ( ! function_exists( 'nros_readiness_semantic_text_chunks' ) ) {
	/**
	 * Split post HTML into text chunks at block-level semantic boundaries.
	 *
	 * @param string $content Post HTML.
	 * @return string[]
	 */
	function nros_readiness_semantic_text_chunks( $content ) {
		$content = (string) $content;
		if ( trim( $content ) === '' ) {
			return [];
		}

		if ( function_exists( 'strip_shortcodes' ) ) {
			$content = strip_shortcodes( $content );
		}

		// Classic/block HTML is often one line — split before stripping tags.
		$marked = preg_replace(
			'/<\/?(?:h[1-6]|p|li|section|aside|article|blockquote|div|ul|ol|figure|figcaption|table|tr|td|th|dl|dt|dd|hr|br)\b[^>]*>/iu',
			"\n\n",
			$content
		);
		if ( ! is_string( $marked ) ) {
			$marked = $content;
		}

		$text = wp_strip_all_tags( $marked );
		$text = preg_replace( '/[ \t]+/u', ' ', $text );
		$text = preg_replace( '/\n{3,}/u', "\n\n", (string) $text );
		$parts = preg_split( '/\n\s*\n+/u', trim( $text ) );
		if ( ! is_array( $parts ) ) {
			return trim( $text ) === '' ? [] : [ trim( $text ) ];
		}

		$out = [];
		foreach ( $parts as $chunk ) {
			$chunk = trim( (string) $chunk );
			if ( $chunk !== '' ) {
				$out[] = $chunk;
			}
		}
		return $out;
	}
}

if ( ! function_exists( 'nros_readiness_largest_chunk_words' ) ) {
	/**
	 * @param string $content Post HTML.
	 * @return int
	 */
	function nros_readiness_largest_chunk_words( $content ) {
		$max = 0;
		foreach ( nros_readiness_semantic_text_chunks( $content ) as $chunk ) {
			$wc  = nros_readiness_count_words( $chunk );
			$max = max( $max, $wc );
		}
		return (int) $max;
	}
}

if ( ! function_exists( 'nros_readiness_content_has_key_takeaways' ) ) {
	/**
	 * Detect Key Takeaways / quick summary blocks (plugin markup or common editorial patterns).
	 *
	 * @param string $content Post HTML.
	 * @return bool
	 */
	function nros_readiness_content_has_key_takeaways( $content ) {
		$content = (string) $content;
		if ( $content === '' ) {
			return false;
		}
		// Plugin Key Takeaways block, GEO summary lead, or common “quick summary” wrappers.
		if ( preg_match( '/(newsai-key-points|nros-geo-summary|editorial-summary|key-takeaways|quick-summary)/i', $content ) ) {
			return true;
		}
		// Visible labels editors often use in Classic HTML.
		return (bool) preg_match( '/(Γρήγορη\s+Σύνοψη|Βασικά\s+Σημεία|Key\s+Takeaways|TL;?DR)/iu', $content );
	}
}

if ( ! function_exists( 'nros_readiness_content_has_faq' ) ) {
	/**
	 * Detect FAQ blocks: plugin FAQ microdata, or semantic Q&A (dl/dt/dd, FAQ heading + pairs).
	 *
	 * @param string $content Post HTML.
	 * @return bool
	 */
	function nros_readiness_content_has_faq( $content ) {
		$content = (string) $content;
		if ( $content === '' ) {
			return false;
		}
		if ( preg_match( '/(newsai-faq|schema\.org\/FAQPage|itemtype=["\']https?:\/\/schema\.org\/FAQPage)/i', $content ) ) {
			return true;
		}
		// Classic Editor FAQ: heading + definition list with real Q/A pairs.
		if ( preg_match( '/<(h[1-6])[^>]*>[^<]*(FAQ|Συχνές\s+Ερωτήσεις|Frequently\s+Asked)[^<]*<\/\1>/iu', $content )
			&& preg_match_all( '/<dt\b/i', $content ) >= 2
			&& preg_match_all( '/<dd\b/i', $content ) >= 2 ) {
			return true;
		}
		// Bare dl with multiple Q/A pairs (question-like dt ending in ; or ?).
		if ( preg_match_all( '/<dt\b[^>]*>.*?<\/dt>\s*<dd\b/is', $content ) >= 2 ) {
			return true;
		}
		return false;
	}
}

if ( ! function_exists( 'nros_readiness_entity_pillar_pct' ) ) {
	/**
	 * Normalize entity bucket to 0–100 for composite score.
	 *
	 * @param array<string,mixed> $ent_raw From nros_discover_score_entities().
	 * @param int                 $post_id Post ID.
	 * @return int
	 */
	function nros_readiness_entity_pillar_pct( array $ent_raw, $post_id ) {
		$max = isset( $ent_raw['max'] ) ? (int) $ent_raw['max'] : 15;
		$score = isset( $ent_raw['score'] ) ? (int) $ent_raw['score'] : 0;
		if ( $max < 1 ) {
			return 0;
		}
		$pct = (int) round( ( $score / $max ) * 100 );
		if ( function_exists( 'newsai_ai_get_post_analysis_for_discover' ) ) {
			$ai = newsai_ai_get_post_analysis_for_discover( (int) $post_id );
			if ( is_array( $ai ) && ( ! empty( $ai['primary_entity'] ) || ! empty( $ai['entities'] ) ) ) {
				$pct = min( 100, $pct + 10 );
			}
		}
		return max( 0, min( 100, $pct ) );
	}
}

if ( ! function_exists( 'nros_readiness_count_external_citations' ) ) {
	/**
	 * External source links + citation-friendly markup.
	 *
	 * @param string $content Post HTML.
	 * @return array{external:int,has_geo:bool,has_blockquote:bool,has_cite:bool}
	 */
	function nros_readiness_count_external_citations( $content ) {
		$content = (string) $content;
		$home    = wp_parse_url( home_url( '/' ), PHP_URL_HOST );
		$home    = is_string( $home ) ? strtolower( $home ) : '';
		$external = 0;
		if ( preg_match_all( '/<a\b[^>]*href=["\']([^"\']+)["\'][^>]*>/i', $content, $m ) ) {
			foreach ( (array) $m[1] as $href ) {
				$href = (string) $href;
				if ( $href === '' || strpos( $href, '#' ) === 0 ) {
					continue;
				}
				$host = wp_parse_url( $href, PHP_URL_HOST );
				if ( ! is_string( $host ) || $host === '' ) {
					continue;
				}
				if ( $home !== '' && strtolower( $host ) === $home ) {
					continue;
				}
				$external++;
			}
		}
		return [
			'external'       => $external,
			'has_geo'        => (bool) preg_match( '/nros-geo-summary/i', $content ),
			'has_blockquote' => (bool) preg_match( '/<blockquote\b/i', $content ),
			'has_cite'       => (bool) preg_match( '/<(cite|figcaption)\b/i', $content ),
		];
	}
}

if ( ! function_exists( 'nros_readiness_analyze_citation' ) ) {
	/**
	 * AI Citation pillar — attributable sources & extractable Q&A.
	 *
	 * @param string $content Post HTML.
	 * @return array{score:int,pct:int,external:int,has_geo:bool,actions:array}
	 */
	function nros_readiness_analyze_citation( $content ) {
		$pack     = nros_readiness_count_external_citations( $content );
		$external = (int) $pack['external'];
		$has_geo  = ! empty( $pack['has_geo'] );
		$score    = 0;
		$actions  = [];

		if ( $has_geo ) {
			$score += 35;
		} else {
			$actions[] = [
				'prio'   => 'MEDIUM',
				'impact' => 9,
				'domain' => 'citation',
				'text'   => __( 'Add a GEO / AI summary block with cited Q&A for AI Overviews.', 'newsroom-ai-assistant' ),
			];
		}
		if ( $external >= 2 ) {
			$score += 35;
		} elseif ( $external === 1 ) {
			$score += 20;
			$actions[] = [
				'prio'   => 'LOW',
				'impact' => 14,
				'domain' => 'citation',
				'text'   => __( 'Add one more external source link to strengthen attribution.', 'newsroom-ai-assistant' ),
			];
		} else {
			$actions[] = [
				'prio'   => 'MEDIUM',
				'impact' => 10,
				'domain' => 'citation',
				'text'   => __( 'Link to primary sources — AI systems reward attributable reporting.', 'newsroom-ai-assistant' ),
			];
		}
		if ( ! empty( $pack['has_blockquote'] ) || ! empty( $pack['has_cite'] ) ) {
			$score += 15;
		}
		if ( nros_readiness_content_has_faq( $content ) ) {
			$score += 15;
		}

		$score = max( 0, min( 100, $score ) );
		return [
			'score'    => $score,
			'pct'      => $score,
			'external' => $external,
			'has_geo'  => $has_geo,
			'actions'  => $actions,
		];
	}
}

if ( ! function_exists( 'nros_readiness_analyze_information_gain' ) ) {
	/**
	 * Information Gain — depth signals beyond generic rewrites.
	 *
	 * @param string $content Post HTML.
	 * @param int    $post_id Post ID.
	 * @return array{score:int,pct:int,word_count:int,stats:int,actions:array}
	 */
	function nros_readiness_analyze_information_gain( $content, $post_id = 0 ) {
		$plain = wp_strip_all_tags( (string) $content );
		$words = nros_readiness_count_words( $plain );
		$stats = 0;
		if ( $plain !== '' ) {
			$stats = (int) preg_match_all( '/\b\d{1,3}(?:[.,]\d+)?%?\b/u', $plain );
		}
		$has_quotes = (bool) preg_match( '/[«""][^«""]{12,}[»""]/u', $plain );
		$has_timeline = function_exists( 'nros_discover_content_has_story_cluster' )
			? nros_discover_content_has_story_cluster( (string) $content )
			: false;
		$has_takeaways = nros_readiness_content_has_key_takeaways( $content );

		$score   = 0;
		$actions = [];

		if ( $words >= 600 ) {
			$score += 30;
		} elseif ( $words >= 350 ) {
			$score += 18;
		} elseif ( $words >= 180 ) {
			$score += 8;
			$actions[] = [
				'prio'   => 'MEDIUM',
				'impact' => 11,
				'domain' => 'information_gain',
				'text'   => __( 'Expand with original reporting detail — thin articles score lower for information gain.', 'newsroom-ai-assistant' ),
			];
		} else {
			$actions[] = [
				'prio'   => 'HIGH',
				'impact' => 6,
				'domain' => 'information_gain',
				'text'   => __( 'Article is very short — add context, data, or on-the-ground detail.', 'newsroom-ai-assistant' ),
			];
		}

		if ( $stats >= 3 ) {
			$score += 25;
		} elseif ( $stats >= 1 ) {
			$score += 12;
		} else {
			$actions[] = [
				'prio'   => 'LOW',
				'impact' => 15,
				'domain' => 'information_gain',
				'text'   => __( 'Add concrete numbers (dates, amounts, percentages) where relevant.', 'newsroom-ai-assistant' ),
			];
		}

		if ( $has_quotes ) {
			$score += 15;
		}
		if ( $has_timeline ) {
			$score += 15;
		} elseif ( $words >= 300 ) {
			$actions[] = [
				'prio'   => 'LOW',
				'impact' => 16,
				'domain' => 'information_gain',
				'text'   => __( 'A timeline or chronology adds information gain for developing stories.', 'newsroom-ai-assistant' ),
			];
		}
		if ( $has_takeaways ) {
			$score += 15;
		}

		$post_id = (int) $post_id;
		if ( $post_id > 0 && function_exists( 'newsai_ai_get_post_analysis_for_discover' ) ) {
			$ai = newsai_ai_get_post_analysis_for_discover( $post_id );
			if ( is_array( $ai ) && ! empty( $ai['entities'] ) && is_array( $ai['entities'] ) && count( $ai['entities'] ) >= 3 ) {
				$score = min( 100, $score + 10 );
			}
		}

		$score = max( 0, min( 100, $score ) );
		return [
			'score'      => $score,
			'pct'        => $score,
			'word_count' => $words,
			'stats'      => $stats,
			'has_quotes' => $has_quotes,
			'actions'    => $actions,
		];
	}
}

if ( ! function_exists( 'nros_readiness_analyze_structure' ) ) {
	/**
	 * Deterministic structure pillar for AI Overviews / chunk-friendly markup.
	 *
	 * @param string $content           Post HTML.
	 * @param int    $post_id           Post ID.
	 * @param bool   $auto_timeline_on  Auto-timeline active for this edit.
	 * @return array<string,mixed>
	 */
	function nros_readiness_analyze_structure( $content, $post_id = 0, $auto_timeline_on = false ) {
		$content = (string) $content;
		$max     = 100;
		$score   = 0;
		$checks  = [];
		$actions = [];

		$largest_chunk = nros_readiness_largest_chunk_words( $content );
		if ( $largest_chunk <= 300 ) {
			$score += 30;
			$checks['chunk'] = 'ok';
		} elseif ( $largest_chunk <= 420 ) {
			$score += 15;
			$checks['chunk'] = 'warn';
			$actions[]       = [
				'prio'   => 'MEDIUM',
				'impact' => 7,
				'domain' => 'structure',
				'text'   => sprintf(
					/* translators: %d: word count */
					__( 'Largest text block has %d words — split into shorter paragraphs (aim for under 300).', 'newsroom-ai-assistant' ),
					$largest_chunk
				),
			];
		} else {
			$checks['chunk'] = 'bad';
			$actions[]       = [
				'prio'   => 'HIGH',
				'impact' => 3,
				'domain' => 'structure',
				'text'   => sprintf(
					/* translators: %d: word count */
					__( 'Largest semantic chunk has %d words — break it up for AI extraction.', 'newsroom-ai-assistant' ),
					$largest_chunk
				),
			];
		}

		$answer_blocks = nros_readiness_count_answer_blocks( $content );
		$target_blocks = 4;
		if ( $answer_blocks >= $target_blocks ) {
			$score += 25;
			$checks['answers'] = 'ok';
		} elseif ( $answer_blocks >= 2 ) {
			$score += 12;
			$checks['answers'] = 'warn';
			$missing           = $target_blocks - $answer_blocks;
			$actions[]         = [
				'prio'   => 'MEDIUM',
				'impact' => 8,
				'domain' => 'structure',
				'text'   => sprintf(
					/* translators: %d: number of blocks */
					_n( '%d answer block missing (add H2/H3, lists, or FAQ).', '%d answer blocks missing (add H2/H3, lists, or FAQ).', $missing, 'newsroom-ai-assistant' ),
					$missing
				),
			];
		} else {
			$checks['answers'] = 'bad';
			$actions[]         = [
				'prio'   => 'HIGH',
				'impact' => 4,
				'domain' => 'structure',
				'text'   => sprintf(
					/* translators: 1: found count, 2: target count */
					__( 'Only %1$d answer-style blocks found — aim for at least %2$d (headings, lists, FAQ).', 'newsroom-ai-assistant' ),
					$answer_blocks,
					$target_blocks
				),
			];
		}

		$has_timeline = function_exists( 'nros_discover_content_has_story_cluster' )
			? nros_discover_content_has_story_cluster( $content )
			: false;
		if ( $has_timeline || $auto_timeline_on ) {
			$score += 20;
			$checks['timeline'] = 'ok';
		} else {
			$checks['timeline'] = 'missing';
			$actions[]          = [
				'prio'   => 'LOW',
				'impact' => 16,
				'domain' => 'structure',
				'text'   => __( 'Add a Timeline or story cluster for developing news.', 'newsroom-ai-assistant' ),
			];
		}

		if ( nros_readiness_content_has_key_takeaways( $content ) ) {
			$score += 15;
			$checks['takeaways'] = 'ok';
		} else {
			$checks['takeaways'] = 'missing';
			$actions[]           = [
				'prio'   => 'MEDIUM',
				'impact' => 10,
				'domain' => 'structure',
				'text'   => __( 'Article has no Key Takeaways — add a skim-friendly bullet block.', 'newsroom-ai-assistant' ),
			];
		}

		if ( nros_readiness_content_has_faq( $content ) ) {
			$score += 10;
			$checks['faq'] = 'ok';
		} else {
			$checks['faq'] = 'missing';
			if ( get_option( 'newsai_editor_faq_enabled', 'no' ) === 'yes' ) {
				$actions[] = [
					'prio'   => 'LOW',
					'impact' => 17,
					'domain' => 'structure',
					'text'   => __( 'Consider an FAQ block for question-style search queries.', 'newsroom-ai-assistant' ),
				];
			}
		}

		return [
			'score'           => min( $max, $score ),
			'max'             => $max,
			'pct'             => max( 0, min( 100, (int) round( ( min( $max, $score ) / $max ) * 100 ) ) ),
			'largest_chunk'   => $largest_chunk,
			'answer_blocks'   => $answer_blocks,
			'answer_target'   => $target_blocks,
			'has_timeline'    => $has_timeline || $auto_timeline_on,
			'has_takeaways'   => nros_readiness_content_has_key_takeaways( $content ),
			'has_faq'         => nros_readiness_content_has_faq( $content ),
			'checks'          => $checks,
			'actions'         => $actions,
		];
	}
}

if ( ! function_exists( 'nros_readiness_analyze_structure_pack' ) ) {
	/**
	 * Structure pillar + Phase 2 citation / information gain blend.
	 *
	 * @param string $content          Post HTML.
	 * @param int    $post_id          Post ID.
	 * @param bool   $auto_timeline_on Auto-timeline active.
	 * @return array<string, mixed>
	 */
	function nros_readiness_analyze_structure_pack( $content, $post_id = 0, $auto_timeline_on = false ) {
		$structure = nros_readiness_analyze_structure( $content, $post_id, $auto_timeline_on );
		$citation  = nros_readiness_analyze_citation( $content );
		$igain     = nros_readiness_analyze_information_gain( $content, $post_id );

		$structure_pct = (int) ( $structure['pct'] ?? 0 );
		$citation_pct  = (int) ( $citation['pct'] ?? 0 );
		$igain_pct     = (int) ( $igain['pct'] ?? 0 );
		$blended       = (int) round( ( $structure_pct * 0.55 ) + ( $citation_pct * 0.20 ) + ( $igain_pct * 0.25 ) );

		$actions = array_merge(
			(array) ( $structure['actions'] ?? [] ),
			(array) ( $citation['actions'] ?? [] ),
			(array) ( $igain['actions'] ?? [] )
		);

		return array_merge(
			$structure,
			[
				'pct'              => max( 0, min( 100, $blended ) ),
				'structure_pct'    => $structure_pct,
				'citation'         => $citation,
				'citation_pct'     => $citation_pct,
				'information_gain' => $igain,
				'igain_pct'        => $igain_pct,
				'word_count'       => (int) ( $igain['word_count'] ?? 0 ),
				'external_sources' => (int) ( $citation['external'] ?? 0 ),
				'has_geo_summary'  => ! empty( $citation['has_geo'] ),
				'actions'          => $actions,
			]
		);
	}
}

if ( ! function_exists( 'nros_readiness_composite_score' ) ) {
	/**
	 * @param int $search_pct    Focus / search pillar 0–100.
	 * @param int $discover_pct  Discover pillar 0–100.
	 * @param int $entity_pct    Entity pillar 0–100.
	 * @param int $structure_pct Structure pillar 0–100.
	 * @return int
	 */
	function nros_readiness_composite_score( $search_pct, $discover_pct, $entity_pct, $structure_pct ) {
		$weights = apply_filters(
			'nros_readiness_composite_weights',
			[
				'search'    => 0.30,
				'discover'  => 0.30,
				'entity'    => 0.20,
				'structure' => 0.20,
			]
		);
		$total = ( (int) $search_pct * (float) ( $weights['search'] ?? 0.30 ) )
			+ ( (int) $discover_pct * (float) ( $weights['discover'] ?? 0.30 ) )
			+ ( (int) $entity_pct * (float) ( $weights['entity'] ?? 0.20 ) )
			+ ( (int) $structure_pct * (float) ( $weights['structure'] ?? 0.20 ) );
		return max( 0, min( 100, (int) round( $total ) ) );
	}
}
