<?php

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

/** Convert a small, safe Markdown subset from AI responses into readable HTML. */
class Content_Miner_AI_Response_Renderer {
	/** Render headings, paragraphs, lists, emphasis, links, and code through a tight allowlist. */
	public static function render( $markdown ) {
		$markdown = str_replace( array( "\r\n", "\r" ), "\n", (string) $markdown );
		$lines    = explode( "\n", $markdown );
		$html     = array();
		$paragraph = array();
		$list_type = '';
		$code      = array();
		$in_code   = false;

		$flush_paragraph = static function () use ( &$html, &$paragraph ) {
			if ( $paragraph ) {
				$html[]    = '<p>' . implode( '<br>', array_map( array( __CLASS__, 'render_inline' ), $paragraph ) ) . '</p>';
				$paragraph = array();
			}
		};
		$close_list = static function () use ( &$html, &$list_type ) {
			if ( $list_type ) {
				$html[]    = '</' . $list_type . '>';
				$list_type = '';
			}
		};

		foreach ( $lines as $line ) {
			if ( preg_match( '/^\s*```/', $line ) ) {
				$flush_paragraph();
				$close_list();
				if ( $in_code ) {
					$html[]  = '<pre><code>' . esc_html( implode( "\n", $code ) ) . '</code></pre>';
					$code    = array();
					$in_code = false;
				} else {
					$in_code = true;
				}
				continue;
			}
			if ( $in_code ) {
				$code[] = $line;
				continue;
			}
			if ( '' === trim( $line ) ) {
				$flush_paragraph();
				$close_list();
				continue;
			}
			if ( preg_match( '/^(#{1,6})\s+(.+)$/', $line, $matches ) ) {
				$flush_paragraph();
				$close_list();
				$level  = min( 6, max( 2, strlen( $matches[1] ) + 1 ) );
				$html[] = '<h' . $level . '>' . self::render_inline( $matches[2] ) . '</h' . $level . '>';
				continue;
			}
			if ( preg_match( '/^\s*[-+*]\s+(.+)$/', $line, $matches ) || preg_match( '/^\s*\d+[.)]\s+(.+)$/', $line, $matches ) ) {
				$flush_paragraph();
				$current_type = preg_match( '/^\s*\d+/', $line ) ? 'ol' : 'ul';
				if ( $list_type && $list_type !== $current_type ) {
					$close_list();
				}
				if ( ! $list_type ) {
					$list_type = $current_type;
					$html[]    = '<' . $list_type . '>';
				}
				$html[] = '<li>' . self::render_inline( $matches[1] ) . '</li>';
				continue;
			}
			$close_list();
			$paragraph[] = trim( $line );
		}
		if ( $in_code ) {
			$html[] = '<pre><code>' . esc_html( implode( "\n", $code ) ) . '</code></pre>';
		}
		$flush_paragraph();
		$close_list();

		return wp_kses( implode( "\n", $html ), self::allowed_html() );
	}

	/** Render a tightly scoped inline Markdown subset after extracting safe links and code. */
	public static function render_inline( $text ) {
		$tokens = array();
		$text   = preg_replace_callback(
			'/`([^`]+)`|\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/i',
			static function ( $matches ) use ( &$tokens ) {
				$token = 'CONTENTMINERTOKEN' . count( $tokens ) . 'ENDTOKEN';
				if ( isset( $matches[1] ) && '' !== $matches[1] ) {
					$tokens[ $token ] = '<code>' . esc_html( $matches[1] ) . '</code>';
				} else {
					$url = esc_url( $matches[3] );
					$tokens[ $token ] = '<a href="' . esc_attr( $url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $matches[2] ) . '</a>';
				}
				return $token;
			},
			(string) $text
		);
		$text = esc_html( $text );
		$text = preg_replace( '/\*\*([^*]+)\*\*/', '<strong>$1</strong>', $text );
		$text = preg_replace( '/(?<!\*)\*([^*]+)\*(?!\*)/', '<em>$1</em>', $text );
		return strtr( $text, $tokens );
	}

	/** Return the only HTML elements and attributes permitted in external AI output. */
	private static function allowed_html() {
		return array(
			'h2'     => array(),
			'h3'     => array(),
			'h4'     => array(),
			'h5'     => array(),
			'h6'     => array(),
			'p'      => array(),
			'strong' => array(),
			'em'     => array(),
			'ul'     => array(),
			'ol'     => array(),
			'li'     => array(),
			'a'      => array(
				'href'   => true,
				'target' => true,
				'rel'    => true,
			),
			'code'   => array(),
			'pre'    => array(),
			'br'     => array(),
		);
	}
}
