<?php
/**
 * Localization Data Builder
 *
 * Builds localization data arrays for JavaScript.
 *
 * @package AIPT\Core\Services
 * @since 2.4.0
 */

namespace AIPT\Core\Services;

use Exception;
use AIPT\Core\Init;
use AIPT\Core\Activator;
use AIPT\Api\BulkGenerator\FAQ\FAQGeneratorService;
use AIPT\Api\Constants\ChatbotConstants;
use AIPT\Api\StoreIndex\ProfileManager;
use AIPT\Core\Services\UiVariantService;
use AIPT\Utils\Credentials\CredentialResolver;
use AIPT\Utils\Credentials\CredentialSource;

/**
 * Builds localization data for JavaScript
 *
 * @internal Used by AssetEnqueuer
 *
 * Note: Since 2.6.0, payloads include `connectors`, `wpSupportsAi`, `wpVersion`,
 * `connectorsApiAvailable`, and `connectorsAdminUrl`. No raw API key strings ever
 * appear in the serialized output.
 */
class LocalizationDataBuilder {

	/**
	 * Init instance
	 *
	 * @var Init
	 */
	private $init;

	/**
	 * Options cache service
	 *
	 * @var OptionsCache
	 */
	private $options_cache;

	/**
	 * UI variant service (injected; lazily instantiated on first use when null).
	 *
	 * @var UiVariantService|null
	 */
	private ?UiVariantService $ui_variant_service;

	/**
	 * Constructor
	 *
	 * @param Init                  $init Init instance.
	 * @param OptionsCache          $options_cache Options cache service.
	 * @param UiVariantService|null $ui_variant_service Optional UI variant service for DI/test override.
	 */
	public function __construct(
		Init $init,
		OptionsCache $options_cache,
		?UiVariantService $ui_variant_service = null
	) {
		$this->init               = $init;
		$this->options_cache      = $options_cache;
		$this->ui_variant_service = $ui_variant_service;
	}

