<?php
/**
 * REST API endpoint used by the block editor to generate AI summaries.
 *
 * @package Priizmo_Engine
 */

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

/**
 * Class Priizmo_Engine_Rest
 *
 * Exposes:
 * - POST /wp-json/priizmo-engine/v1/generate-summary
 * - POST /wp-json/priizmo-engine/v1/generate-faq
 *
 * Both build their respective prompt from settings, call the configured AI
 * provider, and cache the result on the post.
 */
class Priizmo_Engine_Rest {

	/**
	 * REST namespace.
	 *
	 * @var string
	 */
	const NAMESPACE = 'priizmo-engine/v1';

	/**
	 * Maximum number of characters of post content sent to the AI provider,
	 * to keep prompts (and cost) bounded.
	 *
	 * @var int
	 */
	const MAX_CONTENT_LENGTH = 12000;

	/**
	 * Constructor. Hooks REST route registration.
	 */
	public function __construct() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
	}

	/**
	 * Registers the generate-summary and generate-faq routes.
	 */
	public function register_routes() {
		$shared_args = array(
			'post_id' => array(
				'required'          => true,
				'type'              => 'integer',
				'sanitize_callback' => 'absint',
			),
			'content' => array(
				'required' => false,
				'type'     => 'string',
			),
			'force'   => array(
				'required' => false,
				'type'     => 'boolean',
				'default'  => false,
			),
		);

		register_rest_route(
			self::NAMESPACE,
			'/generate-summary',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_generate_summary' ),
				'permission_callback' => array( $this, 'check_permission' ),
				'args'                => $shared_args,
			)
		);

		register_rest_route(
			self::NAMESPACE,
			'/generate-faq',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_generate_faq' ),
				'permission_callback' => array( $this, 'check_permission' ),
				'args'                => $shared_args,
			)
		);
	}

	/**
	 * Permission check: the current user must be able to edit the post.
	 *
	 * @param WP_REST_Request $request Incoming request.
	 *
	 * @return bool|WP_Error
	 */
	public function check_permission( $request ) {
		$post_id = absint( $request->get_param( 'post_id' ) );

		if ( ! $post_id || ! get_post( $post_id ) ) {
			return new WP_Error( 'priizmo_engine_invalid_post', __( 'Invalid post.', 'priizmo-ai-summary-faq-schema' ), array( 'status' => 404 ) );
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return new WP_Error( 'priizmo_engine_forbidden', __( 'You are not allowed to edit this post.', 'priizmo-ai-summary-faq-schema' ), array( 'status' => 403 ) );
		}

		return true;
	}

	/**
	 * Handles the generate-summary request.
	 *
	 * @param WP_REST_Request $request Incoming request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function handle_generate_summary( $request ) {
		$post_id = absint( $request->get_param( 'post_id' ) );
		$post    = get_post( $post_id );
		$force   = (bool) $request->get_param( 'force' );

		if ( ! $post ) {
			return new WP_Error( 'priizmo_engine_invalid_post', __( 'Invalid post.', 'priizmo-ai-summary-faq-schema' ), array( 'status' => 404 ) );
		}

		if ( ! $force ) {
			$cached = get_post_meta( $post_id, Priizmo_Engine_Post_Meta::META_KEY, true );

			if ( ! empty( $cached ) ) {
				return rest_ensure_response(
					array(
						'summary' => $cached,
						'cached'  => true,
					)
				);
			}
		}

		$plain_text = $this->get_plain_text_content( $request, $post );

		if ( is_wp_error( $plain_text ) ) {
			return $plain_text;
		}

		$model           = Priizmo_Engine::get_setting( 'default_model', 'gpt-5.6-terra' );
		$prompt_template = Priizmo_Engine::get_setting( 'tldr_prompt', '' );
		$prompt          = str_replace( '{post_content}', $plain_text, $prompt_template );

		$summary = Priizmo_Engine_AI_Client::generate( $prompt, $model );

		if ( is_wp_error( $summary ) ) {
			return $summary;
		}

		$summary = sanitize_textarea_field( $summary );

		update_post_meta( $post_id, Priizmo_Engine_Post_Meta::META_KEY, $summary );

		return rest_ensure_response(
			array(
				'summary' => $summary,
				'cached'  => false,
			)
		);
	}

	/**
	 * Handles the generate-faq request. Builds the FAQ prompt, calls the AI
	 * provider, strictly validates the returned JSON, and caches it.
	 *
	 * @param WP_REST_Request $request Incoming request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function handle_generate_faq( $request ) {
		$post_id = absint( $request->get_param( 'post_id' ) );
		$post    = get_post( $post_id );
		$force   = (bool) $request->get_param( 'force' );

		if ( ! $post ) {
			return new WP_Error( 'priizmo_engine_invalid_post', __( 'Invalid post.', 'priizmo-ai-summary-faq-schema' ), array( 'status' => 404 ) );
		}

		if ( ! $force ) {
			$cached_items = Priizmo_Engine_Post_Meta::get_faq_items( $post_id );

			if ( ! empty( $cached_items ) ) {
				return rest_ensure_response(
					array(
						'faq'    => $cached_items,
						'cached' => true,
					)
				);
			}
		}

		$plain_text = $this->get_plain_text_content( $request, $post );

		if ( is_wp_error( $plain_text ) ) {
			return $plain_text;
		}

		$model           = Priizmo_Engine::get_setting( 'default_model', 'gpt-5.6-terra' );
		$prompt_template = Priizmo_Engine::get_setting( 'faq_prompt', '' );
		$prompt          = str_replace( '{post_content}', $plain_text, $prompt_template );

		$raw_response = Priizmo_Engine_AI_Client::generate( $prompt, $model, Priizmo_Engine_AI_Client::MAX_TOKENS_FAQ );

		if ( is_wp_error( $raw_response ) ) {
			return $raw_response;
		}

		$faq_items = self::extract_faq_items( $raw_response );

		if ( empty( $faq_items ) ) {
			return new WP_Error(
				'priizmo_engine_invalid_faq_response',
				__( 'The AI response could not be parsed into valid FAQ data. Please try again.', 'priizmo-ai-summary-faq-schema' ),
				array( 'status' => 502 )
			);
		}

		update_post_meta( $post_id, Priizmo_Engine_Post_Meta::FAQ_META_KEY, wp_json_encode( $faq_items ) );

		return rest_ensure_response(
			array(
				'faq'    => $faq_items,
				'cached' => false,
			)
		);
	}

	/**
	 * Extracts and validates a clean array of {question, answer} pairs from
	 * a raw AI completion, tolerating markdown code fences around the JSON.
	 *
	 * @param string $raw_response Raw text returned by the AI provider.
	 *
	 * @return array<int, array{question: string, answer: string}>
	 */
	public static function extract_faq_items( $raw_response ) {
		$text = trim( (string) $raw_response );

		// Strip ```json ... ``` or ``` ... ``` code fences some models add
		// despite being told to return raw JSON.
		if ( preg_match( '/```(?:json)?\s*(.*?)\s*```/is', $text, $matches ) ) {
			$text = trim( $matches[1] );
		}

		// If there is any leading/trailing prose, fall back to the first
		// top-level JSON array found in the text.
		if ( '[' !== substr( $text, 0, 1 ) ) {
			$start = strpos( $text, '[' );
			$end   = strrpos( $text, ']' );

			if ( false !== $start && false !== $end && $end > $start ) {
				$text = substr( $text, $start, $end - $start + 1 );
			}
		}

		$decoded = json_decode( $text, true );

		return Priizmo_Engine_Post_Meta::normalize_faq_items( $decoded );
	}

	/**
	 * Resolves the plain-text content to send to the AI provider, preferring
	 * client-submitted content (the live editor state) and falling back to
	 * the post's saved content.
	 *
	 * @param WP_REST_Request $request Incoming request.
	 * @param WP_Post          $post    Post being processed.
	 *
	 * @return string|WP_Error
	 */
	private function get_plain_text_content( $request, $post ) {
		$raw_content = $request->get_param( 'content' );

		if ( null === $raw_content || '' === trim( (string) $raw_content ) ) {
			$raw_content = $post->post_content;
		}

		$plain_text = trim( preg_replace( '/\s+/', ' ', wp_strip_all_tags( $raw_content ) ) );

		if ( '' === $plain_text ) {
			return new WP_Error( 'priizmo_engine_empty_content', __( 'This post has no content yet.', 'priizmo-ai-summary-faq-schema' ), array( 'status' => 400 ) );
		}

		if ( strlen( $plain_text ) > self::MAX_CONTENT_LENGTH ) {
			$plain_text = substr( $plain_text, 0, self::MAX_CONTENT_LENGTH );
		}

		return $plain_text;
	}
}
