<?php

namespace WPDRMS\ASP\AI\Service;

use RuntimeException;
use WPDRMS\ASP\AI\AIService;
use WPDRMS\ASP\Index\ORM\IndexTableOptions;
use WPDRMS\PluginCore\Traits\SingletonTrait;

if ( ! defined( 'ABSPATH' ) ) {
	die( '-1' );
}

class AIWizardService {
	use SingletonTrait;

	private function __construct() {}

	/**
	 * Generates AI configuration from wizard answers.
	 *
	 * @param array<string, mixed> $answers  Pre-validated wizard parameters
	 * @return array{config: array, tokens_used: int}
	 * @throws RuntimeException When the AI returns an invalid response.
	 */
	public function generate( array $answers ): array {
		$prompt = $this->buildWizardPrompt( $answers );
		$data   = AIService::instance()->generate( 'config_wizard', 'config_wizard', $prompt, 3600 );

		$raw     = AIService::extractJsonPayload( (string) ( $data['result'] ?? '' ) );
		$decoded = json_decode( $raw, true );

		if ( ! is_array( $decoded ) || ! isset( $decoded['options'] ) ) {
			throw new RuntimeException( __( 'Invalid response from AI. Please try again.', 'ajax-search-pro' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
		}

		$decoded['options'] = $this->sanitizeOptions( (array) $decoded['options'] );

		return array(
			'config'      => $decoded,
			'tokens_used' => $data['tokens_used'] ?? 0,
		);
	}

	/**
	 * Creates a new search instance, applies options, and merges index table config when needed.
	 *
	 * @param string   $name
	 * @param string   $engine              'regular'|'index'
	 * @param string[] $content_types
	 * @param array    $raw_options         Unsanitized options from the frontend
	 * @param bool     $search_terms
	 * @param string[] $index_custom_fields
	 * @param bool     $index_pdf
	 * @return array<string, mixed>
	 * @throws RuntimeException When the instance cannot be created.
	 */
	public function createInstance( string $name, string $engine, array $content_types, array $raw_options, bool $search_terms, array $index_custom_fields, bool $index_pdf ): array {
		$options                  = $this->sanitizeOptions( $raw_options );
		$options['search_engine'] = $engine;
		$options['searchinterms'] = $search_terms ? 1 : 0;

		$asp_id = wd_asp()->instances->add( $name, get_current_user_id() );

		if ( $asp_id === false ) {
			throw new RuntimeException( __( 'Failed to create search instance. Please try again.', 'ajax-search-pro' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
		}

		if ( ! empty( $options ) ) {
			wd_asp()->instances->update( $asp_id, $options );
		}

		$index_updated = false;
		if ( $engine === 'index' ) {
			$index_updated = $this->mergeIndexTableConfig( $content_types, $index_custom_fields, $search_terms, $index_pdf );
		}

		return array(
			'asp_id'        => (int) $asp_id,
			'edit_url'      => admin_url( 'admin.php?page=asp_main_settings' ),
			'index_updated' => $index_updated,
			'index_url'     => $index_updated ? admin_url( 'admin.php?page=asp_index_table' ) : '',
		);
	}

	/**
	 * Validates and sanitizes option values coming from wizard answers or AI response.
	 *
	 * @param array<string, mixed> $options
	 * @return array<string, mixed>
	 */
	public function sanitizeOptions( array $options ): array {
		$clean = array();

		if ( isset( $options['customtypes'] ) && is_array( $options['customtypes'] ) ) {
			$clean['customtypes'] = array_values( array_map( 'sanitize_key', $options['customtypes'] ) );
		}
		if ( isset( $options['keyword_logic'] ) && in_array( $options['keyword_logic'], array( 'and', 'or' ), true ) ) {
			$clean['keyword_logic'] = $options['keyword_logic'];
		}
		if ( isset( $options['min_word_length'] ) ) {
			$clean['min_word_length'] = max( 1, min( 10, (int) $options['min_word_length'] ) );
		}
		if ( isset( $options['searchincontent'] ) ) {
			$clean['searchincontent'] = (int) (bool) $options['searchincontent'];
		}
		if ( isset( $options['search_engine'] ) && in_array( $options['search_engine'], array( 'regular', 'index' ), true ) ) {
			$clean['search_engine'] = $options['search_engine'];
		}
		if ( isset( $options['searchinterms'] ) ) {
			$clean['searchinterms'] = (int) (bool) $options['searchinterms'];
		}
		if ( isset( $options['user_search'] ) ) {
			$clean['user_search'] = (int) (bool) $options['user_search'];
		}
		if ( isset( $options['return_attachments'] ) ) {
			$clean['return_attachments'] = (int) (bool) $options['return_attachments'];
		}
		if ( isset( $options['searchinproducts'] ) ) {
			$clean['searchinproducts'] = (int) (bool) $options['searchinproducts'];
		}
		if ( isset( $options['itemscount'] ) ) {
			$clean['itemscount'] = max( 1, min( 50, (int) $options['itemscount'] ) );
		}
		if ( isset( $options['charcount'] ) ) {
			$clean['charcount'] = max( 0, min( 500, (int) $options['charcount'] ) );
		}
		if ( isset( $options['it_language'] ) ) {
			$clean['it_language'] = sanitize_key( (string) $options['it_language'] );
		}

		return $clean;
	}

	/**
	 * Merges new wizard choices into the global index table configuration.
	 * Only adds to existing settings — never removes configured post types, fields, or taxonomies.
	 *
	 * @param string[] $content_types
	 * @param string[] $custom_fields
	 * @param bool     $search_terms
	 * @param bool     $index_pdf
	 * @return bool
	 */
	private function mergeIndexTableConfig( array $content_types, array $custom_fields, bool $search_terms, bool $index_pdf ): bool {
		$it_opts = IndexTableOptions::instance();

		$current_types  = (array) $it_opts->post_types->value;
		$current_fields = (array) $it_opts->custom_fields->value;
		$current_taxs   = (array) $it_opts->index_taxonomies->value;

		$new_types  = array_values( array_unique( array_merge( $current_types, $content_types ) ) );
		$new_fields = array_values( array_unique( array_merge( $current_fields, $custom_fields ) ) );

		// When taxonomy search is enabled, merge all public taxonomies into the index
		$new_taxs = $current_taxs;
		if ( $search_terms ) {
			$public_taxs = array_values( get_taxonomies( array( 'public' => true ) ) );
			$new_taxs    = array_values( array_unique( array_merge( $current_taxs, $public_taxs ) ) );
		}

		$args = array(
			'post_types'       => array( 'value' => $new_types ),
			'custom_fields'    => array( 'value' => $new_fields ),
			'index_taxonomies' => array( 'value' => $new_taxs ),
		);

		if ( $index_pdf && ! $it_opts->index_pdf_content->value ) {
			$args['index_media_files'] = array( 'value' => true );
			$args['index_pdf_content'] = array( 'value' => true );
		}

		$it_opts->setArgs( $args )->save();

		return true;
	}

	/**
	 * @param array<string, mixed> $answers
	 * @return string
	 */
	private function buildWizardPrompt( array $answers ): string {
		return (string) wp_json_encode(
			array(
				'site_type'           => $answers['site_type'] ?? 'mixed',
				'post_types'          => array_values( (array) ( $answers['content_types'] ?? array() ) ),
				'include_users'       => ! empty( $answers['include_users'] ),
				'include_attachments' => ! empty( $answers['include_attachments'] ),
				'engine'              => $answers['engine'] ?? 'regular',
				'search_terms'        => ! empty( $answers['search_terms'] ),
				'keyword_logic'       => $answers['keyword_logic'] ?? 'and',
				'search_in_content'   => $answers['search_in_content'] ?? true,
				'show_descriptions'   => $answers['show_descriptions'] ?? true,
				'language'            => $answers['language'] ?? 'english',
			)
		);
	}
}
