<?php
/**
 * REST controller for the Prompt Builder wizard.
 *
 * @package AIPT\Api\PromptBuilder
 */

namespace AIPT\Api\PromptBuilder;

use AIPT\Api\Services\RestAPIClientFactory;
use AIPT\Api\Traits\RateLimitTrait;
use AIPT\Core\ErrorHandler;
use AIPT\Utils\ApiKeyManager;
use AIPT\Utils\PremiumFeatureManager;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

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

class PromptBuilderController {

	use RateLimitTrait;

	private const REST_NAMESPACE = 'aipt/v1';
	private const REST_BASE      = 'prompt-builder';

	/**
	 * Option names to update when activating a template.
	 *
	 * @var array<string, array{system: string, prompt: string, prompt_key: string}>
	 */
	private const ACTIVATION_OPTION_MAP = array(
		'description' => array(
			'system'     => 'aipt_system_prompt',
			'prompt'     => 'aipt_user_prompt',
			'prompt_key' => 'user_prompt',
		),
		'standard'    => array(
			'system'     => 'aipt_standard_full_system_prompt',
			'prompt'     => 'aipt_standard_full_user_prompt',
			'prompt_key' => 'user_prompt',
		),
		'title'       => array(
			'system'     => '',
			'prompt'     => 'aipt_title_additional_prompt',
			'prompt_key' => 'additional_prompt',
		),
		'faq'         => array(
			'system'     => '',
			'prompt'     => 'aipt_faq_additional_prompt',
			'prompt_key' => 'additional_prompt',
		),
		'seo_meta'    => array(
			'system'     => '',
			'prompt'     => 'aipt_seo_meta_additional_prompt',
			'prompt_key' => 'additional_prompt',
		),
		'taxonomy'    => array(
			'system'     => '',
			'prompt'     => 'aipt_taxonomy_additional_prompt',
			'prompt_key' => 'additional_prompt',
		),
	);

	/**
	 * Wizard schema registry.
	 *
	 * @var WizardSchemaRegistry
	 */
	private $registry;

	/**
	 * Prompt assembler.
	 *
	 * @var PromptAssembler
	 */
	private $assembler;

	/**
	 * Prompt templates table name.
	 *
	 * @var string
	 */
	private $table_name;

	public function __construct(
		WizardSchemaRegistry $registry,
		PromptAssembler $assembler
	) {
		global $wpdb;

		$this->registry   = $registry;
		$this->assembler  = $assembler;
		$this->table_name = $wpdb->prefix . 'aipt_prompt_templates';
	}

