<?php
/**
 * Prompt Processor Class
 *
 * Handles processing of prompts with variable placeholders
 *
 * @package SmartyPress_AI_Engine
 * @since 1.0.0
 */

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

/**
 * Prompt Processor class
 */
class SmartyPress_AI_Prompt_Processor {

	/**
	 * Process prompt with variable replacement
	 *
	 * @param string $prompt Prompt template with variables in curly braces.
	 * @param array  $variables Variables to replace.
	 * @return string Processed prompt.
	 */
	public static function process_prompt( $prompt, $variables = array() ) {
		if ( empty( $prompt ) ) {
			return '';
		}

		$settings         = get_option( 'smartypress_ai_settings', array() );
		$prompt_variables = wp_parse_args(
			$settings['prompt_variables'] ?? array(),
			array(
				'writing_tone'           => 'neutral',
				'writing_style'          => 'informative',
				'number_of_sections'     => '3',
				'paragraphs_per_section' => '3',
			)
		);

		// Default variables
		$default_variables = array(
			'LANGUAGE'              => 'English',
			'TOPIC'                 => '',
			'CONTEXT'               => '',
			'TITLE'                 => '',
			'SECTIONS'              => '',
			'CONTENT'               => '',
			'EXCERPT'               => '',
			'WRITING_TONE'          => $prompt_variables['writing_tone'],
			'WRITING_STYLE'         => $prompt_variables['writing_style'],
			'NUMBER_OF_SECTIONS'    => (string) $prompt_variables['number_of_sections'],
			'PARAGRAPHS_PER_SECTION' => (string) $prompt_variables['paragraphs_per_section'],
		);

		$reserved_runtime_variables = array( 'LANGUAGE', 'TOPIC', 'CONTEXT', 'TITLE', 'SECTIONS', 'CONTENT', 'EXCERPT', 'SECTIONS_COUNT' );
		foreach ( $prompt_variables as $key => $value ) {
			$variable_key = strtoupper( sanitize_key( $key ) );
			$variable_key = str_replace( '-', '_', $variable_key );

			if ( '' === $variable_key ) {
				continue;
			}

			if ( isset( $default_variables[ $variable_key ] ) || ! in_array( $variable_key, $reserved_runtime_variables, true ) ) {
				$default_variables[ $variable_key ] = (string) $value;
			}
		}

		// Merge with provided variables
		$variables = wp_parse_args( $variables, $default_variables );

		// Add alias for SECTIONS_COUNT -> NUMBER_OF_SECTIONS
		$variables['SECTIONS_COUNT'] = $variables['NUMBER_OF_SECTIONS'];

		// Replace variables in curly braces
		foreach ( $variables as $key => $value ) {
			$placeholder = '{' . $key . '}';
			$prompt     = str_replace( $placeholder, $value, $prompt );
		}

		return $prompt;
	}

	/**
	 * Extract variables from prompt
	 *
	 * @param string $prompt Prompt template.
	 * @return array Array of variable names found in prompt.
	 */
	public static function extract_variables( $prompt ) {
		$variables = array();
		
		// Match all variables in curly braces
		preg_match_all( '/\{([A-Z_]+)\}/', $prompt, $matches );
		
		if ( ! empty( $matches[1] ) ) {
			$variables = array_unique( $matches[1] );
		}
		
		return $variables;
	}

	/**
	 * Validate prompt variables
	 *
	 * @param string $prompt Prompt template.
	 * @param array  $required_variables Required variable names.
	 * @return array Validation result with success and missing variables.
	 */
	public static function validate_prompt( $prompt, $required_variables = array() ) {
		$found_variables = self::extract_variables( $prompt );
		$missing         = array_diff( $required_variables, $found_variables );
		
		return array(
			'success'        => empty( $missing ),
			'missing'        => $missing,
			'found_variables' => $found_variables,
		);
	}

	/**
	 * Clean generated content by extracting content between markers
	 *
	 * Extracts content between ===CONTENT_START=== and ===CONTENT_END=== markers.
	 * If markers are not found, returns the original content (for backward compatibility).
	 *
	 * @param string $content Generated content.
	 * @param string $title Optional title (not used in marker-based approach, kept for compatibility).
	 * @return string Cleaned content.
	 */
	public static function clean_generated_content( $content, $title = '' ) {
		if ( empty( $content ) ) {
			return $content;
		}

		$content = trim( $content );

		// Define markers
		$start_marker = '===CONTENT_START===';
		$end_marker   = '===CONTENT_END===';

		// Try to find content between markers
		$start_pos = stripos( $content, $start_marker );
		$end_pos   = stripos( $content, $end_marker );

		if ( $start_pos !== false && $end_pos !== false && $end_pos > $start_pos ) {
			// Extract content between markers
			$start_pos += strlen( $start_marker );
			$extracted = substr( $content, $start_pos, $end_pos - $start_pos );
			$content   = trim( $extracted );
		} else {
			// Markers not found - try to clean up common issues as fallback
			// Remove any leading/trailing markers that might be incomplete
			$content = preg_replace( '/^===CONTENT_(?:START|END)===\s*/i', '', $content );
			$content = preg_replace( '/\s*===CONTENT_(?:START|END)===$/i', '', $content );
			$content = trim( $content );
		}

		// Clean up multiple empty lines
		$content = preg_replace( '/\n{3,}/', "\n\n", $content );

		// Trim whitespace
		$content = trim( $content );

		return $content;
	}

