<?php

namespace Limb_Chatbot\Includes\Data_Schemas;

use Limb_Chatbot\Includes\Ai_Providers\AI_Providers;
use Limb_Chatbot\Includes\AI_Providers\Open_Ai\Open_Ai;
use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\AI_Model;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_User;
use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Job;
use Limb_Chatbot\Includes\Data_Objects\Lead_Field;
use Limb_Chatbot\Includes\Data_Objects\Limit;
use Limb_Chatbot\Includes\Data_Objects\Setting as Setting_Data_Object;
use Limb_Chatbot\Includes\Data_Objects\Vector_Index;
use Limb_Chatbot\Includes\Data_Objects\WP_Post_Data_Object;
use Limb_Chatbot\Includes\Database_Strategies\WPDB;
use Limb_Chatbot\Includes\Scheme_Interface;
use Limb_Chatbot\Includes\Services\Chatbot_Limits_Parser;
use Limb_Chatbot\Includes\Services\Chatbot_Widgets_Parser;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Widget_Collection;
use Limb_Chatbot\Includes\Vector_Dbs\Local\Local;
use Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Pinecone;
use Limb_Chatbot\Includes\Widgets\Items\Link_Widget_Item;
use Limb_Chatbot\Includes\Widgets\Items\Message_Widget_Item;
use Limb_Chatbot\Includes\Widgets\Items\Prompt_Widget_Item;
use Limb_Chatbot\Includes\Widgets\Items\Text_Widget_Item;
use Limb_Chatbot\Includes\Widgets\Items\Lead_Capture_Widget_Item;

/**
 * Class Setting
 *
 * Defines the schema rules for chatbot settings and related configurations.
 * Supports multiple rule categories such as general, chatbot-specific,
 * embedding, fine-tuning, copilot, AI providers, and background process settings.
 *
 * Implements Scheme_Interface for standard schema validation methods.
 *
 * @since 1.0.0
 */
class Setting implements Scheme_Interface {

	/**
	 * Returns the complete set of validation rules for chatbot settings.
	 *
	 * Combines general, chatbot, embedding, fine-tuning, copilot,
	 * AI provider, and background process rules.
	 *
	 * @param  bool  $preview  Whether to return rules for preview mode.
	 *
	 * @return array Associative array of settings validation rules.
	 */
	public static function rules( $preview = false ): array {
		$rules = array_merge(
			self::general_rules(),
			self::chatbot_rules(),
			self::kb_rules(),
			self::fine_tuning_rules(),
			self::copilot_rules(),
			self::ai_provider_rules(),
		);

		return $preview ? self::apply_preview_postfix( $rules ) : $rules;
	}