	/**
	 * Build metabox localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildMetaboxData(): array {

		$system_prompt       = $this->options_cache->get( 'aipt_system_prompt', Activator::get_default_value( 'system_prompt' ) );
		$user_prompt         = $this->options_cache->get( 'aipt_user_prompt', Activator::get_default_value( 'user_prompt' ) );
		$short_system_prompt = $this->options_cache->get( 'aipt_short_system_prompt', Activator::get_default_value( 'short_system_prompt' ) );
		$short_user_prompt   = $this->options_cache->get( 'aipt_short_user_prompt', Activator::get_default_value( 'short_user_prompt' ) );
		$full_system_prompt  = $this->options_cache->get( 'aipt_full_system_prompt', Activator::get_default_value( 'full_system_prompt' ) );
		$full_user_prompt    = $this->options_cache->get( 'aipt_full_user_prompt', Activator::get_default_value( 'full_user_prompt' ) );

		$provider = $this->options_cache->get( 'aipt_api_provider', Activator::get_default_value( 'api_provider' ) );
		$model    = $this->options_cache->get( 'aipt_model', Activator::get_default_value( 'model' ) );

		return array(
			'productId'           => get_the_ID(),
			'nonce'               => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'             => admin_url( 'admin-ajax.php' ),
			'adminUrl'            => admin_url(),
			'restUrl'             => esc_url_raw( rest_url() ),
			'version'             => AIPT_VERSION,
			'system_prompt'       => $system_prompt,
			'user_prompt'         => $user_prompt,
			'short_system_prompt' => $short_system_prompt,
			'short_user_prompt'   => $short_user_prompt,
			'full_system_prompt'  => $full_system_prompt,
			'full_user_prompt'    => $full_user_prompt,
			'provider'            => $provider,
			'model'               => $model,
			'settingsUrl'         => admin_url( 'admin.php?page=aipt-settings&tab=content-settings' ),
		);
	}

	/**
	 * Build settings page localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildSettingsData(): array {

		$settings = $this->init->get_cached_settings_array();

		$premium_manager = $this->init->getPremiumFeatureManager();

		$aipt_data = array(
			'nonce'    => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'  => admin_url( 'admin-ajax.php' ),
			'adminUrl' => admin_url(),
			'restUrl'  => esc_url_raw( rest_url() ),
			'settings' => $settings,
			'onboarding' => $this->init->get_onboarding_status_fast(),
			'fs'       => $this->buildFsBlock(),
		);

		$aipt_data = $premium_manager->extendAiptData( $aipt_data );
		$aipt_data = array_merge( $aipt_data, $this->buildConnectorsBlock() );

		return $aipt_data;
	}

	/**
	 * Build toolkit page localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildToolkitData(): array {
		$current_user = wp_get_current_user();

		$wc_stats = $this->buildWooCommerceStats();

		$toolkit_data = array(
			'nonce'       => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'     => admin_url( 'admin-ajax.php' ),
			'adminUrl'    => admin_url(),
			'restUrl'     => esc_url_raw( rest_url() ),
			'version'     => AIPT_VERSION,
			'onboarding'  => $this->init->get_onboarding_status_fast(),
			'user'        => array(
				'display_name' => $current_user->display_name,
				'user_login'   => $current_user->user_login,
				'first_name'   => $current_user->first_name,
			),
			'woocommerce' => $wc_stats,
			'fs'          => $this->buildFsBlock(),
			'uiVariant'   => $this->buildUiVariantData(),
			'uiPrefs'     => $this->buildUiPrefsData(),

			'widgetOrder' => \AIPT\Api\Dashboard\DashboardController::get_user_widget_order(
				(int) $current_user->ID
			),
		);

		return array_merge( $toolkit_data, $this->buildConnectorsBlock() );
	}

	/**
	 * Build WooCommerce statistics
	 *
	 * @return array WooCommerce statistics.
	 */
	private function buildWooCommerceStats(): array {
		$wc_stats = array(
			'total_products'      => 0,
			'published_products'  => 0,
			'draft_products'      => 0,
			'missing_description' => 0,
			'ai_generated'        => 0,
			'recent_products'     => 0,
			'out_of_stock'        => 0,
			'no_tags'             => 0,
		);

		if ( class_exists( 'WooCommerce' ) ) {
			global $wpdb;

			try {

				$total_products = wp_count_posts( 'product' );
				if ( $total_products ) {
					$wc_stats['total_products']     = intval( $total_products->publish ?? 0 ) + intval( $total_products->draft ?? 0 ) + intval( $total_products->pending ?? 0 ) + intval( $total_products->private ?? 0 );
					$wc_stats['published_products'] = intval( $total_products->publish ?? 0 );
					$wc_stats['draft_products']     = intval( $total_products->draft ?? 0 );
				}
			} catch ( Exception $e ) {
			}

			$stats_cache_key = 'aipt_toolkit_stats';
			$cached_stats    = get_transient( $stats_cache_key );

			if ( is_array( $cached_stats ) ) {
				$wc_stats['missing_description'] = intval( $cached_stats['missing_description'] ?? 0 );
				$wc_stats['ai_generated']        = intval( $cached_stats['ai_generated'] ?? 0 );
				$wc_stats['recent_products']     = intval( $cached_stats['recent_products'] ?? 0 );
				$wc_stats['out_of_stock']        = intval( $cached_stats['out_of_stock'] ?? 0 );
				$wc_stats['no_tags']             = intval( $cached_stats['no_tags'] ?? 0 );
			} else {
				$fresh_stats = array(
					'missing_description' => 0,
					'ai_generated'        => 0,
					'recent_products'     => 0,
					'out_of_stock'        => 0,
					'no_tags'             => 0,
				);

				try {

					$missing_desc = $wpdb->get_var(
						"SELECT COUNT(DISTINCT p.ID)
                        FROM {$wpdb->posts} p
                        WHERE p.post_type = 'product'
                        AND p.post_status IN ('publish', 'draft')
                        AND (p.post_content = '' OR p.post_content IS NULL OR TRIM(p.post_content) = '')"
					);

					if ( null !== $missing_desc ) {
						$fresh_stats['missing_description'] = intval( $missing_desc );
					}
				} catch ( Exception $e ) {
				}

				try {

					$ai_generated = $wpdb->get_var(
						"SELECT COUNT(DISTINCT post_id)
                        FROM {$wpdb->postmeta}
                        WHERE meta_key = '_aipt_generated'
                        AND meta_value = '1'"
					);

					if ( null !== $ai_generated ) {
						$fresh_stats['ai_generated'] = intval( $ai_generated );
					}
				} catch ( Exception $e ) {
				}

				try {
					$seven_days_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );

					$recent = $wpdb->get_var(
						$wpdb->prepare(
							"SELECT COUNT(*)
                            FROM {$wpdb->posts}
                            WHERE post_type = 'product'
                            AND post_status IN ('publish', 'draft')
                            AND post_date >= %s",
							$seven_days_ago
						)
					);

					if ( null !== $recent ) {
						$fresh_stats['recent_products'] = intval( $recent );
					}
				} catch ( Exception $e ) {
				}

				try {

					$out_of_stock = $wpdb->get_var(
						"SELECT COUNT(*)
                        FROM {$wpdb->posts} p
                        INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
                        WHERE p.post_type = 'product'
                        AND p.post_status = 'publish'
                        AND pm.meta_key = '_stock_status'
                        AND pm.meta_value = 'outofstock'"
					);

					if ( null !== $out_of_stock ) {
						$fresh_stats['out_of_stock'] = intval( $out_of_stock );
					}
				} catch ( Exception $e ) {
				}

				try {

					$no_tags = $wpdb->get_var(
						"SELECT COUNT(*)
                        FROM {$wpdb->posts} p
                        WHERE p.post_type = 'product'
                        AND p.post_status IN ('publish', 'draft')
                        AND p.ID NOT IN (
                            SELECT DISTINCT tr.object_id
                            FROM {$wpdb->term_relationships} tr
                            INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
                            WHERE tt.taxonomy = 'product_tag'
                        )"
					);

					if ( null !== $no_tags ) {
						$fresh_stats['no_tags'] = intval( $no_tags );
					}
				} catch ( Exception $e ) {
				}

				set_transient( $stats_cache_key, $fresh_stats, 15 * MINUTE_IN_SECONDS );

				$wc_stats['missing_description'] = $fresh_stats['missing_description'];
				$wc_stats['ai_generated']        = $fresh_stats['ai_generated'];
				$wc_stats['recent_products']     = $fresh_stats['recent_products'];
				$wc_stats['out_of_stock']        = $fresh_stats['out_of_stock'];
				$wc_stats['no_tags']             = $fresh_stats['no_tags'];
			}
		}

