<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

if ( ! class_exists( 'SftcyWordaiOpenai' ) ) {

/**
 * WordAI ↔ OpenAI integration core.
 *
 * - Builds prompts from plugin settings.
 * - Calls OpenAI Chat Completions & Images APIs.
 * - Supports SSE-style streaming via WP HTTP API cURL hook.
 * - Normalizes responses for consistent downstream handling.
 *
 * NOTE: This class is self-contained and includes all helper lists (languages,
 *       title lengths, paragraph counts, etc.) so no fatal "undefined method"
 *       errors occur.
 */
class SftcyWordaiOpenai {
	/** @var bool Tracks hook registration */
	private static $initiated = false;

	/** @var string|null OpenAI API key (loaded lazily from settings) */
	public static $API_KEY = null;

	/** @var string REST endpoints */
	public static $AI_LISTMODEL_EP = 'https://api.openai.com/v1/models';
	public static $AI_COMPLETION_EP = 'https://api.openai.com/v1/chat/completions';
	public static $AI_RESPONSES_EP = 'https://api.openai.com/v1/responses';
	public static $AI_IMAGE_EP = 'https://api.openai.com/v1/images/generations';

	/** @var string Default text model */
	public static $MODEL = 'gpt-5.5';

	/** @var string Default image model */
	public static $IMAGE_MODEL = 'gpt-image-1.5';

	/** @var string Default GPT-5 reasoning effort */
	public static $gpt_5_reasoning_effort = 'low'; // low | medium | high | minimal

	/** @var bool|null Streaming flag */
	public static $STREAM = null;

	/** @var array Public output container for AJAX responders */
	public static $output = [];

	/**
	 * Bootstrap.
	 */
	public function __construct() {
		if ( is_null( self::$API_KEY ) ) {
			$data = SftcyWordaiBase::sc_wordai_get_current_api_settings();
			self::$API_KEY = ( isset( $data['wordai-openai-api-key-data'] ) && ! empty( $data['wordai-openai-api-key-data'] ) )
				? $data['wordai-openai-api-key-data']
				: null;
		}
		if ( ! self::$initiated ) {
			self::initiate_hooks();
		}
	}

	/**
	 * Hook registration (kept minimal; streaming hook added conditionally).
	 */
	private static function initiate_hooks() {
		self::$initiated = true;
	}

	/**
	 * Attach cURL streaming write callback when streaming is enabled.
	 * Must be called before issuing the HTTP request.
	 * Last used
	 */
	// public static function sc_curl_add_streaming_hook() {
	// 	add_action( 'http_api_curl', [ __CLASS__, 'sc_wordai_stream_response' ] );
	// }


	/**
	 * Register cURL streaming hook for WordAI OpenAI calls.
	 * Attaches the writefunction handler to output real-time chunks.
	 */
	public static function sc_curl_add_streaming_hook() {
		add_action( 'http_api_curl', [ __CLASS__, 'sc_wordai_stream_response' ], 10, 3 );
	}


	/**
	 * Read the user's "Streaming" setting.
	 *
	 * @return bool
	 */
	public static function sc_wordai_streaming_status() {
		$data = SftcyWordaiBase::sc_wordai_get_current_api_settings();
		return ( isset( $data['sc-wordai-streaming'] ) && intval( $data['sc-wordai-streaming'] ) === 1 );
	}

	/*───────────────────────────────────────────────────────────────
	| PROMPT FACTORY
	───────────────────────────────────────────────────────────────*/

	/**
	 * ============================================================================
	 * Generate Prompt (All Content Types)
	 * ----------------------------------------------------------------------------
	 * - Never returns boolean; always returns a string (possibly empty).
	 * - Uses content settings (language, style, tone, lengths, counts).
	 * - Produces consistent instructions for cleaner model outputs.
	 *
	 * @param string $prompt_hints  Topic / user hint text.
	 * @param string $prompt_for    One of:
	 *                              'title' | 'suggest-title' | 'content' | 'excerpt' |
	 *                              'tags' | 'seotitle' | 'seometadescription' |
	 *                              'seometafocuskeyword' | 'imagealttext' | 'image'
	 * @return string
	 * ============================================================================
	 */
	public static function generate_prompt( $prompt_hints = null, $prompt_for = null ) {
		// Always return string to avoid "messages[0].content boolean" errors during streaming.
		if ( $prompt_hints === null || $prompt_for === null ) {
			return '';
		}

		// -----------------------------------------------------------------------
		// Load content settings (your helper already sanitizes + defaults)
		// -----------------------------------------------------------------------
		$content_settings = SftcyWordaiBase::sc_wordai_get_current_content_settings();

		// Language name from code via language_list()
		$language_code = isset( $content_settings['sc-wordai-language-code'] )
			? sanitize_text_field( $content_settings['sc-wordai-language-code'] )
			: 'en-US';

		$language_map   = array_values( array_filter(
			self::language_list(),
			function( $lang ) use ( $language_code ) {
				return isset( $lang['code'] ) && $lang['code'] === $language_code;
			}
		) );

		$language_name = ! empty( $language_map ) && isset( $language_map[0]['name'] )
			? sanitize_text_field( $language_map[0]['name'] )
			: 'English (United States)';

		$writing_style = isset( $content_settings['sc-wordai-writing-style'] )
			? sanitize_text_field( $content_settings['sc-wordai-writing-style'] )
			: 'descriptive';

		$writing_tone = isset( $content_settings['sc-wordai-writing-tone'] )
			? sanitize_text_field( $content_settings['sc-wordai-writing-tone'] )
			: 'informative';

		// Title length label from your title_lengths() map (e.g., "between 50 & 60 characters")
		$title_length_key = isset( $content_settings['sc-wordai-title-length'] )
			? sanitize_text_field( $content_settings['sc-wordai-title-length'] )
			: '50n60';

		$title_lengths_map = self::title_lengths();
		$title_length_readable = isset( $title_lengths_map[ $title_length_key ] )
			? sanitize_text_field( $title_lengths_map[ $title_length_key ] )
			: 'between 50 & 60 characters';

		// Paragraph count from your content settings map (1–15)
		$paragraphs_key = isset( $content_settings['sc-wordai-content-paragraphs'] )
			? sanitize_text_field( $content_settings['sc-wordai-content-paragraphs'] )
			: '3';

		$paragraph_count = intval( preg_replace( '/\D+/', '', $paragraphs_key ) );
		if ( $paragraph_count <= 0 ) {
			$paragraph_count = 3;
		}

		$seo_content_guidelines = class_exists( 'SftcyWordaiSeoStandards' )
			? SftcyWordaiSeoStandards::build_content_prompt_guidelines( $content_settings, $paragraph_count )
			: '';
		$seo_title_guideline = class_exists( 'SftcyWordaiSeoStandards' )
			? SftcyWordaiSeoStandards::get_seo_title_generation_guideline()
			: 'Target 50-60 characters. Include the main topic/focus keyword near the beginning and keep it natural, compelling, and search-friendly.';

		// Excerpt words
		$excerpt_words = isset( $content_settings['sc-wordai-excerpt-words'] )
			? intval( $content_settings['sc-wordai-excerpt-words'] )
			: 60;

		// Tags count
		$tags_number = isset( $content_settings['sc-wordai-tags-number'] )
			? intval( $content_settings['sc-wordai-tags-number'] )
			: 5;

		// Suggest title count
		$title_count = isset( $content_settings['wordai-suggested-title-number'] )
			? intval( $content_settings['wordai-suggested-title-number'] )
			: 2;
		$plural_label = ( $title_count > 1 ) ? 'titles' : 'title';

		// Sanitize topic
		$topic = sanitize_text_field( $prompt_hints );

		// -----------------------------------------------------------------------
		// Build prompts by type
		// -----------------------------------------------------------------------
		$generated_prompt = '';

		switch ( $prompt_for ) {

			// -------------------------------------------------------------------
			// Article Page Title (single)
			// -------------------------------------------------------------------
			case 'title':
				$generated_prompt = sprintf(
					"Write a single, compelling article title about \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"The title should be %s and use Title Case.\n".
					"Return only the title text without quotes or extra commentary.",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone,
					$title_length_readable
				);
				break;

			// -------------------------------------------------------------------
			// Suggest Titles (multiple) — double quotes, one per line, no extras
			// -------------------------------------------------------------------
			case 'suggest-title':
				$generated_prompt = sprintf(
					"You are a professional SEO title generator.\n".
					"Generate exactly %d unique, SEO-friendly %s about \"%s\" in %s language.\n".
					"Each title must:\n".
					"- Use %s writing style and %s tone.\n".
					"- Be %s.\n".
					"- Use Title Case.\n".
					"- Be enclosed in double quotes (\").\n".
					"- Appear each on a new line.\n\n".
					"Do not include numbering, bullets, explanations, introductions, or any additional text.\n".
					"Return only the %s, each wrapped in double quotes, one per line.",
					$title_count,
					$plural_label,
					$topic,
					$language_name,
					$writing_style,
					$writing_tone,
					$title_length_readable,
					$plural_label
				);
				break;

			// -------------------------------------------------------------------
			// Long-form Content (multi-paragraph, with headings)
			// -------------------------------------------------------------------
			case 'content':
				$generated_prompt = sprintf(
					"Write an SEO-optimized article about \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"Write exactly %d well-developed body paragraphs with a clear flow from introduction to conclusion. Headings, image captions, and schema notes do not count as body paragraphs. Do not add extra standalone CTA paragraphs unless the user specifically requests them.\n".
					"Do not use bullet lists, numbered lists, <ul>, <ol>, or <li> tags. Use clean paragraphs and helpful headings instead.\n".
					"%s\n".
					"Avoid fluff and avoid repeating the same ideas.\n".
					"Return only the final content body.",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone,
					$paragraph_count,
					$seo_content_guidelines
				);
				break;

			// -------------------------------------------------------------------
			// Excerpt (short summary)
			// -------------------------------------------------------------------
			case 'excerpt':
				$generated_prompt = sprintf(
					"Write a concise, SEO-friendly excerpt for the topic \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"Keep the excerpt within %d words, and make it enticing without clickbait.\n".
					"Return only the excerpt sentence(s).",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone,
					max( 20, $excerpt_words )
				);
				break;

			// -------------------------------------------------------------------
			// Tags (comma-separated only)
			// -------------------------------------------------------------------
			case 'tags':
				$generated_prompt = sprintf(
					"Generate exactly %d SEO tags for the topic \"%s\" in %s language.\n".
					"Return only a single line of comma-separated tags, no numbering, no quotes, no extra text.",
					max( 2, $tags_number ),
					$topic,
					$language_name
				);
				break;

			// -------------------------------------------------------------------
			// SEO Title (single)
			// -------------------------------------------------------------------
			case 'seotitle':
				$generated_prompt = sprintf(
					"Write a single SEO title for \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"%s\n".
					"Use Title Case and avoid filler, quotation marks, punctuation-only endings, or extra commentary.\n".
					"Return only the title without quotes or extra commentary.",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone,
					$seo_title_guideline
				);
				break;

			// -------------------------------------------------------------------
			// SEO Meta Description (single)
			// -------------------------------------------------------------------
			case 'seometadescription':
				$generated_prompt = sprintf(
					"Write a single SEO meta description for \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"Keep it concise and compelling (about 140-160 characters).\n".
					"Return only the description without quotes or extra text.",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone
				);
				break;

			// -------------------------------------------------------------------
			// SEO Meta Focus Keyword (single, short)
			// -------------------------------------------------------------------
			case 'seometafocuskeyword':
				$generated_prompt = sprintf(
					"Generate one short SEO focus keyword or keyphrase (1-4 words) for \"%s\" in %s language.\n".
					"Return only the keyword itself with no quotes, no punctuation, and no extra text.",
					$topic,
					$language_name
				);
				break;

			// -------------------------------------------------------------------
			// Image Alt Text (single, SEO-friendly, accessible)
			// -------------------------------------------------------------------
			case 'imagealttext':
				$generated_prompt = sprintf(
					"Write one SEO-friendly image alt text for an image used with content about \"%s\" in %s language.\n".
					"Use %s writing style and %s tone.\n".
					"Describe the visual subject clearly and naturally for accessibility.\n".
					"Keep it concise, about 6-16 words and under 125 characters.\n".
					"Include the main topic only when it fits naturally.\n".
					"Do not use phrases like \"image of\" or \"picture of\" unless essential.\n".
					"Do not keyword stuff, do not add hashtags, do not add quotes, and do not add extra commentary.\n".
					"Return only the alt text.",
					$topic,
					$language_name,
					$writing_style,
					$writing_tone
				);
				break;

			// -------------------------------------------------------------------
			// Image Prompts (provider-aware)
			// -------------------------------------------------------------------
			case 'image':

				$image_settings = SftcyWordaiBase::sc_wordai_get_current_image_settings();
				$provider       = SftcyWordaiBase::sc_wordai_get_current_ai_provider(); // openai|google

				// ✅ Google Gemini: return ONLY semantic prompt.
				// Gemini prompt enrichment (size/style/quality) is already handled in set_gemini_image_params().
				if ( $provider === 'google' ) {
					$generated_prompt = $topic;
					break;
				}

				// ✅ OpenAI (default)
				$model = isset( $image_settings['sc-wordai-openai-image-model-slug'] )
					? sanitize_text_field( $image_settings['sc-wordai-openai-image-model-slug'] )
					: 'dall-e-2';

				if ( $model === 'dall-e-2' ) {
					$style = isset( $image_settings['sc-wordai-dalle2-image-style'] )
						? sanitize_text_field( $image_settings['sc-wordai-dalle2-image-style'] )
						: 'natural';

					$generated_prompt = sprintf( '%s in %s style', $topic, $style );
				} else {
					// dall-e-3 | gpt-image-1 -> pass-through works best
					$generated_prompt = $topic;
				}

				break;


			// -------------------------------------------------------------------
			// Default: just return the sanitized hint (safe fallback)
			// -------------------------------------------------------------------
			default:
				$generated_prompt = $topic;
				break;
		}

		return trim( $generated_prompt );
	}


	/*──────────────────────────────────────────────
	| Helpers
	──────────────────────────────────────────────*/


	/**
	 * Remove null values recursively (prevents API "invalid_type" errors).
	 *
	 * @param array $arr
	 * @return array
	 */
	private static function array_filter_null_recursive( array $arr ) : array {

		foreach ( $arr as $k => $v ) {

			if ( is_array( $v ) ) {
				$arr[ $k ] = self::array_filter_null_recursive( $v );

				// Optional: drop empty arrays
				if ( empty( $arr[ $k ] ) ) {
					unset( $arr[ $k ] );
				}
				continue;
			}

			if ( $v === null ) {
				unset( $arr[ $k ] );
			}
		}

		return $arr;
	}


	/*──────────────────────────────────────────────
	| Defaults
	──────────────────────────────────────────────*/

	public static function get_default_openai_settings() {
		return [
			'sc-wordai-openai-model-slug'         => self::$MODEL,
			'sc-wordai-streaming'                 => 1,
			'sc-wordai-temperature'               => 0.2,
			'sc-wordai-top-p'                     => 0.1,
			'sc-wordai-max-tokens'                => null,
			'sc-wordai-presence-penalty-input'    => 0,
			'sc-wordai-frequency-penalty-input'   => 0,
			'sc-wordai-best-of-input'             => 1,
			'sc-wordai-stop-input'                => null,
		];
	}
	
	/*───────────────────────────────────────────────────────────────
	| OPENAI PARAMS BUILDERS
	───────────────────────────────────────────────────────────────*/

	/**
	 * Build Chat Completions parameters from saved API settings.
	 * Handles GPT-5 nuances (max_completion_tokens & reasoning_effort).
	 *
	 * @return array
	 */
	public static function set_openai_params() {

		$openai_params = [];
		$data = SftcyWordaiBase::sc_wordai_get_current_api_settings();

		// Model
		$model = self::normalize_openai_model( $data['sc-wordai-openai-model-slug'] ?? '' );

		$openai_params['model'] = $model;

		/**
		 * GPT-5 family detection (future-proof)
		 * Matches: gpt-5, gpt-5-mini, gpt-5.5, gpt-5.6-sol, etc.
		 */
		$is_gpt5 = ( strpos( $model, 'gpt-5' ) === 0 );

		// Tokens
		$max_tokens_setting = empty( $data['sc-wordai-max-tokens'] ) ? '' : $data['sc-wordai-max-tokens'];

		if ( $is_gpt5 ) {

			// GPT-5 family uses max_completion_tokens (NOT max_tokens)
			if ( $max_tokens_setting !== '' ) {
				$openai_params['max_completion_tokens'] = intval( $max_tokens_setting );
			}
			// Keep temperature default behavior safe
			$openai_params['temperature'] = 1;

			// GPT-5 reasoning effort
			$openai_params['reasoning_effort'] = ! empty( $data['sc-wordai-openai-reasoning-effort'] )
				? $data['sc-wordai-openai-reasoning-effort']
				: self::$gpt_5_reasoning_effort;

		} else {

			// Non-GPT-5 uses max_tokens
			if ( $max_tokens_setting !== '' ) {
				$openai_params['max_tokens'] = intval( $max_tokens_setting );
			}

			$openai_params['temperature'] = isset( $data['sc-wordai-temperature'] ) ? floatval( $data['sc-wordai-temperature'] ) : 0.2;
			$openai_params['top_p']       = isset( $data['sc-wordai-top-p'] ) ? floatval( $data['sc-wordai-top-p'] ) : 0.1;
		}

		// Penalties
		$openai_params['presence_penalty']  = isset( $data['sc-wordai-presence-penalty-input'] ) ? floatval( $data['sc-wordai-presence-penalty-input'] ) : 0.0;
		$openai_params['frequency_penalty'] = isset( $data['sc-wordai-frequency-penalty-input'] ) ? floatval( $data['sc-wordai-frequency-penalty-input'] ) : 0.0;

		// Stop sequences (do NOT send stop=null)
		if ( ! empty( $data['sc-wordai-stop-input'] ) ) {
			$openai_params['stop'] = trim( $data['sc-wordai-stop-input'] );
		}

		// Streaming
		if ( self::sc_wordai_streaming_status() ) {
			$openai_params['stream'] = true;
			self::$STREAM = true;
			self::sc_curl_add_streaming_hook();
		} else {
			self::$STREAM = false;
		}

		return $openai_params;
	}
	

	/**
	 * Check if an OpenAI image model is part of the GPT Image family.
	 * Future-proof: any new model that starts with "gpt-image-" will match.
	 *
	 * @param string $model
	 * @return bool
	 */
	private static function is_gpt_image_model( $model ) {
		$model = (string) $model;
		return (bool) preg_match( '/^gpt-image-/', $model );
	}

	private static function normalize_openai_image_model( $model ) {
		$model = sanitize_text_field( (string) $model );

		$allowed = array(
			'gpt-image-2',
			'gpt-image-1.5',
			'gpt-image-1',
			'gpt-image-1-mini',
		);

		if ( in_array( $model, $allowed, true ) ) {
			return $model;
		}

		return self::$IMAGE_MODEL;
	}

	public static function openai_image_output_formats() {
		return array(
			'png'  => __( 'PNG (Lossless, Best Quality)', 'wordai-pro' ),
			'jpeg' => __( 'JPEG (Compressed, Smaller Size)', 'wordai-pro' ),
			'webp' => __( 'WEBP (Modern, Lightweight)', 'wordai-pro' ),
		);
	}

	private static function normalize_openai_image_output_format( $format ) {
		$format  = sanitize_key( (string) $format );
		$allowed = array_keys( self::openai_image_output_formats() );

		return in_array( $format, $allowed, true ) ? $format : 'png';
	}


	/**
	 * Ref: https://platform.openai.com/docs/api-reference/images/create
	 * Build OpenAI image params from saved settings.
	 *
	 * Notes:
	 * - DALL·E 2 / DALL·E 3: supports response_format=b64_json (needed for your base64 pipeline)
	 * - GPT Image models: DO NOT send response_format (API rejects it)
	 * - Keep n=1 because your plugin does parallel calls for multiple images
	 *
	 * @return array
	 */
	public static function set_openai_image_params() {

		$openai_image_params = [];
		$data = SftcyWordaiBase::sc_wordai_get_current_image_settings();

		$model = isset( $data['sc-wordai-openai-image-model-slug'] )
			? self::normalize_openai_image_model( $data['sc-wordai-openai-image-model-slug'] )
			: self::$IMAGE_MODEL;

		$openai_image_params['model'] = $model;
		$openai_image_params['n']     = 1;

		/* ---------------------------------------------------------
		* GPT Image Models (NO response_format allowed)
		* --------------------------------------------------------- */		
		// ✅ GPT Image family (future-proof)
		if ( self::is_gpt_image_model( $model ) ) {		

			// Size: must be one of 1024x1024, 1024x1536, 1536x1024, or auto
			$size = isset($data['sc-wordai-gptimage1-image-size'])
				? sanitize_text_field($data['sc-wordai-gptimage1-image-size'])
				: 'auto';

			// Quality: auto/high/medium/low (your UI already uses these)
			$quality = isset($data['sc-wordai-gptimage1-image-hd-quality'])
				? sanitize_text_field($data['sc-wordai-gptimage1-image-hd-quality'])
				: 'auto';
			$output_format = isset($data['sc-wordai-gptimage1-output-format'])
				? self::normalize_openai_image_output_format($data['sc-wordai-gptimage1-output-format'])
				: 'png';

			$openai_image_params['size']    = $size;
			$openai_image_params['quality'] = $quality;
			$openai_image_params['output_format'] = $output_format;

			// IMPORTANT: do NOT set response_format for GPT Image models
			return $openai_image_params;
		}

		/* ---------------------------------------------------------
		* DALL·E 2
		* --------------------------------------------------------- */
		if ( $model === 'dall-e-2' ) {

			$size = isset($data['sc-wordai-dalle2-image-size'])
				? sanitize_text_field($data['sc-wordai-dalle2-image-size'])
				: '1024x1024';

			$openai_image_params['size']            = $size;
			$openai_image_params['response_format'] = 'b64_json';

			return $openai_image_params;
		}

		/* ---------------------------------------------------------
		* DALL·E 3
		* --------------------------------------------------------- */
		if ( $model === 'dall-e-3' ) {

			$size = isset($data['sc-wordai-dalle3-image-size'])
				? sanitize_text_field($data['sc-wordai-dalle3-image-size'])
				: '1024x1024';

			$style = isset($data['sc-wordai-dalle3-image-style'])
				? sanitize_text_field($data['sc-wordai-dalle3-image-style'])
				: 'vivid';

			$openai_image_params['size']            = $size;
			$openai_image_params['style']           = $style;
			$openai_image_params['response_format'] = 'b64_json';

			// HD checkbox maps to DALL·E 3 quality=hd
			if ( ! empty($data['sc-wordai-dalle3-image-hd-quality']) ) {
				$openai_image_params['quality'] = 'hd';
			}

			return $openai_image_params;
		}

		/* ---------------------------------------------------------
		* Fallback (keep safe defaults)
		* --------------------------------------------------------- */
		$openai_image_params['size'] = ! empty($data['sc-wordai-dalle2-image-size'])
		? sanitize_text_field($data['sc-wordai-dalle2-image-size'])
		: '1024x1024';

		return $openai_image_params;
	}



	/*───────────────────────────────────────────────────────────────
	| OPENAI HTTP CALLS
	───────────────────────────────────────────────────────────────*/

	/**
	 * GET /v1/models
	 *
	 * @return array
	 * @throws Exception
	 */
	public static function get_list_models() {
		$args = [
			'headers'     => [
				'Authorization' => 'Bearer ' . self::$API_KEY,
				'Content-Type'  => 'application/json',
			],
			'timeout'     => 60,
			'httpversion' => '1.0',
			'sslverify'   => false,
		];

		$response = wp_remote_get( self::$AI_LISTMODEL_EP, $args );

		if ( ! is_wp_error( $response ) ) {
			$body = json_decode( wp_remote_retrieve_body( $response ), true );
			return $body;
		}

		$error_message = $response->get_error_message();
		throw new Exception( $error_message );
	}

	/**
	 * Create / Generate Content based on Request
	 * ChatGPT API Call
	 * Check Generated content
	 * If streaming true then 'responseBody' have null value
	 * max_token - NULL - may truncate the response text / content
	 * If max_token value larger than model's maximum support number then response will not be generated
	 * @since 1.0.0
	 */
	public static function create_content( $api_params ) {
		// Keep the cURL callback aligned with this request, not only the saved
		// global streaming setting. Some short workflows intentionally disable it.
		self::$STREAM = ! empty( $api_params['stream'] );
		self::$output = [];
		self::$output['wordAIRequestParams'] = $api_params;
    
		if ( is_null( self::$API_KEY ) || empty( self::$API_KEY) ) {
				self::$output['status'] = 'fail';
				self::$output['comment'] = 'Valid API Key missing.';
				self::$output['wordAIAPIKeyMissing'] = __( 'Valid OpenAI API Key required.', 'wordai-pro' );
		} 
		else {
			// ✅ HARDEN: remove null keys (max_tokens, stop, etc.)
			$api_params = self::array_filter_null_recursive( $api_params );
			$model      = isset( $api_params['model'] ) ? self::normalize_openai_model( $api_params['model'] ) : self::$MODEL;

			if ( self::uses_responses_api( $model ) ) {
				return self::create_responses_content( $api_params );
			}

			// Last using $args
			// $args = array(
			// 	'headers' => array(
			// 		'Authorization' => 'Bearer ' . self::$API_KEY,
			// 		'Content-Type'  => 'application/json',
			// 	),
			// 	'timeout' 			=> 90,
			// 	'httpversion' 		=> '1.0',
			// 	'sslverify'   		=> false,
			// 	'body'				=> wp_json_encode( $api_params )			
			// );

			// Modified - $args
			$args = array(
				'headers' => array(
					'Authorization' => 'Bearer ' . self::$API_KEY,
					'Content-Type'  => 'application/json; charset=UTF-8',
				),
				'timeout' 			=> 90,
				'httpversion' 		=> '1.0',
				'sslverify'   		=> false,								
				'data_format' 		=> 'body',	
				'body'				=> wp_json_encode( $api_params ),
			);

			//  var_dump( $args );
			//  exit();

			$response = wp_remote_post( self::$AI_COMPLETION_EP, $args );	// Last using			
			
			
			// var_dump( wp_remote_retrieve_body( $response ) );
			// exit;

			if ( ! is_wp_error( $response ) ) {
				$body = json_decode( wp_remote_retrieve_body( $response ), true );								
				// var_dump( $body );
				// exit;
				self::$output['responseBody'] = $body;
				// response['choices'][0]['message']['content'] - GPT 3.5 Turbo / GPT-4 response format
				// $body["choices"][0]["text"] 					- Old GPT models response format
				if ( isset( $body['choices'][0]['message']['content'] ) ) {					
					self::$output['responseText'] = $body['choices'][0]['message']['content'];
					self::$output['status']	= 'success';
				}
				elseif ( isset( $body["error"]["message"] ) ) {
					self::$output['errorMessage'] = $body["error"]["message"];
					self::$output['errorType']	= $body["error"]["type"];
					self::$output['status']	= 'fail';
				}				
			} else {
				$error_message = $response->get_error_message();				
				self::$output['errorMessage'] = $error_message;
				self::$output['status']	= 'fail';
				// throw new Exception( $error_message );
			}	
		} // else
		
	 return self::$output;
	}

	public static function sc_wordai_current_model_uses_responses_api() {
		$data  = SftcyWordaiBase::sc_wordai_get_current_api_settings();
		$model = self::normalize_openai_model( $data['sc-wordai-openai-model-slug'] ?? '' );

		return self::uses_responses_api( $model );
	}

	private static function uses_responses_api( $model ) {
		$model = (string) $model;
		return (bool) preg_match( '/^gpt-5(?:\.|$|-)/', $model );
	}

	private static function create_responses_content( $api_params ) {
		self::$STREAM = false;
		$body         = self::convert_chat_params_to_responses_params( $api_params );

		$args = array(
			'headers'     => array(
				'Authorization' => 'Bearer ' . self::$API_KEY,
				'Content-Type'  => 'application/json; charset=UTF-8',
			),
			'timeout'     => 90,
			'httpversion' => '1.0',
			'sslverify'   => false,
			'data_format' => 'body',
			'body'        => wp_json_encode( $body ),
		);

		$response = wp_remote_post( self::$AI_RESPONSES_EP, $args );

		if ( is_wp_error( $response ) ) {
			self::$output['errorMessage'] = $response->get_error_message();
			self::$output['status']       = 'fail';
			return self::$output;
		}

		$response_body = json_decode( wp_remote_retrieve_body( $response ), true );
		self::$output['responseBody'] = $response_body;

		if ( isset( $response_body['error']['message'] ) ) {
			self::$output['errorMessage'] = $response_body['error']['message'];
			self::$output['errorType']    = $response_body['error']['type'] ?? '';
			self::$output['status']       = 'fail';
			return self::$output;
		}

		$text = self::extract_responses_text( $response_body );
		if ( '' !== $text ) {
			self::$output['responseText'] = $text;
			self::$output['status']       = 'success';
			return self::$output;
		}

		self::$output['errorMessage'] = __( 'Unexpected OpenAI Responses API response format.', 'wordai-pro' );
		self::$output['status']       = 'fail';
		return self::$output;
	}

	private static function convert_chat_params_to_responses_params( $api_params ) {
		$model = isset( $api_params['model'] ) ? self::normalize_openai_model( $api_params['model'] ) : self::$MODEL;
		$input = '';

		if ( ! empty( $api_params['messages'] ) && is_array( $api_params['messages'] ) ) {
			$parts = array();
			foreach ( $api_params['messages'] as $message ) {
				$content = isset( $message['content'] ) ? (string) $message['content'] : '';
				if ( '' !== trim( $content ) ) {
					$parts[] = $content;
				}
			}
			$input = trim( implode( "\n\n", $parts ) );
		}

		$body = array(
			'model' => $model,
			'input' => $input,
		);

		if ( isset( $api_params['max_completion_tokens'] ) ) {
			$body['max_output_tokens'] = absint( $api_params['max_completion_tokens'] );
		} elseif ( isset( $api_params['max_tokens'] ) ) {
			$body['max_output_tokens'] = absint( $api_params['max_tokens'] );
		}

		$reasoning_effort = isset( $api_params['reasoning_effort'] ) ? sanitize_key( (string) $api_params['reasoning_effort'] ) : '';
		if ( '' !== $reasoning_effort && 'none' !== $reasoning_effort ) {
			$body['reasoning'] = array( 'effort' => $reasoning_effort );
		}

		return self::array_filter_null_recursive( $body );
	}

	private static function extract_responses_text( $body ) {
		if ( ! is_array( $body ) ) {
			return '';
		}

		if ( isset( $body['output_text'] ) && is_string( $body['output_text'] ) ) {
			return trim( $body['output_text'] );
		}

		$text_parts = array();
		if ( ! empty( $body['output'] ) && is_array( $body['output'] ) ) {
			foreach ( $body['output'] as $item ) {
				if ( empty( $item['content'] ) || ! is_array( $item['content'] ) ) {
					continue;
				}

				foreach ( $item['content'] as $content ) {
					if ( isset( $content['text'] ) && is_string( $content['text'] ) ) {
						$text_parts[] = $content['text'];
					}
				}
			}
		}

		return trim( implode( "\n", $text_parts ) );
	}
	


	/**
	 * POST /v1/images/generations
	 *
	 * @param array $api_params From set_openai_image_params() + prompt.
	 * @return array self::$output (status, responseImageData|errorMessage, responseBody)
	 */
	public static function generate_image( $api_params ) {
		self::$output = [];
		self::$output['wordAIRequestParams'] = $api_params;

		if ( is_null( self::$API_KEY ) || empty( self::$API_KEY ) ) {
			self::$output['status']              = 'fail';
			self::$output['comment']             = 'Valid API Key missing.';
			self::$output['wordAIAPIKeyMissing'] = __( 'Valid OpenAI API Key required.', 'wordai-pro' );
			return self::$output;
		}

		// ✅ HARDEN: remove null keys (max_tokens, stop, etc.)
		$api_params = self::array_filter_null_recursive( $api_params );

		$args = [
			'headers'     => [
				'Authorization' => 'Bearer ' . self::$API_KEY,
				'Content-Type'  => 'application/json',
			],
			'timeout'     => 90,
			'httpversion' => '1.0',
			'sslverify'   => false,
			'body'        => wp_json_encode( $api_params ),
		];

		$response = wp_remote_post( self::$AI_IMAGE_EP, $args );

		if ( is_wp_error( $response ) ) {
			self::$output['errorMessage'] = $response->get_error_message();
			self::$output['status']       = 'fail';
			return self::$output;
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		self::$output['responseBody'] = $body;

		if ( isset( $body['data'] ) ) {
			self::$output['responseImageData'] = $body['data']; // array of images (b64_json or urls)
			self::$output['status']            = 'success';
			// expose model for upload flow
			$model = isset( $api_params['model'] ) ? $api_params['model'] : '';
			self::$output['model']             = $model;
			self::$output['selectedImageModel']= $model;
			return self::$output;
		}

		if ( isset( $body['error']['message'] ) ) {
			self::$output['errorMessage'] = $body['error']['message'];
			self::$output['errorType']    = isset( $body['error']['type'] ) ? $body['error']['type'] : '';
			self::$output['status']       = 'fail';
			return self::$output;
		}

		self::$output['errorMessage'] = __( 'Unexpected OpenAI image response format.', 'wordai-pro' );
		self::$output['status']       = 'fail';
		return self::$output;
	}


	/**
	 * ============================================================
	 * Create Chat Reply (Streaming + Non-Streaming)
	 * ------------------------------------------------------------
	 * Unified helper for OpenAI Chat API.
	 *  - Uses the same endpoint vars and output structure as other methods.
	 *  - Supports streaming or non-streaming depending on $stream flag.
	 *  - Consistent with create_content() and generate_image().
	 * ============================================================
	 *
	 * @param string $prompt       User message.
	 * @param string $model        OpenAI model ID (e.g., gpt-4o-mini).
	 * @param float  $temperature  Creativity parameter (0–1).
	 * @param string $prefix       Optional system instruction.
	 * @param bool   $stream       Whether to use SSE (streaming) mode.
	 *
	 * @return array|string        Array (non-stream) or streamed chunks (echo).
	 */
	// public static function create_chat_reply( $prompt, $model, $temperature, $prefix = '', $stream = false ) {
	// 	self::$output = []; // reset shared output container

	// 	// ------------------------------------------------------------
	// 	// 1️⃣ Basic Validation
	// 	// ------------------------------------------------------------
	// 	if ( empty( $prompt ) ) {
	// 		return [ 'status' => 'error', 'reply' => __( 'Empty message.', 'wordai-pro' ) ];
	// 	}

	// 	if ( is_null( self::$API_KEY ) || empty( self::$API_KEY ) ) {
	// 		return [ 'status' => 'error', 'reply' => __( 'Valid OpenAI API key required.', 'wordai-pro' ) ];
	// 	}

	// 	// Clamp temperature safely
	// 	$temperature = max( 0.0, min( 1.0, floatval( $temperature ) ) );

	// 	// ------------------------------------------------------------
	// 	// 2️⃣ Prepare Request Payload
	// 	// ------------------------------------------------------------
	// 	$body = [
	// 		'model'       => $model,
	// 		'temperature' => $temperature,
	// 		'messages'    => [
	// 			[ 'role' => 'system', 'content' => $prefix ?: 'You are a helpful assistant.' ],
	// 			[ 'role' => 'user',   'content' => $prompt ],
	// 		],
	// 		'stream'      => (bool) $stream,
	// 	];

	// 	self::$output['wordAIRequestParams'] = $body;

	// 	// ------------------------------------------------------------
	// 	// 3️⃣ STREAMING MODE (Server-Sent Events)
	// 	// ------------------------------------------------------------
	// 	if ( $stream ) {
	// 		// Use your internal hook-based streaming system
	// 		self::$STREAM = true;
	// 		self::sc_curl_add_streaming_hook();

	// 		$args = [
	// 			'headers'     => [
	// 				'Authorization' => 'Bearer ' . self::$API_KEY,
	// 				'Content-Type'  => 'application/json; charset=UTF-8',
	// 			],
	// 			'timeout'     => 90,
	// 			'httpversion' => '1.0',
	// 			'sslverify'   => false,
	// 			'data_format' => 'body',
	// 			'body'        => wp_json_encode( $body ),
	// 		];

	// 		// Flush live SSE data
	// 		$response = wp_remote_post( self::$AI_COMPLETION_EP, $args );

	// 		// Stream mode outputs directly; stop further WP JSON
	// 		exit;
	// 	}

	// 	// ------------------------------------------------------------
	// 	// 4️⃣ NON-STREAMING MODE (Default)
	// 	// ------------------------------------------------------------
	// 	$args = [
	// 		'headers'     => [
	// 			'Authorization' => 'Bearer ' . self::$API_KEY,
	// 			'Content-Type'  => 'application/json; charset=UTF-8',
	// 		],
	// 		'timeout'     => 60,
	// 		'httpversion' => '1.0',
	// 		'sslverify'   => false,
	// 		'data_format' => 'body',
	// 		'body'        => wp_json_encode( $body ),
	// 	];

	// 	$response = wp_remote_post( self::$AI_COMPLETION_EP, $args );

	// 	if ( is_wp_error( $response ) ) {
	// 		self::$output['status']       = 'fail';
	// 		self::$output['errorMessage'] = $response->get_error_message();
	// 		return self::$output;
	// 	}

	// 	$code = wp_remote_retrieve_response_code( $response );
	// 	$body_raw = wp_remote_retrieve_body( $response );
	// 	$data = json_decode( $body_raw, true );

	// 	self::$output['responseBody'] = $data;

	// 	if ( $code !== 200 || empty( $data['choices'][0]['message']['content'] ) ) {
	// 		$error_msg = ! empty( $data['error']['message'] )
	// 			? $data['error']['message']
	// 			: __( 'Failed to get response from API.', 'wordai-pro' );

	// 		self::$output['status']       = 'fail';
	// 		self::$output['errorMessage'] = $error_msg;
	// 		return self::$output;
	// 	}

	// 	// ------------------------------------------------------------
	// 	// 5️⃣ Normalize Successful Response
	// 	// ------------------------------------------------------------
	// 	self::$output['status']       = 'success';
	// 	self::$output['reply']        = wp_kses_post( $data['choices'][0]['message']['content'] );
	// 	self::$output['model']        = esc_html( $model );

	// 	return self::$output;
	// }


	/**
	 * =====================================================================
	 *  OpenAI :: create_chat_reply()
	 *  ---------------------------------------------------------------------
	 *  Unified helper for OpenAI Chat Completion API.
	 *  NEW (Dec 2025):
	 *   - Accepts a dedicated $assistant_context argument (RAG content)
	 *   - Builds messages in correct OpenAI format:
	 *        system → assistant(RAG) → user
	 *   - Fully supports streaming + non-streaming
	 * =====================================================================
	 *
	 * @param string $user_prompt        Final user question.
	 * @param string $model              OpenAI model (e.g., gpt-4o-mini).
	 * @param float  $temperature        Creativity (0–1).
	 * @param string $system_message     System/prefix text.
	 * @param bool   $stream             Whether to use SSE mode.
	 * @param string $assistant_context  RAG context (assistant-role msg).
	 *
	 * @return array|string              Array (non-stream) or SSE stream.
	 */
	public static function create_chat_reply(
		$user_prompt,
		$model,
		$temperature,
		$system_message = '',
		$stream = false,
		$assistant_context = ''
	) {
		self::$output = []; // reset shared output

		// ------------------------------------------------------------
		// 1️⃣ Basic validation
		// ------------------------------------------------------------
		if ( empty( $user_prompt ) ) {
			return [ 'status' => 'error', 'reply' => __( 'Empty message.', 'wordai-pro' ) ];
		}

		if ( empty( self::$API_KEY ) ) {
			return [ 'status' => 'error', 'reply' => __( 'Valid OpenAI API key required.', 'wordai-pro' ) ];
		}

		$temperature = max( 0, min( 1, floatval( $temperature ) ) );
		if ( self::uses_responses_api( $model ) ) {
			$temperature = 1;
		}
		$system_message = $system_message ?: 'You are a helpful assistant.';

		// ------------------------------------------------------------
		// 2️⃣ Build messages array (OpenAI standard format)
		// ------------------------------------------------------------
		$messages = [];

		// System message
		$messages[] = [
			'role'    => 'system',
			'content' => $system_message
		];

		// RAG context as assistant role
		if ( ! empty( $assistant_context ) ) {
			$messages[] = [
				'role'    => 'assistant',
				'content' => "Here is website context:\n" . $assistant_context
			];
		}

		// User question
		$messages[] = [
			'role'    => 'user',
			'content' => $user_prompt
		];

		$body = [
			'model'       => $model,
			'temperature' => $temperature,
			'messages'    => $messages,
			'stream'      => (bool) $stream,
		];

		self::$output['wordAIRequestParams'] = $body;

		// ------------------------------------------------------------
		// 3️⃣ STREAMING MODE
		// ------------------------------------------------------------
		if ( $stream ) {

			self::$STREAM = true;
			self::sc_curl_add_streaming_hook();

			$args = [
				'headers'     => [
					'Authorization' => 'Bearer ' . self::$API_KEY,
					'Content-Type'  => 'application/json; charset=UTF-8',
				],
				'timeout'     => 90,
				'httpversion' => '1.0',
				'sslverify'   => false,
				'data_format' => 'body',
				'body'        => wp_json_encode( $body ),
			];

			wp_remote_post( self::$AI_COMPLETION_EP, $args );

			exit; // SSE emitted directly
		}

		// ------------------------------------------------------------
		// 4️⃣ NON-STREAM MODE
		// ------------------------------------------------------------
		$args = [
			'headers'     => [
				'Authorization' => 'Bearer ' . self::$API_KEY,
				'Content-Type'  => 'application/json; charset=UTF-8',
			],
			'timeout'     => 60,
			'httpversion' => '1.0',
			'sslverify'   => false,
			'data_format' => 'body',
			'body'        => wp_json_encode( $body ),
		];

		$response = wp_remote_post( self::$AI_COMPLETION_EP, $args );

		if ( is_wp_error( $response ) ) {
			return [
				'status' => 'fail',
				'reply'  => $response->get_error_message(),
			];
		}

		$code = wp_remote_retrieve_response_code( $response );
		$data = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( $code !== 200 || empty( $data['choices'][0]['message']['content'] ) ) {
			return [
				'status' => 'fail',
				'reply'  => $data['error']['message'] ?? 'OpenAI request failed.',
			];
		}

		return [
			'status' => 'success',
			'reply'  => $data['choices'][0]['message']['content'],
			'model'  => $model
		];
	}


	/**
	 * Create embeddings for an array of text chunks using OpenAI.
	 *
	 * @param array  $texts Array of text chunks.
	 * @param string $model Embedding model (e.g., text-embedding-3-large).
	 * @return array
	 */
	public static function embed_texts( array $texts, $model = 'text-embedding-3-large' ) {

		new self(); // ensure API key loaded

		if ( empty( self::$API_KEY ) ) {
			return [
				'status'      => 'fail',
				'feedbackMsg' => __( 'OpenAI API key is missing.', 'wordai-pro' ),
			];
		}

		if ( empty( $texts ) ) {
			return [
				'status'      => 'fail',
				'feedbackMsg' => __( 'No text provided for embedding.', 'wordai-pro' ),
			];
		}

		$endpoint = 'https://api.openai.com/v1/embeddings';

		$embeddings = [];

		foreach ( $texts as $chunk ) {

			$body = [
				'model' => $model,
				'input' => (string) $chunk,
			];

			$args = [
				'headers' => [
					'Authorization' => 'Bearer ' . self::$API_KEY,
					'Content-Type'  => 'application/json',
				],
				'timeout'  => 40,
				'sslverify'=> false,
				'body'     => wp_json_encode( $body ),
			];

			$response = wp_remote_post( $endpoint, $args );

			if ( is_wp_error( $response ) ) {
				return [
					'status'      => 'fail',
					'feedbackMsg' => __( 'Embedding request failed: ', 'wordai-pro' ) . $response->get_error_message(),
				];
			}

			$code = wp_remote_retrieve_response_code( $response );
			$raw  = wp_remote_retrieve_body( $response );
			$json = json_decode( $raw, true );

			if ( $code !== 200 || empty( $json['data'][0]['embedding'] ) ) {
				return [
					'status'      => 'fail',
					'feedbackMsg' => __( 'Embedding error: ', 'wordai-pro' ) .
						( $json['error']['message'] ?? 'Unknown error' ),
				];
			}

			$embeddings[] = $json['data'][0]['embedding'];
		}

		return [
			'status'      => 'success',
			'feedbackMsg' => __( 'OpenAI embeddings created successfully.', 'wordai-pro' ),
			'embeddings'  => $embeddings,
		];
	}



	/*───────────────────────────────────────────────────────────────
	| STREAMING (SSE-LIKE) LOW LEVEL
	───────────────────────────────────────────────────────────────*/

	/**
	 * cURL write callback for streaming responses.
	 * This echoes chunk data back to the browser as it arrives.
	 *
	 * @param resource $ch cURL handler (provided by WP HTTP API).
	 * @return void
	 */
	// public static function sc_wordai_stream_response( $ch ) {
	// 	if ( self::$STREAM !== true ) {
	// 		return;
	// 	}

	// 	// Avoid conflicts with existing output buffers.
	// 	@ob_end_clean();
	// 	@ob_start();

	// 	curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );
	// 	curl_setopt(
	// 		$ch,
	// 		CURLOPT_WRITEFUNCTION,
	// 		function ( $curl_info, $data ) {
	// 			// Remove the "data: " SSE prefix for easier JSON parsing on the JS side.
	// 			echo self::wordai_raw_contents( $data );
	// 			@ob_end_flush();
	// 			flush();
	// 			return strlen( $data );
	// 		}
	// 	);
	// }


	/**
	 * Low-level cURL write callback for streaming SSE-like responses.
	 *
	 * This is hooked via `http_api_curl` and is called once per HTTP
	 * request. We only attach a CURLOPT_WRITEFUNCTION when streaming
	 * is actually enabled (self::$STREAM === true).
	 *
	 * IMPORTANT:
	 *  - Signature MUST accept 3 parameters to match `http_api_curl`:
	 *      ($handle, $r, $url)
	 *  - Inside, we set CURLOPT_WRITEFUNCTION to a closure that
	 *    echoes chunks as they arrive and flushes output.
	 *
	 * @param resource $handle cURL handle provided by WP HTTP API.
	 * @param array    $r      Request arguments (unused here).
	 * @param string   $url    Request URL (unused here).
	 * @return void
	 */
	public static function sc_wordai_stream_response( $handle, $r, $url ) {
		// If streaming is not enabled for this request, do nothing.
		if ( self::$STREAM !== true ) {
			return;
		}

		// Make sure we don't have stale output buffers interfering.
		@ob_end_clean();
		@ob_start();

		// Attach a write callback that will be called for each chunk.
		curl_setopt(
			$handle,
			CURLOPT_WRITEFUNCTION,
			function ( $curl, $data ) {
				// Strip any leading "data: " prefix so JS can parse raw JSON.
				echo self::wordai_raw_contents( $data );

				// Flush output to the browser immediately.
				@ob_flush();
				flush();

				// cURL expects us to return the exact length of $data.
				return strlen( $data );
			}
		);
	}


	/**
	 * Strip "data: " prefix from streamed chunks.
	 *
	 * @param string $data
	 * @return string
	 */
	public static function wordai_raw_contents( $data = '' ) {
		return str_replace( 'data: ', '', $data );
	}


	/**
	 * Allowed tags for safe admin echo via wp_kses (if you ever need it).
	 *
	 * @return array
	 */
	public static function wordai_allowed_html_tags() {
		return [
			'img'      => [ 'id'=>[], 'class'=>[], 'style'=>[], 'src'=>[], 'alt'=>[] ],
			'label'    => [ 'id'=>[], 'class'=>[], 'style'=>[], 'for'=>[] ],
			'div'      => [ 'id'=>[], 'class'=>[], 'data-*'=>true, 'wfd-invisible'=>[], 'style'=>[] ],
			'ul'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'input'    => [ 'id'=>[], 'class'=>[], 'type'=>[], 'name'=>[], 'value'=>[], 'checked'=>[], 'min'=>[], 'max'=>[], 'step'=>[], 'readonly'=>[], 'disabled'=>[], 'wfd-invisible'=>[], 'placeholder'=>[] ],
			'textarea' => [ 'id'=>[], 'class'=>[], 'name'=>[], 'value'=>[], 'readonly'=>[], 'disabled'=>[], 'wfd-invisible'=>[], 'rows'=>[], 'cols'=>[], 'placeholder'=>[] ],
			'span'     => [ 'id'=>[], 'class'=>[], 'title'=>[], 'style'=>[] ],
			'button'   => [ 'id'=>[], 'class'=>[], 'type'=>[], 'style'=>[] ],
			'i'        => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'li'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h1'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h2'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h3'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h4'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h5'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'h6'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'strong'   => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'b'        => [ 'id'=>[], 'class'=>[] ],
			'table'    => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'thead'    => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'tbody'    => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'tr'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'td'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'form'     => [ 'id'=>[], 'class'=>[], 'name'=>[], 'enctype'=>[], 'method'=>[], 'style'=>[] ],
			'select'   => [ 'id'=>[], 'class'=>[], 'name'=>[], 'style'=>[] ],
			'option'   => [ 'id'=>[], 'class'=>[], 'value'=>[], 'checked'=>[], 'selected'=>[], 'style'=>[] ],
			'small'    => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'hr'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'br'       => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
			'p'        => [ 'id'=>[], 'class'=>[], 'style'=>[] ],
		];
	}

	/*───────────────────────────────────────────────────────────────
	| LOOKUP LISTS & HELPERS (kept here to avoid undefined calls)
	───────────────────────────────────────────────────────────────*/

	/**
	 * OpenAI text models for UI (grouped).
	 *
	 * Backward compatible:
	 * - Returns optgroup arrays.
	 * - Your view renderer must support both grouped + flat arrays.
	 *
	 * @return array
	 */
	public static function openai_models() {

		return [
			'GPT-5.6 Frontier' => [
				'gpt-5.6'       => 'GPT-5.6 Sol',
				'gpt-5.6-sol'   => 'GPT-5.6 Sol',
				'gpt-5.6-terra' => 'GPT-5.6 Terra',
				'gpt-5.6-luna'  => 'GPT-5.6 Luna',
			],

			'GPT-5.5 Recommended Default' => [
				'gpt-5.5' => 'GPT-5.5',
			],

			'GPT-5 Stable Reasoning' => [
				'gpt-5'        => 'GPT-5',
				'gpt-5-mini'   => 'GPT-5 mini',
				'gpt-5-nano'   => 'GPT-5 nano',
			],

			'GPT-4o Fast + Great Quality' => [
				'gpt-4o'       => 'GPT-4o',
				'gpt-4o-mini'  => 'GPT-4o mini',
			],

			'GPT-4.1 Strong General Model' => [
				'gpt-4.1'      => 'GPT-4.1',
				'gpt-4.1-mini' => 'GPT-4.1 mini',
				'gpt-4.1-nano' => 'GPT-4.1 nano',
			],
		];
	}

	public static function openai_model_slugs() {
		$slugs = [];

		foreach ( self::openai_models() as $models ) {
			if ( is_array( $models ) ) {
				$slugs = array_merge( $slugs, array_keys( $models ) );
			}
		}

		return $slugs;
	}

	public static function openai_model_hints() {
		return [
			'gpt-5.6'       => __( 'GPT-5.6 Sol is OpenAI\'s flagship GPT-5.6 model for complex reasoning, coding, and professional content workflows.', 'wordai-pro' ),
			'gpt-5.6-sol'   => __( 'GPT-5.6 Sol is best for complex professional work when quality and reasoning depth matter most.', 'wordai-pro' ),
			'gpt-5.6-terra' => __( 'GPT-5.6 Terra balances intelligence and cost for high-quality everyday content and assistant workflows.', 'wordai-pro' ),
			'gpt-5.6-luna'  => __( 'GPT-5.6 Luna is optimized for cost-sensitive, high-volume content and chat workloads.', 'wordai-pro' ),
			'gpt-5.5'       => __( 'GPT-5.5 is the default stable OpenAI model in WordAI for strong content quality and broad compatibility.', 'wordai-pro' ),
			'gpt-5'         => __( 'GPT-5 is a stable reasoning model for reliable long-form content, titles, and general AI tasks.', 'wordai-pro' ),
			'gpt-5-mini'    => __( 'GPT-5 mini is a faster, lower-cost option for common content generation and chat responses.', 'wordai-pro' ),
			'gpt-5-nano'    => __( 'GPT-5 nano is best for lightweight, high-volume tasks where speed and cost matter most.', 'wordai-pro' ),
			'gpt-4o'        => __( 'GPT-4o remains a fast multimodal model with strong general quality for text and vision-aware workflows.', 'wordai-pro' ),
			'gpt-4o-mini'   => __( 'GPT-4o mini is a cost-efficient model for simpler high-volume content and assistant tasks.', 'wordai-pro' ),
			'gpt-4.1'       => __( 'GPT-4.1 is a strong general-purpose model for content, editing, and structured output workflows.', 'wordai-pro' ),
			'gpt-4.1-mini'  => __( 'GPT-4.1 mini balances speed, cost, and quality for everyday generation tasks.', 'wordai-pro' ),
			'gpt-4.1-nano'  => __( 'GPT-4.1 nano is a lightweight option for fast and lower-cost text generation.', 'wordai-pro' ),
		];
	}

	public static function openai_model_hint( $model = '' ) {
		$model = self::normalize_openai_model( $model );
		$hints = self::openai_model_hints();

		return $hints[ $model ] ?? $hints[ self::$MODEL ];
	}

	public static function normalize_openai_model( $model = '' ) {
		$model = trim( (string) $model );

		return in_array( $model, self::openai_model_slugs(), true ) ? $model : self::$MODEL;
	}
	/**
	 * GPT-5 reasoning effort options.
	 *
	 * @return array [ slug => label ]
	 */
	public static function openai_reasoning_effort() {
		return [
			'none'    => 'None',
			'low'     => 'Low',
			'medium'  => 'Medium',
			'high'    => 'High',
			'xhigh'   => 'Extra High',
			'minimal' => 'Minimal',
		];
	}

	/**
	 * Supported OpenAI image models for UI.
	 *
	 * @return array [ slug => label ]
	 */
	// public static function openai_image_models() {
	// 	return [
	// 		'dall-e-2'    => 'DALL E 2',
	// 		'dall-e-3'    => 'DALL E 3',
	// 		'gpt-image-1' => 'GPT Image 1',
	// 	];
	// }


	/**
	 * Supported OpenAI image models for UI (grouped).
	 *
	 * NOTE:
	 * - Keeps existing slugs so nothing breaks.
	 * - Adds newer GPT Image variants (same workflow as gpt-image-1).
	 *
	 * @return array
	 */
	public static function openai_image_models() {

		return [
			'🖼️ GPT Image' => [
				'gpt-image-2'      => 'ChatGPT Images 2.0 / GPT Image 2',
				'gpt-image-1.5'    => 'GPT Image 1.5',
				'gpt-image-1'      => 'GPT Image 1',
				'gpt-image-1-mini' => 'GPT Image 1 Mini (Faster)',
			],
		];
	}


	/**
	 * DALL-E 2 style tags (used in prompts).
	 *
	 * @return array [ slug => label ]
	 */
	// public static function openai_dalle2_image_styles() {
	// 	return [
	// 		'vivid'            => 'Vivid',
	// 		'natural'          => 'Natural',
	// 		'landscape'        => 'Landscape',
	// 		'portrait'         => 'Portrait',
	// 		'street'           => 'Street',
	// 		'architectural'    => 'Architectural',
	// 		'sports'           => 'Sports',
	// 		'abstract'         => 'Abstract',
	// 		'astrophotography' => 'Astrophotography',
	// 		'composite'        => 'Composite',
	// 		'event'            => 'Event',
	// 		'editorial'        => 'Editorial',
	// 		'wedding'          => 'Wedding',
	// 		'product'          => 'Product',
	// 		'travel'           => 'Travel',
	// 		'food'             => 'Food',
	// 	];
	// }

	/**
	 * DALL·E 2 style tags (used in prompts).
	 *
	 * @return array [ group_label => [ slug => label ] ]
	 */
	public static function openai_dalle2_image_styles() {
		return [
			'✨ Core Styles' => [
				'vivid'   => 'Vivid',
				'natural' => 'Natural',
			],

			'📷 Photography & Scenes' => [
				'landscape'        => 'Landscape',
				'portrait'         => 'Portrait',
				'street'           => 'Street',
				'event'            => 'Event',
				'editorial'        => 'Editorial',
				'wedding'          => 'Wedding',
				'travel'           => 'Travel',
				'food'             => 'Food',
				'product'          => 'Product',
				'architectural'    => 'Architectural',
				'sports'           => 'Sports',
				'astrophotography' => 'Astrophotography',
			],

			'🎨 Artistic & Mixed' => [
				'abstract'  => 'Abstract',
				'composite' => 'Composite',
			],
		];
	}


	/**
	 * DALL-E 3 style tags.
	 *
	 * @return array [ slug => label ]
	 */
	// public static function openai_dalle3_image_styles() {
	// 	return [
	// 		'vivid'   => 'Vivid',
	// 		'natural' => 'Natural',
	// 	];
	// }

	/**
	 * DALL·E 3 style tags.
	 *
	 * @return array [ group_label => [ slug => label ] ]
	 */
	public static function openai_dalle3_image_styles() {
		return [
			'✨ Core Styles' => [
				'vivid'   => 'Vivid',
				'natural' => 'Natural',
			],
		];
	}


	/**
	 * Writing style options (UI).
	 *
	 * @return array [ slug => label ]
	 */
	// public static function writing_styles() {
	// 	return [
	// 		'General' => [
	// 			'narrative'   => 'Narrative',
	// 			'descriptive' => 'Descriptive',
	// 			'expository'  => 'Expository',
	// 			'persuasive'  => 'Persuasive',
	// 			'reflective'  => 'Reflective',
	// 			'personal'    => 'Personal',
	// 		],
	// 		'Professional' => [
	// 			'journalistic'  => 'Journalistic',
	// 			'analytical'    => 'Analytical',
	// 			'technical'     => 'Technical',
	// 			'academic'      => 'Academic',
	// 		],
	// 		'Practical' => [
	// 			'instructional' => 'Instructional / How-to',
	// 		],
	// 		'Marketing' => [
	// 			'marketing'   => 'Marketing',
	// 			'copywriting' => 'Copywriting',
	// 		],
	// 		'Story & Brand' => [
	// 			'conversational' => 'Conversational',
	// 			'storytelling'   => 'Storytelling',
	// 		],
	// 	];
	// }

	/**
	 * Writing style options (UI).
	 *
	 * @return array [ group_label => [ slug => label ] ]
	 */
	public static function writing_styles() {
		return [
			'📝 General Writing' => [
				'narrative'   => 'Narrative',
				'descriptive' => 'Descriptive',
				'expository'  => 'Expository',
				'persuasive'  => 'Persuasive',
				'reflective'  => 'Reflective',
				'personal'    => 'Personal',
			],

			'🏢 Professional & Academic' => [
				'journalistic' => 'Journalistic',
				'analytical'   => 'Analytical',
				'technical'    => 'Technical',
				'academic'     => 'Academic',
			],

			'🧭 Practical & How-To' => [
				'instructional' => 'Instructional / How-to',
			],

			'📣 Marketing & Sales' => [
				'marketing'   => 'Marketing',
				'copywriting' => 'Copywriting',
			],

			'💬 Story & Brand Voice' => [
				'conversational' => 'Conversational',
				'storytelling'   => 'Storytelling',
			],
		];
	}



	/**
	 * Writing tone options (UI)
	 *
	 * @return array [ slug => label ]
	 */
	// public static function writing_tones() {
	// 	return [
	// 		'Professional' => [
	// 			'professional'  => 'Professional',
	// 			'formal'        => 'Formal',
	// 			'neutral'       => 'Neutral',
	// 			'objective'     => 'Objective',
	// 			'authoritative' => 'Authoritative',
	// 			'confident'     => 'Confident',
	// 			'trustworthy'   => 'Trustworthy',
	// 			'concise'       => 'Concise',
	// 		],
	// 		'Friendly / Brand' => [
	// 			'friendly'       => 'Friendly',
	// 			'conversational' => 'Conversational',
	// 			'empathetic'     => 'Empathetic',
	// 			'optimistic'     => 'Optimistic',
	// 		],
	// 		'Marketing' => [
	// 			'persuasive'  => 'Persuasive',
	// 			'promotional' => 'Promotional',
	// 			'enthusiastic'=> 'Enthusiastic',
	// 			'energetic'   => 'Energetic',
	// 		],
	// 		'Informational' => [
	// 			'informative'   => 'Informative',
	// 			'knowledgeable' => 'Knowledgeable',
	// 			'serious'       => 'Serious',
	// 			'calm'          => 'Calm',
	// 		],
	// 		'Fun' => [
	// 			'cheerful'  => 'Cheerful',
	// 			'humorous'  => 'Humorous',
	// 			'witty'     => 'Witty',
	// 		],
	// 	];
	// }


	/**
	 * Writing tone options (UI)
	 *
	 * @return array [ group_label => [ slug => label ] ]
	 */
	public static function writing_tones() {
		return [
			'🏢 Professional' => [
				'professional'  => 'Professional',
				'formal'        => 'Formal',
				'neutral'       => 'Neutral',
				'objective'     => 'Objective',
				'authoritative' => 'Authoritative',
				'confident'     => 'Confident',
				'trustworthy'   => 'Trustworthy',
				'concise'       => 'Concise',
			],

			'🤝 Friendly & Brand' => [
				'friendly'       => 'Friendly',
				'conversational' => 'Conversational',
				'empathetic'     => 'Empathetic',
				'optimistic'     => 'Optimistic',
			],

			'📣 Marketing & Promotion' => [
				'persuasive'   => 'Persuasive',
				'promotional'  => 'Promotional',
				'enthusiastic' => 'Enthusiastic',
				'energetic'    => 'Energetic',
			],

			'📚 Informational' => [
				'informative'   => 'Informative',
				'knowledgeable' => 'Knowledgeable',
				'serious'       => 'Serious',
				'calm'          => 'Calm',
			],

			'😄 Fun & Playful' => [
				'cheerful' => 'Cheerful',
				'humorous' => 'Humorous',
				'witty'    => 'Witty',
			],
		];
	}


	/**
	 * Title length presets used by prompts.
	 *
	 * @return array [ key => human readable ]
	 */
	public static function title_lengths() {
		return [
			'35n45' => 'between 35 & 45 characters',
			'45n55' => 'between 45 & 55 characters',
			'50n60' => 'between 50 & 60 characters',
		];
	}

	/**
	 * Paragraph count presets used by prompts.
	 *
	 * @return array [ key => human readable ]
	 */
	public static function content_paragraphs() {
		return [
			'1'  => '1 Paragraph',
			'2'  => '2 Paragraphs',
			'3'  => '3 Paragraphs',
			'4'  => '4 Paragraphs',
			'5'  => '5 Paragraphs',
			'6'  => '6 Paragraphs',
			'7'  => '7 Paragraphs',
			'8'  => '8 Paragraphs',
			'9'  => '9 Paragraphs',
			'10' => '10 Paragraphs',
			'11' => '11 Paragraphs',
			'12' => '12 Paragraphs',
			'13' => '13 Paragraphs',
			'14' => '14 Paragraphs',
			'15' => '15 Paragraphs',
		];
	}

	/**
	 * Excerpt word-count presets (if needed anywhere else).
	 *
	 * @return array [ words => label ]
	 */
	public static function excerpt_words() {
		return [
			'20'  => '20 Words',
			'30'  => '30 Words',
			'40'  => '40 Words',
			'50'  => '50 Words',
			'60'  => '60 Words',
			'70'  => '70 Words',
			'80'  => '80 Words',
			'90'  => '90 Words',
			'100' => '100 Words',
			'120' => '120 Words',
			'140' => '140 Words',
			'160' => '160 Words',
		];
	}

	/**
	 * Tags number presets (if needed elsewhere).
	 *
	 * @return array [ n => label ]
	 */
	public static function tags_number() {
		return [
			'2'  => '2 Tags',
			'3'  => '3 Tags',
			'4'  => '4 Tags',
			'5'  => '5 Tags',
			'6'  => '6 Tags',
			'7'  => '7 Tags',
			'8'  => '8 Tags',
			'9'  => '9 Tags',
			'10' => '10 Tags',
			'11' => '11 Tags',
			'12' => '12 Tags',
			'13' => '13 Tags',
			'14' => '14 Tags',
			'15' => '15 Tags',
		];
	}

	/**
	 * Language list (code + human-readable name).
	 * Matches your original large list to avoid regressions.
	 *
	 * @return array[] Each: [ 'name' => string, 'code' => string ]
	 */
	public static function language_list() {
		// (Kept identical in spirit to your previous long list for compatibility)
		return [
			[ "name" => "Afrikaans", "code" => "af" ],
			[ "name" => "Albanian - shqip", "code" => "sq" ],
			[ "name" => "Amharic - አማርኛ", "code" => "am" ],
			[ "name" => "Arabic - العربية", "code" => "ar" ],
			[ "name" => "Aragonese - aragonés", "code" => "an" ],
			[ "name" => "Armenian - հայերեն", "code" => "hy" ],
			[ "name" => "Asturian - asturianu", "code" => "ast" ],
			[ "name" => "Azerbaijani - azərbaycan dili", "code" => "az" ],
			[ "name" => "Basque - euskara", "code" => "eu" ],
			[ "name" => "Belarusian - беларуская", "code" => "be" ],
			[ "name" => "Bengali - বাংলা", "code" => "bn" ],
			[ "name" => "Bosnian - bosanski", "code" => "bs" ],
			[ "name" => "Breton - brezhoneg", "code" => "br" ],
			[ "name" => "Bulgarian - български", "code" => "bg" ],
			[ "name" => "Catalan - català", "code" => "ca" ],
			[ "name" => "Central Kurdish - کوردی (دەستنوسی عەرەبی)", "code" => "ckb" ],
			[ "name" => "Chinese - 中文", "code" => "zh" ],
			[ "name" => "Chinese (Hong Kong) - 中文（香港）", "code" => "zh-HK" ],
			[ "name" => "Chinese (Simplified) - 中文（简体）", "code" => "zh-CN" ],
			[ "name" => "Chinese (Traditional) - 中文（繁體）", "code" => "zh-TW" ],
			[ "name" => "Corsican", "code" => "co" ],
			[ "name" => "Croatian - hrvatski", "code" => "hr" ],
			[ "name" => "Czech - čeština", "code" => "cs" ],
			[ "name" => "Danish - dansk", "code" => "da" ],
			[ "name" => "Dutch - Nederlands", "code" => "nl" ],
			[ "name" => "English", "code" => "en" ],
			[ "name" => "English (Australia)", "code" => "en-AU" ],
			[ "name" => "English (Canada)", "code" => "en-CA" ],
			[ "name" => "English (India)", "code" => "en-IN" ],
			[ "name" => "English (New Zealand)", "code" => "en-NZ" ],
			[ "name" => "English (South Africa)", "code" => "en-ZA" ],
			[ "name" => "English (United Kingdom)", "code" => "en-GB" ],
			[ "name" => "English (United States)", "code" => "en-US" ],
			[ "name" => "Esperanto - esperanto", "code" => "eo" ],
			[ "name" => "Estonian - eesti", "code" => "et" ],
			[ "name" => "Faroese - føroyskt", "code" => "fo" ],
			[ "name" => "Filipino", "code" => "fil" ],
			[ "name" => "Finnish - suomi", "code" => "fi" ],
			[ "name" => "French - français", "code" => "fr" ],
			[ "name" => "French (Canada) - français (Canada)", "code" => "fr-CA" ],
			[ "name" => "French (France) - français (France)", "code" => "fr-FR" ],
			[ "name" => "French (Switzerland) - français (Suisse)", "code" => "fr-CH" ],
			[ "name" => "Galician - galego", "code" => "gl" ],
			[ "name" => "Georgian - ქართული", "code" => "ka" ],
			[ "name" => "German - Deutsch", "code" => "de" ],
			[ "name" => "German (Austria) - Deutsch (Österreich)", "code" => "de-AT" ],
			[ "name" => "German (Germany) - Deutsch (Deutschland)", "code" => "de-DE" ],
			[ "name" => "German (Liechtenstein) - Deutsch (Liechtenstein)", "code" => "de-LI" ],
			[ "name" => "German (Switzerland) - Deutsch (Schweiz)", "code" => "de-CH" ],
			[ "name" => "Greek - Ελληνικά", "code" => "el" ],
			[ "name" => "Guarani", "code" => "gn" ],
			[ "name" => "Gujarati - ગુજરાતી", "code" => "gu" ],
			[ "name" => "Hausa", "code" => "ha" ],
			[ "name" => "Hawaiian - ʻŌlelo Hawaiʻi", "code" => "haw" ],
			[ "name" => "Hebrew - עברית", "code" => "he" ],
			[ "name" => "Hindi - हिन्दी", "code" => "hi" ],
			[ "name" => "Hungarian - magyar", "code" => "hu" ],
			[ "name" => "Icelandic - íslenska", "code" => "is" ],
			[ "name" => "Indonesian - Indonesia", "code" => "id" ],
			[ "name" => "Interlingua", "code" => "ia" ],
			[ "name" => "Irish - Gaeilge", "code" => "ga" ],
			[ "name" => "Italian - italiano", "code" => "it" ],
			[ "name" => "Italian (Italy) - italiano (Italia)", "code" => "it-IT" ],
			[ "name" => "Italian (Switzerland) - italiano (Svizzera)", "code" => "it-CH" ],
			[ "name" => "Japanese - 日本語", "code" => "ja" ],
			[ "name" => "Kannada - ಕನ್ನಡ", "code" => "kn" ],
			[ "name" => "Kazakh - қазақ тілі", "code" => "kk" ],
			[ "name" => "Khmer - ខ្មែរ", "code" => "km" ],
			[ "name" => "Korean - 한국어", "code" => "ko" ],
			[ "name" => "Kurdish - Kurdî", "code" => "ku" ],
			[ "name" => "Kyrgyz - кыргызча", "code" => "ky" ],
			[ "name" => "Lao - ລາວ", "code" => "lo" ],
			[ "name" => "Latin", "code" => "la" ],
			[ "name" => "Latvian - latviešu", "code" => "lv" ],
			[ "name" => "Lingala - lingála", "code" => "ln" ],
			[ "name" => "Lithuanian - lietuvių", "code" => "lt" ],
			[ "name" => "Macedonian - македонски", "code" => "mk" ],
			[ "name" => "Malay - Bahasa Melayu", "code" => "ms" ],
			[ "name" => "Malayalam - മലയാളം", "code" => "ml" ],
			[ "name" => "Maltese - Malti", "code" => "mt" ],
			[ "name" => "Marathi - मराठी", "code" => "mr" ],
			[ "name" => "Mongolian - монгол", "code" => "mn" ],
			[ "name" => "Nepali - नेपाली", "code" => "ne" ],
			[ "name" => "Norwegian - norsk", "code" => "no" ],
			[ "name" => "Norwegian Bokmål - norsk bokmål", "code" => "nb" ],
			[ "name" => "Norwegian Nynorsk - nynorsk", "code" => "nn" ],
			[ "name" => "Occitan", "code" => "oc" ],
			[ "name" => "Oriya - ଓଡ଼ିଆ", "code" => "or" ],
			[ "name" => "Oromo - Oromoo", "code" => "om" ],
			[ "name" => "Pashto - پښتو", "code" => "ps" ],
			[ "name" => "Persian - فارسی", "code" => "fa" ],
			[ "name" => "Polish - polski", "code" => "pl" ],
			[ "name" => "Portuguese - português", "code" => "pt" ],
			[ "name" => "Portuguese (Brazil) - português (Brasil)", "code" => "pt-BR" ],
			[ "name" => "Portuguese (Portugal) - português (Portugal)", "code" => "pt-PT" ],
			[ "name" => "Punjabi - ਪੰਜਾਬੀ", "code" => "pa" ],
			[ "name" => "Quechua", "code" => "qu" ],
			[ "name" => "Romanian - română", "code" => "ro" ],
			[ "name" => "Romanian (Moldova) - română (Moldova)", "code" => "mo" ],
			[ "name" => "Romansh - rumantsch", "code" => "rm" ],
			[ "name" => "Russian - русский", "code" => "ru" ],
			[ "name" => "Scottish Gaelic", "code" => "gd" ],
			[ "name" => "Serbian - српски", "code" => "sr" ],
			[ "name" => "Serbo - Croatian", "code" => "sh" ],
			[ "name" => "Shona - chiShona", "code" => "sn" ],
			[ "name" => "Sindhi", "code" => "sd" ],
			[ "name" => "Sinhala - සිංහල", "code" => "si" ],
			[ "name" => "Slovak - slovenčina", "code" => "sk" ],
			[ "name" => "Slovenian - slovenščina", "code" => "sl" ],
			[ "name" => "Somali - Soomaali", "code" => "so" ],
			[ "name" => "Southern Sotho", "code" => "st" ],
			[ "name" => "Spanish - español", "code" => "es" ],
			[ "name" => "Spanish (Argentina) - español (Argentina)", "code" => "es-AR" ],
			[ "name" => "Spanish (Latin America) - español (Latinoamérica)", "code" => "es-419" ],
			[ "name" => "Spanish (Mexico) - español (México)", "code" => "es-MX" ],
			[ "name" => "Spanish (Spain) - español (España)", "code" => "es-ES" ],
			[ "name" => "Spanish (United States) - español (Estados Unidos)", "code" => "es-US" ],
			[ "name" => "Sundanese", "code" => "su" ],
			[ "name" => "Swahili - Kiswahili", "code" => "sw" ],
			[ "name" => "Swedish - svenska", "code" => "sv" ],
			[ "name" => "Tajik - тоҷикӣ", "code" => "tg" ],
			[ "name" => "Tamil - தமிழ்", "code" => "ta" ],
			[ "name" => "Tatar", "code" => "tt" ],
			[ "name" => "Telugu - తెలుగు", "code" => "te" ],
			[ "name" => "Thai - ไทย", "code" => "th" ],
			[ "name" => "Tigrinya - ትግርኛ", "code" => "ti" ],
			[ "name" => "Tongan - lea fakatonga", "code" => "to" ],
			[ "name" => "Turkish - Türkçe", "code" => "tr" ],
			[ "name" => "Turkmen", "code" => "tk" ],
			[ "name" => "Twi", "code" => "tw" ],
			[ "name" => "Ukrainian - українська", "code" => "uk" ],
			[ "name" => "Urdu - اردو", "code" => "ur" ],
			[ "name" => "Uyghur", "code" => "ug" ],
			[ "name" => "Uzbek - o‘zbek", "code" => "uz" ],
			[ "name" => "Vietnamese - Tiếng Việt", "code" => "vi" ],
			[ "name" => "Walloon - wa", "code" => "wa" ],
			[ "name" => "Welsh - Cymraeg", "code" => "cy" ],
			[ "name" => "Western Frisian", "code" => "fy" ],
			[ "name" => "Xhosa", "code" => "xh" ],
			[ "name" => "Yiddish", "code" => "yi" ],
			[ "name" => "Yoruba - Èdè Yorùbá", "code" => "yo" ],
			[ "name" => "Zulu - isiZulu", "code" => "zu" ],
		];
	}
}
}