	/**
	 * Returns chatbot-specific validation rules.
	 *
	 * Includes profile settings, chatbot identity, appearance, AI provider details,
	 * knowledge base, tools, chatbot limits, themes, and other UI-related options.
	 *
	 * @param  string  $prefix  Optional prefix for keys (default is Setting_Data_Object::SETTING_PREFIX + Chatbot::SETTING_PREFIX).
	 *
	 * @return array Associative array of chatbot-related validation rules.
	 */
	public static function chatbot_rules( $prefix = Setting_Data_Object::SETTING_PREFIX . Chatbot::SETTING_PREFIX ): array {
		$wpdb_strategy   = WPDB::instance();
		$table           = str_contains( $prefix, Chatbot::SETTING_PREFIX ) ? 'postmeta' : 'options';
		$supports_emojis = $wpdb_strategy->table_supports_emojis( $table );
		$sync_rules      = [];

		$post_types = get_post_types( [ 'public' => true ] );
		foreach ( $post_types as $post_type ) {
			$sync_rules[ $prefix . 'auto_sync_for_post_' . $post_type ] = [
				'name'     => __( 'Auto Sync for post type', 'limb-chatbot' ),
				'type'     => 'boolean',
				'required' => false,
				'default'  => true,
			];
		}

		return array_merge( $sync_rules, [
			$prefix . 'status'                     => [
				'name'     => __( 'Chatbot status', 'limb-chatbot' ),
				'type'     => 'integer',
				'default'  => WP_Post_Data_Object::STATUS_DRAFT,
				'enum'              => WP_Post_Data_Object::get_statuses(),
				'sanitize_callback' => function ( $value ) {
					return absint( $value );
				},
				'required' => true,
			],
			$prefix . 'type'                       => [
				'name'     => __( 'Chatbot type', 'limb-chatbot' ),
				'type'     => 'string',
				'default'  => 'simple',
				'enum'     => [ 'simple', 'assistant' ],
				'required' => true,
			],
			$prefix . 'icon_bg_shape'              => [
				'name'     => __( 'Chatbot icon background shape', 'limb-chatbot' ),
				'type'     => 'string',
				'default'  => 'squircle',
				'enum'     => [ 'squircle', 'circle', 'drop' ],
				'required' => true,
			],
			$prefix . 'avatar_visibility'              => [
				'name'     => __( 'Avatar visibility', 'limb-chatbot' ),
				'type'     => 'boolean',
				'default'  => false,
				'required' => true,
			],
			$prefix . 'size'                       => [
				'name'     => __( 'Chatbot size', 'limb-chatbot' ),
				'type'     => 'string',
				'default'  => 'md',
				'enum'     => [ 'sm', 'md', 'lg', 'xl' ],
				'required' => true,
			],
			$prefix . 'icon'                       => [
				'name'     => __( 'Chatbot icon', 'limb-chatbot' ),
				'type'     => 'integer',
				'default'  => 1,
				'enum'     => [ 1, 2, 3, 4, 5, 6, 7 ],
				'required' => true,
			],
			$prefix . 'avatar'                     => [
				'name'     => __( 'Chatbot avatar', 'limb-chatbot' ),
				'type'     => 'integer',
				'default'  => 1,
				'enum'     => [ 1, 2, 3, 4, 5, 6 ],
				'required' => true,
			],
			$prefix . 'ai_provider_id'             => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			$prefix . 'default.ai_provider_id'             => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			$prefix . 'config_id'                  => [
				'type'              => 'integer',
				'name'              => __( 'Chatbot config', 'limb-chatbot' ),
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return (bool) Config::count( [ 'id' => $value ] );
				},
			],
			$prefix . 'kb_config_id'               => [
				'type'              => 'integer',
				'name'              => __( 'Knowledge base config', 'limb-chatbot' ),
				'required'          => false,
				'validate_callback' => function ( $value ): bool {
					return (bool) Config::count( [ 'id' => $value ] );
				},
			],
			$prefix . 'ai_model_id'                => [
				'name'              => __( 'AI model', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => ( $model = AI_Model::find_by_name( 'gpt-4o-mini' ) ) ? $model->get_id() : null,
				'validate_callback' => function ( $value ): bool {
					return (bool) AI_Model::count( [ 'id' => $value ] );
				},
				'required'          => true
			],
			$prefix . 'kb_ai_model_id'                => [
				'name'              => __( 'Knowledge base AI model', 'limb-chatbot' ),
				'type'              => 'integer',
				'validate_callback' => function ( $value ): bool {
					return (bool) AI_Model::count( [ 'id' => $value ] );
				},
				'required'          => false
			],
			$prefix . 'kb_dimension' => [
				'name'              => __( 'Knowledge base vectors dimension', 'limb-chatbot' ),
				'type'              => 'integer',
				'validate_callback' => function ( $value ): bool {
					return in_array( $value, [ 1536 ] );
				},
				'required'          => false
			],
			$prefix . 'knowledge_base' => [
				'name'    => __( 'Knowledge base', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true,
			],
			$prefix . 'kb_vector_index_ids'        => [
				'name'              => __( 'Knowledge base storages', 'limb-chatbot' ),
				'type'              => 'array',
				'sanitize_callback' => function ( $value ): array {
					return array_map( function ( $item ) {
						return (int) sanitize_text_field( $item );
					}, $value );
				},
				'validate_callback' => function ( $value ): bool {
					if ( empty( $value ) ) {
						return true;
					}
					if ( is_array( $value ) ) {
						foreach ( $value as $v ) {
							if ( empty( Vector_Index::find( $v ) ) ) {
								return false;
							}
						}

						return true;
					}

					return false;
				}
			],
			$prefix . 'kb_vector_similarity_score' => [
				'name'    => __( 'Vector similarity score', 'limb-chatbot' ),
				'type'    => 'integer',
				'default' => 30
			],
			$prefix . 'notifications.notification_type.chat_created' => [
				'name'    => __( 'Chat created notification type', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'notifications.notification_type.lead_captured' => [
				'name'    => __( 'Chat created notification type', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'kb_vector_index_type'      => [
				'name'     => __( 'Vector storage type', 'limb-chatbot' ),
				'required' => false,
				'type'     => 'string',
				'enum'     => array( Local::$id, Pinecone::$id ),
				'default'  => Local::$id
			],
			$prefix . 'kb_sources_section_items_count'      => [
				'name'     => __( 'KB sources section items count', 'limb-chatbot' ),
				'required' => false,
				'type'     => 'integer',
				'default'  => 1
			],
			$prefix . 'kb_vector_index_config_id' => [
				'name'              => __( 'Vector storage type', 'limb-chatbot' ),
				'type'              => 'integer',
				'required'          => false,
				'validate_callback' => function ( $value ) {
					if ( ! empty( $value ) ) {
						return ! empty( Config::find( $value ) );
					}

					return true;
				},
				'default'           => null
			],
			$prefix . 'kb_vector_similarity_count' => [
				'name' => __( 'Vector similarity count', 'limb-chatbot' ),
				'type' => 'integer',
			],
			$prefix . 'tools_ids'                  => [
				'name' => __( 'Chatbot tools', 'limb-chatbot' ),
				'type' => 'array',
			],
			$prefix . 'greeting_text'              => [
				'name'    => __( 'Greeting text', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => __("Hello", 'limb-chatbot') . ( $supports_emojis ? ' 👋' : '' ),
			],
			$prefix . 'greeting_tagline'           => [
				'name'    => __( 'Greeting tagline', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => __("How can we help?", 'limb-chatbot')
			],
			$prefix . 'message_placeholder'        => [
				'name'    => __( 'Message placeholder', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => __("Write a message...", 'limb-chatbot')
			],
			$prefix . 'title'        => [
				'name'    => __( 'Chatbot title', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => __("AI Agent", 'limb-chatbot')
			],
			$prefix . 'send_button_text'           => [
				'name' => __( 'Send button text', 'limb-chatbot' ),
				'type' => 'string',
			],
			$prefix . 'copy'                       => [
				'name'    => __( 'Copy', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'close_button'               => [
				'name'    => __( 'Close button', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'voice'                      => [
				'name'    => __( 'Voice', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			$prefix . 'image'                      => [
				'name'    => __( 'Image', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			$prefix . 'stream'                     => [
				'name'    => __( 'Streaming', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'clean'                      => [
				'name'    => __( 'Clean', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'theme'                      => [
				'name'    => __( 'Chatbot theme', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => 'system',
				'enum'    => [ 'light', 'dark', 'system' ],
			],
			$prefix . 'color'                      => [
				'name'    => __( 'Color', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => '#458cf5',
			],
			$prefix . 'custom_colors'                      => [
				'name'    => __( 'Custom Colors', 'limb-chatbot' ),
			],
			$prefix . 'duplicate'                  => [
				'name'    => __( 'Duplicate', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			$prefix . 'limits'                     => [
				'name'              => __( 'Chatbot usage limits', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => array(
					Limit::TYPE_USER   => array(
						array( 'unit' => Limit::UNIT_COST, 'value' => '0.1', 'period' => 86400, 'status' => 1 ),
						array( 'unit' => Limit::UNIT_NEW_CHAT, 'value' => 10, 'period' => 86400, 'status' => 0 ),
						array( 'unit' => Limit::UNIT_MESSAGE_PER_CHAT, 'value' => 50, 'period' => 86400, 'status' => 0 ),
					),
					Limit::TYPE_GUEST  => array(
						array( 'unit' => Limit::UNIT_COST, 'value' => '0.05', 'period' => 86400, 'status' => 1 ),
						array( 'unit' => Limit::UNIT_NEW_CHAT, 'value' => 5, 'period' => 86400, 'status' => 1 ),
						array( 'unit' => Limit::UNIT_MESSAGE_PER_CHAT, 'value' => 20, 'period' => 86400, 'status' => 1 )
					),
					Limit::TYPE_INPUT  => array( array( 'unit' => Limit::UNIT_COST, 'value' => '0.001', 'period' => 0, 'status' => 1 ) ),
					Limit::TYPE_OUTPUT => array( array( 'unit' => Limit::UNIT_COST, 'value' => '0.009', 'period' => 0, 'status' => 1 ) )
				),
				'sanitize_callback' => function ( $value ) {
					return ( new Chatbot_Limits_Parser() )->sanitize( $value );
				},
				'validate_callback' => function ( $value ) {
					return ( new Chatbot_Limits_Parser() )->validate( $value );
				}
			],
			$prefix . 'show_in_side'               => [
				'name'    => __( 'Show in side', 'limb-chatbot' ),
				'type'    => 'string',
				'default' => 'right',
				'enum'    => [ 'right', 'left' ]
			],
			$prefix . 'fullscreen'                 => [
				'name'    => __( 'Fullscreen', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			$prefix . 'attachment_storage_scheme'  => [
				'name' => __( 'Attachment storage scheme', 'limb-chatbot' ),
				'type' => 'string',
				'enum' => []
			],
			$prefix . 'image_attachment_support'   => [
				'name' => __( 'Image attachment support', 'limb-chatbot' ),
				'type' => 'boolean',
			],
			$prefix . 'widgets'                    => [
				'name'              => __( 'Widgets', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => self::get_widget_default( $supports_emojis ),
				'sanitize_callback' => function ( $value ) {
					return ( new Chatbot_Widgets_Parser() )->sanitize( $value );
				},
				'validate_callback' => function ( $value ) {
					return ( new Chatbot_Widgets_Parser() )->validate( $value );
				}
			],
			$prefix. 'name' => [
				'name' => __('Website name', 'limb-chatbot'),
				'type' => 'string',
				'default' => get_option('blogname'),
				'required' => true,
			],
			$prefix. 'custom_instructions' => [
				'name' => __('Custom instructions', 'limb-chatbot'),
				'type'    => 'string',
				'default' => self::get_default_custom_instructions(),
				'required' => false,
				'sanitize_callback' => 'sanitize_textarea_field',
			],
			$prefix. 'default_custom_instructions' => [
				'name' => __('Default custom instructions', 'limb-chatbot'),
				'type'    => 'string',
				'default' => self::get_default_custom_instructions(),
				'required' => false,
				'sanitize_callback' => 'sanitize_textarea_field',
			],
			$prefix . 'reload' => [
				'name'    => __( 'Reload', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'sound' => [
				'name'    => __( 'Sound', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'show_unseen_messages_badge' => [
				'name'    => __( 'Show unseen messages badge', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'show_unseen_messages_count' => [
				'name'    => __( 'Show unseen messages count in the badge', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'show_sources' => [
				'name'    => __( 'Show knowledge sources', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => true
			],
			$prefix . 'action_ids' => [
				'name'              => __( 'Action IDs', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => [],
				'sanitize_callback' => function ( $value ): array {
					if ( ! is_array( $value ) ) {
						return [];
					}
					return array_map( function ( $item ) {
						return (int) sanitize_text_field( $item );
					}, $value );
				},
				'validate_callback' => function ( $value ): bool {
					if ( empty( $value ) ) {
						return true;
					}
					if ( is_array( $value ) ) {
						foreach ( $value as $v ) {
							if ( empty( Action::find( $v ) ) ) {
								return false;
							}
						}

						return true;
					}

					return false;
				}
			],
			$prefix . 'live_agent' => [
				'name'              => __( 'Live Agent', 'limb-chatbot' ),
				'type'              => 'boolean',
				'default'           => false,
			],
			$prefix . 'live_agent_config_id' => [
				'name'              => __( 'Live Agent Config ID', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => false,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value ) {
					return ! empty( Config::find( $value ) );
				}
			],
			$prefix . 'agent_ids' => [
				'name'              => __( 'The ids of live agents', 'limb-chatbot' ),
				'type'              => 'array',
				'sanitize_callback' => function ( $value ) {
					return array_map( function ( $item ) {
						return (int) sanitize_text_field( $item );
					}, $value );
				},
				'validate_callback' => function ( $value ) {
					if ( ! is_array( $value ) || empty( $value ) ) {
						return false;
					}
					foreach ( $value as $item ) {
						if ( ! $user = Chatbot_User::find( $item ) ) {
							return false;
						}
						if ( $user->type !== Chatbot_User::TYPE_AGENT ) {
							return false;
						}
					}

					return true;
				}
			],
			$prefix . 'agent_fetch_method' => [
				'name'              => __( 'Live agent fetch method', 'limb-chatbot' ),
				'type'              => 'string',
				'enum'              => [ 'polling', 'event_subscription' ],
				'default'           => 'polling',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value ) {
					if ( ! in_array( $value, [ 'polling', 'event_subscription', '' ], true ) ) {
						return false;
					}
					return true;
				}
			],
			$prefix . 'show_in' => [
				'name'              => __( 'Show chatbot in specific pages', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => ['all'],
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return ['all'];
					}
					return array_map( 'sanitize_text_field', $value );
				},
				'validate_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return false;
					}
					return true;
				}
			],
			$prefix . 'hide_in' => [
				'name'              => __( 'Hide chatbot in specific pages', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => [],
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return [];
					}
					return array_map( 'sanitize_text_field', $value );
				},
				'validate_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return false;
					}
					return true;
				}
			],
		]);
	}

	/**
	 * Returns general settings validation rules.
	 *
	 * Covers debug mode, encryption keys, and Google OAuth credentials.
	 *
	 * @return array Associative array of general validation rules.
	 */
	public static function general_rules(): array {
		return [
			Setting_Data_Object::SETTING_PREFIX . 'settings.debug'                      => [
				'name'    => __( 'Debug', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			Setting_Data_Object::SETTING_PREFIX . 'plugin.remove_plugin_data'           => [
				'name'    => __( 'Remove plugin data', 'limb-chatbot' ),
				'type'    => 'boolean',
				'default' => false
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.encryption_key'             => [
				'name'     => __( 'Encryption key', 'limb-chatbot' ),
				'type'     => 'string',
				'default'  => bin2hex( random_bytes( 32 ) ),
				'required' => true,
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.google.oauth.client_id'     => [
				'name' => __( 'Google Client ID', 'limb-chatbot' ),
				'type' => 'string',
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.google.oauth.client_secret' => [
				'name' => __( 'Google Client Secret', 'limb-chatbot' ),
				'type' => 'string'
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.google.oauth.access_token'  => [
				'name' => __( 'Google Access Token', 'limb-chatbot' ),
				'type' => 'string'
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.google.oauth.refresh_token' => [
				'name' => __( 'Google Refresh Token', 'limb-chatbot' ),
				'type' => 'string'
			],
			Setting_Data_Object::SETTING_PREFIX . 'settings.google.oauth.expires_in'    => [
				'name' => __( 'Google Token Expires In', 'limb-chatbot' ),
				'type' => 'integer'
			],
			Setting_Data_Object::SETTING_PREFIX . 'onboarding.processes'          => [
				'name' => __( 'Onboarding processes state', 'limb-chatbot' ),
				'validate_callback' => function ( $value ) {
					// Check if the value is an array
					if ( ! is_array( $value ) ) {
						return false;
					}

					// Define valid status values
					$valid_statuses = [
						Job::STATUS_PENDING,
						Job::STATUS_GENERATING_TASKS,
						Job::STATUS_PROCESSING,
						Job::STATUS_PAUSED,
						Job::STATUS_COMPLETED,
						Job::STATUS_FAILED,
						Job::STATUS_CANCELLED
					];

					// Validate each item in the array
					foreach ( $value as $item ) {
						// Check if an item is an array (an associative array = object in PHP)
						if ( ! is_array( $item ) ) {
							return false;
						}

						// Check if job_id exists and is numeric
						if ( ! isset( $item['job_id'] ) || ! is_numeric( $item['job_id'] ) ) {
							return false;
						}

						// Check if status exists and is a valid string
						if ( ! isset( $item['status'] ) ||
						     ! is_string( $item['status'] ) ||
						     ! in_array( $item['status'], $valid_statuses, true ) ) {
							return false;
						}
					}

					return true;
				}
			],
			Setting_Data_Object::SETTING_PREFIX . 'onboarding.status'          => [
				'name' => __( 'Onboarding process status', 'limb-chatbot' ),
				'type' => 'string',
			],
			Setting_Data_Object::SETTING_PREFIX . 'onboarding.config_id'          => [
				'name' => __( 'Onboarding process config', 'limb-chatbot' ),
				'type' => 'string',
				'validate_callback' => function ( $value ) {
					return ! empty( Config::find( $value ) );
				}
			],
			Setting_Data_Object::SETTING_PREFIX . 'onboarding.step_2.status'    => [
				'name' => __( 'Onboarding process step 2 status', 'limb-chatbot' ),
				'type' => 'integer'
			],
			Setting_Data_Object::SETTING_PREFIX . 'onboarding.vector_index_id'  => [
				'name' => __( 'Onboarding storage', 'limb-chatbot' ),
				'type' => 'integer',
				'validate_callback' => function ( $value ) {
					return ! empty( Vector_Index::find( $value ) );
				}
			],
		];
	}

	/**
	 * Returns knowledge-base-specific validation rules.
	 *
	 * Primarily the AI provider ID used for embedding services.
	 *
	 * @return array Associative array of embedding validation rules.
	 */
	public static function kb_rules(): array {
		return  [
			Setting_Data_Object::SETTING_PREFIX . 'utilities.embedding.ai_provider_id'            => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			Setting_Data_Object::SETTING_PREFIX . 'utilities.embedding.default.ai_provider_id'            => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			Setting_Data_Object::SETTING_PREFIX . 'kb.auto_vector_for_post_post'                  => [
				'name'     => __( 'Post auto indexing', 'limb-chatbot' ),
				'type'     => 'boolean',
				'default'  => false,
				'required' => true
			],

			Setting_Data_Object::SETTING_PREFIX . 'kb.auto_vector_for_post_page'                  => [
				'name'     => __( 'Page auto indexing', 'limb-chatbot' ),
				'type'     => 'boolean',
				'default'  => false,
				'required' => true
			],
			Setting_Data_Object::SETTING_PREFIX . 'kb.auto_vector_for_dataset_entry'                  => [
				'name'     => __( 'Page auto indexing', 'limb-chatbot' ),
				'type'     => 'boolean',
				'default'  => false,
				'required' => true
			]
		];
	}

	/**
	 * Returns fine-tuning specific validation rules.
	 *
	 * Primarily the AI provider ID used for fine-tuning.
	 *
	 * @return array Associative array of fine-tuning validation rules.
	 */
	public static function fine_tuning_rules(): array {
		return [
			Setting_Data_Object::SETTING_PREFIX . 'utilities.fine-tuning.ai_provider_id' => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			Setting_Data_Object::SETTING_PREFIX . 'utilities.fine-tuning.default.ai_provider_id' => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
		];
	}

	/**
	 * Returns copilot-specific validation rules.
	 *
	 * Covers AI provider and AI model IDs used by the copilot service.
	 *
	 * @return array Associative array of copilot validation rules.
	 */
	public static function copilot_rules(): array {
		return [
			Setting_Data_Object::SETTING_PREFIX . 'utilities.copilot.ai_provider_id' => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			Setting_Data_Object::SETTING_PREFIX . 'utilities.copilot.default.ai_provider_id' => [
				'name'              => __( 'AI provider', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => Open_Ai::$id,
				'required'          => true,
				'validate_callback' => function ( $value ): bool {
					return array_key_exists( $value, AI_Providers::instance()->get_ai_providers() );
				},
			],
			Setting_Data_Object::SETTING_PREFIX . 'utilities.copilot.ai_model_id'    => [
				'name'              => __( 'AI model', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => ( $model = AI_Model::find_by_name( 'gpt-4o-mini' ) ) ? $model->get_id() : null,
				'validate_callback' => function ( $value ): bool {
					return (bool) AI_Model::count( [ 'id' => $value ] );
				},
				'required'          => true
			],
			Setting_Data_Object::SETTING_PREFIX . 'utilities.copilot.config_id'      => [
				'name'              => __( 'AI model', 'limb-chatbot' ),
				'type'              => 'integer',
				'validate_callback' => function ( $value ): bool {
					return (bool) Config::count( [ 'id' => $value ] );
				},
				'required'          => false
			],
		];
	}

	/**
	 * Returns validation rules for all registered AI providers.
	 *
	 * Dynamically loads rules from each AI provider's Setting class if available.
	 *
	 * @return array Combined AI provider rules.
	 */
	public static function ai_provider_rules(): array {
		$rules = [];
		foreach ( AI_Providers::instance()->get_ai_providers() as $ai_provider ) {
			$settings = "Limb_Chatbot\\Includes\\AI_Providers\\{$ai_provider::$name}\\Data_Schemas\\Setting";
			if ( class_exists( $settings ) ) {
				$rules = array_merge( $rules, $settings::rules() );
			}
		}

		return $rules;
	}

	/**
	 * Provides the default widget collection for chatbots in the new flat structure.
	 *
	 * Sets up example widgets such as FAQs, recent posts, and greeting messages.
	 * Each item is now at top level with its own locations.
	 *
	 * @return Widget_Collection Default widgets collection instance.
	 */
	protected static function get_widget_default( $supports_emojis ): Widget_Collection {
		$widgets_collection = new Widget_Collection();

		// Prompt Widget - "Not Sure What to Ask"
		$prompt_title_widget = new Text_Widget_Item();
		$prompt_title_widget
			->set_id( Helper::random_string() )
			->set_published( true )
			->set_notify( false )
			->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'home' ] ] ] )
			->set_data( [
				'content' => 'Not Sure What to Ask ' . ( $supports_emojis ? ' ❓' : '?' ),
			] );
		$widgets_collection->push_item( $prompt_title_widget );

		// Item 1: Prompt - "What services do you offer?"
		$prompt_item = new Prompt_Widget_Item();
		$prompt_item
			->set_id( Helper::random_string() )
			->set_published( true )
			->set_notify( false )
			->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'home' ] ] ] )
			->set_data( [
				'content' => [
					[
						'type' => 'text',
						'text' => [ 'value' => 'What services do you offer?' ]
					]
				]
			] );
		$widgets_collection->push_item( $prompt_item );

		// Link items title widget - "Recent Posts"
		$link_title_widget = new Text_Widget_Item();
		$link_title_widget
			->set_id( Helper::random_string() )
			->set_published( true )
			->set_notify( false )
			->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'home' ] ] ] )
			->set_data( [
				'content' => 'Recent Posts ' . ( $supports_emojis ? ' 📌' : '' ),
			] );
		$widgets_collection->push_item( $link_title_widget );

		// Item 2-4: Link items - Recent posts
		$recent = get_posts( [ 'numberposts' => 3, 'post_status' => 'publish', 'post_type' => 'post' ] );
		if ( $recent ) {
			foreach ( $recent as $post ) {
				$link_item = new Link_Widget_Item();
				$link_item
					->set_id( Helper::random_string() )
					->set_published( true )
					->set_notify( false )
					->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'home' ] ] ] )
					->set_data( [
						'url'  => sanitize_url( get_post_permalink( $post->ID ) ),
						'text' => sanitize_text_field( $post->post_title )
					] );
				$widgets_collection->push_item( $link_item );
			}
		}

		// Item 5: Message - "Hi. How can I help you?"
		$message_item = new Message_Widget_Item();
		$message_item
			->set_id( Helper::random_string() )
			->set_published( true )
			->set_notify( false )
			->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'new_chat' ] ] ] )
			->set_data( [
				'content' => [
					[
						'type' => 'text',
						'text' => [ 'value' => 'Hi' . ( $supports_emojis ? ' 👋' : '' ) . '. How can I help you ?' ]
					]
				]
			] );
		$widgets_collection->push_item( $message_item );

		// Item 6: Lead Capture - "Start Chatting with AI"
		$lead_capture_item = new Lead_Capture_Widget_Item();
		
		// Find Lead_Fields for mapping
		$name_field  = Lead_Field::find_by_key( 'full_name' );
		$email_field = Lead_Field::find_by_key( 'email' );

		$fields = [];

		// Add name field with config if Lead_Field exists
		if ( $name_field instanceof Lead_Field ) {
			$fields[] = [
				'type'        => 'text',
				'name'        => 'name',
				'required'    => true,
				'label'       => 'Name',
				'placeholder' => 'Enter your full name',
				'config'      => [
					'lead_capture_map_field' => $name_field->id,
				],
			];
		}

		// Add email field with config if Lead_Field exists
		if ( $email_field instanceof Lead_Field ) {
			$fields[] = [
				'type'        => 'email',
				'name'        => 'email',
				'required'    => true,
				'label'       => 'Email',
				'placeholder' => 'your@email.com',
				'config'      => [
					'lead_capture_map_field' => $email_field->id,
				],
			];
		}

		$lead_capture_item
			->set_id( Helper::random_string() )
			->set_published( false )
			->set_notify( false )
			->set_locations( [ [ [ 'param' => 'screen', 'operator' => '===', 'value' => 'new_chat' ] ] ] )
			->set_data( [
				'heading'              => 'Start Chatting with AI 👋',
				'description'          => 'Please provide your details to begin your conversation with our AI assistant.',
				'submit_button_text'   => 'Start chat',
				'fields'               => $fields,
			] );
		$widgets_collection->push_item( $lead_capture_item );

		return $widgets_collection;
	}

	/**
	 * Returns the default custom instructions string (role + guidelines).
	 *
	 * Includes the identity/role line for home_url() and the standard guidelines.
	 * Used for custom_instructions and default_custom_instructions defaults.
	 *
	 * @return string
	 */
	public static function get_default_custom_instructions(): string {
		$role = sprintf(
			"### Role\nYou are the AI agent for %s. You help users with their inquiries, issues, and requests. Aim to give excellent, friendly, and efficient replies. Listen to the user, understand their needs, and assist them or point them to the right resources. If a question is unclear, ask clarifying questions. End your replies on a positive note.",
			home_url()
		);

		$guidelines = "\n\n### Guidelines\n" . implode( "\n", [
			'- Be clear, helpful, and professional.',
			'- Answer concisely; add detail when the question needs it.',
			'- Use emojis when they add warmth or play a descriptive role.',
			'- Stay focused on this website. If something is outside scope, say so politely.',
			'- Always respond in the user\'s language.',
		] );

		return $role . $guidelines;
	}

	/**
	 * Appends the preview postfix to all rules keys.
	 *
	 * Used to generate a preview version of the validation rules.
	 *
	 * @param  array  $rules  The original rules array.
	 *
	 * @return array Modified rules array with preview postfix keys.
	 */
	protected static function apply_preview_postfix( $rules ): array {
		$preview_rules = [];
		foreach ( $rules as $key => $value ) {
			$preview_rules[ $key . Setting_Data_Object::PREVIEW_POSTFIX ] = $value;
		}

		return $preview_rules;
	}
}