	/**
	 * Convert Markdown to HTML
	 *
	 * Converts Markdown syntax to HTML so that rawHandler in Gutenberg
	 * can properly create blocks (headings, paragraphs, lists, etc.).
	 *
	 * Uses Parsedown library for reliable parsing when available,
	 * with a regex-based fallback for environments without Parsedown.
	 *
	 * @param string $content Markdown content.
	 * @return string HTML content.
	 */
	public static function markdown_to_html( $content ) {
		if ( empty( $content ) ) {
			return $content;
		}

		// Use Parsedown for reliable markdown parsing.
		if ( class_exists( 'Parsedown' ) ) {
			$parsedown = new Parsedown();
			$parsedown->setSafeMode( true );
			$html = $parsedown->text( $content );

			// Post-processing: convert standalone bold paragraphs to headings.
			// AI models often use **Bold Text** as section headers instead of ## Heading.
			// Pattern: <p><strong>Only bold text here</strong></p> -> <h2>text</h2>
			$html = preg_replace(
				'/<p><strong>([^<]+)<\/strong><\/p>/',
				'<h2>$1</h2>',
				$html
			);

			return $html;
		}

		// Fallback: regex-based implementation for environments without Parsedown.
		return self::markdown_to_html_fallback( $content );
	}

	/**
	 * Fallback regex-based Markdown to HTML converter
	 *
	 * Used when Parsedown library is not available.
	 *
	 * @param string $content Markdown content.
	 * @return string HTML content.
	 */
	private static function markdown_to_html_fallback( $content ) {
		// 1. Convert headings (must go from #### to # to avoid ## matching # first)
		$content = preg_replace( '/^####\s+(.*)$/m', '<h4>$1</h4>', $content );
		$content = preg_replace( '/^###\s+(.*)$/m', '<h3>$1</h3>', $content );
		$content = preg_replace( '/^##\s+(.*)$/m', '<h2>$1</h2>', $content );
		$content = preg_replace( '/^#\s+(.*)$/m', '<h1>$1</h1>', $content );

		// 2. Convert lists BEFORE bold/italic to prevent * list markers being consumed by italic regex
		// Unordered lists: lines starting with * or - followed by space(s)
		$content = preg_replace_callback(
			'/(?:^[\*\-]\s+.+$\n?)+/m',
			function ( $matches ) {
				$items = preg_replace( '/^[\*\-]\s+(.*)$/m', '<li>$1</li>', trim( $matches[0] ) );
				return "\n\n<ul>" . $items . "</ul>\n\n";
			},
			$content
		);

		// Ordered lists: lines starting with 1. 2. etc.
		$content = preg_replace_callback(
			'/(?:^\d+\.\s+.+$\n?)+/m',
			function ( $matches ) {
				$items = preg_replace( '/^\d+\.\s+(.*)$/m', '<li>$1</li>', trim( $matches[0] ) );
				return "\n\n<ol>" . $items . "</ol>\n\n";
			},
			$content
		);

		// 3. Convert bold: **text** -> <strong>text</strong> (no /s flag — don't cross newlines)
		$content = preg_replace( '/\*\*(.*?)\*\*/', '<strong>$1</strong>', $content );

		// 4. Convert italic: *text* -> <em>text</em>
		// No /s flag, don't match * at line start (list markers already converted),
		// require non-space after opening * and before closing *
		$content = preg_replace( '/(?<![*\w])\*(?!\s)([^*\n]+?)(?<!\s)\*(?![*\w])/', '<em>$1</em>', $content );

		// 5. Convert links: [text](url) -> <a href="url">text</a>
		$content = preg_replace( '/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2">$1</a>', $content );

		// 6. Clean up extra whitespace around tags
		$content = preg_replace( '/\s+(<\/?(strong|em|a)\b)/', ' $1', $content );
		$content = preg_replace( '/(<\/(strong|em|a)>)\s+/', '$1 ', $content );

		// 7. Trim leading/trailing whitespace inside <li> tags
		$content = preg_replace( '/<li>\s+/', '<li>', $content );
		$content = preg_replace( '/\s+<\/li>/', '</li>', $content );

		// 8. Convert paragraphs: wrap remaining text blocks in <p> tags
		// Normalize multiple newlines
		$content = preg_replace( '/\n{3,}/', "\n\n", $content );
		$blocks  = preg_split( '/\n{2,}/', $content );
		$result  = array();

		foreach ( $blocks as $block ) {
			$block = trim( $block );
			if ( empty( $block ) ) {
				continue;
			}
			// Don't wrap blocks that already have block-level HTML tags
			if ( preg_match( '/^<(h[1-6]|ul|ol|li|blockquote|pre|div|table|p|hr)[\s>]/i', $block ) ) {
				$result[] = $block;
			} else {
				// Replace single newlines with <br> within paragraphs
				$block    = str_replace( "\n", '<br>', $block );
				$result[] = '<p>' . $block . '</p>';
			}
		}

		$html = implode( "\n\n", $result );

		// Post-processing: convert standalone bold paragraphs to headings.
		$html = preg_replace(
			'/<p><strong>([^<]+)<\/strong><\/p>/',
			'<h2>$1</h2>',
			$html
		);

		return $html;
	}

}

