<?php
/**
 * Content plan parser service.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Parses AI JSON responses for editable content plans.
 */
class SmartyPress_AI_Content_Generator_Plan_Parser {

	/**
	 * Parse generated plan JSON from marked, fenced, or raw response.
	 *
	 * @param string $content AI response.
	 * @param array  $context Optional context for logs.
	 * @return array
	 */
	public function parse_plan_json( $content, $context = array() ) {
		$content = trim( (string) $content );

		$candidates = array();

		if ( preg_match( '/===PLAN_JSON_START===\s*(.*?)\s*===PLAN_JSON_END===/s', $content, $matches ) ) {
			$candidates[] = trim( $matches[1] );
		}

		if ( preg_match_all( '/```(?:json)?\s*(.*?)\s*```/s', $content, $matches ) ) {
			foreach ( $matches[1] as $json_candidate ) {
				$candidates[] = trim( $json_candidate );
			}
		}

		$candidates[] = $content;

		$object_candidate = $this->extract_first_json_object( $content );
		if ( '' !== $object_candidate ) {
			$candidates[] = $object_candidate;
		}

		foreach ( array_unique( array_filter( $candidates ) ) as $candidate ) {
			$data = json_decode( $candidate, true );

			if ( JSON_ERROR_NONE === json_last_error() && is_array( $data ) ) {
				return $data;
			}
		}

		$this->log_plan_parse_failure( $content, $context );

		return array();
	}

	/**
	 * Extract first balanced JSON object from AI response text.
	 *
	 * @param string $content AI response.
	 * @return string JSON object candidate.
	 */
	public function extract_first_json_object( $content ) {
		$start = strpos( $content, '{' );

		if ( false === $start ) {
			return '';
		}

		$length    = strlen( $content );
		$depth     = 0;
		$in_string = false;
		$escaped   = false;

		for ( $index = $start; $index < $length; $index++ ) {
			$char = $content[ $index ];

			if ( $in_string ) {
				if ( $escaped ) {
					$escaped = false;
					continue;
				}

				if ( '\\' === $char ) {
					$escaped = true;
					continue;
				}

				if ( '"' === $char ) {
					$in_string = false;
				}

				continue;
			}

			if ( '"' === $char ) {
				$in_string = true;
				continue;
			}

			if ( '{' === $char ) {
				$depth++;
			}

			if ( '}' === $char ) {
				$depth--;

				if ( 0 === $depth ) {
					return substr( $content, $start, $index - $start + 1 );
				}
			}
		}

		return '';
	}

	/**
	 * Log plan parsing failures without exposing full generated content.
	 *
	 * @param string $content AI response.
	 * @param array  $context Optional context.
	 * @return void
	 */
	public function log_plan_parse_failure( $content, $context = array() ) {
		if ( ! class_exists( 'SmartyPress_AI_Logger' ) ) {
			return;
		}

		$has_start_marker      = false !== strpos( $content, '===PLAN_JSON_START===' );
		$has_end_marker        = false !== strpos( $content, '===PLAN_JSON_END===' );
		$balanced_object       = $this->extract_first_json_object( $content );
		$balanced_object_found = '' !== $balanced_object;

		SmartyPress_AI_Logger::log_module_event(
			'content_generator',
			'plan_parse_failed',
			array(
				'json_error'            => json_last_error_msg(),
				'has_start_marker'      => $has_start_marker,
				'has_end_marker'        => $has_end_marker,
				'missing_end_marker'    => $has_start_marker && ! $has_end_marker,
				'balanced_object_found' => $balanced_object_found,
				'seo_analysis_enabled'  => ! empty( $context['seo_analysis_enabled'] ),
				'content_length'        => strlen( $content ),
				'content_preview'       => mb_substr( wp_strip_all_tags( $content ), 0, 500 ),
			),
			false
		);
	}
}