		return $wc_stats;
	}

	/**
	 * Build description generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildDescriptionGeneratorData(): array {

		$settings = $this->init->get_cached_advanced_settings_array();

		$premium_manager = $this->init->getPremiumFeatureManager();

		$aipt_data = array(
			'nonce'                => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'              => admin_url( 'admin-ajax.php' ),
			'adminUrl'             => admin_url(),
			'restUrl'              => esc_url_raw( rest_url() ),
			'version'              => AIPT_VERSION,
			'settings'             => $settings,
			'locale'               => $this->init->convert_locale_format( get_locale() ),
			'currency'             => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'USD',
			'fs'                   => $this->buildFsBlock(),
			'bulk_terms_accepted'  => $this->options_cache->get( 'aipt_bulk_terms_accepted', '0' ),
			'uiVariant'            => $this->buildUiVariantData(),
			'uiPrefs'              => $this->buildUiPrefsData(),
		);

		$aipt_data = array_merge( $aipt_data, $this->buildConnectorsBlock() );

		$aipt_data = $premium_manager->extendAiptData( $aipt_data );

		return $aipt_data;
	}

	/**
	 * Build standard generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildStandardGeneratorData(): array {
		return $this->buildStandardGeneratorDataImpl();
	}

	/**
	 * Build FAQ generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildFAQGeneratorData(): array {

		$settings = $this->init->get_cached_standard_settings_array();

		$premium_manager = $this->init->getPremiumFeatureManager();

		$faq_defaults = array(
			'faqCount'    => FAQGeneratorService::DEFAULT_FAQ_COUNT,
			'faqFormat'   => Init::get_default_faq_format(),
			'faqMode'     => Init::get_default_faq_mode(),
			'writeMode'   => 'append',
			'targetField' => 'post_content',
		);

		$aipt_data = array(
			'nonce'                => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'              => admin_url( 'admin-ajax.php' ),
			'adminUrl'             => admin_url(),
			'restUrl'              => esc_url_raw( rest_url() ),
			'restNonce'            => wp_create_nonce( 'wp_rest' ),
			'version'              => AIPT_VERSION,
			'settings'             => $settings,
			'locale'               => $this->init->convert_locale_format( get_locale() ),
			'currency'             => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'USD',
			'faqDefaults'          => $faq_defaults,
			'maxFaqCount'          => FAQGeneratorService::MAX_FAQ_COUNT,
			'faqDisplaySettingsEndpoint' => esc_url_raw( rest_url( 'aipt/v1/faq-display-settings' ) ),
			'customFieldsEndpoint' => esc_url_raw( rest_url( 'aipt/v1/custom-fields' ) ),
			'fs'                   => $this->buildFsBlock(),
			'uiVariant'            => $this->buildUiVariantData(),
			'uiPrefs'              => $this->buildUiPrefsData(),
		);

		$aipt_data = array_merge( $aipt_data, $this->buildConnectorsBlock() );

		$aipt_data = $premium_manager->extendAiptData( $aipt_data );

		return $aipt_data;
	}

	/**
	 * Build title generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildTitleGeneratorData(): array {
		return $this->buildStandardGeneratorDataImpl();
	}

	/**
	 * Build SEO Meta Generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildSeoMetaGeneratorData(): array {
		return $this->buildStandardGeneratorDataImpl();
	}
	/**
	 * Build taxonomy generator localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildTaxonomyGeneratorData(): array {
		return $this->buildStandardGeneratorDataImpl();
	}

	public function buildImageAltGeneratorData(): array {
		return $this->buildStandardGeneratorDataImpl();
	}

	/**
	 * Build standard generator data (shared implementation)
	 *
	 * Used by buildStandardGeneratorData, buildTitleGeneratorData, and buildSeoMetaGeneratorData.
	 *
	 * @return array Localization data array.
	 */
	private function buildStandardGeneratorDataImpl(): array {

		$settings = $this->init->get_cached_standard_settings_array();

		$premium_manager = $this->init->getPremiumFeatureManager();

		$aipt_data = array(
			'nonce'     => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'   => admin_url( 'admin-ajax.php' ),
			'adminUrl'  => admin_url(),
			'version'   => AIPT_VERSION,
			'settings'  => $settings,
			'locale'    => $this->init->convert_locale_format( get_locale() ),
			'currency'  => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'USD',
			'fs'        => $this->buildFsBlock(),
			'uiVariant' => $this->buildUiVariantData(),
			'uiPrefs'   => $this->buildUiPrefsData(),
		);

		$aipt_data = array_merge( $aipt_data, $this->buildConnectorsBlock() );

		$aipt_data = $premium_manager->extendAiptData( $aipt_data );

		return $aipt_data;
	}

	/**
	 * Build UI preferences localization data for the Phase 1 provider-cost chip.
	 *
	 * Exposed as `window.aiptData.uiPrefs` so ProviderCostChip can honor the
	 * `aipt_show_provider_cost_usd` opt-in without a REST round-trip on first
	 * paint. Matches the `UiPrefsLocalizedData` type in `src/Types/uiNext.ts`.
	 *
	 * @since 2.6.1
	 * @return array{showProviderCostUsd:bool}
	 */
	private function buildUiPrefsData(): array {
		$default = array( 'showProviderCostUsd' => false );

		if ( ! function_exists( 'is_user_logged_in' ) || ! is_user_logged_in() ) {
			return $default;
		}

		$value = get_user_meta( (int) get_current_user_id(), 'aipt_show_provider_cost_usd', true );

		return array(
			'showProviderCostUsd' => ( '1' === (string) $value ),
		);
	}

	/**
	 * Build UI variant localization data for the Phase 0/1 runtime variant gate.
	 *
	 * Exposed as `window.aiptData.uiVariant` for the `useUiVariant` React hook
	 * (`src/Components/Common/UiVariantGate/useUiVariant.ts`). Matches the
	 * `UiVariantLocalizedData` type in `src/Types/uiVariant.ts`.
	 *
	 * @since 2.6.1
	 * @return array{userVariant:?string,siteVariant:string,isFreshInstall:bool,modalSnoozed:bool,restPath:string}
	 */
	private function buildUiVariantData(): array {
		$default = array(
			'userVariant'    => null,
			'siteVariant'    => 'legacy',
			'isFreshInstall' => false,
			'modalSnoozed'   => false,
			'restPath'       => 'aipt/v1/ui-variant',
		);

		if ( ! function_exists( 'is_user_logged_in' ) || ! is_user_logged_in() ) {
			return $default;
		}

		try {

			if ( null === $this->ui_variant_service ) {
				$this->ui_variant_service = new UiVariantService();
			}
			$service = $this->ui_variant_service;
			$user_id           = (int) get_current_user_id();
			$first_run         = get_option( 'aipt_first_run_completed', null );

			return array(
				'userVariant'    => $service->getUserVariant( $user_id ),
				'siteVariant'    => $service->getSiteVariant(),
				'isFreshInstall' => null === $first_run,
				'modalSnoozed'   => $service->isModalSnoozed( $user_id ),
				'restPath'       => 'aipt/v1/ui-variant',
			);
		} catch ( \Throwable $e ) {
			return $default;
		}
	}

	/**
	 * Build AI Store Index localization data
	 *
	 * Preloads all Store Index data to eliminate initial API calls.
	 * This includes profiles, status, settings, filter options, and available fields.
	 *
	 * @return array Localization data array.
	 */
	public function buildStoreIndexData(): array {
		$fs_manager      = $this->init->getFsManager();
		$profile_manager = new \AIPT\Api\StoreIndex\ProfileManager();

		$profile_manager->ensureDefaultExists();

		$profiles = $profile_manager->getAll();

		$active_profile    = null;
		$active_profile_id = null;
		foreach ( $profiles as $profile ) {
			if ( ! empty( $profile['is_active'] ) ) {
				$active_profile    = $profile;
				$active_profile_id = (int) $profile['id'];
				break;
			}
		}

		if ( ! $active_profile_id ) {
			$default_profile = $profile_manager->getDefault();
			if ( $default_profile ) {
				$active_profile_id = (int) $default_profile['id'];
				$active_profile    = $default_profile;
			}
		}

		$index_settings = new \AIPT\Api\StoreIndex\IndexSettings( $active_profile_id );
		$status         = $index_settings->getStatus();
		$settings       = $index_settings->getAll();

		if ( ( $status['status'] ?? '' ) === 'building' ) {
			$job_id   = (int) ( $status['build_job_id'] ?? 0 );
			$progress = (int) ( $status['progress_percent'] ?? 0 );

			$status['needs_manual_process'] = $job_id > 0 && $progress < 100;
		}

		$global_settings = $profile_manager->getGlobalSettings();

		$global_building_status = $profile_manager->getGlobalBuildingStatus();

		$available_fields = $this->getAvailableFieldsData();

		$filter_options = $this->getFilterOptionsData();

		$next_scheduled_run = null;
		if ( $fs_manager->isUsingPremiumFiles() && class_exists( '\AIPT\Premium\StoreIndex\ScheduledRebuildService' ) ) {
			$next_scheduled_run = \AIPT\Premium\StoreIndex\ScheduledRebuildService::getNextScheduledRun();
		}

		$index_premium = array();
		if ( $fs_manager->isUsingPremiumFiles() && class_exists( '\AIPT\Premium\StoreIndex\PremiumIndexCapability' ) ) {
			$index_premium = \AIPT\Premium\StoreIndex\PremiumIndexCapability::getIndexPremiumData();
		}

		return array(
			'nonce'    => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'  => admin_url( 'admin-ajax.php' ),
			'adminUrl' => admin_url(),
			'restUrl'  => esc_url_raw( rest_url() ),
			'version'  => AIPT_VERSION,
			'locale'   => $this->init->convert_locale_format( get_locale() ),
			'wpCron'   => array(
				'enabled'   => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
				'alternate' => ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ),
			),
			'fs'       => array_merge(
				$this->buildFsBlock(),
				array(

					'features' => array(
						'scheduledIndexUpdates' => $fs_manager->isUsingPremiumFiles(),
					),
				)
			),

			'initialSettings' => array(
				'enabled'           => $global_settings['enabled'] ?? false,

				'scheduledEnabled'  => (bool) ( $global_settings['scheduled_enabled'] ?? false ),
				'scheduleFrequency' => $global_settings['schedule_frequency'] ?? 'daily',
				'nextScheduledRun'  => $next_scheduled_run,
			),

			'preload' => array(
				'profiles'             => $profiles,
				'activeProfileId'      => $active_profile_id,
				'status'               => $status,
				'settings'             => $settings,
				'globalSettings'       => $global_settings,
				'globalBuildingStatus' => $global_building_status,
				'availableFields'      => $available_fields['fields'],
				'fieldGroups'          => $available_fields['groups'],
				'defaultFields'        => $available_fields['default_fields'],
				'filterOptions'        => $filter_options,
			),

			'premium' => $index_premium,
		);
	}

	/**
	 * Get available fields data for Store Index.
	 *
	 * This is static data that rarely changes.
	 * Premium field information is dynamically determined via filter hooks.
	 *
	 * @return array Fields, groups, and default fields.
	 */
	private function getAvailableFieldsData(): array {
		$mandatory_fields    = ProfileManager::getMandatoryFields();
		$premium_only_fields = ProfileManager::getPremiumOnlyFields();

		return array(
			'fields'         => array(
				array(
					'key'         => 'sku',
					'label'       => __( 'SKU', 'ai-product-tools' ),
					'description' => __( 'Product stock keeping unit.', 'ai-product-tools' ),
					'group'       => 'basic',
					'mandatory'   => in_array( 'sku', $mandatory_fields, true ),
					'is_premium'  => in_array( 'sku', $premium_only_fields, true ),
				),
				array(
					'key'         => 'title',
					'label'       => __( 'Title', 'ai-product-tools' ),
					'description' => __( 'Product name/title. Required for product identification.', 'ai-product-tools' ),
					'group'       => 'basic',
					'mandatory'   => in_array( 'title', $mandatory_fields, true ),
					'is_premium'  => in_array( 'title', $premium_only_fields, true ),
				),
				array(
					'key'         => 'short_description',
					'label'       => __( 'Short Description', 'ai-product-tools' ),
					'description' => __( 'Product excerpt/short description. Recommended for better AI context.', 'ai-product-tools' ),
					'group'       => 'content',
					'mandatory'   => in_array( 'short_description', $mandatory_fields, true ),
					'is_premium'  => in_array( 'short_description', $premium_only_fields, true ),
				),
				array(
					'key'         => 'description',
					'label'       => __( 'Full Description', 'ai-product-tools' ),
					'description' => __( 'Complete product description.', 'ai-product-tools' ),
					'group'       => 'content',
					'mandatory'   => in_array( 'description', $mandatory_fields, true ),
					'is_premium'  => in_array( 'description', $premium_only_fields, true ),
				),
				array(
					'key'         => 'price',
					'label'       => __( 'Price', 'ai-product-tools' ),
					'description' => __( 'Regular and sale prices. Required for product recommendations.', 'ai-product-tools' ),
					'group'       => 'basic',
					'mandatory'   => in_array( 'price', $mandatory_fields, true ),
					'is_premium'  => in_array( 'price', $premium_only_fields, true ),
				),
				array(
					'key'         => 'stock_status',
					'label'       => __( 'Stock Status', 'ai-product-tools' ),
					'description' => __( 'In stock, out of stock, on backorder.', 'ai-product-tools' ),
					'group'       => 'inventory',
					'mandatory'   => in_array( 'stock_status', $mandatory_fields, true ),
					'is_premium'  => in_array( 'stock_status', $premium_only_fields, true ),
				),
				array(
					'key'         => 'stock_quantity',
					'label'       => __( 'Stock Quantity', 'ai-product-tools' ),
					'description' => __( 'Number of items in stock.', 'ai-product-tools' ),
					'group'       => 'inventory',
					'mandatory'   => in_array( 'stock_quantity', $mandatory_fields, true ),
					'is_premium'  => in_array( 'stock_quantity', $premium_only_fields, true ),
				),
				array(
					'key'         => 'categories',
					'label'       => __( 'Categories', 'ai-product-tools' ),
					'description' => __( 'Product category assignments. Required for filtering.', 'ai-product-tools' ),
					'group'       => 'taxonomy',
					'mandatory'   => in_array( 'categories', $mandatory_fields, true ),
					'is_premium'  => in_array( 'categories', $premium_only_fields, true ),
				),
				array(
					'key'         => 'tags',
					'label'       => __( 'Tags', 'ai-product-tools' ),
					'description' => __( 'Product tag assignments.', 'ai-product-tools' ),
					'group'       => 'taxonomy',
					'mandatory'   => in_array( 'tags', $mandatory_fields, true ),
					'is_premium'  => in_array( 'tags', $premium_only_fields, true ),
				),
				array(
					'key'         => 'attributes',
					'label'       => __( 'Attributes', 'ai-product-tools' ),
					'description' => __( 'Product attributes (color, size, etc.).', 'ai-product-tools' ),
					'group'       => 'taxonomy',
					'mandatory'   => in_array( 'attributes', $mandatory_fields, true ),
					'is_premium'  => in_array( 'attributes', $premium_only_fields, true ),
				),
			),
			'groups'         => array(
				array(
					'key'   => 'basic',
					'label' => __( 'Basic Info', 'ai-product-tools' ),
				),
				array(
					'key'   => 'content',
					'label' => __( 'Content', 'ai-product-tools' ),
				),
				array(
					'key'   => 'inventory',
					'label' => __( 'Inventory', 'ai-product-tools' ),
				),
				array(
					'key'   => 'taxonomy',
					'label' => __( 'Taxonomy', 'ai-product-tools' ),
				),
			),
			'default_fields' => \AIPT\Api\StoreIndex\ProfileManager::getDefaultIncludedFields(),
		);
	}

	/**
	 * Get filter options data for Store Index.
	 *
	 * Uses smart limits to handle large product catalogs:
	 * - Max 100 categories (ordered by product count)
	 * - Max 50 tags (ordered by product count)
	 *
	 * @return array Filter options with categories, tags, and total products.
	 */
	private function getFilterOptionsData(): array {

		$categories = array();
		$cat_terms  = get_terms(
			array(
				'taxonomy'   => 'product_cat',
				'hide_empty' => true,
				'orderby'    => 'count',
				'order'      => 'DESC',
				'number'     => 100,
			)
		);

		if ( ! is_wp_error( $cat_terms ) ) {
			foreach ( $cat_terms as $term ) {
				$categories[] = array(
					'id'    => (int) $term->term_id,
					'name'  => $term->name,
					'count' => (int) $term->count,
				);
			}
		}

		$tags      = array();
		$tag_terms = get_terms(
			array(
				'taxonomy'   => 'product_tag',
				'hide_empty' => true,
				'orderby'    => 'count',
				'order'      => 'DESC',
				'number'     => 50,
			)
		);

		if ( ! is_wp_error( $tag_terms ) ) {
			foreach ( $tag_terms as $term ) {
				$tags[] = array(
					'id'    => (int) $term->term_id,
					'name'  => $term->name,
					'count' => (int) $term->count,
				);
			}
		}

		$counts         = wp_count_posts( 'product' );
		$total_products = isset( $counts->publish ) ? (int) $counts->publish : 0;

		return array(
			'categories'     => $categories,
			'tags'           => $tags,
			'total_products' => $total_products,
		);
	}

	/**
	 * Build automation jobs localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildAutomationJobsData(): array {

		$settings = $this->init->get_cached_settings_array();

		$premium_manager = $this->init->getPremiumFeatureManager();

		$onboarding = $this->init->get_onboarding_status_fast();

		$aipt_data = array(
			'nonce'    => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'  => admin_url( 'admin-ajax.php' ),
			'adminUrl' => admin_url(),
			'restUrl'  => esc_url_raw( rest_url() ),
			'version'  => AIPT_VERSION,
			'settings'        => $settings,
			'locale'          => $this->init->convert_locale_format( get_locale() ),
			'currency'        => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'USD',
			'currency_symbol' => function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '$',

			'onboarding'                => $onboarding,
			'aipt_setup_completed'      => $onboarding['setupCompleted'] ? '1' : '0',
			'aipt_needs_setup'          => $onboarding['setupCompleted'] ? '0' : '1',

			'aipt_site_registered'      => ( get_option( 'aipt_site_registered', '0' ) === '1' || get_option( 'aipt_credit_registered', false ) ) ? '1' : '0',
			'aipt_bulk_terms_accepted'  => $this->options_cache->get( 'aipt_bulk_terms_accepted', '0' ),

			'bulk_terms_accepted'       => $this->options_cache->get( 'aipt_bulk_terms_accepted', '0' ),
			'wpCron'                    => array(
				'enabled'   => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
				'alternate' => ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ),
			),
			'fs'                        => $this->buildFsBlock(),
		);

		$aipt_data = $premium_manager->extendAiptData( $aipt_data );

		return $aipt_data;
	}

	/**
	 * Build chatbot admin localization data
	 *
	 * @return array Localization data array.
	 */
	public function buildChatbotAdminData(): array {

		$chatbot_settings = new \AIPT\Api\Chatbot\ChatbotSettings();

		$providers = array( 'openai', 'gemini', 'openrouter', 'claude' );
		$available_providers = array();
		foreach ( $providers as $provider ) {
			$api_key_getter = array( '\AIPT\Utils\ApiKeyManager', 'get_' . $provider . '_key' );
			$available_providers[ $provider ] = is_callable( $api_key_getter ) && ! empty( call_user_func( $api_key_getter ) );
		}

		$profile_manager = new \AIPT\Api\StoreIndex\ProfileManager();

		$profile_manager->ensureDefaultExists();
		$all_profiles = $profile_manager->getAll();
		$index_profiles = array_map(
			function ( $profile ) {

				$profile_id = isset( $profile['id'] ) ? (string) $profile['id'] : '';
				$profile_name = isset( $profile['name'] ) ? (string) $profile['name'] : '';

				$product_count = 0;
				$index_status = 'idle';
				if ( isset( $profile['status']['product_count'] ) ) {
					$product_count = max( 0, (int) $profile['status']['product_count'] );
				}

				$valid_statuses = array( 'idle', 'building', 'completed' );
				if ( isset( $profile['status']['status'] ) && in_array( $profile['status']['status'], $valid_statuses, true ) ) {
					$index_status = (string) $profile['status']['status'];
				}
				return array(
					'id'            => $profile_id,
					'name'          => $profile_name,
					'product_count' => $product_count,
					'status'        => $index_status,
				);
			},
			$all_profiles
		);

		array_unshift(
			$index_profiles,
			array(
				'id'            => '0',
				'name'          => __( 'Auto (Active Profile)', 'ai-product-tools' ),
				'product_count' => 0,
				'status'        => 'idle',
			)
		);

		$chatbot_premium = array();
		if ( class_exists( '\\AIPT\\Premium\\PremiumFeatureManager', false ) && class_exists( '\AIPT\Premium\Chatbot\PremiumChatbotCapability' ) ) {
			$chatbot_premium = \AIPT\Premium\Chatbot\PremiumChatbotCapability::getChatbotPremiumData();
		}

		return array(
			'nonce'              => wp_create_nonce( 'wp_rest' ),
			'ajaxurl'            => admin_url( 'admin-ajax.php' ),
			'adminUrl'           => admin_url(),
			'restUrl'            => esc_url_raw( rest_url() ),
			'version'            => AIPT_VERSION,
			'locale'             => $this->init->convert_locale_format( get_locale() ),
			'settings'           => $chatbot_settings->getAll(),
			'defaults'           => \AIPT\Api\Chatbot\ChatbotSettings::getDefaults(),
			'availableProviders' => $available_providers,
			'indexProfiles'      => $index_profiles,
			'endpoints'          => array(
				'settings'     => rest_url( 'aipt/v1/chatbot/settings' ),
				'settingsFull' => rest_url( 'aipt/v1/chatbot/settings/full' ),
				'messages'     => rest_url( 'aipt/v1/chatbot/messages' ),
				'detectColors' => rest_url( 'aipt/v1/chatbot/detect-brand-colors' ),
			),
			'fs'                 => $this->buildFsBlock(),

			'premium'            => $chatbot_premium,
		);
	}

	/**
	 * Build chatbot widget localization data for frontend
	 *
	 * @since 2.5.0
	 * @return array Localization data array.
	 */
	public function buildChatbotWidgetData(): array {

		$chatbot_settings = new \AIPT\Api\Chatbot\ChatbotSettings();
		$settings         = $chatbot_settings->getAll();
		$defaults         = \AIPT\Api\Chatbot\ChatbotSettings::getDefaults();

		$terms_url = null;
		$privacy_url = null;
		if ( ! empty( $settings['general']['terms_page_id'] ) ) {
			$permalink = get_permalink( $settings['general']['terms_page_id'] );
			$terms_url = ( $permalink !== false ) ? esc_url( $permalink ) : null;
		}
		if ( ! empty( $settings['general']['privacy_page_id'] ) ) {
			$permalink = get_permalink( $settings['general']['privacy_page_id'] );
			$privacy_url = ( $permalink !== false ) ? esc_url( $permalink ) : null;
		}

		return array(
			'nonce'        => wp_create_nonce( 'wp_rest' ),
			'restUrl'      => trailingslashit( rest_url() ),
			'asyncEnabled' => (bool) apply_filters( ChatbotConstants::ASYNC_ENABLED_FILTER, false ),

			'is_rtl'       => is_rtl(),
			'dir'          => is_rtl() ? 'rtl' : 'ltr',
			'locale'       => function_exists( 'determine_locale' ) ? determine_locale() : get_locale(),
			'settings'     => array(
				'enabled'                  => $settings['general']['enabled'] ?? $defaults['general']['enabled'],
				'bot_name'                 => $settings['general']['bot_name'] ?? $defaults['general']['bot_name'],
				'greeting_message'         => $settings['general']['greeting_message'] ?? $defaults['general']['greeting_message'],
				'language'                 => $settings['general']['language'] ?? $defaults['general']['language'],
				'pre_chat_form_enabled'    => $settings['general']['pre_chat_form_enabled'] ?? $defaults['general']['pre_chat_form_enabled'],
				'starter_messages_enabled' => $settings['general']['starter_messages_enabled'] ?? $defaults['general']['starter_messages_enabled'],
				'starter_messages'         => $settings['general']['starter_messages'] ?? $defaults['general']['starter_messages'],
				'terms_url'                => $terms_url,
				'privacy_url'              => $privacy_url,

				'whatsapp_number'          => $settings['general']['whatsapp_number'] ?? '',
				'placement'             => array(
					'position' => $settings['placement']['position'] ?? $defaults['placement']['position'],
					'offset_x' => $settings['placement']['offset_x'] ?? $defaults['placement']['offset_x'],
					'offset_y' => $settings['placement']['offset_y'] ?? $defaults['placement']['offset_y'],
					'device'   => $settings['placement']['device'] ?? $defaults['placement']['device'],
				),
				'appearance'       => array(
					'colors'        => array(
						'primary'               => $settings['appearance']['colors']['primary'] ?? $defaults['appearance']['colors']['primary'],
						'secondary'             => $settings['appearance']['colors']['secondary'] ?? $defaults['appearance']['colors']['secondary'],
						'background'            => $settings['appearance']['colors']['background'] ?? $defaults['appearance']['colors']['background'],
						'text'                  => $settings['appearance']['colors']['text'] ?? $defaults['appearance']['colors']['text'],
						'user_bubble'           => $settings['appearance']['colors']['user_bubble'] ?? $defaults['appearance']['colors']['user_bubble'],
						'bot_bubble'            => $settings['appearance']['colors']['bot_bubble'] ?? $defaults['appearance']['colors']['bot_bubble'],
						'header_bg'             => $settings['appearance']['colors']['header_bg'] ?? $defaults['appearance']['colors']['header_bg'],
						'footer_bg'             => $settings['appearance']['colors']['footer_bg'] ?? $defaults['appearance']['colors']['footer_bg'],
						'button_hover'          => $settings['appearance']['colors']['button_hover'] ?? $defaults['appearance']['colors']['button_hover'],
						'online_indicator'      => $settings['appearance']['colors']['online_indicator'] ?? $defaults['appearance']['colors']['online_indicator'],
						'launcher_bg'           => $settings['appearance']['colors']['launcher_bg'] ?? $defaults['appearance']['colors']['launcher_bg'],
						'launcher_icon_color'   => $settings['appearance']['colors']['launcher_icon_color'] ?? $defaults['appearance']['colors']['launcher_icon_color'],
						'attention_bubble_bg'   => $settings['appearance']['colors']['attention_bubble_bg'] ?? $defaults['appearance']['colors']['attention_bubble_bg'],
						'attention_bubble_text' => $settings['appearance']['colors']['attention_bubble_text'] ?? $defaults['appearance']['colors']['attention_bubble_text'],
						'attention_badge_bg'    => $settings['appearance']['colors']['attention_badge_bg'] ?? $defaults['appearance']['colors']['attention_badge_bg'],
						'attention_badge_text'  => $settings['appearance']['colors']['attention_badge_text'] ?? $defaults['appearance']['colors']['attention_badge_text'],
						'hero_bg'              => $settings['appearance']['colors']['hero_bg'] ?? $defaults['appearance']['colors']['hero_bg'],
					),
					'font_family'              => $settings['appearance']['font_family'] ?? $defaults['appearance']['font_family'],
					'border_radius'            => $settings['appearance']['border_radius'] ?? $defaults['appearance']['border_radius'],
					'custom_css'               => $settings['appearance']['custom_css'] ?? $defaults['appearance']['custom_css'],
					'launcher_icon'            => $settings['appearance']['launcher_icon'] ?? $defaults['appearance']['launcher_icon'],
					'custom_launcher_icon_url' => $settings['appearance']['custom_launcher_icon_url'] ?? $defaults['appearance']['custom_launcher_icon_url'],
					'profile_picture_url'      => $settings['appearance']['profile_picture_url'] ?? $defaults['appearance']['profile_picture_url'],

					'hero_image_url'           => $settings['appearance']['hero_image_url'] ?? $defaults['appearance']['hero_image_url'],

					'launcher_animation'       => $settings['appearance']['launcher_animation'] ?? $defaults['appearance']['launcher_animation'],

					'attention_bubble_enabled' => $settings['appearance']['attention_bubble_enabled'] ?? $defaults['appearance']['attention_bubble_enabled'],
					'attention_bubble_text'    => $settings['appearance']['attention_bubble_text'] ?? $defaults['appearance']['attention_bubble_text'],
					'attention_badge_enabled'  => $settings['appearance']['attention_badge_enabled'] ?? $defaults['appearance']['attention_badge_enabled'],
					'attention_badge_text'     => $settings['appearance']['attention_badge_text'] ?? $defaults['appearance']['attention_badge_text'],
				),
				'behavior'         => array(
				'max_products'       => $settings['behavior']['max_products'] ?? $defaults['behavior']['max_products'],
				'show_view_product'  => $settings['behavior']['show_view_product'] ?? $defaults['behavior']['show_view_product'],
				'show_add_to_cart'   => $settings['behavior']['show_add_to_cart'] ?? $defaults['behavior']['show_add_to_cart'],
				'show_stock_status'  => $settings['behavior']['show_stock_status'] ?? $defaults['behavior']['show_stock_status'],
			),
				'labels'           => $chatbot_settings->getLabels(),
				'localization'     => $this->buildLocalizationConfig( $chatbot_settings, $settings, $defaults ),
			),
		);
	}

	/**
	 * Build the connectors block for localization data.
	 *
	 * Returns connector state, WP AI support flag, WP version, Connectors API
	 * availability, and admin URL for the Connectors settings page. No raw API
	 * key values, encrypted blobs, or option names are included in the output.
	 *
	 * @since 2.6.0
	 * @return array Connectors block array.
	 */
	private function buildConnectorsBlock(): array {
		$resolver        = CredentialResolver::instance();
		$providers_state = $resolver->getStatus();

		$managed_count = count(
			array_filter(
				$providers_state,
				function ( $s ) {
					return ! empty( $s['isConnectorsManaged'] );
				}
			)
		);

		$connectors_status = array(
			'hasAnyConfigured'        => $resolver->hasAnyConfiguredProvider(),
			'hasAnyUsableKey'         => $resolver->hasAnyUsableKey(),
			'hasAnyConnectorsManaged' => $managed_count > 0,
			'connectorsManagedCount'  => $managed_count,
			'providers'               => $providers_state,
		);

		return array(
			'connectors'             => $connectors_status,
			'wpSupportsAi'           => WordPressRuntimeDetector::supportsAi(),
			'wpVersion'              => get_bloginfo( 'version' ),
			'connectorsApiAvailable' => WordPressRuntimeDetector::supportsConnectorsApi(),
			'connectorsAdminUrl'     => admin_url( 'options-connectors.php' ),
			'migrationNotice'        => $this->buildMigrationNoticeBlock( $providers_state ),
		);
	}

	/**
	 * Compute eligibility for the one-click "Move local keys into Connectors"
	 * admin notice. Candidates are providers whose key is currently sourced
	 * locally AND whose Connectors DB option is empty.
	 *
	 * Notice is hidden when (a) Connectors API is not available,
	 * (b) the site-wide snooze transient is active, (c) the current user has
	 * permanently dismissed the notice via user meta, or (d) no candidates exist.
	 *
	 * @since 2.6.0
	 *
	 * @param array<string, array<string, mixed>> $providers_state Output of CredentialResolver::getStatus().
	 * @return array{eligible: bool, candidates: array<int, string>, snoozedUntil: int, permanentlyDismissed: bool}
	 */
	private function buildMigrationNoticeBlock( array $providers_state ): array {
		$connectors_available  = WordPressRuntimeDetector::supportsConnectorsApi();
		$user_id               = get_current_user_id();
		$snoozed_until         = (int) get_user_meta( $user_id, 'aipt_migration_notice_snoozed_until', true );
		$permanently_dismissed = (bool) get_user_meta( $user_id, 'aipt_connectors_migration_dismissed', true );

		$candidates = array();
		foreach ( $providers_state as $provider_id => $state ) {

			if ( 'openrouter' === $provider_id ) {
				continue;
			}
			$source = isset( $state['source'] ) ? (string) $state['source'] : '';
			if ( CredentialSource::DATABASE_LOCAL !== $source ) {
				continue;
			}
			try {
				$connector_id = \AIPT\Utils\ProviderNameMapper::toConnectorId( (string) $provider_id );
			} catch ( \InvalidArgumentException $e ) {
				continue;
			}
			$existing = get_option( 'connectors_ai_' . $connector_id . '_api_key', '' );
			if ( empty( $existing ) ) {
				$candidates[] = (string) $provider_id;
			}
		}

		$is_snoozed = $snoozed_until > time();
		$eligible   = $connectors_available
			&& ! $is_snoozed
			&& ! $permanently_dismissed
			&& ! empty( $candidates );

		return array(
			'eligible'             => $eligible,
			'candidates'           => $candidates,
			'snoozedUntil'         => $snoozed_until,
			'permanentlyDismissed' => $permanently_dismissed,
		);
	}

	/**
	 * Build the complete 'fs' block for localization data.
	 *
	 * Centralizes all Freemius plan, registration, and SDK conflict data into a
	 * single reusable block consumed by every build*Data() method.
	 *
	 * @return array Complete 'fs' block array.
	 */
	private function buildFsBlock(): array {
		$fs_manager   = \AIPT\Core\FsManager::getInstance();
		$sdk_conflict = $fs_manager->detectSdkConflict();
		$has_conflict = (bool) ( $sdk_conflict['has_conflict'] ?? false );

		return array(
			'is_premium'        => $fs_manager->isUsingPremiumFiles(),
			'is_registered'     => $fs_manager->is_registered(),
			'plan'              => $fs_manager->getPlanType(),
			'siteId'            => $fs_manager->getSiteId(),
			'pluginId'          => $fs_manager->getPluginId(),
			'licenseId'         => $fs_manager->getLicenseId(),
			'upgradeUrl'        => \admin_url( 'admin.php?page=ai-product-tools-pricing' ),
			'sdkConflict'       => $has_conflict, 
			'sdkConflictDetail' => array(
				'hasConflict'     => $has_conflict,
				'pluginName'      => $sdk_conflict['conflicting_plugin_name'] ?? '',
				'sdkVersion'      => $sdk_conflict['active_version'] ?? '',
				'sdkFile'         => $sdk_conflict['conflicting_sdk_file'] ?? '',
				'pluginFile'      => $sdk_conflict['conflicting_plugin_file'] ?? '',
				'dismissNonce'    => \wp_create_nonce( 'aipt_dismiss_sdk_conflict' ),
				'reverifyNonce'   => \wp_create_nonce( 'aipt_force_license_reverify' ),
				'reverifyUrl'     => \admin_url( 'admin-ajax.php' ),
				'pluginsAdminUrl' => \admin_url( 'plugins.php' ),
			),
		);
	}

	/**
	 * Build localization configuration for auto-detect feature.
	 *
	 * OPTIMIZATION: Instead of sending all 42 language labels (~500KB),
	 * we detect the browser language server-side and send only the
	 * detected language's labels (~5KB). This provides:
	 * - 100x smaller payload
	 * - Faster page load
	 * - Same user experience
	 *
	 * @since 2.5.3
	 * @param \AIPT\Api\Chatbot\ChatbotSettings $chatbot_settings ChatbotSettings instance.
	 * @param array $settings Current settings.
	 * @param array $defaults Default settings.
	 * @return array Localization configuration.
	 */
	private function buildLocalizationConfig( \AIPT\Api\Chatbot\ChatbotSettings $chatbot_settings, array $settings, array $defaults ): array {
		$auto_detect_enabled = $settings['localization']['auto_detect_enabled'] ?? $defaults['localization']['auto_detect_enabled'];
		$fallback_language   = $settings['localization']['fallback_language'] ?? $defaults['localization']['fallback_language'];
		$default_language    = $settings['general']['language'] ?? $defaults['general']['language'];
		$custom_labels       = $settings['localization']['custom_labels'] ?? $defaults['localization']['custom_labels'];

		$config = array(
			'auto_detect_enabled' => $auto_detect_enabled,
			'default_language'    => $default_language,
			'fallback_language'   => $fallback_language,
			'supported_languages' => \AIPT\Api\Chatbot\ChatbotSettings::getSupportedLanguages(),
		);

		if ( $auto_detect_enabled ) {

			$detected_language = \AIPT\Api\Chatbot\ChatbotSettings::detectBrowserLanguage( $fallback_language );

			$config['detected_language'] = $detected_language;

			$config['detected_labels'] = \AIPT\Api\Chatbot\ChatbotSettings::getLabelsForLanguage(
				$detected_language,
				$custom_labels
			);
		}

		return $config;
	}
}
