<?php
/**
 * SEO content optimization fallback helpers.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Applies deterministic keyword placement only as a controlled fallback.
 */
class SmartyPress_AI_Content_Generator_SEO_Content_Optimization_Service {

	/**
	 * Optimize generated content for basic SEO checks when AI metadata repair is unavailable.
	 *
	 * @param string $content Markdown content.
	 * @param string $primary_keyword Primary keyword.
	 * @param array  $context Optimization context.
	 * @return string
	 */
	public function optimize( $content, $primary_keyword, array $context = array() ) {
		$content         = trim( (string) $content );
		$primary_keyword = trim( sanitize_text_field( $primary_keyword ) );

		if ( '' === $content || '' === $primary_keyword ) {
			return $content;
		}

		$source  = sanitize_key( $context['seo_source'] ?? 'fallback' );
		$enabled = apply_filters(
			'smartypress_ai_seo_content_deterministic_fallback_enabled',
			'ai' !== $source,
			$content,
			$primary_keyword,
			$context
		);

		if ( ! $enabled ) {
			return $content;
		}

		$content = $this->ensure_content_begins_with_keyword( $content, $primary_keyword );
		$content = $this->ensure_heading_contains_keyword( $content, $primary_keyword );

		$word_count          = str_word_count( wp_strip_all_tags( $content ) );
		$target_occurrences  = min( 8, max( 5, (int) ceil( max( 1, $word_count ) / 220 ) ) );
		$current_occurrences = $this->count_keyword_occurrences( $content, $primary_keyword );
		$needed_occurrences  = min( 4, max( 0, $target_occurrences - $current_occurrences ) );

		if ( $needed_occurrences > 0 ) {
			$content = $this->add_keyword_to_body_paragraphs( $content, $primary_keyword, $needed_occurrences );
		}

		return $content;
	}

	/**
	 * Ensure the first readable content starts with the focus keyword.
	 *
	 * @param string $content Markdown content.
	 * @param string $primary_keyword Primary keyword.
	 * @return string
	 */
	private function ensure_content_begins_with_keyword( $content, $primary_keyword ) {
		$blocks = preg_split( "/\n\s*\n/", trim( $content ) );
		$blocks = is_array( $blocks ) ? $blocks : array();

		foreach ( $blocks as &$block ) {
			$trimmed = trim( $block );
			if ( '' === $trimmed || preg_match( '/^(#{1,6}|[-*]|\d+\.|>)/', $trimmed ) ) {
				continue;
			}

			if ( 0 !== $this->stripos( $trimmed, $primary_keyword ) ) {
				$block = $primary_keyword . ': ' . ltrim( $block );
			}
			unset( $block );

			return implode( "\n\n", $blocks );
		}
		unset( $block );

		return $primary_keyword . ".\n\n" . $content;
	}

	/**
	 * Ensure at least one H2-H4 heading contains the primary keyword.
	 *
	 * @param string $content Markdown content.
	 * @param string $primary_keyword Primary keyword.
	 * @return string
	 */
	private function ensure_heading_contains_keyword( $content, $primary_keyword ) {
		if ( preg_match_all( '/^(#{2,4})\s+(.+)$/m', $content, $matches, PREG_SET_ORDER ) ) {
			foreach ( $matches as $match ) {
				if ( false !== $this->stripos( $match[2], $primary_keyword ) ) {
					return $content;
				}
			}

			$first_heading = $matches[0][0];
			$replacement  = $matches[0][1] . ' ' . trim( $matches[0][2] ) . ' - ' . $primary_keyword;

			return preg_replace( '/^' . preg_quote( $first_heading, '/' ) . '$/m', $replacement, $content, 1 );
		}

		return '## ' . $primary_keyword . "\n\n" . $content;
	}

	/**
	 * Add exact keyword phrase to early body paragraphs.
	 *
	 * @param string $content Markdown content.
	 * @param string $primary_keyword Primary keyword.
	 * @param int    $needed_occurrences Needed occurrences.
	 * @return string
	 */
	private function add_keyword_to_body_paragraphs( $content, $primary_keyword, $needed_occurrences ) {
		$blocks = preg_split( "/\n\s*\n/", $content );
		$blocks = is_array( $blocks ) ? $blocks : array();
		$added  = 0;

		foreach ( $blocks as &$block ) {
			if ( $added >= $needed_occurrences ) {
				break;
			}

			$trimmed = trim( $block );
			if ( '' === $trimmed || preg_match( '/^(#{1,6}|[-*]|\d+\.|>)/', $trimmed ) || false !== $this->stripos( $trimmed, $primary_keyword ) ) {
				continue;
			}

			$block = rtrim( $block );
			$block = rtrim( $block, " \t\n\r\0\x0B." ) . '. ' . $primary_keyword . '.';
			$added++;
		}
		unset( $block );

		return implode( "\n\n", $blocks );
	}

	/**
	 * Count exact keyword occurrences.
	 *
	 * @param string $content Content.
	 * @param string $primary_keyword Primary keyword.
	 * @return int
	 */
	private function count_keyword_occurrences( $content, $primary_keyword ) {
		$pattern = '/' . preg_quote( $primary_keyword, '/' ) . '/iu';
		return (int) preg_match_all( $pattern, $content );
	}

	/**
	 * Case-insensitive substring search with mbstring fallback.
	 *
	 * @param string $haystack Haystack.
	 * @param string $needle Needle.
	 * @return int|false
	 */
	private function stripos( $haystack, $needle ) {
		if ( function_exists( 'mb_stripos' ) ) {
			return mb_stripos( $haystack, $needle );
		}

		return stripos( $haystack, $needle );
	}
}