	public function register_routes(): void {
		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/schema/(?P<generator_type>[\w-]+)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_schema' ),
					'permission_callback' => array( $this, 'check_permissions' ),
					'args'                => array(
						'generator_type' => array(
							'required'          => true,
							'sanitize_callback' => 'sanitize_text_field',
							'validate_callback' => array( $this, 'validate_generator_type_param' ),
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/option-sets',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_option_sets' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/variables',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_variables' ),
					'permission_callback' => array( $this, 'check_permissions' ),
					'args'                => array(
						'group' => array(
							'type'              => 'string',
							'required'          => false,
							'sanitize_callback' => 'sanitize_text_field',
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/assemble',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'assemble' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
					'args'                => array(
						'generator_type' => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'description', 'standard', 'title', 'faq', 'seo_meta', 'taxonomy' ),
							'sanitize_callback' => 'sanitize_text_field',
						),
						'step_values'    => array(
							'type'     => 'object',
							'required' => true,
							'default'  => array(),
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/auto-fill',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'auto_fill' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
					'args'                => array(
						'generator_type' => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'description', 'standard', 'title', 'faq', 'seo_meta', 'taxonomy' ),
							'sanitize_callback' => 'sanitize_text_field',
						),
						'product_id'     => array(
							'type'              => 'integer',
							'required'          => false,
							'sanitize_callback' => 'absint',
						),
						'term_id'        => array(
							'type'              => 'integer',
							'required'          => false,
							'sanitize_callback' => 'absint',
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/save-template',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'save_template' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
					'args'                => array(
						'name'           => array(
							'type'              => 'string',
							'required'          => true,
							'sanitize_callback' => 'sanitize_text_field',
						),
						'generator_type' => array(
							'type'              => 'string',
							'required'          => true,
							'enum'              => array( 'description', 'standard', 'title', 'faq', 'seo_meta', 'taxonomy' ),
							'sanitize_callback' => 'sanitize_text_field',
						),
						'step_values'    => array(
							'type'     => 'object',
							'required' => true,
							'default'  => array(),
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/activate-template/(?P<id>\d+)',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'activate_template' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
					'args'                => array(
						'id' => array(
							'required'          => true,
							'sanitize_callback' => 'absint',
						),
					),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/' . self::REST_BASE . '/polish',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'polish' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
				),
			)
		);
	}

	public function check_permissions(): bool {
		return current_user_can( 'manage_woocommerce' );
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return true|WP_Error
	 */
	public function check_permissions_write( WP_REST_Request $request ) {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return new WP_Error(
				'rest_forbidden',
				__( 'You do not have permission to perform this action.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		$nonce = $request->get_header( 'X-WP-Nonce' );
		if ( empty( $nonce ) && isset( $_REQUEST['_wpnonce'] ) ) {
			$nonce = sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) );
		}

		if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
			return new WP_Error(
				'rest_invalid_nonce',
				__( 'Invalid or missing security nonce.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * @param mixed           $value   Route param value.
	 * @param WP_REST_Request $request Request.
	 * @param string          $param   Param name.
	 * @return bool
	 */
	public function validate_generator_type_param( $value, WP_REST_Request $request, string $param ): bool {
		unset( $request, $param );

		return $this->is_supported_generator_type( sanitize_text_field( (string) $value ) );
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_schema( WP_REST_Request $request ) {
		$generator_type = sanitize_text_field( (string) $request->get_param( 'generator_type' ) );

		try {
			return new WP_REST_Response( $this->registry->get_schema( $generator_type ), 200 );
		} catch ( \InvalidArgumentException $exception ) {
			return new WP_Error(
				'invalid_generator_type',
				$exception->getMessage(),
				array( 'status' => 400 )
			);
		} catch ( \Throwable $exception ) {
			return $this->create_internal_error( __( 'Failed to load wizard schema.', 'ai-product-tools' ), $exception );
		}
	}

	/**
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_option_sets() {
		try {
			return new WP_REST_Response( $this->registry->get_option_sets(), 200 );
		} catch ( \Throwable $exception ) {
			return $this->create_internal_error( __( 'Failed to load option sets.', 'ai-product-tools' ), $exception );
		}
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_variables( WP_REST_Request $request ) {

		$raw_group = $request->get_param( 'group' );
		$group     = ( null === $raw_group || '' === $raw_group ) ? null : sanitize_text_field( (string) $raw_group );

		try {
			$registry = VariableRegistry::getInstance();

			return new WP_REST_Response( $registry->toRestResponse( $group ), 200 );
		} catch ( \Throwable $exception ) {
			return $this->create_internal_error( __( 'Failed to load variables.', 'ai-product-tools' ), $exception );
		}
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function assemble( WP_REST_Request $request ) {
		$payload = $this->get_json_payload( $request );

		$generator_type = sanitize_text_field( (string) ( $payload['generator_type'] ?? '' ) );
		if ( ! $this->is_supported_generator_type( $generator_type ) ) {
			return new WP_Error(
				'invalid_generator_type',
				__( 'Invalid generator_type value.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$step_values = $this->sanitize_step_values( $payload['step_values'] ?? null );
		if ( is_wp_error( $step_values ) ) {
			return $step_values;
		}

		$validation = $this->registry->validateStepValues( $generator_type, $step_values );
		if ( empty( $validation['valid'] ) ) {
			return new WP_Error(
				'invalid_step_values',
				__( 'Wizard step values failed validation.', 'ai-product-tools' ),
				array(
					'status' => 400,
					'errors' => $validation['errors'],
				)
			);
		}

		try {
			return new WP_REST_Response( $this->assembler->assemble( $generator_type, $step_values ), 200 );
		} catch ( \InvalidArgumentException $exception ) {
			return new WP_Error(
				'invalid_request',
				$exception->getMessage(),
				array( 'status' => 400 )
			);
		} catch ( \Throwable $exception ) {
			return $this->create_internal_error( __( 'Failed to assemble prompt.', 'ai-product-tools' ), $exception );
		}
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function auto_fill( WP_REST_Request $request ) {
		$payload = $this->get_json_payload( $request );

		$generator_type = sanitize_text_field( (string) ( $payload['generator_type'] ?? '' ) );
		if ( ! $this->is_supported_generator_type( $generator_type ) ) {
			return new WP_Error(
				'invalid_generator_type',
				__( 'Invalid generator_type value.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$product_id = isset( $payload['product_id'] ) ? absint( $payload['product_id'] ) : null;
		$term_id    = isset( $payload['term_id'] ) ? absint( $payload['term_id'] ) : null;

		/** This filter is documented in src/Premium/PromptBuilder/PremiumAutoFillService.php */
		if ( ! apply_filters( 'aipt_prompt_builder_can_auto_fill', false ) ) {
			return new WP_Error(
				'premium_required',
				__( 'Auto-fill requires a Pro or Business plan.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		$rate_check = $this->checkRateLimit( (int) get_current_user_id(), 'prompt_builder_autofill', 10, 60 );
		if ( ! $rate_check['allowed'] ) {
			return $this->rateLimitResponse( $rate_check );
		}

		/** This filter is documented in src/Premium/PromptBuilder/PremiumAutoFillService.php */
		$result = apply_filters(
			'aipt_prompt_builder_auto_fill',
			null,
			$generator_type,
			$product_id ?: null,
			$term_id ?: null
		);

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

		if ( ! $this->is_valid_auto_fill_response( $result ) ) {
			return new WP_Error(
				'auto_fill_failed',
				__( 'Failed to auto-fill wizard fields.', 'ai-product-tools' ),
				array( 'status' => 500 )
			);
		}

		return new WP_REST_Response( $result, 200 );
	}

	/**
	 * Validate the structure of the auto-fill filter response.
	 *
	 * @param mixed $result Filter result.
	 * @return bool
	 */
	private function is_valid_auto_fill_response( $result ): bool {
		return is_array( $result )
			&& isset( $result['step_values'], $result['confidence'] )
			&& is_array( $result['step_values'] )
			&& is_array( $result['confidence'] );
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function save_template( WP_REST_Request $request ) {
		global $wpdb;

		$payload = $this->get_json_payload( $request );

		$generator_type = sanitize_text_field( (string) ( $payload['generator_type'] ?? '' ) );
		$name           = sanitize_text_field( (string) ( $payload['name'] ?? '' ) );
		$description    = isset( $payload['description'] ) ? sanitize_textarea_field( (string) $payload['description'] ) : null;

		if ( ! $this->is_supported_generator_type( $generator_type ) ) {
			return new WP_Error(
				'invalid_generator_type',
				__( 'Invalid generator_type value.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		if ( '' === $name || strlen( $name ) > 255 ) {
			return new WP_Error(
				'invalid_name',
				__( 'Template name is required and must be 255 characters or less.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$step_values = $this->sanitize_step_values( $payload['step_values'] ?? null );
		if ( is_wp_error( $step_values ) ) {
			return $step_values;
		}

		$wizard_state = $this->sanitize_wizard_state( $payload['wizard_state'] ?? null );
		if ( is_wp_error( $wizard_state ) ) {
			return $wizard_state;
		}

		$validation = $this->registry->validateStepValues( $generator_type, $step_values );
		if ( empty( $validation['valid'] ) ) {
			return new WP_Error(
				'invalid_step_values',
				__( 'Wizard step values failed validation.', 'ai-product-tools' ),
				array(
					'status' => 400,
					'errors' => $validation['errors'],
				)
			);
		}

		try {
			$prompt_data = $this->assembler->assemble( $generator_type, $step_values );
		} catch ( \InvalidArgumentException $exception ) {
			return new WP_Error(
				'invalid_request',
				$exception->getMessage(),
				array( 'status' => 400 )
			);
		} catch ( \Throwable $exception ) {
			return $this->create_internal_error( __( 'Failed to assemble prompt before saving template.', 'ai-product-tools' ), $exception );
		}

		$inserted = $wpdb->insert(
			$this->table_name,
			array(
				'generator_type' => $generator_type,
				'name'           => $name,
				'description'    => $description,
				'prompt_data'    => wp_json_encode(
					array(
						'system_prompt'     => $prompt_data['system_prompt'],
						'user_prompt'       => $prompt_data['user_prompt'],
						'additional_prompt' => $prompt_data['additional_prompt'],
					)
				),
				'wizard_state'   => wp_json_encode( $wizard_state ),
				'source'         => 'user_builder',
				'is_default'     => 0,
				'created_at'     => current_time( 'mysql' ),
				'updated_at'     => current_time( 'mysql' ),
			),
			array( '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s' )
		);

		if ( false === $inserted ) {
			return new WP_Error(
				'template_create_failed',
				__( 'Failed to save prompt template.', 'ai-product-tools' ),
				array( 'status' => 500 )
			);
		}

		$template = $this->get_template_by_id( (int) $wpdb->insert_id );
		if ( null === $template ) {
			return new WP_Error(
				'template_create_failed',
				__( 'Template was saved but could not be loaded.', 'ai-product-tools' ),
				array( 'status' => 500 )
			);
		}

		return new WP_REST_Response( $template, 201 );
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function activate_template( WP_REST_Request $request ) {
		$template_id = absint( $request->get_param( 'id' ) );
		$template    = $this->get_template_by_id( $template_id );

		if ( null === $template ) {
			return new WP_Error(
				'template_not_found',
				__( 'Prompt template not found.', 'ai-product-tools' ),
				array( 'status' => 404 )
			);
		}

		$generator_type = sanitize_text_field( (string) $template['generator_type'] );
		if ( ! isset( self::ACTIVATION_OPTION_MAP[ $generator_type ] ) ) {
			return new WP_Error(
				'unsupported_generator_type',
				__( 'Unsupported generator_type for activation.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$mapping           = self::ACTIVATION_OPTION_MAP[ $generator_type ];
		$prompt_data       = is_array( $template['prompt_data'] ) ? $template['prompt_data'] : array();
		$activated_options = array();

		if ( '' !== $mapping['system'] ) {
			update_option( $mapping['system'], (string) ( $prompt_data['system_prompt'] ?? '' ), true );
			$activated_options[] = $mapping['system'];
		}

		update_option( $mapping['prompt'], (string) ( $prompt_data[ $mapping['prompt_key'] ] ?? '' ), true );
		$activated_options[] = $mapping['prompt'];

		return new WP_REST_Response(
			array(
				'success'           => true,
				'activated_options' => $activated_options,
			),
			200
		);
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return WP_REST_Response|WP_Error
	 */
	public function polish( WP_REST_Request $request ) {
		$premium_manager = new PremiumFeatureManager();
		if ( ! $premium_manager->canUsePremiumFeatures() ) {
			return new WP_Error(
				'rest_forbidden',
				__( 'This feature requires a premium license.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		$rate_check = $this->checkRateLimit( (int) get_current_user_id(), 'prompt_builder_polish', 5, 60 );
		if ( ! $rate_check['allowed'] ) {
			return $this->rateLimitResponse( $rate_check );
		}

		$payload        = $this->get_json_payload( $request );
		$prompt         = isset( $payload['prompt'] ) ? sanitize_textarea_field( (string) $payload['prompt'] ) : '';
		$generator_type = sanitize_text_field( (string) ( $payload['generator_type'] ?? '' ) );

		if ( '' === $prompt ) {
			return new WP_Error(
				'rest_missing_param',
				__( 'The prompt field is required.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		if ( ! $this->is_supported_generator_type( $generator_type ) ) {
			return new WP_Error(
				'rest_invalid_param',
				sprintf(
					/* translators: %s: Comma-separated list of allowed generator_type values */
					__( 'Invalid or missing generator_type. Allowed values: %s.', 'ai-product-tools' ),
					implode( ', ', $this->registry->getSupportedGeneratorTypes() )
				),
				array( 'status' => 400 )
			);
		}

		$provider = sanitize_key( (string) get_option( 'aipt_api_provider', 'openai' ) );

		if ( ! ApiKeyManager::has_api_key( $provider ) ) {
			return new WP_Error(
				'ai_client_error',
				ErrorHandler::format_api_error_message(
					$provider,
					__( 'No API key configured for the active provider. Configure an API key in Settings before using prompt polishing.', 'ai-product-tools' )
				),
				array( 'status' => 400 )
			);
		}

		$system_prompt = sprintf(
			'You are a prompt engineering expert. Improve the following AI prompt for %s content generation. Make it more specific, effective, and well-structured while keeping the same intent and language. Return only the improved prompt text.',
			$generator_type
		);

		try {
			$factory   = new RestAPIClientFactory();
			$ai_client = $factory->create();
			$polished  = $ai_client->generate_description( $system_prompt, $prompt );
		} catch ( \Exception $exception ) {
			$message = ErrorHandler::format_api_error_message( $provider, $exception->getMessage() );

			return new WP_Error(
				'ai_client_error',
				$message,
				array( 'status' => 500 )
			);
		}

		return new WP_REST_Response(
			array(
				'success' => true,
				'data'    => array(
					'polished_prompt' => $polished,
				),
			),
			200
		);
	}

	/**
	 * @param WP_REST_Request $request REST request.
	 * @return array<string, mixed>
	 */
	private function get_json_payload( WP_REST_Request $request ): array {
		$payload = $request->get_json_params();

		return is_array( $payload ) ? $payload : array();
	}

	/**
	 * @param string $generator_type Generator type.
	 * @return bool
	 */
	private function is_supported_generator_type( string $generator_type ): bool {
		return in_array( $generator_type, $this->registry->getSupportedGeneratorTypes(), true );
	}

	/**
	 * @param mixed $step_values Raw step values.
	 * @return array<string, mixed>|WP_Error
	 */
	private function sanitize_step_values( $step_values ) {
		if ( ! is_array( $step_values ) ) {
			return new WP_Error(
				'invalid_step_values',
				__( 'step_values must be an object.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$sanitized = array();
		foreach ( $step_values as $key => $value ) {
			if ( ! is_string( $key ) || '' === $key ) {
				return new WP_Error(
					'invalid_step_values',
					__( 'step_values must use string keys.', 'ai-product-tools' ),
					array( 'status' => 400 )
				);
			}

			$sanitized_key              = sanitize_text_field( $key );
			$sanitized[ $sanitized_key ] = $this->sanitize_recursive_value( $value );
		}

		return $sanitized;
	}

	/**
	 * @param mixed $wizard_state Raw wizard state.
	 * @return array<string, mixed>|WP_Error
	 */
	private function sanitize_wizard_state( $wizard_state ) {
		if ( ! is_array( $wizard_state ) ) {
			return new WP_Error(
				'invalid_wizard_state',
				__( 'wizard_state must be an object.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		return $this->sanitize_recursive_array( $wizard_state );
	}

	/**
	 * @param array<mixed> $value Raw array.
	 * @return array<string, mixed>
	 */
	private function sanitize_recursive_array( array $value ): array {
		$sanitized = array();

		foreach ( $value as $key => $item ) {
			$sanitized_key               = is_string( $key ) ? sanitize_text_field( $key ) : (string) absint( $key );
			$sanitized[ $sanitized_key ] = $this->sanitize_recursive_value( $item );
		}

		return $sanitized;
	}

	/**
	 * @param mixed $value Raw value.
	 * @return mixed
	 */
	private function sanitize_recursive_value( $value ) {
		if ( is_array( $value ) ) {
			return $this->sanitize_recursive_array( $value );
		}

		if ( is_bool( $value ) ) {
			return $value;
		}

		if ( is_int( $value ) || is_float( $value ) ) {
			return $value;
		}

		if ( null === $value ) {
			return '';
		}

		return sanitize_text_field( (string) $value );
	}

	/**
	 * @param int $template_id Template ID.
	 * @return array<string, mixed>|null
	 */
	private function get_template_by_id( int $template_id ): ?array {
		global $wpdb;

		if ( $template_id <= 0 ) {
			return null;
		}

		$row = $wpdb->get_row(
			$wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE id = %d", $template_id ),
			ARRAY_A
		);

		if ( ! is_array( $row ) ) {
			return null;
		}

		$decoded_prompt_data = json_decode( (string) $row['prompt_data'], true );
		$decoded_wizard      = json_decode( (string) $row['wizard_state'], true );

		$row['id']           = (int) $row['id'];
		$row['prompt_data']  = is_array( $decoded_prompt_data ) ? $decoded_prompt_data : array();
		$row['wizard_state'] = is_array( $decoded_wizard ) ? $decoded_wizard : null;
		$row['is_default']   = (bool) $row['is_default'];

		return $row;
	}

	/**
	 * @param string     $message   Public error message.
	 * @param \Throwable $exception Original exception.
	 * @return WP_Error
	 */
	private function create_internal_error( string $message, \Throwable $exception ): WP_Error {

		error_log( sprintf(
			'[AIPT PromptBuilder] %s — %s in %s:%d',
			$message,
			$exception->getMessage(),
			$exception->getFile(),
			$exception->getLine()
		) );

		return new WP_Error(
			'prompt_builder_error',
			$message,
			array( 'status' => 500 )
		);
	}
}
