<?php
/*
Author: Qubic Media
Author URI: https://qubicmedia.io
Plugin Name: Content Curator
Plugin URI: https://contentcurator.ai
Description: Encuentra oportunidades de palabras clave y mejora tus contenidos en pocos clics: la manera más simple y rápida de aumentar tu tráfico. Agrega imágenes, videos, metaetiquetas y más en bulk en todos tus artículos.
Version: 2.7.1
License: GPL2
*/

global $wpdb;

defined( 'ABSPATH' ) or die();

define('AICC_PLUGIN_DIR', plugin_dir_path(__FILE__) );

define('AICC_PLUGIN_URL', plugins_url('' , __FILE__));

define('AICC_HISTORICAL_TABLE', $wpdb->prefix . 'aicc_historical');

define('AICC_PENDING_TABLE', $wpdb->prefix . 'aicc_pending');

define('AICC_LOG_TABLE', $wpdb->prefix . 'aicc_log');

define('AICC_BULK_TABLE', $wpdb->prefix . 'aicc_bulk');

define('AICC_UPGRADE_PATH', ABSPATH . 'wp-admin/includes/upgrade.php' );

define( 'AICC_STORE_URL', 'https://contentcurator.ai' );

define( 'AICC_ITEM_ID', 19 ); 

define( 'AICC_ITEM_NAME', 'Content Curator' ); 

define( 'AICC_LICENSE_PAGE', 'aicc-license' );

define('AICC_FILE', __FILE__);

define('AICC_SC_LIMIT', 2);
	
class SeoCuratorPlugin 
{
	public function __construct() 
	{
		
		register_activation_hook(__FILE__, array($this, 'aicc_on_activation'));
		
		$this->page_title = 'Content Curator';
		$this->menu_title = 'Content Curator';
		$this->capability = 'publish_posts';
		$this->menu_slug = 'aicc';
		$this->function = array(
			$this,
			'aicc_CreatePage'
		);
		$this->icon_url = 'dashicons-chart-bar';
		$this->position = 99;
		
		add_action('admin_menu', array(
			$this,
			'aicc_CreatePageMenu'
		) , 10);
 		
		add_action('save_post', array($this, 'aicc_insert_content'), 100, 3 );
  		
		add_action('admin_enqueue_scripts', array($this, 'aicc_scripts'));        
    	
		add_action('add_meta_boxes', array($this, 'aicc_add_metabox'));  
    	
		add_action('admin_notices', array($this, 'show_aicc_update_message'));
	
  		add_action('plugins_loaded', array( $this, 'aicc_init' ) );   		
	
		add_action('shutdown', array( $this, 'aicc_capture_fatal_error' ) );
	
		add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array($this, 'aicc_action_links' ));	

		add_action('aicc_cron_sc', array($this, 'aicc_search_console_first'));	
		
		add_action('wp_ajax_aicc_process_keyword', array($this, 'aicc_process_keyword'));
		add_action('wp_ajax_aicc_process_bulkc_content', array($this, 'aicc_process_bulkc_content')); 
		add_action('wp_ajax_aicc_process_bulki_content', array($this, 'aicc_process_bulki_content')); 
		add_action('wp_ajax_aicc_process_bulkv_content', array($this, 'aicc_process_bulkv_content')); 
		add_action('wp_ajax_aicc_process_bulkt_content', array($this, 'aicc_process_bulkt_content')); 
		add_action('wp_ajax_aicc_process_bulkr_content', array($this, 'aicc_process_bulkr_content')); 
    	add_action('wp_ajax_aicc_ajax_load_page', array($this, 'aicc_ajax_load_page'));
		add_action('wp_ajax_aicc_show_message', array($this, 'aicc_show_message'));
		add_action('wp_ajax_aicc_check_running', array($this, 'aicc_check_running'));
		add_action('wp_ajax_aicc_check_bulk_running', array($this, 'aicc_check_bulk_running'));
		add_action('wp_ajax_aicc_accept_content',  array($this, 'aicc_accept_content'));
		add_action('wp_ajax_aicc_get_transient_content', array($this, 'aicc_get_transient_content_callback'));
		add_action('wp_ajax_aicc_delete_transient_content', array($this,'aicc_delete_transient_content_callback'));
		add_action('wp_ajax_aicc_cancel_process',  array($this, 'aicc_cancel_process'));
		add_action('wp_ajax_export_data',  array($this, 'aicc_fetch_data_for_export')); 
		add_action('wp_ajax_aicc_mark_thin_as_resolved',  array($this, 'aicc_mark_thin_as_resolved')); 
		add_action('wp_ajax_aicc_mark_canibal_as_resolved',  array($this, 'aicc_mark_canibal_as_resolved')); 
		add_action('wp_ajax_aicc_return_total_cost', array($this, 'aicc_return_total_cost')); 
		add_action('wp_ajax_aicc_cancel_bulk_process', array($this, 'aicc_cancel_bulk_process')); 
		add_action('wp_ajax_aicc_clear_log', array($this, 'aicc_clear_log')); 
        add_action('wp_ajax_aicc_unlock_process', array($this, 'aicc_unlock_process')); 
        add_action('wp_ajax_aicc_get_posts_to_recover', array($this,'aicc_get_posts_to_recover'));

		add_action('admin_post_aicc_export_urls_csv',  array($this, 'aicc_export_urls_to_csv'));
		add_action('admin_post_aicc_export_canibal_csv',  array($this, 'aicc_export_canibal_to_csv'));

		// Creamos la nueva columna de modelo para la tabla historical
		add_action('admin_init', array($this, 'aicc_add_new_column_historical'), 10, 1);

		add_action('wp_head',  array($this,'aicc_add_faq_schema_to_head'));
	
  	}

	function aicc_create_dbs() 
	{
		global $wpdb;
		$charset_collate = $wpdb->get_charset_collate();
		$this->aicc_create_posts_table( $wpdb, $charset_collate );
	}

	
	function aicc_create_posts_table( $wpdb, $charset_collate ) 
	{
		global $wpdb;
		
		if ( ! $this->aicc_table_exists( AICC_HISTORICAL_TABLE ) )
		{
			$sql = $wpdb->prepare( "
				CREATE TABLE " . AICC_HISTORICAL_TABLE . " (
					id mediumint(9) NOT NULL AUTO_INCREMENT,
					post_id mediumint(9) NOT NULL,
					tokens mediumint(9) NOT NULL,
					cost DECIMAL(12,9) NOT NULL,
					status mediumint(9) NOT NULL,
					content TEXT,
					date DATETIME DEFAULT CURRENT_TIMESTAMP,
					PRIMARY KEY  (id),
        			INDEX (post_id)
				) " . $charset_collate . ";
			" );
			require_once( AICC_UPGRADE_PATH );
			dbDelta( $sql );
		}
		
		if ( ! $this->aicc_table_exists( AICC_PENDING_TABLE ) )
		{
			$sql = $wpdb->prepare( "
				CREATE TABLE " . AICC_PENDING_TABLE . " (
					id mediumint(9) NOT NULL AUTO_INCREMENT,
					post_id mediumint(9) NOT NULL,
					content TEXT  NOT NULL,
					new_post TINYINT(1) DEFAULT 0,
					date DATETIME DEFAULT CURRENT_TIMESTAMP,
					PRIMARY KEY  (id),
					UNIQUE (post_id)
				) " . $charset_collate . ";
			" );
			require_once( AICC_UPGRADE_PATH );
			dbDelta( $sql );
		}
		
		if ( ! $this->aicc_table_exists( AICC_LOG_TABLE ) )
		{
			$sql = $wpdb->prepare( "
				CREATE TABLE " . AICC_LOG_TABLE . " (
					id mediumint(9) NOT NULL AUTO_INCREMENT,
					post_id mediumint(9),					
					type varchar(55) NOT NULL,
					message text  NOT NULL,
			    	`function` varchar(255),
					date DATETIME DEFAULT CURRENT_TIMESTAMP,					
					PRIMARY KEY  (id),
					INDEX (post_id)
					) " . $charset_collate . ";
			" );
			require_once( AICC_UPGRADE_PATH );
			dbDelta( $sql );
		}

		if ( ! $this->aicc_table_exists( AICC_BULK_TABLE ) )
		{
			$sql = $wpdb->prepare( "
				CREATE TABLE " . AICC_BULK_TABLE . " (
					id mediumint(9) NOT NULL AUTO_INCREMENT,
					type varchar(55) NOT NULL,
					amount mediumint(9) NOT NULL,
					tokens mediumint(9) NOT NULL,
					cost DECIMAL(12,9) NOT NULL,
					budget mediumint(9) NOT NULL,
					model varchar(55) NOT NULL,
					ids_processed TEXT NOT NULL,
					date DATETIME DEFAULT CURRENT_TIMESTAMP,
					PRIMARY KEY  (id),
        			INDEX (type)
				) " . $charset_collate . ";
			" );
			require_once( AICC_UPGRADE_PATH );
			dbDelta( $sql );
		}
		
	}

	function aicc_table_exists($table)
	{
		global $wpdb;
		$tables = $wpdb->get_col("SHOW TABLES");
		return in_array($table, $tables);
	}	
	
	
    public function aicc_on_activation() 
    {
        /*
         * Creamos las tablas cuando se activa el plugin 
         */
        $this->aicc_create_dbs();				
    }	
	
	function aicc_add_new_column_historical() {
	    global $wpdb;
	    $column_name = 'model';
	    $column_type = 'VARCHAR(255)';
	    $table_name = AICC_HISTORICAL_TABLE;

	    // Comprueba si la columna ya existe
	    $column_exists = $wpdb->get_results($wpdb->prepare(
	        "SHOW COLUMNS FROM {$table_name} LIKE %s",
	        $column_name
	    ));

	    // Si la columna no existe, altera la tabla para agregar la nueva columna
	    if (empty($column_exists)) {
    		$sql = "ALTER TABLE {$table_name} ADD {$column_name} {$column_type} AFTER content";
	        $wpdb->query($sql);
	    }

	    $column_name = 'budget';
	    $column_type = 'mediumint(9)';
	    $table_name = AICC_BULK_TABLE;

	    // Comprueba si la columna ya existe
	    $column_exists = $wpdb->get_results($wpdb->prepare(
	        "SHOW COLUMNS FROM {$table_name} LIKE %s",
	        $column_name
	    ));

	    // Si la columna no existe, altera la tabla para agregar la nueva columna
	    if (empty($column_exists)) {
    		$sql = "ALTER TABLE {$table_name} ADD {$column_name} {$column_type} AFTER cost";
	        $wpdb->query($sql);
	    }	    

	}

	public function aicc_scripts() 
  	{
		wp_enqueue_style( 'aicc-css', 
			plugin_dir_url( __FILE__ ) . 'assets/css/main.css', 
			array(), 
			'32002240923'
		);
				 
		wp_enqueue_script( 'aicc-scripts',  
			plugin_dir_url( __FILE__ ) . 'assets/js/main.js', 
			array( 'jquery'), 
			'04360422', 
			true
		);
		
		wp_enqueue_style( 'aicc-sweet-css', 
			plugin_dir_url( __FILE__ ) . 'assets/css/sweetalert.min.css', 
			array(), 
			'02070124'
		);
				 
		wp_enqueue_script( 'aicc-sweet-scripts',  
			plugin_dir_url( __FILE__ ) . 'assets/js/sweetalert.min.js', 
			array( 'jquery'), 
			'02250823', 
			true
		);		
				
		
  	}
  
	public function aicc_add_metabox() {
	    global $post;

	    $post_types = get_post_types(array('public' => true), 'names');
	    $excluded_post_types = array('attachment');
	    $post_types = array_diff($post_types, $excluded_post_types);

	    foreach ($post_types as $post_type) {
	        $enable_option = get_option("aicc_enable_{$post_type}");
	        if ($enable_option && isset($post) && $post->post_status != 'auto-draft') {
	            add_meta_box(
	                "search-console-data",
	                "Content Curator",
	                array($this, 'render_metabox'),
	                $post_type,
	                'normal',
	                'high'
	            );
	        }
	    }
	}

  	public function aicc_init()
  	{
		
		load_plugin_textdomain('aicc', false, dirname(plugin_basename(__FILE__)) . '/languages');
		require_once plugin_dir_path( __FILE__ ) . 'classes/wp-async-request.php';
		require_once plugin_dir_path( __FILE__ ) . 'classes/wp-background-process.php';

		$this->manual = new WP_AutoContent_Background_Process();
		$this->bulk = new WP_Bulk_Background_Process($this);
  	}
	
	
	
	// Create menu pages
	 public function aicc_CreatePageMenu()
		{

			$this->submenu_title = 'Opciones';

			$this->page_menu = add_menu_page(
				$this->page_title, 
				$this->menu_title, 
				$this->capability, 
				$this->menu_slug, 
				$this->function, 
				$this->icon_url, 
				$this->position
			);
				
			$this->submenu_page = add_submenu_page(
				$this->menu_slug, 
				$this->submenu_title, 
				$this->submenu_title, 
				$this->capability,
				$this->menu_slug, 
				$this->function
			);

		 	add_submenu_page(
				'aicc',
				'Curación masiva', 
				'Curación masiva', 
				'manage_options',
				'aicc-bulk',
            	array($this, 'aicc_CreateBulkPage')
			);		 

		 	add_submenu_page(
				'aicc',
				'Registro de depuración', 
				'Registro de depuración', 
				'manage_options',
				'aicc-log',
            	array($this, 'aicc_CreateLogPage')
			);	

		 	add_submenu_page(
				'aicc',
				'Licencia Content Curator', 
				'Licencia',
				'manage_options',
				'aicc-license',
				'aicc_license_page'
			);
		 
		}

		
		function aicc_action_links ( $actions ) {
		 	$actions[] = '<a href="'. esc_url( get_admin_url(null, 'admin.php?page=aicc') ) .'">Ajustes</a>';
		   $actions[] = '<a href="https://contentcurator.ai/tutoriales/" target="_blank">Tutoriales</a>';
		   $actions[] = '<a href="https://contentcurator.ai/docs/" target="_blank">Documentación</a>';
		   return $actions;
		}


		public function aicc_CreatePage()
		{

			if (isset($_GET['tab'])) { $active_tab = $_GET['tab'];	}

			if (empty($active_tab)) {	$active_tab = 'options_settings';	}
			
			global $wpdb;

			$sql = "SELECT COUNT(*) FROM " . AICC_PENDING_TABLE;
				
			$total_registros = $wpdb->get_var($sql);

		

			?>
		
			<div class="wrap wrapper-aicc-options">
			
				<div class="nav-tab-wrapper">
					
					<div style="text-align:center; ">
						<img class="aicc-logo" src="<?php echo AICC_PLUGIN_URL; ?>/assets/img/logo.png" width="840px" />
					</div>

					<?php if ( current_user_can('manage_options') ) : ?>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=options_settings" class="nav-tab <?php echo $active_tab == 'options_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-adjustments-horizontal" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							<path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 6l8 0" /><path d="M16 6l4 0" /><path d="M8 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 12l2 0" /><path d="M10 12l10 0" /><path d="M17 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M4 18l11 0" /><path d="M19 18l1 0" />
						</svg>
						<span><?php _e('General Options', 'aicc'); ?></span>
					</a>	

					<?php endif; ?>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=list_settings" class="nav-tab <?php echo $active_tab == 'list_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
						  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
						  <path d="M15 15m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" />
						  <path d="M18.5 18.5l2.5 2.5" />
						  <path d="M4 6h16" />
						  <path d="M4 12h4" />
						  <path d="M4 18h4" />
						</svg>
						<span><?php _e('List of posts', 'aicc'); ?></span>
					</a>		

					<a href="?page=<?php echo $_GET['page']; ?>&tab=thin_settings" class="nav-tab <?php echo $active_tab == 'thin_settings' ? 'nav-tab-active' : ''; ?>">
							<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-notes-off" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							  <path d="M7 3h10a2 2 0 0 1 2 2v10m0 4a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-14" />
							  <path d="M11 7h4" />
							  <path d="M9 11h2" />
							  <path d="M9 15h4" />
							  <path d="M3 3l18 18" />
							</svg>
							<span><?php _e('Thin content', 'aicc'); ?></span>
					</a>
				
						
					<a href="?page=<?php echo $_GET['page']; ?>&tab=canibal_settings" class="nav-tab <?php echo $active_tab == 'canibal_settings' ? 'nav-tab-active' : ''; ?>">
							<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-bell-ringing" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							  <path d="M10 5a2 2 0 0 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6" />
							  <path d="M9 17v1a3 3 0 0 0 6 0v-1" />
							  <path d="M21 6.727a11.05 11.05 0 0 0 -2.794 -3.727" />
							  <path d="M3 6.727a11.05 11.05 0 0 1 2.792 -3.727" />
							</svg>
							<span>
								<?php _e('Cannibalizations', 'aicc'); ?>
							</span>
					</a>		

					<a href="?page=<?php echo $_GET['page']; ?>&tab=prompts_settings" class="nav-tab <?php echo $active_tab == 'prompts_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-prompt" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
						  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
						  <path d="M5 7l5 5l-5 5" />
						  <path d="M13 17l6 0" />
						</svg>
												
						<span><?php _e('Instrucciones', 'aicc'); ?></span>
					</a>							

					<!--<a href="?page=<?php //echo $_GET['page']; ?>&tab=pending_settings" class="nav-tab <?php //echo $active_tab == 'pending_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-history" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							<path d="M12 8l0 4l2 2" />
							<path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" />
						</svg>
						<span>
							<?php //_e('Pending Curations', 'aicc'); ?>
							<?php //if($total_registros > 0): ?>
								<span><?php echo $total_registros; ?></span>
							<?php //endif; ?>
						</span>
					</a>-->						

					<a href="?page=<?php echo $_GET['page']; ?>&tab=historical_settings" class="nav-tab <?php echo $active_tab == 'historical_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							<path d="M9 6l11 0" />
							<path d="M9 12l11 0" />
							<path d="M9 18l11 0" />
							<path d="M5 6l0 .01" />
							<path d="M5 12l0 .01" />
							<path d="M5 18l0 .01" />
						</svg>
						<span><?php _e('Historical', 'aicc'); ?></span>
					</a>

					<?php if ( current_user_can('manage_options') ) : ?>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=apis_settings" class="nav-tab <?php echo $active_tab == 'apis_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="1" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							<path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z" />
							<path d="M15 9h.01" />
						</svg>
						<span><?php _e('Claves API', 'aicc'); ?></span>
					</a>

					<?php endif; ?>

				</div>
					
				<section id="aicc-options options-general" class="aicc-options section-content active">
						
						<?php

						if ($active_tab == 'options_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/options.php');
						}
			
												
						if ($active_tab == 'pending_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/pending.php');
						}		
					
						if ($active_tab == 'historical_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/historical.php');
						}		
			
						if ($active_tab == 'apis_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/apis.php');
						}	

						if ($active_tab == 'prompts_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/prompts.php');
						}

						if ($active_tab == 'list_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/list.php');
						}
			
						if ($active_tab == 'canibal_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/canibal.php');
						}
												
						if ($active_tab == 'thin_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/thin-content.php');
						}

						?>
												
				</section>
				
				<script>
				jQuery(function(){
				  jQuery('input[type=checkbox]').switchify();
				});
				</script>

			</div>

		<?php
			
	}


	/*
	 * Creamos la opción del menú de curación en bulk
	 */
	public function aicc_CreateListPage()
		{

			if (isset($_GET['tab'])) { $active_tab = $_GET['tab'];	}

			if (empty($active_tab)) { $active_tab = 'list_settings'; }

			?>
		
			<div class="wrap wrapper-aicc-options">
			
				<div class="nav-tab-wrapper">
					
					<div style="text-align:center; ">
						<img class="aicc-logo" src="<?php echo AICC_PLUGIN_URL; ?>/assets/img/logo.png" width="840px" />
					</div>

						
				</div>
					
				<section id="aicc-options options-general" class="aicc-options section-content active">
					<?php
																								
					?>
				</section>
				
				<script>
				jQuery(function(){
				  jQuery('input[type=checkbox]').switchify();
				});
				</script>

			</div>

		<?php
			
		} 


/*
	 * Creamos la opción del menú de curación en bulk
	 */
	public function aicc_CreateBulkPage()
		{

			if (isset($_GET['tab'])) { $active_tab = $_GET['tab'];	}

			if (empty($active_tab)) { $active_tab = 'bulk_content_settings'; }

			?>
		
			<div class="wrap wrapper-aicc-options">
			
				<div class="nav-tab-wrapper">
					
					<div style="text-align:center; ">
						<img class="aicc-logo" src="<?php echo AICC_PLUGIN_URL; ?>/assets/img/logo.png" width="840px" />
					</div>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_content_settings" class="nav-tab <?php echo $active_tab == 'bulk_content_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-align-box-left-middle" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 3m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /><path d="M9 15h-2" /><path d="M13 12h-6" />  <path d="M11 9h-4" /></svg>
						<span><?php _e('Content bulk curation', 'aicc'); ?></span>
					</a>	

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_image_settings" class="nav-tab <?php echo $active_tab == 'bulk_image_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-photo" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path d="M15 8h.01" />  <path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" />  <path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3" /></svg>
						<span><?php _e('Image bulk curation', 'aicc'); ?></span>
					</a>	

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_video_settings" class="nav-tab <?php echo $active_tab == 'bulk_video_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" x-bind:width="size" x-bind:height="size" viewBox="0 0 24 24" fill="none" stroke="currentColor" x-bind:stroke-width="stroke" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="1.5">
						  <path d="M2 8a4 4 0 0 1 4 -4h12a4 4 0 0 1 4 4v8a4 4 0 0 1 -4 4h-12a4 4 0 0 1 -4 -4v-8z"></path>
						  <path d="M10 9l5 3l-5 3z"></path>
						</svg>
						<span><?php _e('Curación de videos', 'aicc'); ?></span>
					</a>	

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_meta_settings" class="nav-tab <?php echo $active_tab == 'bulk_meta_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-bookmarks" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M13 7a2 2 0 0 1 2 2v12l-5 -3l-5 3v-12a2 2 0 0 1 2 -2h6z" /><path d="M9.265 4a2 2 0 0 1 1.735 -1h6a2 2 0 0 1 2 2v12l-1 -.6" /></svg>
						<span><?php _e('Metas bulk curation', 'aicc'); ?></span>
					</a>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_historical_settings" class="nav-tab <?php echo $active_tab == 'bulk_historical_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
							<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
							<path d="M9 6l11 0" />
							<path d="M9 12l11 0" />
							<path d="M9 18l11 0" />
							<path d="M5 6l0 .01" />
							<path d="M5 12l0 .01" />
							<path d="M5 18l0 .01" />
						</svg>
						<span><?php _e('Historical', 'aicc'); ?></span>
					</a>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=bulk_autosave_settings" class="nav-tab <?php echo $active_tab == 'bulk_autosave_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#757575" stroke-linecap="round" stroke-linejoin="round" width="24" height="24" stroke-width="1.5"> <path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5"></path> <path d="M5.63 7.16l0 .01"></path> <path d="M4.06 11l0 .01"></path> <path d="M4.63 15.1l0 .01"></path> <path d="M7.16 18.37l0 .01"></path> <path d="M11 19.94l0 .01"></path> </svg> 
						<span><?php _e('Recuperar revisión', 'aicc'); ?></span>
					</a>
					
				</div>
					
				<section id="aicc-options options-general" class="aicc-options section-content active">
					<?php
						if ($active_tab == 'bulk_content_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/content.php');
						}
						if ($active_tab == 'bulk_image_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/image.php');
						}		
						if ($active_tab == 'bulk_video_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/video.php');
						}							
						if ($active_tab == 'bulk_meta_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/metatags.php');
						}	
						if ($active_tab == 'bulk_autosave_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/recovery.php');
						}
						if ($active_tab == 'bulk_historical_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/bulk/historical_bulk.php');
						}																
					?>
				</section>
				
				<script>
				jQuery(function(){
				  jQuery('input[type=checkbox]').switchify();
				});
				</script>

			</div>

		<?php
			
		} 
		

	/*
	 * Creamos la opción del menú de registro de procesamiento
	 */
	public function aicc_CreateLogPage()
		{

			if (isset($_GET['tab'])) { $active_tab = $_GET['tab'];	}

			if (empty($active_tab)) { $active_tab = 'logs_settings'; }


			?>
		
			<div class="wrap wrapper-aicc-options">
			
				<div class="nav-tab-wrapper">
					
					<div style="text-align:center; ">
						<img class="aicc-logo" src="<?php echo AICC_PLUGIN_URL; ?>/assets/img/logo.png" width="840px" />
					</div>

					<a href="?page=<?php echo $_GET['page']; ?>&tab=logs_settings" class="nav-tab <?php echo $active_tab == 'logs_settings' ? 'nav-tab-active' : ''; ?>">
						<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-article" width="20" height="20" viewBox="0 0 24 24" stroke-width="1.5" stroke="#757575" fill="none" stroke-linecap="round" stroke-linejoin="round">
						  <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
						  <path d="M3 4m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" />
						  <path d="M7 8h10" />
						  <path d="M7 12h10" />
						  <path d="M7 16h10" />
						</svg>
						<span><?php _e('Registro de depuración', 'aicc'); ?></span>
					</a>	

				</div>
					
				<section id="aicc-options options-general" class="aicc-options section-content active">
					<?php
						if ($active_tab == 'logs_settings') 
						{
							include ( AICC_PLUGIN_DIR . 'settings/log.php');
						}																
					?>
				</section>
				
				<script>
				jQuery(function(){
				  jQuery('input[type=checkbox]').switchify();
				});
				</script>

			</div>

		<?php
			
		} 
	

	
	// Render metabox
  
	public function render_metabox( $post ) 
	{

		$apikey_sc = get_option('aicc_options_sc') ;
				
		if ( isset($apikey_sc) && !empty($apikey_sc) )  {
		
		?>
 
		<script>
		

		jQuery(document).ready(function($) {

			sessionStorage.removeItem('checkboxStates');

			var currentPage = 1;
			var currentSort = 'impressions';
			var positionFilter = null;
			var searchTerm = null;
			var currentOrder = {
				'impressions': 'desc',
				'clicks': 'asc',
				'position': 'desc',
				'mention': 'asc'
			};

			function initializePage() {
				loadData(currentPage, currentOrder[currentSort], currentSort, positionFilter, searchTerm);
			}

			function loadData(page, order, sort, filter, searchTerm) {
				var url = '<?php echo admin_url('admin-ajax.php'); ?>';
				var data = {
					'action': 'aicc_ajax_load_page',
					'post_id': <?php echo $post->ID; ?>,
					'page' : page,
					'order': order,
					'sort': sort,
					'position_filter': filter,
					'search_term': searchTerm,
					'current_search': $('#aicc-keyword-search').val(), 
        			'current_position': $('#position-select').val()
				};

				$.post(url, data, function(response) {
					$('#results').html(response);
					$('.aicc-table-body').removeClass('loading');
					currentPage = page;
					currentSort = sort;
					positionFilter = filter;
					searchTerm = null;
					reapplyCheckboxStates();
				});
			}


			$(document).on('click', '.pagination a', function(e) {
				e.preventDefault();

				$('.aicc-table-body').addClass('loading');
				
				var page = parseInt($(this).attr('data-page'));

				if (page !== currentPage) {
					loadData(page, currentOrder[currentSort], currentSort, positionFilter, searchTerm);
				}
			});

			$(document).on('click', '#sort-impressions', function(e) {
				e.preventDefault();
				$('.aicc-table-body').addClass('loading');				
				if (currentOrder['impressions'] === 'asc') {
					loadData(1, 'desc', 'impressions', positionFilter, searchTerm);
					currentOrder['impressions'] = 'desc';
				} else {
					loadData(1, 'asc', 'impressions', positionFilter, searchTerm);
					currentOrder['impressions'] = 'asc';
				}
			});

			$(document).on('click', '#sort-clicks', function(e) {
				e.preventDefault();
				$('.aicc-table-body').addClass('loading');	
				if (currentOrder['clicks'] === 'asc') {
					loadData(1, 'desc', 'clicks', positionFilter, searchTerm);
					currentOrder['clicks'] = 'desc';
				} else {
					loadData(1, 'asc', 'clicks', positionFilter, searchTerm);
					currentOrder['clicks'] = 'asc';
				}
			});

			$(document).on('click', '#sort-position', function(e) {
				e.preventDefault();
				$('.aicc-table-body').addClass('loading');	
				if (currentOrder['position'] === 'asc') {
					loadData(1, 'desc', 'position', positionFilter, searchTerm);
					currentOrder['position'] = 'desc';
				} else {
					loadData(1, 'asc', 'position', positionFilter, searchTerm);
					currentOrder['position'] = 'asc';
				}
			});

			$(document).on('click', '#sort-mention', function(e) {
				e.preventDefault();
				$('.aicc-table-body').addClass('loading');	
				if (currentOrder['mention'] === 'asc') {
					loadData(1, 'desc', 'mention', positionFilter, searchTerm);
					currentOrder['mention'] = 'desc';
				} else {
					loadData(1, 'asc', 'mention', positionFilter, searchTerm);
					currentOrder['mention'] = 'asc';
				}
			});

			$(document).on('click', '#apply-filter', function(e) {
			  	e.preventDefault();
			  	var filterSelection = $('#position-select').val();
				$('.aicc-table-body').addClass('loading');	
			  	switch (filterSelection) {
					case "all":
					  positionFilter = null;
					  break;
					case "second":
					  positionFilter = {min: 11, max: 20};
					  break;
					case "not_in_text":
			            positionFilter = 'not_in_text';
			            break;
			  	}

			  	loadData(1, currentOrder[currentSort], currentSort, positionFilter, searchTerm);
				
			});
			
			$(document).on('click', '#aicc-search-button', function(e) {
				e.preventDefault();
				$('.aicc-table-body').addClass('loading');	
				searchTerm = $('#aicc-keyword-search').val(); 
				loadData(1, currentOrder[currentSort], currentSort, positionFilter, searchTerm); 
			});

			function reapplyCheckboxStates() {
			    // Recupera el estado de los checkboxes del almacenamiento local
			    checkboxStates = JSON.parse(sessionStorage.getItem('checkboxStates')) || {};

			    $('.aicc-table-body input[type=checkbox]').each(function() {
			        var id = $(this).closest('tr').data('id'); 
			        // Aquí comprobamos si 'checkboxStates[id]' es 'true', 'false' o 'undefined'
			        if(checkboxStates[id] !== undefined) {
			            $(this).prop('checked', checkboxStates[id]); // Si está definido (ya sea true o false), usamos ese valor
			        } else {
			            $(this).prop('checked', false); // Si no está definido (undefined), desmarcamos el checkbox
			        }
			    });
			}


			 initializePage();
		});

		</script>

		<div id="results">
			 
			<div style="display: flex; align-items: center; justify-content: center; ">
				<span class="spinner is-active"></span>
				<p>
					Cargando datos de Search Console
				</p>
			</div>
		</div>

 		<?php
			
		}
		else
		{
			
		 $apis_url = admin_url('admin.php?page=aicc&tab=apis_settings');
			
		?>

		<div class="notice notice-warning inline active-plugin-edit-warning" style="margin:10px !important;"><p><strong>Atención:</strong> Es necesario ingresar tus claves de API. <a href="<?php echo esc_url($apis_url) ?>">Ir a la configuración del plugin</a></p></div>

		<?php
			
		}
	}

	
 
 	/*
 	 * Render
	 */
	
	public function aicc_ajax_load_page() 
	{
    	if (isset($_POST['post_id']) && isset($_POST['page'])) 
		{

			$current_search = isset($_POST['current_search']) ? $_POST['current_search'] : '';
			$current_position = isset($_POST['current_position']) ? $_POST['current_position'] : 'all';

			?>

			<style>
				
				.modal {
					display: none; 
					position: fixed;
					top: 0;
					left: 0;
					width: 100%;
					height: 100%;
					background-color: rgba(0, 0, 0, 0.5); 
					z-index: 9999;
				}

				.modal-content {
					position: absolute;
					top: 50%;
					left: 50%;
					transform: translate(-50%, -50%);
					background-color: #fff;
					padding: 20px;
					border-radius: 5px;
					box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
				}

				.close {
					color: #aaa;
					float: right;
					font-size: 28px;
					font-weight: bold;
					cursor: pointer;
				}

				.close:hover,
				.close:focus {
					color: black;
					text-decoration: none;
					cursor: pointer;
				}


				
				.aicc-hide {
					display:none;
				}
				
				.aicc-trns-content {
					max-width:80%;
				}
				
				.aicc-position-container {
					padding: 20px;
					background: #fff;
					border-bottom: 0;
					display: flex;
					align-items: center;
					justify-content: flex-end;
				}
				.aicc-table-container {
					    border: 1px solid #c3c4c7;		
					background:#fff;
					margin-bottom:12px;
					
				}
				.pagination-link-off {
					color:#999 !important;
					border:none !important;
					background:transparent !important;
				}

			</style>

			<?php
			
      		$post_id = intval($_POST['post_id']);
			
     	 	$page = intval($_POST['page']);
			
      		$data = $this->aicc_search_console_data_cached();

      		if (empty($data)) 
			{
				echo '<div class="notice notice-warning inline active-plugin-edit-warning" style="margin:10px !important;"><p>Hubo un error al conectarse con Search Console. Verifica los permisos de tu clave API de Search Console o prueba cambiando de tipo de propiedad en las opciones del plugin.</p></div>';
				return; 
			}
			
      		$post_content = strtolower(get_post_field('post_content', $post_id));
			
      		$post_content = remove_accents($post_content);

			$url = $this->aicc_check_canonical($post_id);
						
			$data_array = $data['data'];

			$filtered_data = array_filter($data_array, function ($row) use ($url) {
			    return $row['url'] == $url;
			});

			// Mostramos solo las primeras 1000 consultas para cada URL 
			$filtered_data = array_slice($filtered_data, 0, 1000);
			
		 	$results_per_page = 10;
			
		  	$start_index = ($page - 1) * $results_per_page;
						
      		$column = $_POST['sort'];
			
      		$order = $_POST['order'];
			
      		$column_data = array();
	
			foreach ($filtered_data as $row) 
			{
				$keyword = $row['keyword'];

				switch ($column) 
				{
					case 'impressions':
						$column_data[] = $row['impressions'];
						break;
					case 'clicks':
						$column_data[] = $row['clicks'];
						break;
					case 'position':
						$column_data[] = $row['position'];
						break;
					case 'mention':
						$column_data[] = substr_count($post_content, remove_accents(strtolower($keyword)));
						break;    
				}
			}

		  	if ($order === 'asc') 
			{
				array_multisort($column_data, SORT_ASC, $filtered_data);
		  	} 
			else 
			{
				array_multisort($column_data, SORT_DESC, $filtered_data);
		  	}

		  	$position_filter = isset($_POST['position_filter']) ? $_POST['position_filter'] : null;
			
			if ($position_filter == 'not_in_text') {
			    $filtered_data = array_filter($filtered_data, function ($row) use ($post_content) {
			        $keyword = remove_accents(strtolower($row['keyword']));
			        $post_content_processed = remove_accents(strtolower($post_content));
			        return substr_count($post_content_processed, $keyword) == 0;
			    });
			}
			elseif (is_array($position_filter)) {
			    $min = $position_filter['min'] ?? null;
			    $max = $position_filter['max'] ?? null;

			    $filtered_data = array_filter($filtered_data, function ($row) use ($min, $max) {
			        $position = $row['position'];
			        if ($min !== null && $position < $min) {
			            return false;
			        }
			        if ($max !== null && $position > $max) {
			            return false;
			        }
			        return true;
			    });
			}

			$search_term = isset($_POST['search_term']) ? sanitize_text_field($_POST['search_term']) : '';

			if ($search_term !== '') {
				$filtered_data = array_filter($filtered_data, function ($row) use ($search_term) {
					$keyword = $row['keyword']; // Aquí hemos asignado el keyword directamente.
					return strpos(strtolower($keyword), strtolower($search_term)) !== false;
				});
			}


			$paged_data = array_slice($filtered_data, $start_index, $results_per_page);
	
			global $wpdb;

			$sql = $wpdb->prepare("SELECT content FROM " . AICC_PENDING_TABLE . " WHERE post_id = %d", $post_id);

			$content = $wpdb->get_var($sql);
			
			if ( $content )
			{
				echo '<div id="transient-message" class="notice notice-warning inline active-plugin-edit-warning"><p><strong>Atención:</strong> Es necesario guardar los cambios para terminar la curación de contenidos. <a href="#" id="open-modal-link">Vista previa</a> | <a href="#" id="delete-transient-link">Eliminar</a><span class="spinner is-active" style="display:none;"></span></p></div>';
			
			}
			else
			{
			echo '<div id="transient-message" class="aicc-hide notice notice-warning inline active-plugin-edit-warning"></div>';				
			}

			?>

			<div style="display:flex; justify-content: space-between; padding: 0 0 12px 0;">

			 	<div> 		
			  		<select id="position-select" style="margin-right:1px; min-width:230px;">
						<option value="all" <?php selected($current_position, 'all'); ?>>Mostrar todos los resultados</option>
    					<option value="second" <?php selected($current_position, 'second'); ?>>Mostrar solo segunda página</option>
    					<option value="not_in_text" <?php selected($current_position, 'not_in_text'); ?>>Mostrar solo palabras clave sin mención</option>
					</select>
					<input class="button"  id="apply-filter" type="submit" value="Buscar">
				</div>

				<div>
					<input type="text" id="aicc-keyword-search" value="<?php echo esc_attr($current_search); ?>" placeholder="Buscar por palabra clave" style="margin-right:1px; min-width:180px;">
					<input class="button" id="aicc-search-button" type="submit" value="Buscar">

				</div>

			</div>

			<?php

			echo '<div class="aicc-table-container">';

			$total_pages = ceil(count($filtered_data) / $results_per_page);

			$this->render_search_console_table($paged_data, $post_id, $order);

			if (count($filtered_data) > $results_per_page) 
			{
				$total_filtered_data = count($filtered_data);
				$this->render_pagination($page, $total_pages, $total_filtered_data );
			}
			
			echo '</div>';
			
			?>

			<script>
				jQuery(document).ready(function($) {
					var modal = $("#aicc-modal");
					var close = $(".aicc-cancel-btn");
					var postId = <?php echo $post_id; ?>;
					var modalContent = $(".aicc-modal-content");
					var isProcessing = false;
					var loader = $(".loader");
					var intervalID;
					var modal = document.querySelector('.aicc-modal');



					$(document).on('change', '.aicc-table-body input[type=checkbox]', function() {
					    var id = $(this).closest('tr').data('id');

					    // Recupera checkboxStates del sessionStorage o inicializa a un objeto vacío si no existe
					    var checkboxStates = JSON.parse(sessionStorage.getItem('checkboxStates')) || {};

					    // Actualiza checkboxStates con el nuevo estado
					    checkboxStates[id] = $(this).is(':checked');

					    // Guarda en el almacenamiento del navegador
					    sessionStorage.setItem('checkboxStates', JSON.stringify(checkboxStates));
					});

					function resetCheckboxStates() {
					   	sessionStorage.removeItem('checkboxStates');

					    // Independientemente de si hubo un error o no, se procede a desmarcar todos los checkboxes
					    $('.aicc-table-body input[type=checkbox]').each(function() {
					        $(this).prop('checked', false); // Desmarca todos los checkboxes
					    });
					}


					function openModal() 
					{
						modal.style.display = 'flex';

					}

					function closeModal() 
					{
						modal.style.display = 'none';
					}

					function resetModal() {
						isProcessing = false;
    					$(".aicc-modal-content-scroll").html('<p style="margin-bottom:0 !important;text-align:center;">Iniciando la curación de contenidos...</p>');
						$('.modal-actions-gen').hide();
						loader.hide();
					}
					
					function checkRunning(callback) {
						var checkboxStates = JSON.parse(sessionStorage.getItem('checkboxStates')) || {};

						var selectedKeywords = [];
						$.each(checkboxStates, function(id, isChecked) {
						    if (isChecked) {
						        selectedKeywords.push(id);
						    }
						});

						var uniqueSelectedKeywords = [];
						$.each(selectedKeywords, function(i, el){
						    if($.inArray(el, uniqueSelectedKeywords) === -1) uniqueSelectedKeywords.push(el);
						});

						var keywordsCount = uniqueSelectedKeywords.length;

						var optionType = $('#aicc_option_type').val();

						if (optionType === 'new' && keywordsCount > 1) {
						    Swal.fire({
						        icon: 'error',
						        text: 'No es posible crear varios contenidos en simultáneo. Selecciona una sola palabra clave para crear un nuevo contenido.',
						        confirmButtonText: 'Aceptar'
						    });
						    callback(false);
						    return;
						} else if (optionType !== 'new' && keywordsCount > 5) {
						    Swal.fire({
						        icon: 'error',
						        text: 'Has seleccionado demasiadas palabras clave juntas. Selecciona cinco (o menos) para ejecutar la curación.',
						        confirmButtonText: 'Aceptar'
						    });
						    callback(false);
						    return;
						}

						$.ajax({
							url: ajaxurl,
							type: "POST",
							data: {
								action: "aicc_check_running",
								post_id: postId
							},
							dataType: "json",
							success: function(response) {
								if (response.check_status === 'processing') {
									Swal.fire({
										icon: 'error',
										text: response.message,
										confirmButtonText: 'Aceptar'
									});
									callback(false);
								} else if (response.check_status === 'missing') { 
									Swal.fire({
										icon: 'error',
										text: response.message,
										confirmButtonText: 'Aceptar'
									});
									callback(false);
								} else if (response.check_status === 'pending') { 
									Swal.fire({
										text: response.message,
										icon: 'warning',
										showCancelButton: true,
										confirmButtonColor: '#3085d6',
										cancelButtonColor: '#d33',
										confirmButtonText: 'Aceptar',
										cancelButtonText: 'Cancelar'
									}).then((result) => {
										if (result.isConfirmed) {
											callback(true); 
										} else {
											callback(false);
										}
									});
								} else {
									callback(true);
								}
							},
							error: function(xhr, status, error) {
								alert("Error: " + error);
								callback(false);
							}
						});
					}




					function showMessage() 
					{
					  $.ajax({
						url: ajaxurl,
						type: "POST",
						data: {
						  action: "aicc_show_message",
						  post_id: postId
						},
						dataType: "json",
						success: function(response) {
						  if (response.status === 'processing' && !isProcessing) {
							isProcessing = true;
							loader.show();
							setTimeout(function() {
							  modalContent.find('p').fadeOut(200, function() {
								$(this).text('Conectandose con OpenAI...').fadeIn(200, function() {
								  setTimeout(function() {
									modalContent.find('p').fadeOut(200, function() {
									  $(this).text('Generando contenidos...').fadeIn(200);
									});
								  }, 3000);
								});
							  });
							  intervalID = setTimeout(showMessage, 5000); 
							}, 1500);
						  } else if (response.status === 'finished') {
							loader.hide();
							modalContent.find('p').fadeOut(200, function() {
							  $(this).html(response.content).fadeIn(200);
								if (!response.fatal) {
							  	$('.modal-actions-gen').show();
								}
							});
							clearTimeout(intervalID); 
						  } else {
							intervalID = setTimeout(showMessage, 5000); 
						  }
						},
						error: function(xhr, status, error) {
						  alert("Error: " + error);
						}
					  });
					}

						

					$("#aicc-form-container").on("click", "#aicc-submit-button", function(e) 
					{
						e.preventDefault();
						
						var spinner = $(this).next('.spinner'); 
						
						spinner.css({
							'float': 'none',          
							'margin-top': '5px',     
							'margin-left': '10px'    
						});
						
						spinner.show(); 
						
						//var selectedKeywords = $("input[name='selected[]']:checked").map(function() {
						//	return this.value;
						//)}).get();

						// Recuperamos checkboxStates del sessionStorage
						var checkboxStates = JSON.parse(sessionStorage.getItem('checkboxStates')) || {};

						// Recopila las keywords de checkboxStates en lugar del DOM
						var selectedKeywords = [];
						$.each(checkboxStates, function(id, isChecked) {
						    if (isChecked) {
						        selectedKeywords.push(id);
						    }
						});

						// Eliminación de duplicados
						var uniqueSelectedKeywords = [];
						$.each(selectedKeywords, function(i, el){
						    if($.inArray(el, uniqueSelectedKeywords) === -1) uniqueSelectedKeywords.push(el);
						});

						if (uniqueSelectedKeywords.length === 0) {
							Swal.fire({
								icon: 'error',
								text: 'Selecciona al menos una palabra clave',
								confirmButtonText: 'Aceptar'
							});
							spinner.hide();
							return;
						}

						var selectedOption = $("select[name='aicc_options']").val();
						var selectedOptionType = $("select[name='aicc_option_type']").val();
						var selectedOptionFormat = $("select[name='aicc_option_format']").val();
						var selectedOptionLanguage = $("select[name='aicc_option_language']").val()	
						var selectedOptionPromptName = $("select[name='aicc_option_prompt_name']").val()	
						
						var nonce = '<?php echo esc_js(wp_create_nonce('aicc_process_keyword_nonce')); ?>';			
	    
						clearTimeout(intervalID); 
						
						resetModal();
						
						loader.show();
						
						checkRunning(function(canContinue) {
							
							if (canContinue) {
								
								showMessage();
								
								openModal();
			
								$.ajax({
									url: ajaxurl,
									type: "POST",
									data: {
										action: "aicc_process_keyword",
										aicc_keywords: uniqueSelectedKeywords,
										aicc_type: selectedOptionType,
										aicc_format: selectedOptionFormat,
										aicc_language: selectedOptionLanguage,			
										aicc_prompt: selectedOptionPromptName,
										post_id: postId,
										nonce: nonce
									},
									success: function(response) {
										spinner.hide();
									},
									error: function(xhr, status, error) {
										spinner.hide();
										alert("Error: " + error);
									}
								});	
								
							}
							else
							{
								spinner.hide();
							}
						});
						
					});

					$(document).off("click", ".aicc-cancel-btn").on("click", ".aicc-cancel-btn", function(e) {

						e.preventDefault();
				    
						clearTimeout(intervalID); 
		
						resetCheckboxStates();
						
						closeModal();
						
						resetModal();
						
						var nonce = '<?php echo esc_js(wp_create_nonce('aicc_cancel_process_nonce')); ?>';			
					
						$.ajax({
							url: ajaxurl,
							type: "POST",
							data: {
								action: "aicc_cancel_process",							
								post_id: postId,
								nonce: nonce
							},
							dataType: "json",
							success: function(response) {
								 if (response.status === 'error') {
									Swal.fire({
										icon: 'error',
										text: response.message,
										confirmButtonText: 'Aceptar'
									});	 
								 }
								
								if (response.status === 'pending') {
									Swal.fire({
										icon: 'warning',
										text: response.message,
										confirmButtonText: 'Aceptar'
									});										
								}
							},
							error: function(xhr, status, error) {
								alert("Error: " + error);
							}
						});
					});
					
					$(document).off("click", "#aicc-modal").on("click", "#aicc-modal", function(event) {
						event.stopPropagation();
					});

					
					
					$(document).off('click', '#aicc-accept-button').on('click', '#aicc-accept-button', function(e) {


						e.preventDefault();

						e.stopPropagation();

						resetCheckboxStates();

						$.ajax({
							url: ajaxurl,
							type: "POST",
							data: {
								action: "aicc_accept_content",
								post_id: postId
							},
							dataType: "json",
							success: function(response) {

								$("input[name='selected[]']:checked").prop("checked", false);
								
								closeModal();
								
								if (response.status === 'success') {
									
									var messageText = '<p><strong>Atención:</strong> Es necesario guardar los cambios para terminar la curación de contenidos. <a href="#" id="open-modal-link">Vista previa</a> | <a href="#" id="delete-transient-link">Eliminar</a><span class="spinner is-active" style="display:none;"></span></p>';

                					$("#transient-message").html(messageText).slideDown();
				
									var barraAdminHeight = $("#wpadminbar").outerHeight(); 
									
									$('html, body').animate({
										scrollTop: $("#transient-message").offset().top - barraAdminHeight
									}, 1000); 
								}
	
								if (response.status === 'error') {
									 Swal.fire({
										icon: 'error',
										text: response.message,
										confirmButtonText: 'Aceptar'
									});
								}								
							
							},
							error: function(xhr, status, error) {
								alert("Error al ejecutar la función PHP: " + error);
							}
						});
					});
					
					$(document).off('click', '#open-modal-link').on('click', '#open-modal-link', function(event) {

						event.preventDefault(); 
						
						var nonce = '<?php echo esc_js(wp_create_nonce('aicc_get_transient_nonce')); ?>';
						
    					var spinner = $(this).closest('#transient-message').find('.spinner');
						
						spinner.css({
							'float': 'none',          
							'margin-top': '-4px',     
							'margin-left': '10px'    
						});
						
						spinner.show(); 
						
						$.ajax({
							url: ajaxurl,
							type: "POST",
							data: {
								action: "aicc_get_transient_content",
								post_id: postId,
								nonce: nonce
							},
							success: function(response) {
								$("#modal-content-text").html(response);
								$("#aicc-modal-bp").css("display", "flex");
								spinner.hide();						
							},
							error: function(xhr, status, error) {
								alert("Error al obtener el contenido del transient: " + error);
								spinner.hide();
							}
						});
					});

					$(document).off('click', '#delete-transient-link').on('click', '#delete-transient-link', function(event) {

						event.preventDefault();
						var nonce = '<?php echo esc_js(wp_create_nonce('aicc_delete_transient_nonce')); ?>';
						Swal.fire({
						  	text: "¿Seguro quieres eliminar esta curación de contenidos?",
						  	icon: 'warning',
						  	showCancelButton: true,
						  	confirmButtonColor: '#3085d6',
						  	cancelButtonColor: '#d33',
						  	confirmButtonText: 'Aceptar',
							cancelButtonText: 'Cancelar'					
						}).then((result) => {
						if (result.isConfirmed) {
							$.ajax({
								url: ajaxurl,
								type: "POST",
								data: {
									action: "aicc_delete_transient_content",
									post_id: postId,
									nonce: nonce
								},
								dataType: "json",
								success: function(response) {
									if (response.status === 'success') {
										$("#transient-message").slideUp();									
									}
									if (response.status === 'error') {
										Swal.fire({
											icon: 'error',
											text: response.message,
											confirmButtonText: 'Aceptar'
										});								
									}									
								},
								error: function(xhr, status, error) {
									alert("Error al eliminar el contenido pendiente: " + error);
								}
							});
						} 
						})
					});


					$(document).off('click', '.aicc-cancel-bp').on('click', '.aicc-cancel-bp', function(e) {
						e.preventDefault();
						$("#aicc-modal-bp").css("display", "none");
					});
					


					$(document).off('click', '#aicc-regenerate-button').on('click', '#aicc-regenerate-button', function(e) {
						
						e.preventDefault();
						
						clearTimeout(intervalID); 
					
						closeModal();
						
						resetModal();
						
						loader.show();
				
						modalContent.find('p').text('Regenerando contenidos...').fadeIn(200);
    				  					
  						showMessage();						
						
						setTimeout(function() {
						  openModal();
						}, 300);
						
						//var selectedKeywords = $("input[name='selected[]']:checked").map(function() {
						//	return this.value;
						//}).get();

						// Recuperamos checkboxStates del sessionStorage
						var checkboxStates = JSON.parse(sessionStorage.getItem('checkboxStates')) || {};

						// Recopila las keywords de checkboxStates en lugar del DOM
						var selectedKeywords = [];
						$.each(checkboxStates, function(id, isChecked) {
						    if (isChecked) {
						        selectedKeywords.push(id);
						    }
						});

						// Eliminación de duplicados
						var uniqueSelectedKeywords = [];
						$.each(selectedKeywords, function(i, el){
						    if($.inArray(el, uniqueSelectedKeywords) === -1) uniqueSelectedKeywords.push(el);
						});

						if (uniqueSelectedKeywords.length === 0) {
							Swal.fire({
								icon: 'error',
								text: 'Selecciona al menos una palabra clave',
								confirmButtonText: 'Aceptar'
							});
							return;
						}

						var selectedOption = $("select[name='aicc_options']").val();
						var selectedOptionType = $("select[name='aicc_option_type']").val();
						var selectedOptionFormat = $("select[name='aicc_option_format']").val();
						var selectedOptionLanguage = $("select[name='aicc_option_language']").val()	
						var selectedOptionPromptName = $("select[name='aicc_option_prompt_name']").val()	
						
						var nonce = '<?php echo esc_js(wp_create_nonce('aicc_process_keyword_nonce')); ?>';
						
						$.ajax({
							url: ajaxurl,
							type: "POST",
							data: {
								action: "aicc_process_keyword",
								aicc_keywords: uniqueSelectedKeywords,
								aicc_type: selectedOptionType,
								aicc_format: selectedOptionFormat,
								aicc_language: selectedOptionLanguage,	
								aicc_prompt: selectedOptionPromptName,															
								post_id: postId,
								nonce: nonce
							},
							success: function(response) {
								//alert(response);
							},
							error: function(xhr, status, error) {
								alert("Error al ejecutar la función PHP: " + error);
							}
						});
					});

					function aicc_copyGeneratedToClipboard(selector) {
					    const contentDiv = document.querySelector(selector);
					    if (!contentDiv) {
					        console.error('Elemento no encontrado:', selector);
					        return;
					    }

					    let clonedContent = contentDiv.cloneNode(true);

					    // Verificar si el contenido tiene un solo hijo y es un <p>
					    if (clonedContent.children.length === 1 && clonedContent.children[0].tagName === 'P') {
					        let innerContent = Array.from(clonedContent.children[0].childNodes);
					        clonedContent.innerHTML = '';
					        innerContent.forEach(node => {
					            clonedContent.appendChild(node.cloneNode(true));
					        });
					    }

					    let contentHtml = clonedContent.innerHTML;
					    contentHtml = contentHtml.replace(/^(&nbsp;)+|(&nbsp;)+$/g, '');

					    function fallbackCopyTextToClipboard(text) {
					        const tempTextArea = document.createElement('textarea');
					        tempTextArea.value = text;
					        document.body.appendChild(tempTextArea);
					        tempTextArea.select();
					        try {
					            document.execCommand('copy');
					            Swal.fire({
					                icon: 'success',
					                text: 'Texto copiado al portapapeles.',
					                confirmButtonText: 'Aceptar'
					            });
					        } catch (err) {
					            console.error('Error al copiar el contenido:', err);
					            Swal.fire({
					                icon: 'error',
					                text: 'Error al copiar el contenido.',
					                confirmButtonText: 'Aceptar'
					            });
					        }
					        document.body.removeChild(tempTextArea);
					    }

					    if (navigator.clipboard && navigator.clipboard.write) {
					        const blob = new Blob([contentHtml], { type: 'text/html' });
					        navigator.clipboard.write([
					            new ClipboardItem({
					                'text/html': blob
					            })
					        ]).then(function() {
					            Swal.fire({
					                icon: 'success',
					                text: 'Texto copiado al portapapeles.',
					                confirmButtonText: 'Aceptar'
					            });
					        }).catch(function(error) {
					            console.error('Error al copiar el contenido:', error);
					            Swal.fire({
					                icon: 'error',
					                text: 'Error al copiar el contenido.',
					                confirmButtonText: 'Aceptar'
					            });
					            fallbackCopyTextToClipboard(contentHtml);
					        });
					    } else {
					        fallbackCopyTextToClipboard(contentHtml);
					    }
					}


					document.getElementById('aicc-copy-button').addEventListener('click', function(e) {
					    e.preventDefault();
					    aicc_copyGeneratedToClipboard('.aicc-modal-content-scroll');
					});

					document.getElementById('aicc-copy-button-bp').addEventListener('click', function(e) {
					    e.preventDefault();
					    aicc_copyGeneratedToClipboard('.aicc-modal-content-scroll-bp');
					});

					jQuery(function($) {
						$('select[name="aicc_option_format"]').on('change', function() {
							if ($(this).val() === 'prompt') {
								$('#aicc_option_prompt_name').show();
							} else {
								$('#aicc_option_prompt_name').hide();
							}
						});
					});

					jQuery(function($) {
					    $('select[name="aicc_option_type"]').on('change', function() {
					        // Obtener el valor actual del primer select
					        var selectedOption = $(this).val();

					        // Comparar el valor seleccionado
					        if (selectedOption === 'sentence') {
					            // Si el usuario selecciona "sentence", ocultar las opciones "li", "table", y "response"
					            $('select[name="aicc_option_format"] option[value="li"]').hide();
					            $('select[name="aicc_option_format"] option[value="table"]').hide();
					            $('select[name="aicc_option_format"] option[value="response"]').hide();
					        } else {
					            // De lo contrario, mostrar estas opciones
					            $('select[name="aicc_option_format"] option[value="li"]').show();
					            $('select[name="aicc_option_format"] option[value="table"]').show();
					            $('select[name="aicc_option_format"] option[value="response"]').show();
					        }
					    });

					    // Disparar el evento 'change' del primer select al cargar la página, 
					    // para asegurarse de que las opciones del segundo select están en un estado coherente con la opción seleccionada por defecto
					    $('select[name="aicc_option_type"]').trigger('change');
					});

					

				});

			</script>

			<?php
			if ( get_post_type( $post_id ) == 'post' ) {
				$content_type = 'entrada';
			} elseif ( get_post_type( $post_id ) == 'page' ) {
				$content_type = 'página';
			}

			$defaultLang = get_option('aicc_language', 'es');

			$saved_prompts = get_option('aicc_prompts_data');

			?>

			<div id="aicc-form-container">
				<form id="aicc-form">
					<select name="aicc_option_type" id="aicc_option_type">
						<option value="h2" selected><?php _e('Generate', 'aicc'); ?> H2</option>
						<option value="h3"><?php _e('Generate', 'aicc'); ?> H3</option>
						<option value="h4"><?php _e('Generate', 'aicc'); ?> H4</option>
						<option value="sentence"><?php _e('Generate paragraph', 'aicc'); ?></option>
						<option value="new"><?php echo __('Generar artículo completo', 'aicc'); ?></option>
					</select>
					<select name="aicc_option_format">
						<option value="p" selected><?php _e('With paragraph format', 'aicc'); ?></option>
						<option value="li"><?php _e('With list format', 'aicc'); ?></option>
						<option value="table"><?php _e('With table format', 'aicc'); ?></option>
						<option value="response"><?php _e('With response format', 'aicc'); ?></option>
						<option value="multiple"><?php _e('Con formato variado', 'aicc'); ?></option>
						<?php if ($saved_prompts !== false && is_array($saved_prompts) && !empty($saved_prompts)) : ?>
						<option value="prompt"><?php _e('Custom Instruction', 'aicc'); ?></option>
						<?php endif; ?>
					</select>	

					<?php if ($saved_prompts !== false && is_array($saved_prompts) && !empty($saved_prompts)) : ?>
					<select name="aicc_option_prompt_name" id="aicc_option_prompt_name" style="display:none;">
					<?php foreach($saved_prompts as $prompt): ?>
						<option value="<?php echo esc_attr($prompt['name']); ?>"><?php echo esc_html($prompt['name']); ?></option>
					<?php endforeach; ?>
					</select>
					<?php endif; ?>

					<select name="aicc_option_language">
					    <option value="es" <?php selected($defaultLang, 'es'); ?>><?php _e('Spanish', 'aicc'); ?></option>
					    <option value="en" <?php selected($defaultLang, 'en'); ?>><?php _e('English', 'aicc'); ?></option>
					    <option value="pt" <?php selected($defaultLang, 'pt'); ?>><?php _e('Portuguese', 'aicc'); ?></option>
					    <option value="fr" <?php selected($defaultLang, 'fr'); ?>><?php _e('French', 'aicc'); ?></option>
					    <option value="it" <?php selected($defaultLang, 'it'); ?>><?php _e('Italian', 'aicc'); ?></option>
					  	<option value="de" <?php selected($defaultLang, 'de'); ?>><?php _e('German', 'aicc'); ?></option>
					  	<option value="zh" <?php selected($defaultLang, 'zh'); ?>><?php _e('Chinese', 'aicc'); ?></option>
					   	<option value="ja" <?php selected($defaultLang, 'ja'); ?>><?php _e('Japanese', 'aicc'); ?></option>
					  	<option value="no" <?php selected($defaultLang, 'no'); ?>><?php _e('Noruego', 'aicc'); ?></option>
					  	<option value="fi" <?php selected($defaultLang, 'fi'); ?>><?php _e('Finlandés', 'aicc'); ?></option>
					   	<option value="sv" <?php selected($defaultLang, 'sv'); ?>><?php _e('Sueco', 'aicc'); ?></option>
					</select>
				
					<span><button type="button" id="aicc-submit-button" class="button button-primary"><?php _e('Generar', 'aicc'); ?></button>
					<span class="spinner is-active" style="display:none;"></span> <!-- Spinner de WordPress -->
				</span>
				</form>
			</div>

			<!-- Modal generación de contenidos -->			
			<div id="aicc-modal" class="aicc-modal">
				<div class="aicc-modal-content">
					<div class="loader"></div>
					<div class="aicc-modal-content-scroll">
						<p style="margin-bottom:0 !important;text-align:center;">Iniciando la curación de contenidos...</p>
					</div>
					<div id="modal-actions">
						<div class="modal-actions-gen"  style="display:none;">
							<button id="aicc-copy-button" class="button button-primary" >
								<span>Copiar</span>
							</button>							
							<button id="aicc-regenerate-button" class="button secondary" >
								<span>Regenerar</span>
							</button>								
						</div>
						<button class="aicc-cancel-btn">
							<span>Cerrar</span>
						</button>		
					</div>
				</div>
			</div>

			<!-- Modal contenidos pendientes -->
			<div id="aicc-modal-bp" class="aicc-modal-bp">
				<div class="aicc-modal-content-bp">
					<span class="aicc-close-bp">&times;</span>
					<div class="aicc-modal-content-scroll-bp">
						<div id="modal-content-text"></div>
					</div>
					<div id="modal-actions">
						<button id="aicc-copy-button-bp" class="button button-secondary" >
							<span>Copiar</span>
						</button>	
						<span style="margin-left: 10px;" class="aicc-cancel-bp button secondary">
							<span>Cerrar ventana</span>
						</span>		
					</div>
				</div>
			</div>

			<?php
			
		}

		wp_die();
		
	}


  	public function render_search_console_table($data, $post_id, $currentOrder) 
  	{
		if (!empty($data))
		{
		 
		 	$post_content = strtolower(remove_accents(get_post_field('post_content', $post_id)));

		  	// Eliminamos stop words para la comparación
			//$post_content_clean = $this->aicc_cleanText($post_content);
		 
		  	?>

		  <table class="aicc-table">
			<thead>
			  <tr>
				<th style="text-align: left;padding-left:.75rem; width:52%" colspan="2"><?php _e('Keyword', 'aicc'); ?></th>
				
				<th style="padding-left:0;padding-right:0;width:12%;">
					<a href="#" id="sort-mention" class="sort-arrow" style="display: flex;"><?php _e('Mention', 'aicc'); ?>
				   <span class="sorting-indicators"><span class="sorting-indicator asc" aria-hidden="true"></span><span class="sorting-indicator desc" aria-hidden="true"></span></span></a>
				</th>				  
				
				<th style="padding-left:0;padding-right:0;width:12%;">
					<a href="#" id="sort-impressions" class="sort-arrow">
					<span><?php _e('Impressions', 'aicc'); ?></span>
					<span class="sorting-indicators"><span class="sorting-indicator asc" aria-hidden="true"></span><span class="sorting-indicator desc" aria-hidden="true"></span></span></a>
				</th>
			
				<th style="padding-left:0;padding-right:0;width:12%;">
					<a href="#" id="sort-clicks" class="sort-arrow">
				  	<span><?php _e('Clicks', 'aicc'); ?></span>
				  	<span class="sorting-indicators"><span class="sorting-indicator asc" aria-hidden="true"></span><span class="sorting-indicator desc" aria-hidden="true"></span></span></a>
				</th>
				<th style="padding-left:0;padding-right:.75rem;width:12%;">
				  	<a href="#" id="sort-position" class="sort-arrow">
				  	<span><?php _e('Position', 'aicc'); ?></span>
				  	<span class="sorting-indicators"><span class="sorting-indicator asc" aria-hidden="true"></span><span class="sorting-indicator desc" aria-hidden="true"></span></span></a>
				</th>
			  </tr>
			</thead>
			<tbody class="aicc-table-body">
				<style>
					.aicc-green,.warning-text {
						position: relative; 
						display: inline-block; 
						cursor: help; 
					}

					.tooltip-content {
						visibility: hidden;  
						opacity: 0;  
						transition: opacity 0.15s ease-in-out, visibility 0.15s ease-in-out; 
						position: absolute;
						top: 50%; 
						left: 100%; 
						transform: translateY(-50%);  
						margin-left: 10px;
						padding: 6px 8px;
						font-size:13px;
						margin-top:3px;
						background: #202225;
						color: #fff; 
						border-radius: 4px; 
						min-width:220px;
						z-index: 1; 
						text-align:left;
					}

					.aicc-green:hover .tooltip-content,
					.warning-text:hover .tooltip-content {
						visibility: visible;
						opacity: 1; 
					}

					.tooltip-content::before {
						content: "";  
						position: absolute;
						top: 50%; 
						left: -5px;
						transform: translateY(-50%); 
						border-top: 5px solid transparent; 
						border-bottom: 5px solid transparent; 
						border-right: 5px solid #202225;  
					}
					
					.tooltip-content > span {
						display:block;
					}
				
				</style>
				<?php 
				
				$urls_by_keyword = get_transient('aicc_urls_by_keywords');

				foreach ($data as $row) : ?>

					<?php
			
					$count = 0;
					$original_keyword = $row['keyword'];
					$url = $row['url'];			
					$impressions = $row['impressions'];
					$position = $row['position'];
					$clicks = $row['clicks'];

					$keyword = remove_accents(strtolower($original_keyword));
					$post_content_processed = remove_accents(strtolower($post_content));
					$count_content = substr_count($post_content_processed, $keyword);
					
					$post_title = remove_accents(strtolower(get_the_title($post_id)));
					$count_title = substr_count($post_title, $keyword);
				
					$count = $count_content + $count_title;
			

					// Detectamos canibalizaciones
					$percentage_threshold = 20;
					$encoded_keyword = urlencode($original_keyword);
					$cannibalization_text = '';

					if (isset($urls_by_keyword[$original_keyword]) && count($urls_by_keyword[$original_keyword]) > 1) {
					    $cannibalization_text = $this->aicc_check_cannibalization($urls_by_keyword[$original_keyword], $percentage_threshold, $encoded_keyword);
					}

			
					// Detectamos keyword stuffing
					$total_words_content = str_word_count($post_content_processed);
					$total_words_title = str_word_count($post_title);
					$total_words = $total_words_content + $total_words_title;
					
					$keyword_density = ($count / $total_words) * 100;

					$threshold_density = 3;
					$keyword_stuffing_text = '';
								
					if ($keyword_density > $threshold_density) 
					{
					    $keyword_stuffing_text = '<span class="warning-text"><span class="dashicons dashicons-megaphone"></span><span class="tooltip-content">¡Cuidado! Posible keyword stuffing con una densidad del ' . round($keyword_density, 2) . '%.</span></span>';
					}



					if ( $count > 0 ) 
					{

						$pattern = '/(alt|title)=[\'"]([^\'"]*'.$keyword.'[^\'"]*)[\'"]/i';
						preg_match_all($pattern, $post_content_processed, $matches);

						$attr_count = count($matches[0]);

						$pattern_headers = '/<h[1-6][^>]*>\s*.*?\b' . preg_quote($keyword, '/') . '\b.*?\s*<\/h[1-6]>/i';
						preg_match_all($pattern_headers, $post_content_processed, $matches_headers);

						$header_count = count($matches_headers[0]) + $count_title;

						$new_count = $count - $attr_count - $header_count;		

						$mention = ($count == 1) ? 'vez' : 'veces';

						$text_included = '<span class="aicc-badge aicc-green"><span class="dashicons dashicons-yes"></span><span>Aparece ' . $count . ' ' . $mention . '</span><span class="tooltip-content">';

						if ($header_count > 0) {
							$text_included .= '<span>' . $header_count . ' dentro de encabezados</span>';
						}

						if ($new_count > 0) {
							$text_included .= '<span>' . $new_count . ' dentro de contenido</span>';
						}

						if ($attr_count > 0) {
							$text_included .= '<span>' . $attr_count . ' dentro de atributos de imagen</span>';
						}

						$text_included .= '</span></span>';


					} 
					else 
					{

					  $text_included = '<span class="aicc-badge">No aparece</span>';

					}

					?>

					<tr data-id="<?php echo htmlspecialchars($original_keyword); ?>">
					  <td style="width:5%; text-align: right;"  ><input type="checkbox" name="selected[]" id="<?php echo $original_keyword; ?>" value="<?php echo $original_keyword; ?>"></td>
					  <td style="width:45%; text-align: left;padding-left:0;"><label for="<?php echo $original_keyword; ?>"><?php echo $original_keyword; ?></label> <?php echo $cannibalization_text . $keyword_stuffing_text; ?></td>
					  <td style="width:20%; "><?php echo $text_included; ?></td>					
					  <td style="width:10%;"><?php echo $impressions; ?></td>
					  <td style="width:10%;"><?php echo $clicks; ?></td>
					  <td style="width:10%;"><?php echo number_format($position, 1); ?></td>
				</tr>

			  	<?php endforeach; ?>

			</tbody>
			  
		  </table>

		<?php
		} 
		else 
		{
		  echo '<div class="notice notice-warning inline active-plugin-edit-warning" style="margin:10px !important;"><p>No hay datos disponibles en Search Console para este contenido.</p></div>
';
		}
	}

	public function render_pagination($current_page, $total_pages, $total_posts)
	{
	    if ($total_pages > 1) {
			if ($total_pages <= 5) {
				$start = 1;
				$end = $total_pages;
			} else {
				$start = max($current_page - 2, 1);
				$end = min($current_page + 2, $total_pages);

				if ($current_page <= 3) {
					$start = 1;
					$end = 5;
				} elseif ($current_page >= $total_pages - 2) {
					$start = $total_pages - 4;
					$end = $total_pages;
				}
			}

			$inicio = ($current_page * 10) - 9;

			if ($current_page < $total_pages) {
				$fin = $current_page * 10;
			} else {
				$fin = $total_posts;
			}

	        ?>
	        <div class="pagination">
	            <div>
	                Mostrando <?php echo $inicio; ?> al <?php echo $fin; ?> de <?php echo $total_posts; ?>
	            </div>
	            <div>

	                <?php if ($current_page > 1) : ?>
	                    <a href="#" class="pagination-link" data-page="<?php echo $current_page - 1; ?>">&laquo;</a>
	                <?php else : ?>
	                    <span class="pagination-link-off">&laquo;</span>
	                <?php endif; ?>

	                <?php if ($start > 1) : ?>
	                    <a href="#" class="pagination-link" data-page="1">1</a>
	                    <?php if ($start > 2) : ?>
	                        <span class="pagination-link-off">...</span>
	                    <?php endif; ?>
	                <?php endif; ?>

	                <?php for ($i = $start; $i <= $end; $i++) : ?>
	                    <?php if ($i == $current_page) : ?>
	                        <span><?php echo $i; ?></span>
	                    <?php else : ?>
	                        <a href="#" class="pagination-link" data-page="<?php echo $i; ?>"><?php echo $i; ?></a>
	                    <?php endif; ?>
	                <?php endfor; ?>

	                <?php if ($end < $total_pages) : ?>
	                    <?php if ($end < $total_pages - 1) : ?>
	                        <span class="pagination-link-off">...</span>
	                    <?php endif; ?>
	                    <a href="#" class="pagination-link" data-page="<?php echo $total_pages; ?>"><?php echo $total_pages; ?></a>
	                <?php endif; ?>

	                <?php if ($current_page < $total_pages) : ?>
	                    <a href="#" class="pagination-link" data-page="<?php echo $current_page + 1; ?>">&raquo;</a>
	                <?php else : ?>
	                    <span class="pagination-link-off">&raquo;</span>
	                <?php endif; ?>
	            </div>
	        </div>
	        <?php
	    }
	}	


	// Función para limpiar texto eliminando palabras de parada y normalizando
	function aicc_cleanText($text) {
	    $stopWords = ['de', 'para', 'mi', 'en', 'y', 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas'];

	    // Eliminar acentos
	    $text = remove_accents($text);
	    
	    // Convertir a minúsculas
	    $text = strtolower($text);

	    // Eliminar stop words
	    foreach ($stopWords as $word) {
	        $text = preg_replace('/\b' . preg_quote($word, '/') . '\b/', '', $text);
	    }

	    // Eliminar espacios extras
	    $text = trim(preg_replace('/\s+/', ' ', $text));
	    return $text;
	}

	function aicc_check_cannibalization($keyword_data, $percentage_threshold, $encoded_keyword) {
	    $impressions_array = [];
	    foreach ($keyword_data as $url_data) {
	        if (isset($url_data["impressions"])) {
	            $impressions_array[] = $url_data["impressions"];
	        }
	    }

	    $max_impressions = max($impressions_array);
	    $threshold_impressions = ($percentage_threshold / 100) * $max_impressions;
	    $cannibalizing_data = [];

	    foreach ($keyword_data as $url_data) {
	        $impression = $url_data["impressions"];
	        if ($impression >= $threshold_impressions) {
	            $cannibalizing_data[] = $url_data;
	        }
	    }

	    if (count($cannibalizing_data) >= 2) {
	        $base_admin_url = admin_url();
	        $dynamic_url = $base_admin_url . "admin.php?page=aicc&tab=canibal_settings&s=" . $encoded_keyword;

	        return '<a href="' . $dynamic_url . '" target="_blank" rel="noopener"><span class="warning-text" style="cursor:pointer !important; color:#50575e !important; margin-right: 4px !important;"><span class="dashicons dashicons-info"></span><span class="tooltip-content">¡Cuidado! Esta palabra clave se está posicionando para varias URLs.</span></span></a>';
	    }

	    return null; // O puedes devolver algo más significativo si es necesario
	}



	//
	//
 	// Funciones
	//
	//	
	public function aicc_accept_content()
	{

		if (isset($_POST['post_id'])) 
		{
			$post_id = intval($_POST['post_id']);

			if (get_post_status($post_id) === false) 
			{
				
				$error_message = 'El post no existe.';

				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_accept_content');	
				
				 $response = array(
                	'status' => 'error',
                	'message' => $error_message
           	 	);
				
				echo json_encode($response);
				
				wp_die();
			
			}
			
			$data = get_transient('aicc_content_' . $post_id);

			if ($data === false) 
			{
				$error_message = 'No se encontró contenido pendiente en este artículo. Contacta al soporte del plugin.';

				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_accept_content');	
				
				 $response = array(
                	'status' => 'error',
                	'message' => $error_message
           	 	);
				
				echo json_encode($response);
				
				wp_die();
			}

			$content = $data['content'];
			
			$isNew = $data['new_post'];	

								
			global $wpdb;
			
			$sql = $wpdb->prepare("
				INSERT INTO " . AICC_PENDING_TABLE . " (post_id, content, new_post) 
				VALUES (%d, %s, %d) 
				ON DUPLICATE KEY UPDATE content = %s, new_post = %d, date = NOW()",
				$post_id, $content, $isNew, $content, $isNew
			);

			$wpdb->query($sql);

			if ($wpdb->last_error)
			{
				$error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;

				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_accept_content');	
				
				 $response = array(
                	'status' => 'error',
                	'message' => $error_message
           	 	);
								
				echo json_encode($response);				
			}
			else
			{
				$response = array(
               		'status' => 'success',
            	);
				
				echo json_encode($response);
			
			}
			
			delete_transient('aicc_content_' . $post_id);

		}
		
		 wp_die();
		
	}
	
	public function aicc_insert_content($id, $post) 
	{
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		if(!$is_bulk_running && !$is_recovery_running) {

			$postID = $post->ID;

			if (( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) || !current_user_can('edit_post', $postID))
			{
				return;
			}

			// Verifica si el estado del post es 'publish'
		    if (get_post_status($postID) !== 'publish') {
		        return;
		    }

			// Si está habilitada la opción, llamamos a la API Indexing de Google
			if ( get_option('aicc_enable_autoindex') ) 
			{
				$url_to_notify = get_permalink($postID);
		        $status_code = $this->aicc_call_index_api($url_to_notify);	

		        if ( $status_code === 200 ) 
		        {
		        	$error_message = 'Este contenido se envío automáticamente a la API de Indexing de Google con éxito.';
		        }
		        else
		        {
					$error_message = 'Hubo un error en la solicitud a la API de Indexing de Google. Revisa el registro de errores del plugin para averiguar qué ocurrió. Además, recuerda que es necesario darle permisos de propietario al usuario en Search Console y habilitar la API de Indexing en Google Cloud para que funcione correctamente. <a href="https://www.youtube.com/watch?v=zcZmQHMvB60" target="_blank" rel="noopener">Si tienes dudas puedes mirar este video</a>.';
		        }
		        $message = array('post_id' => $postID, 'message' => $error_message);
				set_transient('aicc_autoindex_message_' . $postID, $message, 3600);
	        }

		}
		
	}


	/*
	* DEPRECATED FUNCTIONS V2.4
	*/
	private function aicc_update_post_content($postID, $content) 
	{
		$post_content = get_post_field('post_content', $postID);

		if (false === $post_content) 
		{
			$error_message = 'Error al obtener contenido del post.';
			self::aicc_log_register($postID, 'error', $error_message, 'aicc_insert_content');
			return new WP_Error('post_content_retrieval_failed', $error_message);
		}

		$new_content = strpos($post_content, '[content_curator]') !== false ? str_replace('[content_curator]', $content, $post_content) : ($post_content . $content);

		$post_data = array(
			'ID'           => $postID,
			'post_content' => $new_content
		);
		
		$update_result = wp_update_post($post_data);
		if ($update_result === 0) 
		{
			return new WP_Error('update_failed', 'Failed to update post content.');
		}

		return $update_result;
	}

	/*
	* DEPRECATED FUNCTIONS V2.4
	*/
	private function aicc_create_post_content($postID, $content) 
	{
		$post = get_post( $postID ); 

		if ( ! $post ) 
		{
			return new WP_Error('not_found', 'El post con ID ' . $postID . ' no fue encontrado.');
		}		

		$post_status = $post->post_status;
		$post_author = $post->post_author;
		$post_type = $post->post_type;
		$original_categories = wp_get_post_categories( $postID );

		preg_match("/<h1>(.*?)<\/h1>/", $content, $matches);
		
		$title = isset($matches[1]) ? $matches[1] : ' ';

		$cleanedContent = preg_replace("/<h1>.*?<\/h1>/", "", $content);

		$current_date = current_time('mysql');

		$post_data = array(
			"post_title" => $title,
			"post_content" => $cleanedContent,
			"post_status" => 'publish',
			"post_author" => $post_author,  
			"post_date" => $current_date,
			"post_type" => $post_type,     
		);

		$new_post_id = wp_insert_post($post_data);

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

		wp_set_post_categories( $new_post_id, $original_categories );

		if ( $new_post_id )
		{
			$new_post_link = get_permalink($new_post_id);

			$original_content = $post->post_content;
			
			$updated_content = $original_content . "\n\n" . '<a href="' . $new_post_link . '">' . $title . '</a>'; 

			$update_data = array(
				'ID'           => $postID,
				'post_content' => $updated_content
			);

			$update_result = wp_update_post( $update_data );
			
			if ( is_wp_error( $update_result ) ) 
			{
				return new WP_Error('update_failed', 'Error al actualizar el post original.');
			}
			
		}

		return $new_post_id;

	} 
	

	
	public function show_aicc_update_message() 
	{
		global $post;

		if(isset($post) && isset($post->ID)) {

			$message_info = get_transient('aicc_update_message_' . $post->ID);

			if ($message_info && $post->ID == $message_info['post_id']) {
				echo '<div id="message-' . $post->ID . '" class="notice notice-success is-dismissible updated">
						<p>' . $message_info['message'] . '</p>
						<button type="button" class="notice-dismiss"><span class="screen-reader-text">Descartar este aviso.</span></button>
					  </div>';

				delete_transient('aicc_update_message_' . $post->ID);
			}
			
			$message_autoindex = get_transient('aicc_autoindex_message_' . $post->ID);

			if ($message_autoindex && $post->ID == $message_autoindex['post_id']) {
				echo '<div id="message-index-' . $post->ID . '" class="notice notice-success is-dismissible updated" style="border-left-color:#72AEE6 !important;">
						<p>' . $message_autoindex['message'] . '</p>
					  </div>';

				delete_transient('aicc_autoindex_message_' . $post->ID);
			}

		}
	}


	public function aicc_check_running() 
	{

		$post_id = intval($_POST['post_id']);

		// Obtener el estado de los transients
		$is_manual_running = get_transient('aicc_is_running');
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		// Verificar si alguno de los dos está activo
		if ( $is_manual_running || $is_bulk_running  || $is_recovery_running) {
		    $is_running = true;
		} else {
		    $is_running = false;
		}
				
		if ( $is_running ) {
			
			echo json_encode(['check_status' => 'processing', 'message' => 'Hay un proceso ejecutándose en segundo plano. Espera unos segundos hasta que termine para ejecutar una nueva curación.']);

			wp_die();		
			
		}
		
		$apikey_openai = get_option('aicc_options_openai');
		
		if ( empty($apikey_openai) )
		{
			echo json_encode(['check_status' => 'missing', 'message' => 'Es necesario agregar una clave de API de OpenAI para ejecutar la curación de contenidos.']);

			wp_die();		
		}
		
		global $wpdb;
		
		$sql = $wpdb->prepare("SELECT COUNT(*) FROM " . AICC_PENDING_TABLE . " WHERE post_id = %d", $post_id);

		$count = intval($wpdb->get_var($sql));
		
		if ($count > 0) {
	
			echo json_encode(['check_status' => 'pending', 'message' => 'Hay una curación de contenidos pendiente para este artículo. ¿Deseas sobrescribir?']);
		
			wp_die();		
			
		}
		
		
	}
	
	public function aicc_show_message() 
	{
		if (isset($_POST['post_id'])) 
		{
						
			$post_id = intval($_POST['post_id']);
						
			$transient_error = get_transient ('aicc_error_' .  $post_id);
			
			$transient_fatal = get_transient ('aicc_fatal_error');
			
			if ( $transient_error  )
			{
				/*
				 * No guardamos error en DB de logs porque ya está guardado antes. 
				 * Acá solo terminamos y borramos transient de error.
				 */			
				
				$error_message = 'Ocurrió un error. ' . $transient_error ;				

				echo json_encode(['status' => 'finished', 'content' => $error_message, 'fatal' => true ]);	
				
				delete_transient('aicc_error_' .  $post_id);
				
				wp_cache_flush();
			}
			elseif ( $transient_fatal ) 
			{
				$error_message = 'Ocurrió un error fatal. Por favor, contacta al soporte del plugin.';				

				echo json_encode(['status' => 'finished', 'content' => $error_message, 'fatal' => true ]);	
				
				delete_transient('aicc_fatal_error');		
				
				wp_cache_flush();
				
			}
			else 
			{

				$transient_created = 'aicc_created_' . $post_id;

				if (false === get_transient($transient_created)) 
				{
					echo json_encode(['status' => 'processing']);
				}
				else 
				{

					//$content = get_transient('aicc_content_' . $post_id);

					$data = get_transient('aicc_content_' . $post_id);

					if ($data === false) 
					{
						$error_message = 'No se encontró contenido pendiente en este artículo. Por favor, contacta al soporte del plugin.';

						self::aicc_log_register($post_id, 'error', $error_message, 'aicc_show_message');	

						 $response = array(
							'status' => 'error',
							'message' => $error_message
						);

						echo json_encode($response);

						wp_die();
					}

					$content = $data['content'];
					
					echo json_encode(['status' => 'finished', 'content' => $content, 'fatal' => false]);

					delete_transient($transient_created);
				}					
			}
		}
		
		wp_die();
		
	}

	public function aicc_process_keyword() 
	{

		set_time_limit(300);
		
		delete_transient('aicc_fatal_error');
				
		// Obtener el estado de los transients
		$is_manual_running = get_transient('aicc_is_running');
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		// Verificar si alguno de los dos está activo
		if ( $is_manual_running || $is_bulk_running || $is_recovery_running ) {
		    $is_running = true;
		} else {
		    $is_running = false;
		}
		
		if ( $is_running )
		{
			return;
		}
		
		check_ajax_referer('aicc_process_keyword_nonce', 'nonce');
		
		if ( isset($_POST['aicc_type']) && isset($_POST['aicc_format'])  && isset($_POST['aicc_language']) ) 
		{
						
			$post_id = intval($_POST['post_id']);
			delete_transient('aicc_error_' .  $post_id);
			
			
			if ( !current_user_can('edit_post', $post_id) ) 
			{
				$error_message = 'El usuario no tiene permisos para realizar esta acción.';
				set_transient('aicc_error_' . $post_id, $error_message, 3600); 				
				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_process_keyword');				
				return;
			}
			
			$type = isset($_POST['aicc_type']) ? sanitize_text_field($_POST['aicc_type']) : '';
			$format = isset($_POST['aicc_format']) ? sanitize_text_field($_POST['aicc_format']) : '';
			$language = isset($_POST['aicc_language']) ? sanitize_text_field($_POST['aicc_language']) : '';				
			$instruction = isset($_POST['aicc_prompt']) ? sanitize_text_field($_POST['aicc_prompt']) : '';				
			$keywords = isset($_POST['aicc_keywords']) ? $_POST['aicc_keywords'] : array();
						
			if (empty($_POST['aicc_type']) || 
				empty($_POST['aicc_format']) ||
				empty($_POST['aicc_language']) ||
				empty($_POST['aicc_keywords']) || 
				empty($_POST['post_id'])) 
			{
								
				$error_message = 'Hay algún dato incompleto: ' .
					'Tipo: ' . $_POST['aicc_type'] . ', ' .
					'Formato: ' . $_POST['aicc_format'] . ', ' .
					'Idioma: ' . $_POST['aicc_language'] . ', ' .
					'Palabras clave: ' . $_POST['aicc_keywords'] . ', ' .
					'ID del post: ' . $_POST['post_id'];

				set_transient('aicc_error_' . $post_id, $error_message, 3600); 				
				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_process_keyword');					
				return;
			}


			$prompt_text = '';

			if ($format == 'prompt') 
			{
				$saved_prompts = get_option('aicc_prompts_data', []);

				foreach ($saved_prompts as $prompt) {
				    if ($prompt['name'] === $instruction) {
				        $prompt_text = $prompt['text'];
				        break;
				    }
				}
			}
	
			delete_transient('aicc_content_' . $post_id);
			delete_transient('aicc_created_' . $post_id);			
			
			$data = array(
				'id' => $post_id,
				'type' => $type,
				'format' => $format,
				'language' => $language,
				'keywords' => $keywords,
				'prompt' => $prompt_text
			);

			$this->manual->push_to_queue($data);
			$this->manual->save()->dispatch();

			sleep(1);
		}
		wp_die();
	}

	public static function aicc_call_openai($item) {
			
		try {
			
		set_transient('aicc_is_running', true, 600); 

		global $wpdb;

		$context 		= '';
		
		$post_id 		= $item['id'];
		$type 			= $item['type'];
		$format 		= $item['format'];
		$keywords 		= $item['keywords'];	
		$languageCode 	= $item['language'];
		$customPrompt 	= $item['prompt'];
				
		$languageMap = [
		    'es' => 'Español',
		    'en' => 'Inglés',
		    'ar' => 'Árabe',
		    'bg' => 'Búlgaro',
		    'cs' => 'Checo',
		    'da' => 'Danés',
		    'de' => 'Alemán',
		    'el' => 'Griego',
		    'fi' => 'Finlandés',
		    'fr' => 'Francés',
		    'he' => 'Hebreo',
		    'hi' => 'Hindi',
		    'hu' => 'Húngaro',
		    'id' => 'Indonesio',
		    'it' => 'Italiano',
		    'ja' => 'Japonés',
		    'ko' => 'Coreano',
		    'ms' => 'Malayo',
		    'nl' => 'Holandés',
		    'no' => 'Noruego',
		    'pl' => 'Polaco',
		    'pt' => 'Portugués',
		    'ro' => 'Rumano',
		    'ru' => 'Ruso',
		    'sv' => 'Sueco',
		    'th' => 'Tailandés',
		    'tr' => 'Turco',
		    'uk' => 'Ucraniano',
		    'vi' => 'Vietnamita',
		    'zh' => 'Chino',
		];
	
		$language = $languageMap[$languageCode] ?? 'español';

		$post_title 	= get_the_title($post_id);
	
		$ch = curl_init();

		$url = 'https://api.openai.com/v1/chat/completions';
			
		$combinedData = '';

		$include_context = get_option('aicc_include_context');
 		
		$ajust_model = get_option('aicc_ajust_model', 1);

		if ($ajust_model) {
		  	$model = 'gpt-4o-mini'; 
		} else {
			$model = get_option('aicc_model', 'gpt-4o-mini');

			// Actualiza modelos antiguos del plugin a los nuevos modelos
			$models = [
			    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
			    'gpt-4' => 'gpt-4-1106-preview',
			    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
			];

			$model = $models[$model] ?? $model;
		}

		if ($include_context) {
		    $post_content = get_post_field('post_content', $post_id); 
			$context = '\n\nAquí está el contenido existente del artículo que estás por mejorar, para que tengas contexto, generes un mejor contenido y no repitas información:\n\n' . $post_content;
		}

		$totalTokens = 0;
		$totalCost = 0;			

		$openai_encrypted_key = get_option('aicc_options_openai'); 
			
		$api_key = self::aicc_decrypt_api_key($openai_encrypted_key);
	
		if (empty($api_key)) 
		{					
			throw new Exception('API Key para OpenAI no configurada o vacía.');
		}	

		$header  = [
			'Content-Type: application/json',
			'Authorization: Bearer ' . $api_key
		];
			
		$countPrompts = 5;

		$paragraphPrompts = [
			'Necesito cuatro o cinco párrafos cortos (máximo 5 líneas) sobre "{keyword}" redactados en idioma {language}. Cada párrafo debe estar envuelto en etiquetas de párrafo de HTML (<p></p>). En medio de cada párrafo, por favor, utiliza la etiqueta <strong></strong> para destacar la información más relevante. No se necesitan subtítulos ni menciones del formato HTML, ya que el texto se incorporará directamente en un artículo de WordPress sobre "{post_title}". Por favor, asegúrate de que el contenido esté listo para ser publicado tal como está. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Requiero algunos párrafos cortos (máximo 5 líneas) (alrededor de cuatro o cinco) acerca de "{keyword}" redactados en idioma {language}. Envuelve cada uno de ellos con las etiquetas HTML de párrafo (<p></p>). En medio de cada párrafo, utiliza la etiqueta <strong></strong> para destacar la información más relevante. El contenido será parte de un artículo de WordPress titulado "{post_title}", así que no incluyas detalles de formato ni subtítulos. El texto debe ser apto para publicación inmediata. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy elaborando un artículo para WordPress sobre "{post_title}". Proporcióname cuatro o cinco párrafos cortos (máximo 5 líneas) relacionados con "{keyword}" redactados en idioma {language}. Asegúrate de que cada párrafo esté entre etiquetas de párrafo HTML (<p></p>) y que no se necesite edición adicional antes de publicarse. En medio de cada párrafo, por favor, utiliza la etiqueta <strong></strong> para destacar la información más relevante. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un post en WordPress llamado "{post_title}", necesito que redactes unos cuatro o cinco párrafos cortos (máximo 5 líneas) sobre "{keyword}" redactados en idioma {language}. Recuerda envolver cada párrafo en etiquetas de párrafo HTML <p></p>. En medio de cada párrafo, utiliza la etiqueta <strong></strong> para destacar información más relevante. Por favor, omite referencias al formato HTML y asegúrate de que esté listo para su publicación. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un artículo titulado "{post_title}" en WordPress, redacta de cuatro a cinco párrafos cortos (máximo 5 líneas) centrados en "{keyword}" y redactados en idioma {language}. En medio de cada párrafo y siempre que sea posible, utiliza la etiqueta <strong></strong> para destacar la información más relevante. Asegúrate de envolver el contenido en etiquetas <p></p> de formato HTML y que sea adecuado para una publicación directa sin ediciones adicionales. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
		];

		$listPrompts = [
			'Estoy trabajando en un artículo sobre "{post_title}". Por favor, escribe un párrafo inicial relacionado con "{keyword}", seguido directamente por una lista de elementos que correspondan a "{keyword}". La lista debe estar envuelta en etiqueta de lista HTML (<ul>) con elementos <li> adentro. Utiliza la etiqueta HTML <strong></strong> para destacar algunas partes del texto que sean relevantes.  El texto completo debe estar redactado en idioma {language} y en formato HTML, adaptado para su inclusión directa en WordPress, por lo que no es necesario mencionar el formato HTML ni introducir la lista. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un artículo titulado "{post_title}", necesito un párrafo introductorio sobre "{keyword}" y, después de ello, una lista con elementos asociados. La lista debe estar en formato HTML (<ul> con elementos <li>). Tanto el párrafo introductorio como la lista deben estar redactados en idioma {language}. Por favor, utiliza la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres relevantes. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para enriquecer mi artículo titulado "{post_title}", redacta un párrafo que aborde el tema de "{keyword}". A continuación de ese párrafo, crea una lista con ítems relacionados con "{keyword}" en formato HTML (<ul> con <li>). Siempre que sea posible utiliza la etiqueta HTML <strong></strong> para destacar algunas partes del texto que sean relevantes. Tanto el párrafo introductorio como la lista deben estar redactado en idioma {language} y estar lista para ser incorporada a WordPress sin menciones adicionales sobre el formato HTML ni sobre estas instrucciones. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Con el propósito de desarrollar un contenido para "{post_title}", requiero un párrafo que introductorio sobre "{keyword}". Tras ese párrafo, presenta una serie de elementos en formato de lista HTML (<ul> con <li>) relacionados con "{keyword}". Utiliza la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres relevantes.  El texto completo debe ser redactado en idioma {language}, y ser adecuado para una incorporación inmediata en WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy componiendo un contenido sobre "{post_title}". Sería de gran ayuda si pudieras redactar un párrafo introductorio sobre "{keyword}", seguido de una serie de puntos relacionados. Presenta el contenido en formato HTML: el párrafo introductorio envuélvelo en etiquetas HTML de párrafo (<p>) y los elementos de lista en etiquetas <ul> con elementos <li>). Siempre que sea posible, usa la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. Asegúrate de que el texto completo esté redactado en idioma {language} y listo para ser insertado en WordPress sin necesidad de ajustes adicionales en formato. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
		];

		$tablePrompts = [
			'Estoy trabajando en un artículo sobre "{post_title}". Por favor, genera una tabla relacionada con "{keyword}". La tabla debe estar envuelta en etiquetas de tabla HTML (<table></table>) con filas (<tr>) y celdas (<td>) adecuadas. No es necesario mencionar el formato HTML ni estas instrucciones, ya que se incorporará directamente en un artículo de WordPress. El contenido debe estar redactado en idioma {language} y listo para ser publicado. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy creando un contenido para "{post_title}" y necesito una tabla que trate sobre "{keyword}". Por favor, utiliza las etiquetas HTML adecuadas para la tabla (<table>, <tr>, <td>). El texto debe estar redactado en idioma {language} y ser apto para su integración directa en WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un artículo titulado "{post_title}", me gustaría tener una tabla que se centre en "{keyword}". Haz uso de las etiquetas estándar de tabla HTML (<table> con filas <tr> y celdas <td>). Por favor, redacta el contenido en idioma  {language} y asegúrate de que esté listo para incorporarse a WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Con el propósito de complementar mi artículo sobre "{post_title}", necesito una tabla que aborde el tema de "{keyword}". Usa las etiquetas HTML correspondientes (<table>, <tr>, <td>) para estructurarla. La redacción debe ser en idioma {language}, y la tabla debe ser adecuada para una inserción directa en WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones..',
			'Mientras desarrollo un artículo para "{post_title}", quisiera contar con una tabla temática sobre "{keyword}". Por favor, estructúrala utilizando las etiquetas de tabla HTML (<table> con <tr> y <td>). Todo el contenido debe ser redactado en idioma {language} y estar preparado para su uso inmediato en WordPress sin hacer alusión al formato HTML. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
		];		
			
			
		$responsePrompts = [
			'Estoy trabajando en un artículo sobre "{post_title}" y quiero enriquecerlo semánticamente. Necesito que redactes en idioma {language} tres o cuatro párrafos cortos (máximo 5 líneas) para responder la pregunta "{keyword}". Cada párrafo debe estar envuelto en etiquetas de párrafo de HTML (<p></p>). Siempre que puedas utiliza listas HTML (<ul>) para aportar valor adicional y usa la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. No se necesitan subtítulos ni menciones del formato HTML, ya que el texto se incorporará directamente en un artículo de WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy componiendo un artículo centrado en "{post_title}". Me gustaría enriquecer su contenido. Por favor, redacta en {language} tres o cuatro párrafos cortos (máximo 5 líneas) que respondan a la pregunta "{keyword}". Encierra cada párrafo con las etiquetas HTML correspondientes (<p></p>). Si es pertinente, utiliza listas (<ul>) para aclarar puntos. Cuando puedas, la etiqueta HTML <strong></strong> para destacar algunas partes del texto que sean importantes. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. ',
			'Me encuentro en el proceso de elaboración de un artículo sobre "{post_title}". Sería de gran ayuda si pudieras proporcionar tres o cuatro párrafos cortos (máximo 5 líneas) en {language} que aborden la pregunta "{keyword}". Cada fragmento debe estar contenido dentro de etiquetas de párrafo (<p></p>). Apreciaría si puedes incluir listas HTML (<ul>) para detalles adicionales. El texto debe estar listo para ser integrado en WordPress directamente. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy en la fase de redacción de un post sobre "{post_title}". Me gustaría profundizar en el tema. Para ello, escribe en idioma {language} tres o cuatro párrafos cortos (máximo 5 líneas) que respondan la pregiunta"{keyword}". Es importante que cada párrafo esté envuelto con etiquetas <p></p>. Si crees que es relevante, añade listas en formato HTML (<ul>) para complementar y también usa la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. La intención es que el contenido esté preparado para publicarse en WordPress de inmediato. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Actualmente, estoy desarrollando un artículo sobre "{post_title}". ¿Podrías ayudarme a expandir el contenido? Necesito tres o cuatro párrafos cortos (máximo 5 líneas) en idioma {language} que responda la pregunta "{keyword}". Recuerda envolver cada párrafo con etiquetas de párrafo HTML (<p></p>) y, en caso de ser necesario, emplear listas HTML (<ul>) para ofrecer información adicional y la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.'
		];


		$sentencePrompts = [
			'Necesito un párrafo corto y conciso (máximo 3 o 4 líneas) sobre "{keyword}" redactado en {language}. Por favor, incluye la frase exacta "{keyword}" dentro del cuerpo del párrafo, sin alteraciones ni modificaciones. Este párrafo será parte de un artículo más extenso, así que debe ser capaz de encajar naturalmente en medio del contenido existente. El texto debe estar envuelto en una etiqueta de párrafo HTML (<p></p>) y contener la etiqueta <strong></strong> para destacar la información más relevante. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Requiero un párrafo corto (máximo 3 o 4 líneas) que contenga la frase exacta "{keyword}" sin alteraciones ni modificaciones, redactado en idioma {language}. Asegúrate de que pueda integrarse de manera coherente en un contexto más amplio dentro de un artículo titulado "{post_title}". El párrafo debe estar encerrado en etiquetas HTML de párrafo (<p></p>). Usa la etiqueta <strong></strong> para enfatizar la parte más importante del texto. Este contenido será parte de un artículo en WordPress y debe estar listo para su publicación inmediata, sin menciones a formato HTML ni necesidad de subtítulos. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy redactando un artículo en WordPress y necesito un párrafo corto y consiso (máximo 3 o 4 líneas) sobre "{keyword}" en {language}. Debe poder insertarse en un contenido preexistente sin introducciones. Asegúrate de incorporar la palabra clave "{keyword}" dentro del texto. El párrafo debe estar dentro de las etiquetas <p></p> y no requerir edición adicional antes de la publicación. Utiliza <strong></strong> para destacar la información crítica. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un post en WordPress, redacta un párrafo único y corto (máximo 3 o 4 líneas) que se centre en "{keyword}" y esté escrito en {language}. El párrafo debe encajar en una discusión más amplia dentro de un artículo titulado "{post_title}". La frase exacta "{keyword}" debe estar presente en el texto sin alteraciones ni modificaciones. Envuelve el párrafo en etiquetas HTML de párrafo <p></p> y utiliza <strong></strong> para resaltar cualquier punto clave. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Escribe un párrafo corto (máximo 3 o 4 líneas) en {language} que proporcione información detallada sobre "{keyword}". Este párrafo será parte de un artículo más extenso, así que debe ser capaz de encajar naturalmente en medio del contenido existente. No olvides usar las etiquetas <p></p> y resaltar partes importantes con <strong></strong>, asegurándote de incluir la frase "{keyword}" de manera orgánica en el texto, sin alteraciones ni modificaciones. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). No incluyas subtítulos. Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.'
		];



		$multipleSentencePrompts = [
			'Necesito un texto corto y unificado que integre de manera coherente y explícita las siguientes frases exactas: {keyword}. Estas frases deben ser utilizadas tal como están, sin alteraciones, y deben integrarse de manera coherente y natural en el contenido. Este texto se añadirá a un artículo preexistente; por tanto, los párrafos que redactes deben ser autónomos y no servir como introducción. Cada párrafo debe estar contenido dentro de las etiquetas HTML <p></p>. Utiliza la etiqueta <strong></strong> para destacar las frases o ideas más importantes, especialmente las frases clave proporcionadas. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. No incluyas subtítulos.',
			'Estoy buscando un texto corto y unificado redactado en idioma {language}, que integre de manera coherente y explícita las frases exactas: "{keyword}". Dichas frases deben aparecer tal como se proporcionan, sin modificaciones, y su uso debe ser natural dentro del contexto del texto. Estos párrafos se añadirán a un artículo preexistente, por lo que deben ser autónomos y no funcionar como una introducción. Los párrafos deben estar contenidos dentro de las etiquetas HTML <p></p> y cualquier punto o idea crucial, especialmente aquellos relacionados con las frases clave, deben resaltarse usando <strong></strong>. Por favor, no incluyas subtítulos. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Para un artículo de WordPress, redacta un texto corto y unificado de tres o cuatro párrafos en idioma {language}, que incorpore explícitamente las frases exactas: "{keyword}". Estas frases deben ser utilizadas tal como se proporcionan y fluir de manera natural en el texto, evitando que parezcan insertadas a la fuerza o fuera de contexto. Cada párrafo debe estar contenido dentro de las etiquetas HTML <p></p>, con ideas o puntos importantes, especialmente aquellos relacionados con las frases clave, resaltados mediante la etiqueta <strong></strong>. Además, el texto debe ser autónomo y no requerir subtítulos adicionales ni mencionar estas instrucciones. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. Tampoco incluyas subtítulos.',
			'Requiero un texto corto y unificado, compuesto por tres o cuatro párrafos, que incorpore de manera explícita y natural las frases exactas "{keyword}" en {language}. Estos párrafos formarán parte de un artículo preexistente, por lo que deben ser autónomos y no servir como introducción. Es imperativo que cada párrafo se presente dentro de las etiquetas <p></p> y que los elementos más relevantes, en especial aquellos relacionados con las frases proporcionadas, se enfaticen utilizando <strong></strong>. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. No incluyas subtítulos.',
			'Escribe un texto corto y unificado en {language} que incorpore de manera explícita y fluida las siguientes frases: "{keyword}". Este contenido debe formar una unidad coherente, apta para complementar un artículo más extenso. Debe ser una parte cohesiva que se integre en un artículo más amplio, utilizando las etiquetas <p></p> para los párrafos y <strong></strong> para enfatizar las partes relevantes. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. No incluyas subtítulos.'
		];


		$multiplePrompts = [
			'Estoy trabajando en un artículo sobre "{post_title}" y quiero enriquecerlo semánticamente. Necesito que redactes en idioma {language} tres o cuatro párrafos cortos (máximo 5 líneas) sobre "{keyword}". Cada párrafo debe estar envuelto en etiquetas de párrafo de HTML (<p></p>). Siempre que puedas utiliza listas HTML (<ul>) para aportar valor adicional y usa la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. También puedes usar tablas HTML si consideras que la intención de búsqueda de "{keyword}" se responde mejor de esta forma, como por ejemplo en comparativas o donde los datos se muestren mejor en tablas. No se necesitan subtítulos ni menciones del formato HTML, ya que el texto se incorporará directamente en un artículo de WordPress. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Estoy componiendo un artículo centrado en "{post_title}". Me gustaría enriquecer su contenido. Por favor, redacta en {language} tres o cuatro párrafos cortos (máximo 5 líneas) sobre "{keyword}". Encierra cada párrafo con las etiquetas HTML correspondientes (<p></p>). Si es pertinente, utiliza listas (<ul> o <ol>) para aclarar puntos, ya que esto enriquece mucho más el contenido. Cuando puedas, la etiqueta HTML <strong></strong> para destacar algunas partes del texto que sean importantes. También puedes usar tablas HTML si consideras que la intención de búsqueda de "{keyword}" se responde mejor de esta forma: cuánto mejor se responda la intención de búsqueda, más satisfecho estará el usuario, así que usa el formato HTML que consideres para la respuesta. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones. ',
			'Me encuentro en el proceso de elaboración de un artículo sobre "{post_title}". Sería de gran ayuda si pudieras proporcionar tres o cuatro párrafos cortos (máximo 5 líneas) en {language} que aborden la temática "{keyword}". Cada fragmento debe estar contenido dentro de etiquetas de párrafo (<p></p>). Apreciaría si puedes incluir listas HTML (<ul> o <ol>) para detalles adicionales, ya que esto mejora mucho el formateo del texto y la experiencia de usuario. También puedes usar tablas HTML si consideras que la intención de búsqueda de "{keyword}" se responde mejor de esta forma, como por ejemplo en comparativas o donde los datos se muestren mejor en tablas. El texto debe estar listo para ser integrado en WordPress directamente, por eso, no hagas mención al formato HTML ni a estas instrucciones',
			'Estoy en la fase de redacción de un post sobre "{post_title}". Me gustaría profundizar en el tema. Para ello, escribe en idioma {language} tres o cuatro párrafos cortos (máximo 5 líneas) sobre "{keyword}". Es importante que cada párrafo esté envuelto con etiquetas <p></p>. Si crees que es relevante, añade listas en formato HTML (<ul> o <ol>) para complementar y también usa la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.',
			'Actualmente, estoy desarrollando un artículo sobre "{post_title}". ¿Podrías ayudarme a expandir el contenido? Necesito tres o cuatro párrafos cortos (máximo 5 líneas) en idioma {language} que responda la pregunta "{keyword}". Recuerda envolver cada párrafo con etiquetas de párrafo HTML (<p></p>) y, en caso de ser necesario, emplear listas HTML (<ul> o <ol>) para ofrecer información adicional y la etiqueta HTML <strong></strong> para destacar algunas partes del texto que consideres importantes. También puedes usar tablas HTML si consideras que la intención de búsqueda de "{keyword}" se responde mejor de esta forma, como por ejemplo en comparativas o donde los datos se muestren mejor en tablas. Asegúrate de que el contenido esté listo para ser publicado tal como está. Es imporante también que para el formateo del contenido no uses Markdown. Por ejemplo, usa <strong></strong> para negritas en lugar de asteriscos (**). Por último, por ningún motivo debes hacer mención a estas instrucciones, al formato HTML, o cualquier comentario adicional fuera del contenido del artículo. El contenido debe ser puramente informativo y directamente relacionado con el tema, sin referencias a su propia creación y sin referencias al formato HTML. Evita también completamente agregar una frase que comente sobre la optimización SEO, la preparación del contenido para WordPress, o cualquier tipo de metadatos o instrucciones sobre la creación del artículo. No hagas mención a estas instrucciones.'
		];

	 	$transientKey = 'aicc_index_' . $post_id;
    
    	$currentIndex = get_transient($transientKey) ?: 0;
		
		$force_ssl = get_option('aicc_force_ssl');
			
		switch ($type) 
		{
		    case 'new':
		        if ($format == 'prompt') {
		            include(AICC_PLUGIN_DIR . 'generate/newCustom.php');
		        } else {
		            include(AICC_PLUGIN_DIR . 'generate/new.php');
		        }
		        break;
		        
		    case 'sentence':
		        include(AICC_PLUGIN_DIR . 'generate/curatorSentence.php');
		        break;

		    default:
		        include(AICC_PLUGIN_DIR . 'generate/curator.php');
		}

		
		wp_cache_flush();
			
		$is_canceled =  get_transient('aicc_cancel_' . $post_id);	

		if (!empty($combinedData)) 
		{
			if (!$is_canceled)
			{
				set_transient('aicc_created_' . $post_id, 1, DAY_IN_SECONDS );
								
				$transient_data = array(
					'content' => $combinedData,
					'new_post'     => ($type == 'new') ? 1 : 0
				);

				set_transient('aicc_content_' . $post_id, $transient_data, HOUR_IN_SECONDS);				
				
			}
		
					
			$status = 0;
			
			$content = wp_kses_post($combinedData);

			$sql = $wpdb->prepare( "
				INSERT INTO " . AICC_HISTORICAL_TABLE . " (post_id, tokens, cost, status, content, model) 
    			VALUES (%d, %d, %f, %d, %s, %s)", 
				$post_id, $totalTokens, $totalCost, $status, $content, $model
			);

			$wpdb->query($sql);
			
			if ($wpdb->last_error)
			{

				throw new Exception('Error al insertar en la base de datos: ' . $wpdb->last_error);
			}
			
		}
				
		} catch (Exception $e) {
			
			$error_message = $e->getMessage();

			set_transient('aicc_error_' . $post_id, $error_message, 3600);
			
			self::aicc_log_register($post_id, 'error', $error_message, 'aicc_call_openai');

			return;
			
		} finally {

		    if (isset($ch)) {
		        curl_close($ch);
		    }			
			
			delete_transient('aicc_is_running');
			delete_transient('aicc_cancel_' . $post_id);
			sleep(1);
			
		}
		
	}
	
	private static function aicc_replacePrompt($prompt, $keyword, $language, $post_title, $post_content) 
	{
    	return str_replace(['{keyword}', '{language}', '{post_title}', '{post_content}'], [$keyword, $language, $post_title, $post_content], $prompt);
	}
	
	private static function aicc_formatKeywords($keywordString) {
    
	    if (empty($keywordString)) {
	        return '';
	    }

	    $keywords = explode(',', $keywordString);
	    $keywords = array_map('trim', $keywords);
	    return '"' . implode('" y "', $keywords) . '"';
	}

	private static function aicc_replaceSentence($prompt, $keyword, $language, $post_title) {
	    // Formatear la cadena de palabras clave antes del reemplazo
	    $formattedKeywords = self::aicc_formatKeywords($keyword);

	    return str_replace(['{keyword}', '{language}', '{post_title}'], [$formattedKeywords, $language, $post_title], $prompt);
	}


	public function aicc_cancel_process() 
	{
		check_ajax_referer('aicc_cancel_process_nonce', 'nonce');

		if (!isset($_POST['post_id']) || !is_numeric($_POST['post_id'])) 
		{
			$error_message = 'ID inválido.';

			self::aicc_log_register(0, 'error', $error_message, 'aicc_cancel_process');	
			
			echo json_encode(['status' => 'error', 'message' => $error_message]);			
			
			wp_die();
		}

		$post_id = intval($_POST['post_id']);

		if (false === get_transient('aicc_is_running')) 
		{
			delete_transient('aicc_content_' . $post_id);
			
			delete_transient('aicc_created_' . $post_id);
			
		} 
		else 
		{
			set_transient('aicc_cancel_' . $post_id, true, HOUR_IN_SECONDS);
			
			$error_message = 'El proceso se está cancelando. Puede demorar unos minutos hasta terminar.';
			
			echo json_encode(['status' => 'pending', 'message' => $error_message]);
			
			wp_die();
			
		}
	}


	public function aicc_get_transient_content_callback() 
	{
		check_ajax_referer('aicc_get_transient_nonce', 'nonce');

		if (!isset($_POST['post_id'])) 
		{
			wp_die(); 
		}

		global $wpdb;

		$post_id = intval($_POST['post_id']);

		$sql = $wpdb->prepare("SELECT content, new_post FROM " . AICC_PENDING_TABLE . " WHERE post_id = %d", $post_id);

		$row = $wpdb->get_row($sql);

		if (!$row) 
		{
			$error_message = $wpdb->last_error ? 'Error al recuperar el contenido de la base de datos: ' . $wpdb->last_error : 'No hay registros.';
			
			self::aicc_log_register($post_id, 'error', $error_message, 'aicc_get_transient_content_callback'); 
			
			wp_die(); 
		}       

		$content = $row->content;
		
		$new_post = isset($row->new_post) ? $row->new_post : 0;

		if ( get_post_type( $post_id ) == 'post' ) {
				$content_type = 'entrada';
		} elseif ( get_post_type( $post_id ) == 'page' ) {
				$content_type = 'página';
		}
		
		$return_content = $new_post ? sprintf('<div class="notice notice-warning inline active-plugin-edit-warning"><p>%s</p></div>', 'Nueva ' . $content_type ): '';
		
		$return_content .= $content;

		echo $return_content;
		
		wp_die(); 
	}


	public function aicc_delete_transient_content_callback() 
	{
		check_ajax_referer('aicc_delete_transient_nonce', 'nonce');

		if (isset($_POST['post_id'])) 
		{
			$post_id = intval($_POST['post_id']);
			
			global $wpdb;
			
			$sql_delete = $wpdb->prepare("DELETE FROM " . AICC_PENDING_TABLE . " WHERE post_id = %d", $post_id);
			    
			$result = $wpdb->query($sql_delete);
			
			if ($result === false) 
			{
				$error_message = 'Error al borrar registro: ' . $wpdb->last_error;

				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_delete_transient_content_callback');
				
				echo json_encode(['status' => 'error', 'message' => $error_message]);
				
			} 
			elseif ( $wpdb->rows_affected > 0 )
			{
				echo json_encode(['status' => 'success']);
			}
			else 
			{
				$error_message = 'No había ningún registro para borrar.';

				self::aicc_log_register($post_id, 'error', $error_message, 'aicc_delete_transient_content_callback');
				
				echo json_encode(['status' => 'error', 'message' => $error_message]);

			}
		}

		wp_die(); 
	}



	// Cannibalizations mark as resolved

	public static function aicc_is_thin_resolved($url, $resolved_urls_set) 
	{
	    return isset($resolved_urls_set[$url]);
	}

	function aicc_mark_thin_as_resolved() 
	{
		check_ajax_referer('aicc_thin_nonce', 'nonce');

		if (!current_user_can('publish_posts')) {
		    wp_send_json_error('No tienes permiso para realizar esta acción.');
		}
		
	    if (!isset($_POST['url'])) {
	        wp_send_json_error('Error al obtener la URL.');
	    }

	    $url = sanitize_text_field($_POST['url']);

	    $resolved_urls = get_option('aicc_thin_resolved_urls', array());

	    if (!is_array($resolved_urls)) {
	        $resolved_urls = array();
	    }

	    $resolved_urls[] = $url;

	    update_option('aicc_thin_resolved_urls', array_unique($resolved_urls));

	    wp_send_json_success();
	}

	function aicc_mark_canibal_as_resolved() 
	{
		
		check_ajax_referer('aicc_canibal_nonce', 'nonce');

		if (!current_user_can('publish_posts')) {
		    wp_send_json_error('No tienes permiso para realizar esta acción.');
		}

	    if (!isset($_POST['keyword'])) {
	        wp_send_json_error('Error al obtener la URL.');
	    }

	    $keyword = sanitize_text_field($_POST['keyword']);

	    $resolved_canibal = get_option('aicc_canibal_resolved_keywords', array());

	    if (!is_array($resolved_canibal)) {
	        $resolved_canibal = array();
	    }

	    $resolved_canibal[] = $keyword;

	    update_option('aicc_canibal_resolved_keywords', array_unique($resolved_canibal));

	    wp_send_json_success();
	}



	//
	//
 	// Search Console	
	//
	//

	public function aicc_search_console_data($start_index = 0, &$urls_by_keyword = [], &$all_urls_metrics = []) 
	{
		try 
		{
			$custom_days = intval(get_option('aicc_custom_days', 90)); 

			$url = get_site_url();
			
			$property_type = get_option('aicc_options_property_type', 'url_prefix'); 

			if ($property_type === 'domain') {
				$url = 'sc-domain:' . parse_url($url, PHP_URL_HOST);  
			}
			
			$start_date = date('Y-m-d', strtotime("-$custom_days days"));
			$end_date = date('Y-m-d');
			$api_key = get_option('aicc_options_sc');
			
			if (empty($api_key)) 
			{	
				$error_message = 'Hubo un error al conectarse con Search Console. Clave de API de Search Console no configurada o vacía.';
				self::aicc_log_register('', 'error', $error_message, 'aicc_search_console_data');
				set_transient('aicc_error_sc', $error_message, 3600);
				return ['success' => false];
			}		

			$client = new Google_Client();
			$client->setApplicationName('Search Console API PHP Quickstart');
			$client->setScopes(Google_Service_Webmasters::WEBMASTERS_READONLY);
			
			$credentials = wp_specialchars_decode(trim($api_key));
			$credentials = json_decode($credentials, true);
			
			if (!is_array($credentials)) 
			{
				$error_message = 'Hubo un error al conectarse con Search Console. La credencial proporcionada tiene un formato inválido.';
				self::aicc_log_register(0, 'error', $error_message, 'aicc_search_console_data');
				set_transient('aicc_error_sc', $error_message, 3600);
				return ['success' => false];
			}

			$client->setAuthConfig($credentials);
			$client->setAccessType('offline');

			$service = new Google_Service_Webmasters($client);

			$request = new Google_Service_Webmasters_SearchAnalyticsQueryRequest();
			$request->setStartDate($start_date);
			$request->setEndDate($end_date);
			$request->setDimensions(['query', 'page']);
			$request->setSearchType('web');
			$request->setRowLimit(25000);
			$request->setStartRow($start_index);
			
			$response = $service->searchanalytics->query($url, $request);

			$rows = $response->getRows();

			$rows = array_filter($rows, function($row) {
				$keys = $row->getKeys();
				return !isset($keys[1]) || (strpos($keys[1], '#') === false && !preg_match('/\.(jpg|jpeg|png|gif)$/i', $keys[1]));
			});

		
			if (empty($rows)) 
			{
				return ['success' => true];
			}
			
			// Transforma y limpia las filas
			foreach ($rows as $row) {
				$keyword = $row->keys[0];
				$url = $row->keys[1];
				$clicks = $row->clicks;
				$impressions = $row->impressions;
				$ctr = $row->ctr;
				$position = $row->position;
				$position_weighted = $position * $impressions; // Aquí calculas la posición ponderada

				$cleaned_rows[] = [
					'keyword' => $keyword,
					'url' => $url,
					'clicks' => $clicks,
					'impressions' => $impressions,
					'ctr' => $ctr,
					'position' => $position,
					'position_weighted' => $position_weighted,
				];

				$urls_by_keyword[$keyword][] = [
					'url' => $url,
					'clicks' => $clicks,
					'impressions' => $impressions,
					'ctr' => $ctr,
					'position' => $position,
					'position_weighted' => $position_weighted,
				];
			
				
				$all_urls_metrics[$url]['clicks'] = ($all_urls_metrics[$url]['clicks'] ?? 0) + $clicks;
				$all_urls_metrics[$url]['impressions'] = ($all_urls_metrics[$url]['impressions'] ?? 0) + $impressions;
				$all_urls_metrics[$url]['position_weighted'] = ($all_urls_metrics[$url]['position_weighted'] ?? 0) + $position_weighted;
			}

			foreach ($all_urls_metrics as $url => $metrics) 
			{
				if ($metrics['impressions'] != 0) 
				{
				$all_urls_metrics[$url]['average_position'] = number_format($metrics['position_weighted'] / $metrics['impressions'], 1);
				} 
				else 
				{
				$all_urls_metrics[$url]['average_position'] = 0;
				}
			}
		
			return ['success' => true, 'data' => $cleaned_rows, 'reached_limit' => count($rows) === 25000];

		} 
		catch (Google_Service_Exception $e) 
		{
			$error_message = "Error en la API de Google Service: " . $e->getMessage();
			self::aicc_log_register(0, 'error', $error_message, 'aicc_search_console_data');
			$message = "Hubo un error al conectarse con Search Console. Verifica los permisos de tu clave API de Search Console o prueba cambiando de tipo de propiedad en las opciones del plugin.";			
			set_transient('aicc_error_sc', $message, 3600);			
			return ['success' => false, 'error_message' => $message];
		} 
		catch (Google_Auth_Exception $e) 
		{
			$error_message = "Error de autenticación en Google API: " . $e->getMessage();
			self::aicc_log_register(0, 'error', $error_message, 'aicc_search_console_data');
			$message = "Hubo un error al conectarse con Search Console. Verifica los permisos de tu clave API de Search Console o prueba cambiando de tipo de propiedad en las opciones del plugin.";			
			set_transient('aicc_error_sc', $message, 3600);			
			return ['success' => false, 'error_message' => $message];		
		} 
		catch (Exception $e) 
		{
			$error_message = "Error desconocido: " . $e->getMessage();
			self::aicc_log_register(0, 'error', $error_message, 'aicc_search_console_data');
			$message = "Error desconocido al intentar conectarse con Search Console.";						
			set_transient('aicc_error_sc', $message, 3600);						
			return ['success' => false, 'error_message' => $message];
		}
	}
	

	public function aicc_search_console_data_cached() 
	{
		ini_set('memory_limit', '512M');

	    $data_cache = get_transient('aicc_data_sc_cache');

	   	// Si hay datos en caché, devolvemos la información.
	    if (!empty($data_cache)) {
	        return ['success' => true, 'data' => $data_cache];
	    }

		// Sino, llamamos a la función aicc_search_console_first si no hay datos en caché.
    	$response = $this->aicc_search_console_first();

    	if ($response['success']) {
    		return $response;
    	}
	}

	public function aicc_search_console_first() 
	{
	    ini_set('memory_limit', '512M');
		
		require_once __DIR__ . '/inc/vendor/autoload.php';
		
		delete_transient('aicc_data_sc_cache');
		delete_transient('aicc_urls_by_keywords');
		delete_transient('aicc_all_urls_metrics');

		$all_data = [];
		$iteration = 0;
		$start_index = 0;
		$row_limit = 25000; 

		$all_urls_metrics = [];		
		$url_by_keyword = [];		

		do {
			$response = $this->aicc_search_console_data($start_index, $urls_by_keyword, $all_urls_metrics);

			if (!$response['success']) {
				return false;
			}

			$data = $response['data'];
			$reached_limit = $response['reached_limit'];

			if (is_array($data) && count($data) > 0) 
			{
				$all_data = array_merge($all_data, $data);
				$start_index += $row_limit; 
			} 
			else 
			{
				break;
			}

			if ($reached_limit) {
				sleep(1);
			}

	        unset($data);

			$iteration++;

		} while ($reached_limit && $iteration < AICC_SC_LIMIT);

		if (count($all_data) > 0) {
			set_transient('aicc_data_sc_cache', $all_data, 30 * DAY_IN_SECONDS);
			set_transient('aicc_urls_by_keywords', $urls_by_keyword, 30 * DAY_IN_SECONDS); 
			set_transient('aicc_all_urls_metrics', $all_urls_metrics, 30 * DAY_IN_SECONDS); 
			return ['success' => true, 'data' => $all_data];
		} else {
			$error_message = 'La conexión se realizó exitosamente pero no hay datos disponibles en Search Console para este dominio.';
			self::aicc_log_register(0, 'error', $error_message, 'aicc_search_console_data');
			set_transient('aicc_error_sc', $error_message, 3600);
			return ['success' => false];
		}

	}


	public function aicc_call_index_api($url_to_notify) {

		require_once __DIR__ . '/inc/vendor/autoload.php';

	    $api_key = get_option('aicc_options_sc');
	    // Asumiendo que 'aicc_options_sc' contiene lo necesario para autenticarse.

	    if (empty($api_key)) {
	        $error_message = 'API Key para Google Indexing no configurada o vacía.';
	        self::aicc_log_register('', 'error', $error_message, 'notify_google_indexing_api');
	        return null;
	    }

	    $client = new Google_Client();
	    $client->setApplicationName('Google Indexing API PHP');
	    $client->setScopes(['https://www.googleapis.com/auth/indexing']);
	    
	    $credentials = wp_specialchars_decode(trim($api_key));
	    $credentials = json_decode($credentials, true);
	    
	    if (!is_array($credentials)) {
	        $error_message = 'Las credenciales proporcionadas no son válidas para Indexing API.';
	        self::aicc_log_register(0, 'error', $error_message, 'notify_google_indexing_api');
	        return null;
	    }

	    $client->setAuthConfig($credentials);
	    $client->setAccessType('offline');

	     try {

	        $endpoint = 'https://indexing.googleapis.com/v3/urlNotifications:publish';

	        $httpClient = $client->authorize(); // Obtiene un cliente HTTP autorizado
	        $response = $httpClient->post($endpoint, [
	            'json' => ['url' => $url_to_notify, 'type' => 'URL_UPDATED']
	        ]);

	        $status_code = $response->getStatusCode();
	        $body = $response->getBody()->getContents();

	        if ($status_code == 200) {
	            // Si la respuesta es exitosa, registramos la respuesta.
	            self::aicc_log_register(0, 'info', 'Indexing API notificada exitosamente: ' . $body, 'notify_google_indexing_api');
	        } else {
	            // Si hay un error, registramos ese error.
	            self::aicc_log_register(0, 'error', 'Error al notificar a Indexing API: ' . $body, 'notify_google_indexing_api');
	        }

	    } catch (Exception $e) {
	        // Captura cualquier excepción y registra el mensaje de error.
	        self::aicc_log_register(0, 'error', 'Excepción al notificar a Indexing API: ' . $e->getMessage(), 'notify_google_indexing_api');
	    }

    	return $status_code;	   	

	}

	
	private static function aicc_log_register($id, $type, $message, $function) 
	{
		global $wpdb;

		$sql = $wpdb->prepare(
	        "INSERT INTO " . AICC_LOG_TABLE . " (post_id, type, message, `function`) 
			VALUES (%d, %s, %s, %s)", 
			$id,
			$type, 
			$message, 
			$function
		);

		$wpdb->query($sql);
	}
	
    public static function aicc_call_log_register($message) {
        self::aicc_log_register('', 'aviso', $message, 'background_processing');
    }

	public function aicc_fetch_data_for_export() {
		
		if (!check_ajax_referer('export_data_nonce', 'security', false)) {
			echo "Error de seguridad.";
			wp_die();
		}
		
		global $wpdb;

		$query = $wpdb->prepare('SELECT post_id, type, message, `function`, date FROM ' . AICC_LOG_TABLE .' ORDER BY id DESC LIMIT 1000');

		$rows = $wpdb->get_results($query, ARRAY_A);
		
		if ($wpdb->last_error) {
			echo "Error en la consulta: " . $wpdb->last_error;
			wp_die();
		}
		
		$content = "";
		foreach ($rows as $row) {
			$content .= $row['post_id'] . ";" . 
						$row['type'] . ";" . 
						$row['message'] . ";" . 
						$row['function'] . ";" . 
						$row['date'] . "\n";
		}

		echo $content;
		wp_die();  
	}
	

	public function aicc_export_urls_to_csv() {

	    if (!current_user_can('publish_posts')) {
	        wp_die('No tienes suficientes permisos para acceder a esta página.');
	    }

	    $data = get_transient('aicc_all_urls_metrics');

	    foreach ($data as $url => $url_data) {
	        $post_id = url_to_postid($url);

	        if ($post_id) {
	            $post_type = get_post_type($post_id);
	        } else {
	            $post_type = false; 
	        }

	        switch ($post_type) {
	            case 'post':
	            case 'page':
	                $data[$url]['title'] = get_the_title($post_id);
	                $data[$url]['published_date'] = get_the_date('Y-m-d', $post_id);
	                $data[$url]['modified_date'] = get_the_modified_date('Y-m-d', $post_id);
	                $ctr = ($data[$url]['impressions'] != 0) ? $data[$url]['clicks'] / $data[$url]['impressions'] : 0;
					$data[$url]['ctr'] = $ctr;
	                break;

	            default:
	                $data[$url]['title'] = ''; 
	                $data[$url]['modified_date'] = '';
	                $data[$url]['published_date'] = '';
	                $data[$url]['ctr'] = '';
	                break;
	        }
	    }

    	$apply_filter = isset($_POST['apply_filter']) && $_POST['apply_filter'] === '1';

	   	if ($apply_filter) {
	        $data = $this->aicc_filter_thin_content($data);
	        $filename = 'thin-content-' . date('Y-m-d') . '.csv';
	    }
	    else
	    {
	    	$filename = 'export-' . date('Y-m-d') . '.csv';
	    }

	    if (ob_get_level()) ob_end_clean();

	    header('Content-Type: text/csv; charset=utf-8');
	    header('Content-Disposition: attachment; filename=' . $filename);

	    $output = fopen('php://output', 'w');

	    fputcsv($output, array('Título', 'URL', 'Impresiones', 'Clics', 'Posición media', 'Última actualización'));

	    foreach ($data as $url => $url_data) {
	        fputcsv($output, array(
	            $url_data['title'],
	            $url,
	            $url_data['impressions'],
	            $url_data['clicks'],
        		is_numeric($url_data['average_position']) ? number_format($url_data['average_position'], 1, ',', '') : $url_data['average_position'],
	            $url_data['modified_date']
	        ));
	    }

	    fclose($output);
	    exit;
	}

	public function aicc_filter_thin_content($data) {
		$click_threshold = 1; 
		$impression_threshold = 1000;
		$position_threshold = 5; 
		$low_ctr_threshold = 0.005; 

		$resolved_urls_set = array_flip(get_option('aicc_thin_resolved_urls', array()));

		$current_timestamp = time();
		$six_months_seconds = 6 * 30 * 24 * 60 * 60; // Aproximación de 6 meses en segundos

		return array_filter($data, function($metrics, $url) use ($click_threshold, $impression_threshold, $low_ctr_threshold, $resolved_urls_set, $current_timestamp, $six_months_seconds) {
		  
		 	if ($this->aicc_is_thin_resolved($url, $resolved_urls_set)) {
		        return false;
		    }
				
			$post_timestamp = strtotime($metrics['published_date']);

			$is_older_than_six_months = ($current_timestamp - $post_timestamp) > $six_months_seconds;

		    $is_low_by_ctr = $metrics['ctr'] < $low_ctr_threshold;
		    $is_low_by_impressions = $metrics['impressions'] < $impression_threshold && $is_low_by_ctr;
		    $is_low_by_clics = $metrics['clicks'] < $click_threshold;

				
		    return ( ( $is_low_by_impressions || $is_low_by_clics ) && $is_older_than_six_months );
		}, ARRAY_FILTER_USE_BOTH);
	}


	public function aicc_export_canibal_to_csv() {
	   
	    if (!current_user_can('publish_posts')) {
	        wp_die('No tienes suficientes permisos para acceder a esta página.');
	    }

  		$urls_by_keyword = get_transient("aicc_urls_by_keywords");

  		$resolved_keywords_set = array_flip(get_option('aicc_canibal_resolved_keywords', array()));

  		$cannibalizing_keywords = [];

  		foreach ($urls_by_keyword as $keyword => $data) {
    		if (count($data) > 1) {

	      		if (isset($resolved_keywords_set[$keyword])) {
	        		continue;
	      		}

		      	$percentage_threshold = 20; // Define el umbral del porcentaje

		      	$impressions = [];
		      	foreach ($data as $url_data) {
		        	if (isset($url_data["impressions"])) {
		          		$impressions[] = $url_data["impressions"];
		        	}
		      	}

		      	if (empty($impressions)) continue;

		      	$max_impressions = max($impressions);
		      	$threshold_impressions = ($percentage_threshold / 100) * $max_impressions;

		      	$cannibalizing_data = [];

		      	foreach ($data as $url_data) {
		        	$impression = $url_data["impressions"];

		        	if ($impression >= $threshold_impressions) {
		          		$cannibalizing_data[] = $url_data;
		        	}
		      	}

		      	if (count($cannibalizing_data) >= 2) {
		        	$cannibalizing_keywords[$keyword] = $cannibalizing_data;
		      	}
	    	}
  		}

  		if ( $cannibalizing_keywords ) {

  			// Calcular suma de impresiones para cada palabra clave y ordenar por eso
  			uasort($cannibalizing_keywords, function($a, $b) {
    			$sum_a = array_sum(array_map(function($item) {
      			return $item["impressions"];
    			}, $a));

   				$sum_b = array_sum(array_map(function($item) {
      			return $item["impressions"];
    			}, $b));

   				return $sum_b - $sum_a; // Orden descendente
  			});

  			$current_keywords = $cannibalizing_keywords;

  			$filename = 'cannibalizations-' . date('Y-m-d') . '.csv';

  			if (ob_get_level()) ob_end_clean();

		    header('Content-Type: text/csv; charset=utf-8');
		    header('Content-Disposition: attachment; filename=' . $filename);

		    $output = fopen('php://output', 'w');

		    fputcsv($output, array('Palabra clave', 'URLs', 'Impresiones', 'Clics',  'Posición media'));

		 	foreach ($current_keywords as $keyword => $data) {

		 		foreach ($data as $metrics) {
					fputcsv($output, array(
			            $keyword,
			           	$metrics['url'],
			           	$metrics['impressions'],
			           	$metrics['clicks'],
			           	number_format($metrics['position'], 1, ',', '')
			        ));

		 		}

		    }

		    fclose($output);
		    exit;

  		}

	}

	public function aicc_get_token_cost($model) {
	    switch($model) {
	      case 'gpt-3.5-turbo-0125':
		    return '0.0015$ cada 750 palabras';
			case 'gpt-4-1106-preview':
			    return '0.03$ cada 750 palabras';
			case 'gpt-4o':
			    return '0.01$ cada 750 palabras';
			case 'gpt-4o-mini':
			    return '0.0006$ cada 750 palabras';
			case 'gpt-4.1-mini':
			    return '0.0016$ cada 750 palabras';
			case 'gpt-4.1-nano':
			    return '0.0004$ cada 750 palabras';
	    }
	}

	public static function aicc_calculate_process_cost($prompt, $model, $tokens) {

		if ($model == 'gpt-3.5-turbo'){
		   	$model = 'gpt-3.5-turbo-0125';
		}elseif ($model == 'gpt-4' || $model == 'gpt-4-0125-preview'){
		   	$model = 'gpt-4-1106-preview';
		}

		$costsPerToken = [
		    'gpt-3.5-turbo-0125' => ['input' => 0.0000005, 'output' => 0.0000015],
		    'gpt-4-1106-preview' => ['input' => 0.00001, 'output' => 0.00003],
		    'gpt-4o' => ['input' => 0.0000025, 'output' => 0.00001],
		    'gpt-4o-mini' => ['input' => 0.00000015, 'output' => 0.0000006],
		    'gpt-4.1-nano' => ['input' => 0.00000010, 'output' => 0.0000004],
		    'gpt-4.1-mini' => ['input' => 0.00000040, 'output' => 0.0000016],
		];

		$relacionPalabrasTokens = 1000 / 750;

	    // Contamos la cantidad de palabras
	    $cantidadPalabrasPrompt = str_word_count($prompt);

	    // Calcula tokens de entrada
	    $tokensInput = $cantidadPalabrasPrompt * $relacionPalabrasTokens;

	    // Obtiene el costo por token de entrada y salida para el modelo seleccionado
	    $costoInputPorToken = $costsPerToken[$model]['input'];
	    $costoOutputPorToken = $costsPerToken[$model]['output'];

	    // Calcula el costo total de entrada
	    $costoInput = $tokensInput * $costoInputPorToken;

	    // Al total de tokens que devuelve le restamos los tokens de entrada
	    $tokensOutput = $tokens - $tokensInput;

	    // Calcula el costo total de salida
	    $costoOutput = $tokensOutput * $costoOutputPorToken;

	    // Calculamos el costo total de esta solicitud
	    $costoTotal = $costoInput + $costoOutput;

	    return $costoTotal;
	}


	/*
     * Nuevas funciones generación de contenidos en bulk 
     */	

	/*
	 * Función para curar contenido en bulk
	 */
	public function aicc_bulk_curation_content($item) {

	    try {

	        update_option('aicc_last_item_time', current_time('timestamp'));

	        ini_set('memory_limit', '512M');
	        set_time_limit(300);

	        global $wpdb;

	        set_transient('aicc_is_bulk_running', true, 604800);

	        $totalCost = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_cost' LIMIT 1");

	        if(!$totalCost) {
	            $totalCost = 0;
	        }

	        $totalTokens = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_tokens' LIMIT 1");

	        if(!$totalTokens) {
	            $totalTokens = 0;
	        }

	        $processed_count = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_processed_count' LIMIT 1");

	        if(!$processed_count) {
	            $processed_count = 0;
	        }

	        $discarded = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_discarded_count' LIMIT 1");

	        if(!$discarded) {
	            $discarded = 0;
	        }

	        $ids_processed = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_ids_processed' LIMIT 1");

	        if ($ids_processed) {
	            $ids_processed = unserialize($ids_processed);
	        } else {
	            $ids_processed = array();
	        }

	        $keywords = '';
	        $formattedKeywords = '';
	        $exclude_posts_ids = array();

	        $api_key = self::aicc_decrypt_api_key(get_option('aicc_options_openai'));

	        if (empty($api_key)) {
	            throw new Exception('API Key para OpenAI no configurada o vacía.');
	        }

	        $post_id = $item['post_id'];
	        $post_type = $item['post_type'];
	        $sort_by = $item['sort_by'] ?: 'impressions';
	        $num_keywords = $item['num_keywords'] ?: 5;
	        $include_headers = $item['include_headers'];
	        $user_prompt = $item['prompt'] ?: '';
	        $budget = (isset($item['budget']) && !empty($item['budget'])) ? (float)$item['budget'] : 101;
	        $exclude_already = $item['exclude_already'];
	        $remove_validations = $item['remove_validations'];
	        $type = $item['type'];
	        $execution_id = $item['execution_id'];

	        update_option('aicc_processing_id', $post_id);

	        $new_length = 0;
	        $minimum_length = 0;
	        $percent = 0;
	        $all_headers_included = false;

			$ajust_model = get_option('aicc_ajust_model', 1);

		    if ($ajust_model) {
		    	if ($type == 'improve') {
		      		$model = 'gpt-4-1106-preview'; 
			   	} else {
		      		$model = 'gpt-4o-mini'; 
			   	}
		   	} else {
				$model = get_option('aicc_model', 'gpt-4o-mini');

				// Actualiza modelos antiguos del plugin a los nuevos modelos
				$models = [
				    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
				    'gpt-4' => 'gpt-4-1106-preview',
				    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
				];

				$model = $models[$model] ?? $model;
			}

	        $recordId = get_transient('aicc_current_bulk_process_id');

	        if ($type == 'custom' && !$user_prompt) {
	            throw new Exception('Es necesario introducir un prompt personalizado.');
	        }

			$url = 'https://api.openai.com/v1/chat/completions';

	        $header  = [
	            'Content-Type: application/json',
	            'Authorization: Bearer ' . $api_key
	        ];

	        $ch = curl_init();

	        curl_setopt($ch, CURLOPT_URL, $url);
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	        curl_setopt($ch, CURLOPT_POST, 1);
	        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

	        $processedPosts = get_option('aicc_bulk_processed_posts', array());
	        $processedPostsLast = [];

	        $post = get_post($post_id);

	        if (!$post) {
	            return;
	        }

	        // Variables para reintento de cURL
	        $maxRetries = 3;
	        $maxConsecutiveFailures = 10;
	        $consecutiveFailures = 0;

	        // Verificamos que el proceso no haya sido cancelado
	        $is_canceled = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_cancel_bulk_process' LIMIT 1");
	        $is_canceled = $is_canceled === '1';

	        if ($is_canceled) {
	            self::aicc_log_register(0, 'warning', 'Proceso cancelado manualmente.', 'aicc_bulk_curation_content');
	            if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	                $this->bulk->cancel();
	            }
	            return false;
	        }

	        // La primera vez que lo gastado supere el presupuesto, cancelamos
	        if ($totalCost >= $budget && $budget > 0) {
	            self::aicc_log_register(0, 'warning', 'Proceso cancelado por alcanzar límite de presupuesto.', 'aicc_bulk_curation_content');
	            if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	                $this->bulk->cancel();
	            }
	            return false;
	        }

	        $post_id = $post->ID;
	        $post_url = get_permalink($post->ID);
	        $post_title = $post->post_title;
	        $post_content = $post->post_content;

	        // Validar que el post tenga contenido suficiente
	        if (empty(trim($post_content))) {
	            $discarded++;
	            update_option('aicc_bulk_discarded_count', $discarded);
	            $error_message = 'Contenido descartado por estar vacío: ' . $post_title;
	            self::aicc_log_register($post_id, 'warning', $error_message, 'aicc_bulk_curation_content');
	            return false;
	        }

	        $postContentNoAccents = remove_accents(strtolower($post_content));

	        if ($type == 'improve' || $type == 'custom' || $type == 'regenerate') {

	            $data = get_transient('aicc_data_sc_cache');
	            if (!$data && $type == 'improve') {
	                throw new Exception('No hay contenido en Search Console.');
	            }

	            $keywordDataMap = [];
	            $percentage_threshold = 20;
	            $nonCanibalizedKeywords = [];

	            foreach ($data as $entry) {
	                if (!isset($entry['keyword'], $entry['url'], $entry['impressions'])) {
	                    continue;
	                }

	                $keyword = strtolower($entry['keyword']);
	                if (!isset($keywordDataMap[$keyword])) {
	                    $keywordDataMap[$keyword] = [];
	                }

	                $keywordDataMap[$keyword][] = [
	                    'url' => $entry['url'],
	                    'impressions' => $entry['impressions']
	                ];
	            }

	            // Identificar las keywords no canibalizadas
	            foreach ($keywordDataMap as $keyword => $urlsData) {
	                $maxImpressions = max(array_column($urlsData, 'impressions'));
	                $thresholdImpressions = ($percentage_threshold / 100) * $maxImpressions;

	                $qualifiedUrls = array_filter($urlsData, function ($urlData) use ($thresholdImpressions) {
	                    return $urlData['impressions'] >= $thresholdImpressions;
	                });

	                if (count($qualifiedUrls) <= 1) {
	                    $nonCanibalizedKeywords[] = $keyword;
	                }
	            }

				// Optimizar la búsqueda con un conjunto (set)
		        $nonCanibalizedKeywordsNoAccentsSet = array_flip(array_map('remove_accents', array_map('strtolower', $nonCanibalizedKeywords)));

				// Inicializar arrays para almacenar las diferentes categorías de palabras clave
				$filtered_data_not_in_content = [];
				$filtered_data_in_content = [];
				$filtered_data_all = [];

				foreach ($data as $row) {
				    $keywordNoAccents = remove_accents(strtolower($row['keyword']));
				    if ($row['url'] == $post_url && isset($nonCanibalizedKeywordsNoAccentsSet[$keywordNoAccents])) {
				        $filtered_data_all[] = $row;
				        if (stripos($postContentNoAccents, $keywordNoAccents) === false) {
				            $filtered_data_not_in_content[] = $row;
				        } else {
				            $filtered_data_in_content[] = $row;
				        }
				    }
				}

		        // Ordenar si es necesario, considerando la eficiencia
		        if (!in_array($sort_by, ['clicks', 'impressions'])) {
		            $sort_by = 'impressions';
		        }

				usort($filtered_data_not_in_content, function ($a, $b) use ($sort_by) {
				    return $b[$sort_by] <=> $a[$sort_by];
				});

				$keywords = array_slice(array_column($filtered_data_not_in_content, 'keyword'), 0, $num_keywords);

		        if (empty($keywords) && $type == 'improve') {
		            $discarded++;
		            update_option('aicc_bulk_discarded_count', $discarded);
		            $error_message = 'Contenido descartado: "' . $post_title . '". Puede que no haya palabras clave en Search Console para ese contenido o que ya estén todas agregadas dentro del artículo.';
		            self::aicc_log_register($post_id, 'warning', $error_message, 'aicc_bulk_curation_content');
		        	return false;
		        }

		        $keywordsString = implode(', ', $keywords);
		        $formattedKeywords = self::aicc_formatKeywords($keywordsString);

				$includedKeywords = array_column($filtered_data_in_content, 'keyword');
		        $includedKeywordsString = implode(',', $includedKeywords);
		        $includedKeywords = self::aicc_formatKeywords($includedKeywordsString);

				$allKeywords = array_unique(array_slice(array_column($filtered_data_all, 'keyword'), 0, 15));
				$allKeywords = implode(',', $allKeywords);
		        $allKeywords = self::aicc_formatKeywords($allKeywords);

			} else {
			    // Inicializar arrays vacíos para evitar errores
			    $keywordsString = '';
			    $formattedKeywords = '';
			    $includedKeywords = [];
			    $includedKeywordsString = '';
			    $allKeywords = '';
			    $filtered_data_not_in_content = [];
			    $filtered_data_in_content = [];
			    $filtered_data_all = [];
			}

			$fetch_videos = false;
	        if ($type == 'improve' || $type == 'custom') {
	        	include(AICC_PLUGIN_DIR . 'generate/bulk/curator.php');
	        } elseif ($type == 'regenerate') {
	        	include(AICC_PLUGIN_DIR . 'generate/bulk/new.php');
	        } elseif ($type == 'paa') {
	        	include(AICC_PLUGIN_DIR . 'generate/bulk/paa.php');
	        }
	        /*
	         * Chequeamos validaciones
	         */

	        if (($new_length >= $minimum_length && ($percent > 70 || $all_headers_included)) || $remove_validations) {

				// Crear un autoguardado de la entrada
				wp_create_post_autosave($post->ID);

				sleep(1);
			
				$autosave = $wpdb->get_row($wpdb->prepare(
			        "SELECT * FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'revision' ORDER BY post_date DESC LIMIT 1",
			        $post->ID
			    ));

				// Verificar que el autoguardado se haya creado
				if ($autosave) {
				    // Inserta el meta dato directamente en la tabla de meta datos
				    $wpdb->insert(
				        $wpdb->postmeta,
				        [
				            'post_id'    => $autosave->ID,
				            'meta_key'   => '_autosave_execution_id',
				            'meta_value' => $execution_id
				        ]
				    );

				    // Verificar si la inserción fue exitosa
				    if ($wpdb->insert_id) {
				        $error_message = "Meta dato '_autosave_execution_id' agregado al autoguardado con ID " . $autosave->ID;
				        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				    } else {
				        $error_message = "Error al agregar el meta dato al autoguardado con ID " . $autosave->ID;
				        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				    }
				} else {
				    $error_message = "No se pudo crear un autoguardado para el post con ID " . $post->ID;
				    self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				}

	            wp_update_post(array(
	                'ID' => $post->ID,
	                'post_content' => $new_content
	            ));

	            if ($type == 'paa') {
	            	update_post_meta($post_id, 'aicc_bulkc_is_paa_included', true);
	            }

	            // Generamos una array asociativo con los IDS procesados y las keywords que debe incluir
	            $ids_processed[$post_id] = $keywordsString;
	            update_option('aicc_bulk_ids_processed', $ids_processed);

	           	// Histórico completo de posts procesados
	            if (!in_array($post_id, $processedPosts)) {
	                $processedPosts[] = $post_id;
	            }
	            $processedPosts = array_unique($processedPosts);
	            update_option('aicc_bulk_processed_posts', $processedPosts);


				// **Estructura de recuperación** - Agregar el sistema de recuperación
				// Obtener o iniciar la opción temporal de IDs y tipo de curación para la ejecución actual
				$processedPostsCurrent = get_option('aicc_current_bulk_execution', [
					'execution_id' => $execution_id,
				    'type' => 'Contenido',
				    'post_ids' => [] 
				]);

				// Agregar el post_id actual a la lista de IDs procesados
				$processedPostsCurrent['post_ids'][] = $post_id;
				$processedPostsCurrent['post_ids'] = array_unique($processedPostsCurrent['post_ids']); // Evitar duplicados

				// Guardar la opción temporal con el tipo de curación y los IDs procesados
				update_option('aicc_current_bulk_execution', $processedPostsCurrent);

	            $processed_count++;
	            update_option('aicc_bulk_processed_count', $processed_count);

	        } else {
	        	if ($type == 'paa') {
		            $discarded++;
		            update_option('aicc_bulk_discarded_count', $discarded);
		            $error_message = 'No se encontraron PAA para el artículo: ' . $post->post_title;
		            self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
		        } else {
	            	$discarded++;
		            update_option('aicc_bulk_discarded_count', $discarded);
		            $error_message = 'Contenido descartado por no superar las validaciones de contenido: ' . $post->post_title;
		            self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');		        	
		        }
	        }

	        $serializedIdsProcessed = serialize($ids_processed);

	        $sqlUpdate = $wpdb->prepare(
	            "UPDATE " . AICC_BULK_TABLE . " SET amount = %d, tokens = %d, cost = %f, ids_processed = %s WHERE id = %d",
	            $processed_count, $totalTokens, $totalCost, $serializedIdsProcessed, $recordId
	        );
	        $wpdb->query($sqlUpdate);
	        if ($wpdb->last_error) {
	            $error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;
	            self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
	        }

	    } catch (Exception $e) {
	        set_transient('aicc_error_bulk', true, 604800);
	        $error_message = $e->getMessage();
	        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
	        if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	            $this->bulk->cancel();
	        }
	        return;
	    } finally {
	        if (isset($ch)) {
	            curl_close($ch);
	        }
	        sleep(1);
	    }
	}

	

	/*
	 * Función para agregar imágenes destacadas en bulk
	 */
	public function aicc_bulk_curation_image($item) {
	    try {

			ini_set('memory_limit', '512M');
			
			set_time_limit(300);

	        global $wpdb;

	        set_transient('aicc_is_bulk_running', true, 604800);

	        $totalCost = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_cost' LIMIT 1");
	        if (!$totalCost) {
	            $totalCost = 0;
	        }

	        $totalTokens = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_tokens' LIMIT 1");
	        if (!$totalTokens) {
	            $totalTokens = 0;
	        }

	        $processed_count = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_processed_count' LIMIT 1");
	        if (!$processed_count) {
	            $processed_count = 0;
	        }

			$discarded = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_discarded_count' LIMIT 1");

			if(!$discarded) {
				$discarded = 0;
			}	        

	        $ids_processed = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_ids_processed' LIMIT 1");
	        if ($ids_processed) {
	            $ids_processed = unserialize($ids_processed);
	        } else {
	            $ids_processed = array();
	        }

	        $total_posts = 0;
	        $costPromptImage = 0;
	        $prompt_image = '';
	        $image_url = '';


	        $post_id = $item['post_id'];
	        $post_type = $item['post_type'];
	        $excludeNoPublish = $item['exclude_no_publish'];
	        $set_prompt = $item['set_prompt'];
	        $user_prompt = $item['prompt'] ?: '';
	        $budget = (isset($item['budget']) && !empty($item['budget'])) ? (float)$item['budget'] : 101;
	        $quality = $item['quality'] ?: 'standard';
	        $size = $item['resolution'] ?: '1792x1024';
	        $type = $item['type'] ?: 'featured';
	        $source = $item['source'] ?: 'ia';
	        $motor = $item['motor'] ?: 'openai';
	        $aspect_ratio = $item['aspect_ratio'] ?: 'openai';
	        $replace = $item['replace'] ?: 'openai';
	        $execution_id = $item['execution_id'];

	        $api_key = self::aicc_decrypt_api_key(get_option('aicc_options_openai'));
	        if (empty($api_key)) {
	            throw new Exception('API Key para OpenAI no configurada o vacía.');
	        }

	        $api_key_replicate = self::aicc_decrypt_api_key(get_option('aicc_options_replicate'));
	        if (empty($api_key_replicate) && $motor == 'replicate') {
	            throw new Exception('API Key para Replicate no configurada o vacía.');
	        }

			$ajust_model = get_option('aicc_ajust_model', 1);

	        if ($ajust_model) {
	        	$model_prompt = 'gpt-4o-mini';
	        } else {

				$model_prompt = get_option('aicc_model', 'gpt-4o-mini');

				// Solo actualiza modelos antiguos a las versiones nuevas
				$models = [
				    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
				    'gpt-4' => 'gpt-4-1106-preview',
				    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
				];

				$model_prompt = $models[$model_prompt] ?? $model_prompt;

			}

			$recordId = get_transient('aicc_current_bulk_process_id');

	        $header = [
	            'Content-Type: application/json',
	            'Authorization: Bearer ' . $api_key
	        ];

	        $ch = curl_init();
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	        curl_setopt($ch, CURLOPT_POST, 1);
	        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
	        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
			if ( get_option('aicc_force_ssl') )
			{
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			}		    
	        
	        $post = get_post($post_id);
	        if (empty($post)) {
	            return;
	        }

			$maxRetries = 3;				
			$maxConsecutiveFailures = 10;
			$consecutiveFailures = 0;	

	        $post_id = $post->ID;
	        $post_title = $post->post_title;
	        $post_content = $post->post_content;
			$post_content = wpautop($post_content);

			// Llamar a wp_create_post_autosave para crear el autoguardado
			wp_create_post_autosave($post->ID);

			sleep(1);
			
			$autosave = $wpdb->get_row($wpdb->prepare(
			   "SELECT * FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'revision' ORDER BY post_date DESC LIMIT 1",
			    $post->ID
			));

			// Verificar que el autoguardado se haya creado
			if ($autosave) {
			    // Inserta el meta dato directamente en la tabla de meta datos
			    $wpdb->insert(
			        $wpdb->postmeta,
			        [
			            'post_id'    => $autosave->ID,
			            'meta_key'   => '_autosave_execution_id',
			            'meta_value' => $execution_id
			        ]
			    );

			    // Verificar si la inserción fue exitosa
			    if ($wpdb->insert_id) {
			        $error_message = "Meta dato '_autosave_execution_id' agregado al autoguardado con ID " . $autosave->ID;
			        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
			    } else {
			        $error_message = "Error al agregar el meta dato al autoguardado con ID " . $autosave->ID;
			        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
			    }
			} else {
			    $error_message = "No se pudo crear un autoguardado para el post con ID " . $post->ID;
			    self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
			}

	      	if ($type == 'featured') {
	       		if ($source == 'ia') {
					include ( AICC_PLUGIN_DIR . 'functions/generateFeaturedImage.php');
				} else {
					include ( AICC_PLUGIN_DIR . 'functions/obtainFeaturedImage.php');		
	        	}
	        } else {
				include ( AICC_PLUGIN_DIR . 'functions/obtainPostImages.php');
	        }

			// **Estructura de recuperación** - Agregar el sistema de recuperación
			// Obtener o iniciar la opción temporal de IDs y tipo de curación para la ejecución actual
			$processedPostsCurrent = get_option('aicc_current_bulk_execution', [
				'execution_id' => $execution_id,
			    'type' => 'Imagen',
			    'post_ids' => [] 
			]);

			// Agregar el post_id actual a la lista de IDs procesados
			$processedPostsCurrent['post_ids'][] = $post_id;
			$processedPostsCurrent['post_ids'] = array_unique($processedPostsCurrent['post_ids']); // Evitar duplicados

			// Guardar la opción temporal con el tipo de curación y los IDs procesados
			update_option('aicc_current_bulk_execution', $processedPostsCurrent);

	        $serializedIdsProcessed = serialize($ids_processed);
	        $sqlUpdate = $wpdb->prepare(
	            "UPDATE " . AICC_BULK_TABLE . " SET amount = %d, tokens = %d, cost = %f, ids_processed = %s WHERE id = %d",
	            $processed_count, $totalTokens, $totalCost, $serializedIdsProcessed, $recordId
	        );
	        $wpdb->query($sqlUpdate);
	        if ($wpdb->last_error) {
	            $error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;
	            self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_image');
	        }

	    } catch (Exception $e) {
			set_transient('aicc_error_bulk', true, 604800);				        
	        $error_message = $e->getMessage();	        
	        self::aicc_log_register('', 'error', $error_message, 'aicc_bulk_curation_image');
	        if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
			    $this->bulk->cancel();
			}	
	        return;
	    } finally {
	        if (isset($ch)) {
	            curl_close($ch);
	        }
			sleep(1);			
	    }
	}

	public function aicc_bulk_curation_video($item) {

	    try {

	        update_option('aicc_last_item_time', current_time('timestamp'));

	        ini_set('memory_limit', '512M');

	        set_time_limit(300);

	        global $wpdb;

	        set_transient('aicc_is_bulk_running', true, 604800);

	        $totalCost = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_cost' LIMIT 1");

	        if(!$totalCost) {
	            $totalCost = 0;
	        }

	        $totalTokens = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_tokens' LIMIT 1");

	        if(!$totalTokens) {
	            $totalTokens = 0;
	        }

	        $processed_count = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_processed_count' LIMIT 1");

	        if(!$processed_count) {
	            $processed_count = 0;
	        }

	        $discarded = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_discarded_count' LIMIT 1");

	        if(!$discarded) {
	            $discarded = 0;
	        }

	        $ids_processed = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_ids_processed' LIMIT 1");

	        if ($ids_processed) {
	            $ids_processed = unserialize($ids_processed);
	        } else {
	            $ids_processed = array();
	        }

	        $exclude_posts_ids = array();

	        $api_key = self::aicc_decrypt_api_key(get_option('aicc_options_openai'));

	        if (empty($api_key)) {
	            throw new Exception('API Key para OpenAI no configurada o vacía.');
	        }

	        $post_id = $item['post_id'];
	        $post_type = $item['post_type'];
	        $budget = (isset($item['budget']) && !empty($item['budget'])) ? (float)$item['budget'] : 101;
	        $discard_already_video = $item['discard_already_video'];
	        $type = $item['type'];
	        $execution_id = $item['execution_id'];

	        update_option('aicc_processing_id', $post_id);

	        $recordId = get_transient('aicc_current_bulk_process_id');

			$url = 'https://api.openai.com/v1/chat/completions';

	        $header  = [
	            'Content-Type: application/json',
	            'Authorization: Bearer ' . $api_key
	        ];

	        $ch = curl_init();

	        curl_setopt($ch, CURLOPT_URL, $url);
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	        curl_setopt($ch, CURLOPT_POST, 1);
	        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

	        $post = get_post($post_id);

	        if (!$post) {
	            return;
	        }

	        // Variables para reintento de cURL
	        $maxRetries = 3;
	        $maxConsecutiveFailures = 10;
	        $consecutiveFailures = 0;

	        // Verificamos que el proceso no haya sido cancelado
	        $is_canceled = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_cancel_bulk_process' LIMIT 1");
	        $is_canceled = $is_canceled === '1';

	        if ($is_canceled) {
	            self::aicc_log_register(0, 'warning', 'Proceso cancelado manualmente.', 'aicc_bulk_curation_content');
	            if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	                $this->bulk->cancel();
	            }
	            return false;
	        }

	        // La primera vez que lo gastado supere el presupuesto, cancelamos
	        if ($totalCost >= $budget && $budget > 0) {
	            self::aicc_log_register(0, 'warning', 'Proceso cancelado por alcanzar límite de presupuesto.', 'aicc_bulk_curation_content');
	            if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	                $this->bulk->cancel();
	            }
	            return false;
	        }

	        $post_id = $post->ID;
	        $post_url = get_permalink($post->ID);
	        $post_title = $post->post_title;
	        $post_content = $post->post_content;

			$ajust_model = get_option('aicc_ajust_model', 1);

	        if ($ajust_model) {
	        	$model = 'gpt-4o-mini';
	        } else {

				$model = get_option('aicc_model', 'gpt-4o-mini');

				// Solo actualiza modelos antiguos a las versiones nuevas
				$models = [
				    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
				    'gpt-4' => 'gpt-4-1106-preview',
				    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
				];

				$model = $models[$model] ?? $model;

			}

	        // Validar que el post tenga contenido suficiente
	        if (empty(trim($post_content))) {
	            $discarded++;
	            update_option('aicc_bulk_discarded_count', $discarded);
	            $error_message = 'Contenido descartado por estar vacío: ' . $post_title;
	            self::aicc_log_register($post_id, 'warning', $error_message, 'aicc_bulk_curation_content');
	            return false;
	        }

	        if ($discard_already_video) {
	        	$youtube_pattern = '/(https?:\/\/(www\.)?youtube\.com\/watch\?v=|https?:\/\/youtu\.be\/|<iframe[^>]*src="https?:\/\/(www\.)?youtube\.com\/embed\/)/i';
		        if (preg_match($youtube_pattern, $post_content)) {
		            $discarded++;
		            update_option('aicc_bulk_discarded_count', $discarded);
		            $error_message = 'Contenido descartado por ya tener un video incorporado previamente: ' . $post_title;
		            self::aicc_log_register($post_id, 'warning', $error_message, 'aicc_bulk_curation_video');
		            return false;
		        }
	        }
	        
			$fetch_videos = true;

			// Bandera para comprobar si fue agregado el video, y en ese caso, actualizamos
			$video_included = false;
	        
	        include(AICC_PLUGIN_DIR . 'generate/bulk/videos.php');

	        /*
	         * Chequeamos validaciones
	         */

	        if ($video_included) {

				wp_create_post_autosave($post->ID);

				sleep(1);
			
				$autosave = $wpdb->get_row($wpdb->prepare(
			        "SELECT * FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'revision' ORDER BY post_date DESC LIMIT 1",
			        $post->ID
			    ));
							  
				// Verificar que el autoguardado se haya creado
				if ($autosave) {
				    // Inserta el meta dato directamente en la tabla de meta datos
				    $wpdb->insert(
				        $wpdb->postmeta,
				        [
				            'post_id'    => $autosave->ID,
				            'meta_key'   => '_autosave_execution_id',
				            'meta_value' => $execution_id
				        ]
				    );

				    // Verificar si la inserción fue exitosa
				    if ($wpdb->insert_id) {
				        $error_message = "Meta dato '_autosave_execution_id' agregado al autoguardado con ID " . $autosave->ID;
				        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				    } else {
				        $error_message = "Error al agregar el meta dato al autoguardado con ID " . $autosave->ID;
				        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				    }
				} else {
				    $error_message = "No se pudo crear un autoguardado para el post con ID " . $post->ID;
				    self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
				}

	            wp_update_post(array(
	                'ID' => $post->ID,
	                'post_content' => $new_content
	            ));

	            update_post_meta($post_id, 'aicc_bulkv_is_video_included', true);

	            // Generamos una array asociativo con los IDS procesados y las keywords que debe incluir
	            $ids_processed[$post_id] = '';
	            update_option('aicc_bulk_ids_processed', $ids_processed);

				// **Estructura de recuperación** - Agregar el sistema de recuperación
				// Obtener o iniciar la opción temporal de IDs y tipo de curación para la ejecución actual
				$processedPostsCurrent = get_option('aicc_current_bulk_execution', [
					'execution_id' => $execution_id,
				    'type' => 'Video',
				    'post_ids' => [] 
				]);

				// Agregar el post_id actual a la lista de IDs procesados
				$processedPostsCurrent['post_ids'][] = $post_id;
				$processedPostsCurrent['post_ids'] = array_unique($processedPostsCurrent['post_ids']); // Evitar duplicados

				// Guardar la opción temporal con el tipo de curación y los IDs procesados
				update_option('aicc_current_bulk_execution', $processedPostsCurrent);

	            $processed_count++;
	            update_option('aicc_bulk_processed_count', $processed_count);

	        } else {
		       	$discarded++;
		        update_option('aicc_bulk_discarded_count', $discarded);
		        $error_message = 'No se encontraron videos para el artículo: ' . $post->post_title;
		        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
	        }

	        $serializedIdsProcessed = serialize($ids_processed);

	        $sqlUpdate = $wpdb->prepare(
	            "UPDATE " . AICC_BULK_TABLE . " SET amount = %d, tokens = %d, cost = %f, ids_processed = %s WHERE id = %d",
	            $processed_count, $totalTokens, $totalCost, $serializedIdsProcessed, $recordId
	        );
	        $wpdb->query($sqlUpdate);
	        if ($wpdb->last_error) {
	            $error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;
	            self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
	        }

	    } catch (Exception $e) {
	        set_transient('aicc_error_bulk', true, 604800);
	        $error_message = $e->getMessage();
	        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_content');
	        if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	            $this->bulk->cancel();
	        }
	        return;
	    } finally {
	        if (isset($ch)) {
	            curl_close($ch);
	        }
	        sleep(1);
	    }
	}

	/*
	 * Función para agregar metatags en bulk
	 */
	public function aicc_bulk_curation_metatags($item) {
	    try {
	        ini_set('memory_limit', '512M');
	        set_time_limit(300);

	        global $wpdb;

	        set_transient('aicc_is_bulk_running', true, 604800);

	        $totalCost = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_cost' LIMIT 1");
	        if (!$totalCost) {
	            $totalCost = 0;
	        }

	        $totalTokens = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_total_tokens' LIMIT 1");
	        if (!$totalTokens) {
	            $totalTokens = 0;
	        }

	        $processed_count = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_processed_count' LIMIT 1");
	        if (!$processed_count) {
	            $processed_count = 0;
	        }

	        $ids_processed = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_bulk_ids_processed' LIMIT 1");
	        if ($ids_processed) {
	            $ids_processed = unserialize($ids_processed);
	        } else {
	            $ids_processed = array();
	        }

	        $api_key = self::aicc_decrypt_api_key(get_option('aicc_options_openai'));

	        if (empty($api_key)) {
	            throw new Exception('API Key para OpenAI no configurada o vacía.');
	        }

	        $post_type = $item['post_type'];
	        $overwrite = $item['overwrite'];
	        $budget = (isset($item['budget']) && !empty($item['budget'])) ? (float)$item['budget'] : 101;
	        $include = $item['include'] ?: '';
	        $include_ids = !empty($include) ? explode(',', $include) : array();
	        $exclude = $item['exclude'] ?: '';
	        $exclude_ids = !empty($exclude) ? explode(',', $exclude) : array();
	        $seo_plugin = $item['seo_plugin'];
	        $exclude_already = $item['exclude_already'];
	        $offset = $item['offset'];
	        $limit = $item['limit'];

	        $ajust_model = get_option('aicc_ajust_model', 1);

	        if ($ajust_model) {
	        	$model = 'gpt-4o-mini';
	        } else {
				$model = get_option('aicc_model', 'gpt-4o-mini');

				// Actualiza modelos antiguos del plugin a los nuevos modelos
				$models = [
				    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
				    'gpt-4' => 'gpt-4-1106-preview',
				    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
				];

				$model = $models[$model] ?? $model;	        	
	        }

			$recordId = get_transient('aicc_current_bulk_process_id');

	        $url = 'https://api.openai.com/v1/chat/completions';
	        $header = [
	            'Content-Type: application/json',
	            'Authorization: Bearer ' . $api_key
	        ];

	        $ch = curl_init();
	        curl_setopt($ch, CURLOPT_URL, $url);
	        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	        curl_setopt($ch, CURLOPT_POST, 1);
	        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
	        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
	        if (get_option('aicc_force_ssl')) {
	            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	        }

	        $processedPosts = get_option('aicc_bulk_processed_posts_metatags', array());
	        if ($exclude_already) {
	            $combined_exclude_ids = array_unique(array_merge($exclude_ids, $processedPosts));
	        } else {
	            $combined_exclude_ids = $exclude_ids;
	        }
	        $combined_exclude_ids = array_filter($combined_exclude_ids, function ($id) {
	            return is_numeric($id) && $id > 0;
	        });

	        // Variables para reintento de cURL
	        $maxRetries = 3;
	        $maxConsecutiveFailures = 10;
	        $consecutiveFailures = 0;

	        $args = [
	            'post_type' => $post_type,
	            'post_status' => 'publish',
	            'posts_per_page' => $limit,
	            'offset' => $offset,
	            'post__not_in' => $combined_exclude_ids
	        ];
	        if (!empty($include_ids)) {
	            $args['post__in'] = $include_ids;
	            $args['ignore_sticky_posts'] = 1;
	        }

	        $posts = get_posts($args);
	        if (empty($posts)) {
	            return;
	        }

	        foreach ($posts as $index => $post) {
	            update_option('aicc_last_item_time', current_time('timestamp'));
	            $post_id = $post->ID;
	            $hasMetatitle = false;
	            $hasMetadescription = false;

	            if ($seo_plugin == 'Yoast SEO' && !$overwrite) {
	                $hasMetatitle = !empty(get_post_meta($post_id, '_yoast_wpseo_title', true));
	                $hasMetadescription = !empty(get_post_meta($post_id, '_yoast_wpseo_metadesc', true));
	            } elseif ($seo_plugin == 'Rank Math' && !$overwrite) {
	                $hasMetatitle = !empty(get_post_meta($post_id, 'rank_math_title', true));
	                $hasMetadescription = !empty(get_post_meta($post_id, 'rank_math_description', true));
	            } elseif ($seo_plugin == 'All in One SEO Pack' && !$overwrite) {
	                $hasMetatitle = !empty(get_post_meta($post_id, '_aioseop_title', true));
	                $hasMetadescription = !empty(get_post_meta($post_id, '_aioseop_description', true));
	            }

	            if ($hasMetatitle && $hasMetadescription && !$overwrite) {
	                self::aicc_log_register(0, 'warning', 'Descartó este registro porque ya tenía metaetiquetas: ' . $post->ID, 'aicc_bulk_curation_metatags');
	                continue;
	            }

	            if ($index % 5 === 0) {
	                $is_canceled = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_cancel_bulk_process' LIMIT 1");
	                if ($is_canceled === '1') {
		               	if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
							$this->bulk->cancel();
						}	                	
	                    break;
	                }
	            }

	            // La primera vez que lo gastado supere el presupuesto, cancelamos
	            if ($totalCost >= $budget && $budget > 0) {
	                self::aicc_log_register(0, 'warning', 'Cancelación del proceso por superar límite de presupuesto.', 'aicc_bulk_curation_metatags');
	               	if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
						$this->bulk->cancel();
					}
	                break;
	            }

	            $post_title = $post->post_title;
	            if (($hasMetatitle && $overwrite) || !$hasMetatitle) {
	                // Generamos el metatitle
	                $prompt = 'Genera un metatitle SEO atractivo para mejorar el CTR, respetando el idioma original del título del artículo: "' . $post_title . '". La keyword principal es: "' . $post_title . '". Incluye palabras IMPORTANTES en MAYÚSCULAS para enfatizarlas. Asegúrate de que el título sea claro, conciso y promueva la curiosidad o el valor del contenido. Adapta el título para que se ajuste a un límite de máximo 50 caracteres, maximizando su impacto visual y relevancia. Ejemplo para un artículo en español: "Cómo hacer PAN en CASA - Receta FÁCIL y RÁPIDA". Ejemplo para un artículo en inglés: "Unlock SECRET Strategies - Boost Your SEO NOW". Así debes hacer con cada idioma, respetando el original del título del artículo: "' . $post_title . '". Evita completamente hacer mención a estas instrucciones, simplemente devuelve el metatitle SEO sin ningún comentario adicional.';
	                $role = 'Eres un experto en redacción SEO';

	                $messages = [
	                    [
	                        "role" => "system",
	                        "content" => $role
	                    ],
	                    [
	                        "role" => "user",
	                        "content" => $prompt
	                    ]
	                ];

	                $post_fields = [
	                    "model" => $model,
	                    "messages" => $messages,
	                    "temperature" => 0.7
	                ];

	                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_fields));

	                $retryCount = 0;
	                $result = false;
	                while ($retryCount < $maxRetries && !$result) {
	                    $result = curl_exec($ch);
	                    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

	                    if ($result === false || $httpcode >= 400) {
	                        $curlError = curl_errno($ch);
	                        $curlErrorMsg = curl_error($ch);
	                        $consecutiveFailures++;
	                        if ($consecutiveFailures >= $maxConsecutiveFailures) {
	                            throw new Exception("Número máximo de fallos consecutivos alcanzado, cancelando el proceso.");
	                        }

	                        if ($curlError) {
	                            self::aicc_log_register($post_id, 'warning', "Reintento $retryCount debido al error de cURL: $curlErrorMsg", 'aicc_bulk_curation_content');
	                        } else {
	                            self::aicc_log_register($post_id, 'warning', "Reintento $retryCount debido al código de estado HTTP: $httpcode", 'aicc_bulk_curation_content');
	                        }

	                        if ($httpcode == 401 || $httpcode == 429) {
	                            throw new Exception("Error con código $httpcode, verifique las credenciales de la API o la política de límites de tarifas.");
	                        }

	                        $retryCount++;
	                        sleep(5);
	                        $result = false;
	                    } else {
	                        $consecutiveFailures = 0;
	                    }
	                }

	                if (!$result) {
	                    $error_message = 'Error por respuesta de OpenAI vacía.';
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                $response = json_decode($result);
	                if (json_last_error() !== JSON_ERROR_NONE) {
	                    $error_message = 'La respuesta de OpenAI no se pudo decodificar. Por favor, intenta de nuevo.';
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                if (!isset($response->choices[0]->message->content) || !isset($response->usage->total_tokens)) {
	                    $error_message = 'Respuesta inesperada de OpenAI. Respuesta completa: ' . $result;
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                $responseContent = $response->choices[0]->message->content;
	                if (strpos($responseContent, 'como modelo de lenguaje de IA') === false &&
	                    strpos($responseContent, 'como modelo de lenguaje de inteligencia artificial') === false &&
	                    strpos($responseContent, '¿Hay algo más en lo que pueda ayudarte?') === false) {
	                    $metatitle = $responseContent;
	                    $metatitle = str_replace(["\"", "'"], '', $metatitle);
	                } else {
	                    $metatitle = '';
	                }

	                $usage = $response->usage;
	                $tokens = $usage->total_tokens;

	                $totalTokens += $tokens;
			    	update_option('aicc_bulk_total_tokens', $totalTokens);	                
	                $totalCost += self::aicc_calculate_process_cost($prompt, $model, $tokens);
			    	update_option('aicc_bulk_total_cost', $totalCost);

	            } else {
	                $metatitle = '';
	            }

	            if (($hasMetadescription && $overwrite) || !$hasMetadescription) {
	                // Generamos la metadescription
	                $prompt = 'Genera una meta descripción SEO atractiva para mejorar el CTR, basada en la keyword principal: "' . $post_title . '". La descripción debe ser concisa, incluir palabras IMPORTANTES en MAYÚSCULAS y usar iconos (como ✔️, ✅, 🚀) para aumentar la visibilidad. Debe resumir el valor principal o el contenido del artículo/promoción e incluir un llamado a la acción (CTA) como "Descubre cómo", "Aprende más" o "No te pierdas". La longitud debe ser de aproximadamente 140 caracteres para asegurar que se muestre completa en los resultados de búsqueda. Ejemplo para un artículo en español: "Descubre las MEJORES técnicas para HACER PAN en casa 🍞✅. Recetas fáciles, rápidas y deliciosas que transformarán tu cocina. ¡Empieza ahora!". Ejemplo para un artículo en inglés: "Unlock SECRET Strategies to Boost Your SEO NOW 🚀✅. Learn cutting-edge tips and tricks that will elevate your online presence. Start today!". Adapta la descripción según el idioma del título del artículo: "' . $post_title . '". Evita completamente hacer mención a estas instrucciones, simplemente devuelve la meta descripción SEO sin ningún comentario adicional.';
	                $role = 'Eres un experto en redacción SEO';

	                $messages = [
	                    [
	                        "role" => "system",
	                        "content" => $role
	                    ],
	                    [
	                        "role" => "user",
	                        "content" => $prompt
	                    ]
	                ];

	                $post_fields = [
	                    "model" => $model,
	                    "messages" => $messages,
	                    "temperature" => 0.7
	                ];

	                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_fields));

	                $retryCount = 0;
	                $result = false;
	                while ($retryCount < $maxRetries && !$result) {
	                    $result = curl_exec($ch);
	                    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

	                    if ($result === false || $httpcode >= 400) {
	                        $curlError = curl_errno($ch);
	                        $curlErrorMsg = curl_error($ch);
	                        $consecutiveFailures++;

	                        if ($consecutiveFailures >= $maxConsecutiveFailures) {
	                            throw new Exception("Número máximo de fallos consecutivos alcanzado, cancelando el proceso.");
	                        }

	                        if ($curlError) {
	                            self::aicc_log_register($post_id, 'warning', "Reintento $retryCount debido al error de cURL: $curlErrorMsg", 'aicc_bulk_curation_content');
	                        } else {
	                            self::aicc_log_register($post_id, 'warning', "Reintento $retryCount debido al código de estado HTTP: $httpcode", 'aicc_bulk_curation_content');
	                        }

	                        if ($httpcode == 401 || $httpcode == 429) {
	                            throw new Exception("Error con código $httpcode, verifique las credenciales de la API o la política de límites de tarifas.");
	                        }

	                        $retryCount++;
	                        sleep(5);
	                        $result = false;
	                    } else {
	                        $consecutiveFailures = 0;
	                    }
	                }

	                if (!$result) {
	                    $error_message = 'Error por respuesta de OpenAI vacía.';
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                $response = json_decode($result);
	                if (json_last_error() !== JSON_ERROR_NONE) {
	                    $error_message = 'La respuesta de OpenAI no se pudo decodificar. Por favor, intenta de nuevo.';
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                if (!isset($response->choices[0]->message->content) || !isset($response->usage->total_tokens)) {
	                    $error_message = 'Respuesta inesperada de OpenAI. Respuesta completa: ' . $result;
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_curation_metatags');
	                    continue;
	                }

	                $responseContent = $response->choices[0]->message->content;
	                if (strpos($responseContent, 'como modelo de lenguaje de IA') === false &&
	                    strpos($responseContent, 'como modelo de lenguaje de inteligencia artificial') === false &&
	                    strpos($responseContent, '¿Hay algo más en lo que pueda ayudarte?') === false) {
	                    $metadescription = $responseContent;
	                    $metadescription = str_replace(["\"", "'"], '', $metadescription);
	                } else {
	                    $metadescription = '';
	                }

	                $usage = $response->usage;
	                $tokens = $usage->total_tokens;

	                $totalTokens += $tokens;
			    	update_option('aicc_bulk_total_tokens', $totalTokens);	                
	                $totalCost += self::aicc_calculate_process_cost($prompt, $model, $tokens);
			    	update_option('aicc_bulk_total_cost', $totalCost);


	            } else {
	                $metadescription = '';
	            }

	            $actualizado = false;
	            if ($metatitle || $metadescription) {
	                if ($seo_plugin == 'Yoast SEO') {
	                    if ((empty(get_post_meta($post_id, '_yoast_wpseo_title', true)) || $overwrite) && $metatitle) {
	                        update_post_meta($post_id, '_yoast_wpseo_title', $metatitle);
	                        $actualizado = true;
	                    }
	                    if ((empty(get_post_meta($post_id, '_yoast_wpseo_metadesc', true)) || $overwrite) && $metadescription) {
	                        update_post_meta($post_id, '_yoast_wpseo_metadesc', $metadescription);
	                        $actualizado = true;
	                    }
	                }

	                if ($seo_plugin == 'Rank Math') {
	                    if ((empty(get_post_meta($post_id, 'rank_math_title', true)) || $overwrite) && $metatitle) {
	                        update_post_meta($post_id, 'rank_math_title', $metatitle);
	                        $actualizado = true;
	                    }
	                    if ((empty(get_post_meta($post_id, 'rank_math_description', true)) || $overwrite) && $metadescription) {
	                        update_post_meta($post_id, 'rank_math_description', $metadescription);
	                        $actualizado = true;
	                    }
	                }

	                if ($seo_plugin == 'All in One SEO Pack') {
	                    if ((empty(get_post_meta($post_id, '_aioseop_title', true)) || $overwrite) && $metatitle) {
	                        update_post_meta($post_id, '_aioseop_title', $metatitle);
	                        $actualizado = true;
	                    }
	                    if ((empty(get_post_meta($post_id, '_aioseop_description', true)) || $overwrite) && $metadescription) {
	                        update_post_meta($post_id, '_aioseop_description', $metadescription);
	                        $actualizado = true;
	                    }
	                }

	                if ($actualizado) {
	                    $processed_count++;
	                    update_option('aicc_bulk_processed_count', $processed_count);
	                    
	                    $ids_processed[$post_id] = '';
						update_option('aicc_bulk_ids_processed',$ids_processed);

	                    // Guardamos un registro de todos los posts procesados
	                    if (!in_array($post_id, $processedPosts)) {
	                        $processedPosts[] = $post_id;
	                    }
	                    $processedPosts = array_unique($processedPosts);
	                    update_option('aicc_bulk_processed_posts_metatags', $processedPosts);
	                }
	            }

	            $serializedIdsProcessed = serialize($ids_processed);
	            $sqlUpdate = $wpdb->prepare(
	                "UPDATE " . AICC_BULK_TABLE . " SET amount = %d, tokens = %d, cost = %f, ids_processed = %s WHERE id = %d",
	                $processed_count, $totalTokens, $totalCost, $serializedIdsProcessed, $recordId
	            );
	            $wpdb->query($sqlUpdate);
	            if ($wpdb->last_error) {
	                $error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;
	                self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_metatags');
	            }

	            sleep(1);

	            if ($index % 10 === 0) {
	                sleep(10);
	            }
	        }
	    } catch (Exception $e) {
	        set_transient('aicc_error_bulk', true, 604800);
	        $error_message = $e->getMessage();
	        self::aicc_log_register($post->ID, 'error', $error_message, 'aicc_bulk_curation_metatags');
	        if (isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
	            $this->bulk->cancel();
	        }
	    } finally {
	        if (isset($ch)) {
	            curl_close($ch);
	        }
	        sleep(2);
	    }
	}

		


	public function aicc_process_bulkc_content() 
	{
	    check_ajax_referer('aicc_process_bulkc_content', 'nonce');

	    $message = 'Inicio del proceso de curación masiva de contenidos.';
	    self::aicc_log_register('', 'aviso', $message, 'aicc_process_bulkc_content');        

	    global $wpdb;
	    
	    // Borramos registro de último procesamiento ya que iniciamos un nuevo proceso
	    delete_option('aicc_bulk_processed_posts_last');

	    delete_transient('aicc_fatal_error');
	            
	    // Obtener el estado de los transients
	    $is_manual_running = get_transient('aicc_is_running');
	    $is_bulk_running = get_transient('aicc_is_bulk_running');
	    $is_recovery_running = get_transient('aicc_is_recovery_running');

	    // Verificar si alguno de los dos está activo
	    if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
	        $is_running = true;
	    } else {
	        $is_running = false;
	    }
	    
	    if ( $is_running )
	    {
	        return;
	    }
	            
	    $post_type = isset($_POST['aicc_bulkc_post_type']) ? $_POST['aicc_bulkc_post_type'] : 'post';
	    $type = isset($_POST['aicc_bulkc_type']) ? $_POST['aicc_bulkc_type'] : 'improve';
	    $num_keywords = isset($_POST['aicc_bulkc_number']) ? absint($_POST['aicc_bulkc_number']) : 5;
	    $sort_by = isset($_POST['aicc_bulkc_sort_by']) ? $_POST['aicc_bulkc_sort_by'] : 'clicks';
	    $include_headers = isset($_POST['aicc_bulkc_include_headers']) && $_POST['aicc_bulkc_include_headers'] === '1' ? true : false;
	    $prompt = isset($_POST['aicc_bulkc_prompt']) ? sanitize_textarea_field(wp_unslash($_POST['aicc_bulkc_prompt'])) : '';
	    $budget = !empty($_POST['aicc_bulkc_budget']) ? absint($_POST['aicc_bulkc_budget']) : 100;
	    $include = isset($_POST['aicc_bulkc_include']) ? $_POST['aicc_bulkc_include'] : '';
	    $include_ids = !empty($include) ? explode(',', $include) : array();
	    $exclude = isset($_POST['aicc_bulkc_exclude']) ? $_POST['aicc_bulkc_exclude'] : '';
	    $exclude_ids = !empty($exclude) ? explode(',', $exclude) : array();
	    $exclude_already = isset($_POST['aicc_bulkc_exclude_already']) && $_POST['aicc_bulkc_exclude_already'] === '1' ? true : false;
	    $remove_validations = isset($_POST['aicc_bulkc_remove_validations']) && $_POST['aicc_bulkc_remove_validations'] === '1' ? true : false;

	    if (empty($post_type) || empty($num_keywords) || empty($sort_by) ) {
	        $error_message = 'Hay algún dato incompleto: ' .
	            'Tipo de contenido: ' . $_POST['aicc_bulkc_post_type'] . ', ' .
	            'Número de palabras clave: ' . $_POST['aicc_bulkc_number'] . ', ' .
	            'Ordenamiento: ' . $_POST['aicc_bulkc_sort_by'] . ', ' .
	        self::aicc_log_register('', 'error', $error_message, 'aicc_process_keyword');                    
	        wp_die();
	    }

	    if (isset($prompt)) {
	        update_option('aicc_bulkc_prompt', $prompt, '', 'yes');
	    } else {
	    	delete_option('aicc_bulkc_prompt');
	   	}

	    update_option('aicc_bulkc_post_type', $post_type, '', 'yes');
	    update_option('aicc_bulkc_type', $type, '', 'yes');
	    update_option('aicc_bulkc_num_keywords', $num_keywords, '', 'yes');
	    update_option('aicc_bulkc_sort_by', $sort_by, '', 'yes');
	    update_option('aicc_bulkc_include', $include, '', 'yes');
	    update_option('aicc_bulkc_exclude', $exclude, '', 'yes');
	    update_option('aicc_bulkc_exclude_already', $exclude_already ? '1' : '0', '', 'yes');
	    update_option('aicc_bulkc_remove_validations', $remove_validations ? '1' : '0', '', 'yes');
		update_option('aicc_bulkc_include_headers', $include_headers ? '1' : '0', '', 'yes');

	    if ($budget > 0 && $budget < 100) {
	        update_option('aicc_bulkc_budget', $budget, '', 'yes');
	    } else {
	        delete_option('aicc_bulkc_budget');                        
	    }
	        
		$ajust_model = get_option('aicc_ajust_model', 1);

	    if ($ajust_model) {
	    	if ($type == 'improve') {
	      		$model = 'gpt-4-1106-preview'; 
		   	} else {
	      		$model = 'gpt-4o-mini'; 
		   	}
	   	} else {
			$model = get_option('aicc_model', 'gpt-4o-mini');

			// Actualiza modelos antiguos del plugin a los nuevos modelos
			$models = [
			    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
			    'gpt-4' => 'gpt-4-1106-preview',
			    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
			];

			$model = $models[$model] ?? $model;
		}

	    $initialData = [
	        'type' => 'Contenido',
	        'amount' => 0,
	        'tokens' => 0,
	        'cost' => 0, 
	        'budget' => $budget, 
	        'model' => $model,
	        'ids_processed' => '' 
	    ];

	    $sql = $wpdb->prepare(
	        "INSERT INTO " . AICC_BULK_TABLE . " (type, amount, tokens, cost, budget, model, ids_processed) VALUES (%s, %d, %d, %f, %d, %s, %s)",
	        $initialData['type'], $initialData['amount'], $initialData['tokens'], $initialData['cost'], $initialData['budget'], $initialData['model'], $initialData['ids_processed']
	    );
	    $wpdb->query($sql);

	    $recordId = $wpdb->insert_id;

	    set_transient('aicc_current_bulk_process_id', $recordId, 604800);

	    $processedPosts = get_option('aicc_bulk_processed_posts', array());

	    if ($exclude_already) {
	        $combined_exclude_ids = array_unique(array_merge($exclude_ids, $processedPosts));
	    } else {
	        $combined_exclude_ids = $exclude_ids;
	    }

	    $combined_exclude_ids = array_filter($combined_exclude_ids, function($id) {
	        return is_numeric($id) && $id > 0;
	    });

	    $where_conditions = ['post_type = %s', 'post_status = \'publish\'']; 
	    $query_args = [$post_type]; 

	    if (!empty($include_ids)) {
	        $include_placeholders = implode(', ', array_fill(0, count($include_ids), '%d'));
	        $where_conditions[] = "ID IN ($include_placeholders)";
	        $query_args = array_merge($query_args, $include_ids);
	    }

	    if (!empty($combined_exclude_ids)) {
	        $exclude_placeholders = implode(', ', array_fill(0, count($combined_exclude_ids), '%d'));
	        $where_conditions[] = "ID NOT IN ($exclude_placeholders)";
	        $query_args = array_merge($query_args, $combined_exclude_ids);
	    }

	    $sql = "SELECT ID FROM {$wpdb->posts} WHERE " . implode(' AND ', $where_conditions);

	    $post_ids = $wpdb->get_col($wpdb->prepare($sql, $query_args));

	    if (empty($post_ids)) {
	        delete_transient('aicc_current_bulk_process_id');
	        $error_message = 'No hay ningún artículo para procesar. Finalización del proceso de curación masiva de contenidos.';
	        self::aicc_log_register(0, 'aviso', $error_message, 'aicc_bulk_curation_content');                
	        return;                
	    }

	    update_option('aicc_bulk_total_posts', count($post_ids));

	    $execution_id = current_time('Ymd_His');

	    foreach ($post_ids as $post_id) {
	        $data = [
	            'post_id' => $post_id,
	            'from' => 'content',
	            'post_type' => $post_type,
	            'type' => $type,	            
	            'num_keywords' => $num_keywords,
	            'include_headers' => $include_headers,
	            'sort_by' => $sort_by,
	            'prompt' => $prompt,
	            'budget' => $budget,
	            'exclude_already' => $exclude_already,
	            'remove_validations' => $remove_validations,
	            'execution_id' => $execution_id
	        ];
	        $this->bulk->push_to_queue($data);
	    }

	    $this->bulk->save()->dispatch();
	                                
	    wp_die();
	}


	public function aicc_process_bulki_content() 
	{
	    check_ajax_referer('aicc_process_bulki_content', 'nonce');

	    $message = 'Inicio del proceso de curación masiva de imágenes.';
	    self::aicc_log_register('', 'aviso', $message, 'aicc_process_bulki_content');    

	    global $wpdb;

	    delete_transient('aicc_fatal_error');
	            
	    $is_manual_running = get_transient('aicc_is_running');
	    $is_bulk_running = get_transient('aicc_is_bulk_running');
	    $is_recovery_running = get_transient('aicc_is_recovery_running');

	    // Verificar si alguno de los dos está activo
	    if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
	        $is_running = true;
	    } else {
	        $is_running = false;
	    }        

	    if ( $is_running ) {
	        return;
	    }
	            
	    $post_type = isset($_POST['aicc_bulki_post_type']) ? $_POST['aicc_bulki_post_type'] : 'post';
	    $excludeNoPublish = isset($_POST['aicc_bulki_exclude_no_publish']) && $_POST['aicc_bulki_exclude_no_publish'] === '1' ? true : false;            
	    $set_prompt = isset($_POST['aicc_bulki_set_prompt']) && $_POST['aicc_bulki_set_prompt'] === '1' ? true : false;
	    $prompt = isset($_POST['aicc_bulki_prompt']) ? sanitize_textarea_field(wp_unslash($_POST['aicc_bulki_prompt'])) : '';
	    $budget = !empty($_POST['aicc_bulki_budget']) ? absint($_POST['aicc_bulki_budget']) : 100;
	    $include = isset($_POST['aicc_bulki_include']) ? $_POST['aicc_bulki_include'] : '';
	    $include_ids = !empty($include) ? explode(',', $include) : array();        
	    $exclude = isset($_POST['aicc_bulki_exclude']) ? $_POST['aicc_bulki_exclude'] : '';
	    $exclude_ids = !empty($exclude) ? explode(',', $exclude) : array();        
	    $quality = isset($_POST['aicc_bulki_quality']) ? $_POST['aicc_bulki_quality'] : 'standard';
	    $resolution = isset($_POST['aicc_bulki_resolution']) ? $_POST['aicc_bulki_resolution'] : '1792x1024';
	    $type = isset($_POST['aicc_bulki_type']) ? $_POST['aicc_bulki_type'] : 'featured';
	    $source = isset($_POST['aicc_bulki_source']) ? $_POST['aicc_bulki_source'] : 'ia';
	    $motor = isset($_POST['aicc_bulki_motor']) ? $_POST['aicc_bulki_motor'] : 'openai';
	    $aspect_ratio = isset($_POST['aicc_bulki_aspect_ratio']) ? $_POST['aicc_bulki_aspect_ratio'] : '16:9';
	    $replace = isset($_POST['aicc_bulki_replace']) && $_POST['aicc_bulki_replace'] === '1' ? true : false;            

	    if (empty($post_type) || empty($quality) || empty($resolution) ) {
	        $error_message = 'Hay algún dato incompleto: ' .
	            'Tipo de contenido: ' . $_POST['aicc_bulki_post_type'] . ', ' .
	            'Calidad: ' . $_POST['aicc_bulki_quality'] . ', ' .
	            'Resolución: ' . $_POST['aicc_bulki_resolution'] . ', ' .
	        self::aicc_log_register('', 'error', $error_message, 'aicc_process_keyword');                    
	        wp_die();
	    }

	    update_option('aicc_bulki_post_type', $post_type, '', 'yes');
	    update_option('aicc_bulki_type', $type, '', 'yes');
	    update_option('aicc_bulki_excludeNoPublish', $excludeNoPublish ? '1' : '0', '', 'yes');
	    update_option('aicc_bulki_include', $include, '', 'yes');
	    update_option('aicc_bulki_exclude', $exclude, '', 'yes');
	    update_option('aicc_bulki_quality', $quality, '', 'yes');
	    update_option('aicc_bulki_resolution', $resolution, '', 'yes');
	    update_option('aicc_bulki_source', $source, '', 'yes');
	    update_option('aicc_bulki_motor', $motor, '', 'yes');
	    update_option('aicc_bulki_aspect_ratio', $aspect_ratio, '', 'yes');
	    update_option('aicc_bulki_replace', $replace, '', 'yes');

	    if (isset($set_prompt) && $set_prompt == 1) {
	        update_option('aicc_bulki_set_prompt', '1', '', 'yes');
	        if (isset($prompt)) {
	            update_option('aicc_bulki_prompt', $prompt, '', 'yes');
	        } else {
	            delete_option('aicc_bulki_prompt');
	        }
	    } else {
	        delete_option('aicc_bulki_set_prompt');
	        delete_option('aicc_bulki_prompt');            
	    }

	    if ($budget > 0 && $budget < 100) {
	        update_option('aicc_bulki_budget', $budget, '', 'yes');
	    } else {
	        delete_option('aicc_bulki_budget');                        
	    }        

	    if ($source == 'ia') {
	        $initial_model = 'dall-e-3';
	    } else {
	        $initial_model = $source;
	    }

	    $initialData = [
	        'type' => 'Imagen',
	        'amount' => 0, 
	        'tokens' => 0,
	        'cost' => 0,
	        'budget' => $budget,
	        'model' => $initial_model,
	        'ids_processed' => '' 
	    ];
	    $sql = $wpdb->prepare(
	        "INSERT INTO " . AICC_BULK_TABLE . " (type, amount, tokens, cost, budget, model, ids_processed) VALUES (%s, %d, %d, %f, %d, %s, %s)",
	        $initialData['type'], $initialData['amount'], $initialData['tokens'], $initialData['cost'], $initialData['budget'], $initialData['model'], $initialData['ids_processed']
	    );
	    $wpdb->query($sql);
	    $recordId = $wpdb->insert_id; 

	    set_transient('aicc_current_bulk_process_id', $recordId, 604800);

	    $post_statuses = $excludeNoPublish ? "'publish'" : "'publish', 'draft', 'pending', 'future'";

	    $includeCondition = '';
	    $excludeCondition = '';

	    // Construir la condición de inclusión si hay IDs para incluir
	    if (!empty($include_ids)) {
	        $include_ids_placeholder = implode(',', array_fill(0, count($include_ids), '%d'));
	        $includeCondition = "AND p.ID IN ($include_ids_placeholder)";
	    }

	    // Construir la condición de exclusión si hay IDs para excluir
	    if (!empty($exclude_ids)) {
	        $exclude_ids_placeholder = implode(',', array_fill(0, count($exclude_ids), '%d'));
	        $excludeCondition = "AND p.ID NOT IN ($exclude_ids_placeholder)";
	    }

	    if ($type == 'featured') {
	    	if (!$replace) { 
		        $sql = "
		            SELECT p.ID FROM {$wpdb->posts} p
		            WHERE p.post_type = %s
		            AND p.post_status IN ($post_statuses)
		            $includeCondition
		            $excludeCondition
		            AND NOT EXISTS (
		                SELECT 1 FROM {$wpdb->postmeta} pm
		                WHERE pm.post_id = p.ID
		                AND pm.meta_key = '_thumbnail_id'
		            )
		        ";
		    } else {
		        $sql = "
		            SELECT p.ID FROM {$wpdb->posts} p
		            WHERE p.post_type = %s
		            AND p.post_status IN ($post_statuses)
		            $includeCondition
		            $excludeCondition
		        ";		    	
		    }
	    } else {
	        $sql = "
	            SELECT p.ID FROM {$wpdb->posts} p
	            WHERE p.post_type = %s
	            AND p.post_status IN ($post_statuses)
	            $includeCondition
	            $excludeCondition
	            AND NOT EXISTS (
	                SELECT 1 FROM {$wpdb->posts} p2
	                WHERE p2.ID = p.ID
	                AND p2.post_content LIKE '%<img %>'
	            )
	        ";
	    }

	    // Inicializar el arreglo de placeholders con el tipo de post
	    $placeholders = [$post_type];

	    // Añadir $include_ids y $exclude_ids al arreglo de placeholders, si es necesario
	    if (!empty($include_ids)) {
	        $placeholders = array_merge($placeholders, $include_ids);
	    }
	    if (!empty($exclude_ids)) {
	        $placeholders = array_merge($placeholders, $exclude_ids);
	    }

	    $sql_prepared = $wpdb->prepare($sql, ...$placeholders);
	    error_log($sql_prepared);
	    $post_ids = $wpdb->get_col($sql_prepared); // Obtener los IDs de los posts a procesar

	    if (empty($post_ids)) {
	        delete_transient('aicc_current_bulk_process_id');
	        $error_message = 'No hay ningún artículo para procesar. Finalización del proceso de curación masiva de imágenes.';
	        self::aicc_log_register(0, 'aviso', $error_message, 'aicc_bulk_curation_content');                
	        return;                
	    }

	    update_option('aicc_bulk_total_posts', count($post_ids));

	  	$execution_id = current_time('Ymd_His');

	    foreach ($post_ids as $post_id) {
	        $data = array(
	            'post_id' => $post_id,
	            'from' => 'image',
	            'post_type' => $post_type,
	            'exclude_no_publish' => $excludeNoPublish,
	            'set_prompt' => $set_prompt,
	            'prompt' => $prompt,
	            'budget' => $budget,            
	            'quality' => $quality,
	            'resolution' => $resolution,
	            'type' => $type,
	            'source' => $source,
	            'motor' => $motor,	            
	            'aspect_ratio' => $aspect_ratio,	            
	            'replace' => $replace,	            
	            'execution_id' => $execution_id
	        );
	        $this->bulk->push_to_queue($data);
	    }

	    $this->bulk->save()->dispatch();

	    wp_die();
	}

	public function aicc_process_bulkv_content() 
	{
	    check_ajax_referer('aicc_process_bulkv_content', 'nonce');

	    $message = 'Inicio del proceso de curación masiva de videos.';
	    self::aicc_log_register('', 'aviso', $message, 'aicc_process_bulkv_content');        

	    global $wpdb;
	    
	    // Borramos registro de último procesamiento ya que iniciamos un nuevo proceso
	    delete_option('aicc_bulk_processed_posts_last');

	    delete_transient('aicc_fatal_error');
	            
	    // Obtener el estado de los transients
	    $is_manual_running = get_transient('aicc_is_running');
	    $is_bulk_running = get_transient('aicc_is_bulk_running');
	    $is_recovery_running = get_transient('aicc_is_recovery_running');

	    // Verificar si alguno de los dos está activo
	    if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
	        $is_running = true;
	    } else {
	        $is_running = false;
	    }
	    
	    if ( $is_running )
	    {
	        return;
	    }
	            
	    $post_type = isset($_POST['aicc_bulkv_post_type']) ? $_POST['aicc_bulkv_post_type'] : 'post';
	    $type = isset($_POST['aicc_bulkv_type']) ? $_POST['aicc_bulkv_type'] : 'improve';
	    $budget = !empty($_POST['aicc_bulkv_budget']) ? absint($_POST['aicc_bulkv_budget']) : 100;
	    $include = isset($_POST['aicc_bulkv_include']) ? $_POST['aicc_bulkv_include'] : '';
	    $include_ids = !empty($include) ? explode(',', $include) : array();
	    $exclude = isset($_POST['aicc_bulkv_exclude']) ? $_POST['aicc_bulkv_exclude'] : '';
	    $exclude_ids = !empty($exclude) ? explode(',', $exclude) : array();
	    $discard_already_video = isset($_POST['aicc_bulkv_discard_already_video']) && $_POST['aicc_bulkv_discard_already_video'] === '1' ? true : false;

	    if (empty($post_type)) {
	        $error_message = 'Hay algún dato incompleto: ' .
	            'Tipo de contenido: ' . $_POST['aicc_bulkv_post_type'] . ', ' .
	        self::aicc_log_register('', 'error', $error_message, 'aicc_process_keyword');                    
	        wp_die();
	    }

	    update_option('aicc_bulkv_post_type', $post_type, '', 'yes');
	    update_option('aicc_bulkv_type', $type, '', 'yes');
	    update_option('aicc_bulkv_include', $include, '', 'yes');
	    update_option('aicc_bulkv_exclude', $exclude, '', 'yes');
	    update_option('aicc_bulkv_discard_already_video', $discard_already_video ? '1' : '0', '', 'yes');
	    update_option('aicc_bulkv_remove_validations', $remove_validations ? '1' : '0', '', 'yes');

	    if ($budget > 0 && $budget < 100) {
	        update_option('aicc_bulkv_budget', $budget, '', 'yes');
	    } else {
	        delete_option('aicc_bulkv_budget');                        
	    }
	        
		$ajust_model = get_option('aicc_ajust_model', 1);

	    if ($ajust_model) {
	      	$model = 'gpt-4o-mini';
	   	} else {
			$model = get_option('aicc_model', 'gpt-4o-mini');

			// Actualiza modelos antiguos del plugin a los nuevos modelos
			$models = [
			    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
			    'gpt-4' => 'gpt-4-1106-preview',
			    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
			];

			$model = $models[$model] ?? $model;
		}

	    $initialData = [
	        'type' => 'Video',
	        'amount' => 0,
	        'tokens' => 0,
	        'cost' => 0, 
	        'budget' => $budget, 
	        'model' => $model,
	        'ids_processed' => '' 
	    ];

	    $sql = $wpdb->prepare(
	        "INSERT INTO " . AICC_BULK_TABLE . " (type, amount, tokens, cost, budget, model, ids_processed) VALUES (%s, %d, %d, %f, %d, %s, %s)",
	        $initialData['type'], $initialData['amount'], $initialData['tokens'], $initialData['cost'], $initialData['budget'], $initialData['model'], $initialData['ids_processed']
	    );
	    $wpdb->query($sql);

	    $recordId = $wpdb->insert_id;

	    set_transient('aicc_current_bulk_process_id', $recordId, 604800);

	    $processedPosts = get_option('aicc_bulk_processed_posts', array());

	    if ($exclude_already) {
	        $combined_exclude_ids = array_unique(array_merge($exclude_ids, $processedPosts));
	    } else {
	        $combined_exclude_ids = $exclude_ids;
	    }

	    $combined_exclude_ids = array_filter($combined_exclude_ids, function($id) {
	        return is_numeric($id) && $id > 0;
	    });

	    $where_conditions = ['post_type = %s', 'post_status = \'publish\'']; 
	    $query_args = [$post_type]; 

	    if (!empty($include_ids)) {
	        $include_placeholders = implode(', ', array_fill(0, count($include_ids), '%d'));
	        $where_conditions[] = "ID IN ($include_placeholders)";
	        $query_args = array_merge($query_args, $include_ids);
	    }

	    if (!empty($combined_exclude_ids)) {
	        $exclude_placeholders = implode(', ', array_fill(0, count($combined_exclude_ids), '%d'));
	        $where_conditions[] = "ID NOT IN ($exclude_placeholders)";
	        $query_args = array_merge($query_args, $combined_exclude_ids);
	    }

	    $sql = "SELECT ID FROM {$wpdb->posts} WHERE " . implode(' AND ', $where_conditions);

	    $post_ids = $wpdb->get_col($wpdb->prepare($sql, $query_args));

	    if (empty($post_ids)) {
	        delete_transient('aicc_current_bulk_process_id');
	        $error_message = 'No hay ningún artículo para procesar. Finalización del proceso de curación masiva de contenidos.';
	        self::aicc_log_register(0, 'aviso', $error_message, 'aicc_bulk_curation_content');                
	        return;                
	    }

	    update_option('aicc_bulk_total_posts', count($post_ids));

	  	$execution_id = current_time('Ymd_His');

	    foreach ($post_ids as $post_id) {
	        $data = [
	            'post_id' => $post_id,
	            'from' => 'video',
	            'post_type' => $post_type,
	            'type' => $type,	            
	            'prompt' => $prompt,
	            'budget' => $budget,
	            'discard_already_video' => $discard_already_video,
	            'execution_id' => $execution_id
	        ];
	        $this->bulk->push_to_queue($data);
	    }

	    $this->bulk->save()->dispatch();
	                                
	    wp_die();
	}



	public function aicc_process_bulkt_content() 
	{

		$message = 'Inicio del proceso de curación masiva de metaetiquetas.';
		self::aicc_log_register('', 'aviso', $message, 'aicc_process_bulkt_content');	

		global $wpdb;

		delete_transient('aicc_fatal_error');
				
		// Obtener el estado de los transients
		$is_manual_running = get_transient('aicc_is_running');
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		// Verificar si alguno de los dos está activo
		if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
		    $is_running = true;
		} else {
		    $is_running = false;
		}

		if ( $is_running )
		{
			return;
		}
		
		check_ajax_referer('aicc_process_bulkt_content', 'nonce');
		
		$post_type = isset($_POST['aicc_bulkt_post_type']) ? $_POST['aicc_bulkt_post_type'] : 'post';
 	   	$overwrite = isset($_POST['aicc_bulkt_overwrite']) && $_POST['aicc_bulkt_overwrite'] === '1' ? true : false;
		$budget = !empty($_POST['aicc_bulkt_budget']) ? absint($_POST['aicc_bulkt_budget']) : 100;		 	   				
		$include = isset($_POST['aicc_bulkt_include']) ? $_POST['aicc_bulkt_include'] : '';
		$include_ids = !empty($include) ? explode(',', $include) : array();		
		$exclude = isset($_POST['aicc_bulkt_exclude']) ? $_POST['aicc_bulkt_exclude'] : '';
		$exclude_ids = !empty($exclude) ? explode(',', $exclude) : array();	
		$seo_plugin = isset($_POST['aicc_bulkt_seo_plugin']) ? $_POST['aicc_bulkt_seo_plugin'] : '';
       	$exclude_already = isset($_POST['aicc_bulkt_exclude_already']) && $_POST['aicc_bulkt_exclude_already'] === '1' ? true : false;

		if (empty($post_type) || empty($seo_plugin)) {
			$error_message = 'Hay algún dato incompleto: ' .
				'Tipo de contenido: ' . $_POST['aicc_bulkt_post_type'] . ', ' .
				'Plugin de SEO: ' . $_POST['aicc_bulkt_seo_plugin'] . ', ' .
			self::aicc_log_register('', 'error', $error_message, 'aicc_process_bulkt_content');					
			wp_die();
		}

	    update_option('aicc_bulkt_post_type', $post_type, '', 'yes');
	    update_option('aicc_bulkt_overwrite', $overwrite ? '1' : '0', '', 'yes');
	    update_option('aicc_bulkt_include', $include, '', 'yes');
	    update_option('aicc_bulkt_exclude', $exclude, '', 'yes');
	    update_option('aicc_bulkt_exclude_already', $exclude_already ? '1' : '0', '', 'yes');

		if ($budget > 0 && $budget < 100) {
		   	update_option('aicc_bulkt_budget', $budget, '', 'yes');
		}else{
			delete_option('aicc_bulkt_budget');						
		}	

		$ajust_model = get_option('aicc_ajust_model', 1);

	    if ($ajust_model) {
	      	$model = 'gpt-4o-mini';
	   	} else {
			$model = get_option('aicc_model', 'gpt-4o-mini');

			// Actualiza modelos antiguos del plugin a los nuevos modelos
			$models = [
			    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
			    'gpt-4' => 'gpt-4-1106-preview',
			    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
			];

			$model = $models[$model] ?? $model;
		}

		$initialData = [
		    'type' => 'Metaetiqueta',
		    'amount' => 0,
		    'tokens' => 0,
		    'cost' => 0, 
		    'budget' => $budget,			    
		    'model' => $model,
		    'ids_processed' => ''
		];
		
		$sql = $wpdb->prepare(
		    "INSERT INTO " . AICC_BULK_TABLE . " (type, amount, tokens, cost, budget, model, ids_processed) VALUES (%s, %d, %d, %f, %d, %s, %s)",
		    $initialData['type'], $initialData['amount'], $initialData['tokens'], $initialData['cost'], $initialData['budget'], $initialData['model'], $initialData['ids_processed']
		);
		$wpdb->query($sql);
		$recordId = $wpdb->insert_id;
			
		set_transient('aicc_current_bulk_process_id', $recordId, 604800);

		$processedPosts = get_option('aicc_bulk_processed_posts_metatags', array());

		if ($exclude_already) {
		    $combined_exclude_ids = array_unique(array_merge($exclude_ids, $processedPosts));
		} else {
		    $combined_exclude_ids = $exclude_ids;
		}

		$combined_exclude_ids = array_filter($combined_exclude_ids, function($id) {
		    return is_numeric($id) && $id > 0;
		});

		$placeholders = [];
		$where_conditions = ['post_type = %s', 'post_status = \'publish\'']; 
		$query_args = [$post_type]; 

		if (!empty($include_ids)) {
		    $include_placeholders = implode(', ', array_fill(0, count($include_ids), '%d'));
		    $where_conditions[] = "ID IN ($include_placeholders)";
		    $query_args = array_merge($query_args, $include_ids);
		}

		if (!empty($combined_exclude_ids)) {
		    $exclude_placeholders = implode(', ', array_fill(0, count($combined_exclude_ids), '%d'));
		    $where_conditions[] = "ID NOT IN ($exclude_placeholders)";
		    $query_args = array_merge($query_args, $combined_exclude_ids);
		}

		$sql = "SELECT COUNT(*) FROM {$wpdb->posts} WHERE " . implode(' AND ', $where_conditions);

		$total_posts = $wpdb->get_var($wpdb->prepare($sql, $query_args));

		if ($total_posts <= 0) {
			delete_transient('aicc_current_bulk_process_id');
			$error_message = 'No hay ningún artículo para procesar. Finalizacion del proceso de curación masiva de metaetiquetas.';
			self::aicc_log_register(0, 'aviso', $error_message, 'aicc_bulk_curation_content');	        	
	       	return;				
		}

		update_option('aicc_bulk_total_posts', $total_posts);

		$posts_per_batch = 10;
		$total_batches = ceil($total_posts / $posts_per_batch);

	  	$execution_id = current_time('Ymd_His');

		for ($batch = 0; $batch < $total_batches; $batch++) {
		    $offset = $batch * $posts_per_batch;			
			$data = array(
				'from' => 'metatags',
				'post_type' => $post_type,
				'overwrite' => $overwrite,
				'budget' => $budget,
				'include' => $include,
				'exclude' => $exclude,
				'seo_plugin' => $seo_plugin,
				'exclude_already' => $exclude_already,
				'offset' => $offset,
		    	'limit' => $posts_per_batch,
		    	'execution_id' => $execution_id 
			);

			$this->bulk->push_to_queue($data);
		}

		$this->bulk->save()->dispatch();
											
		wp_die();
	}	

	public function aicc_process_bulkr_content() 
	{
		delete_transient('aicc_fatal_error');
				
		// Obtener el estado de los transients
		$is_manual_running = get_transient('aicc_is_running');
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		// Verificar si alguno de los dos está activo
		if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
		    $is_running = true;
		} else {
		    $is_running = false;
		}

		if ( $is_running )
		{
			return;
		}
		
		check_ajax_referer('aicc_process_bulkr_content', 'nonce');

		$recovery_action = isset($_POST['aicc_recovery_action']) ? $_POST['aicc_recovery_action'] : '';

		if ($recovery_action === null) {
			$error_message = 'No hay una acción seleccinoada.';
			self::aicc_log_register(0, 'aviso', $error_message, 'aicc_process_bulkr_content');	        	
	       	return;	
		}

		$data = array(
			'from' => 'recovery',
			'action' => $recovery_action
		);

		$this->bulk->push_to_queue($data);
		$this->bulk->save()->dispatch();
											
		wp_die();
	}	

	public function aicc_check_bulk_running() 
	{
		// Obtener el estado de los transients
		$is_manual_running = get_transient('aicc_is_running');
		$is_bulk_running = get_transient('aicc_is_bulk_running');
		$is_recovery_running = get_transient('aicc_is_recovery_running');

		// Verificar si alguno de los dos está activo
		if ( $is_manual_running || $is_bulk_running || $is_recovery_running) {
		    $is_running = true;
		} else {
		    $is_running = false;
		}

		if ( $is_running ) 
		{
			echo json_encode(['check_status' => 'processing', 'message' => 'Hay un proceso ejecutándose en segundo plano. Espera que termine para ejecutar una nueva curación masiva.']);
			wp_die();		
		}
		
		$apikey_openai = get_option('aicc_options_openai');
		
		if ( empty($apikey_openai) )
		{
			echo json_encode(['check_status' => 'missing', 'message' => 'Es necesario agregar una clave de API de OpenAI para ejecutar la curación de contenidos.']);
			wp_die();		
		}
		
	}

	public function aicc_bulk_recover_from_autosave($item) {
	    global $wpdb;

	    set_transient('aicc_is_recovery_running', true, 604800);

	    $initialData = [
	        'type' => 'Recuperación',
	        'amount' => 0,
	        'tokens' => 0,
	        'cost' => 0,
	        'model' => '',
	        'ids_processed' => ''
	    ];

	    $sql = $wpdb->prepare(
	        "INSERT INTO " . AICC_BULK_TABLE . " (type, amount, tokens, cost, model, ids_processed) VALUES (%s, %d, %d, %f, %s, %s)",
	        $initialData['type'], $initialData['amount'], $initialData['tokens'], $initialData['cost'], $initialData['model'], $initialData['ids_processed']
	    );
	    $wpdb->query($sql);

	    $recordId = $wpdb->insert_id;
	    set_transient('aicc_current_bulk_process_id', $recordId, 604800);

	    $action = $item['action'];

	    // Obtener historial de ejecuciones
	    $processedPostsHistory = get_option('aicc_bulk_processed_posts_history', []);

	    // Seleccionar posts a restaurar y obtener el execution_id según la acción
	    if ($action == 'recover_last') {
	        $lastExecution = end($processedPostsHistory);
	        $processedPosts = $lastExecution['post_ids'];
	        $execution_id = $lastExecution['execution_id'];
	        
	    } elseif ($action == 'recover_all') {
	        $processedPosts = [];
	        foreach ($processedPostsHistory as $execution) {
	            $processedPosts = array_merge($processedPosts, $execution['post_ids']);
	        }
	        $execution_id = null;  // No se usa en "recover_all"

	    } elseif (is_numeric($action) && isset($processedPostsHistory[$action])) {
	        $selected_execution = $processedPostsHistory[$action];
	        $processedPosts = $selected_execution['post_ids'];
	        $execution_id = $selected_execution['execution_id'];

	    } else {
	        return;  // Salimos si no es una opción válida
	    }

	    // Recuperar y restaurar posts procesados
	    if ($processedPosts):
	        $totalPosts = count($processedPosts);
	        set_transient('aicc_total_recover_posts', $totalPosts, 604800);

	        $counter = 0;

	        foreach ($processedPosts as $post_id) {
	            // Obtener el autoguardado específico por execution_id
	            $autosave = $wpdb->get_row($wpdb->prepare("
		            SELECT p.* 
		            FROM {$wpdb->posts} p
		            INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
		            WHERE p.post_type = 'revision'
		            AND p.post_parent = %d
		            AND pm.meta_key = '_autosave_execution_id'
		            AND pm.meta_value = %s
		            ORDER BY p.post_date DESC
		            LIMIT 1
		        ", $post_id, $execution_id));

		        // Log para verificar la consulta
		        $error_message = 'Consulta realizada para ID ' . $post_id . ' con execution_id: ' . $execution_id;
		        self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_recover_from_autosave');

	            if ($autosave) {
	                $post_data = [
	                    'ID'           => $post_id,
	                    'post_content' => $autosave->post_content,
	                ];

	                // Restaurar el post desde el autoguardado específico
	                $result = wp_update_post($post_data, true);

	                $error_message = 'Restaurada última revisión para ID ' . $post_id;
	                self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_recover_from_autosave');

	                if (is_wp_error($result)) {
	                    $error_message = 'Error al restaurar el post con ID ' . $post_id . ': ' . $result->get_error_message();
	                    self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_recover_from_autosave');
	                }
	            }

	            $counter++;
	            set_transient('aicc_processed_recovery_posts', $counter, 604800);

	            $ids_processed[$post_id] = '';
	            $serializedIdsProcessed = serialize($ids_processed);

	            $sqlUpdate = $wpdb->prepare(
	                "UPDATE " . AICC_BULK_TABLE . " SET amount = %d, ids_processed = %s WHERE id = %d",
	                $counter, $serializedIdsProcessed, $recordId
	            );
	            $wpdb->query($sqlUpdate);

	            if ($wpdb->last_error) {
	                $error_message = 'Error al insertar en la base de datos: ' . $wpdb->last_error;
	                self::aicc_log_register($post_id, 'error', $error_message, 'aicc_bulk_recover_from_autosave');
	            }

	            // Pausar cada 50 posts para evitar sobrecarga
	            if ($counter % 50 == 0) {
	                $is_canceled = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'aicc_cancel_bulk_process' LIMIT 1");
	                $is_canceled = $is_canceled === '1';
	                if ($is_canceled) {
	                    break;
	                }
	                sleep(1);
	            }
	        }

	    endif;

	    // Limpieza final
	    if ($action == 'recover_all'){
	        delete_option('aicc_bulk_processed_posts');
	    }

	    set_transient('aicc_end_bulk', true, 604800);
	    delete_transient('aicc_is_recovery_running');
	    delete_option('aicc_cancel_bulk_process');
	    delete_transient('aicc_current_bulk_process_id');
	    delete_transient('aicc_processed_recovery_posts');
	}




	function aicc_detect_seo_plugin() {
	    // Obtener todos los plugins activos
	    $plugins_activos = apply_filters('active_plugins', get_option('active_plugins'));

	    // Lista de plugins de SEO para verificar
	    $plugins_seo = [
	        'wordpress-seo/wp-seo.php' => 'Yoast SEO',
	        'seo-by-rank-math/rank-math.php' => 'Rank Math',
	        'all-in-one-seo-pack/all_in_one_seo_pack.php' => 'All in One SEO Pack',
	        'autodescription/autodescription.php' => 'The SEO Framework'
	    ];

	    // Verificar cada plugin de SEO
	    foreach ($plugins_seo as $path => $name) {
	        if (in_array($path, $plugins_activos)) {
	            return $name; // Devuelve el nombre del primer plugin de SEO activo encontrado
	        }
	    }

	}


/*
     * Función para desbloquear proceso
     */
    function aicc_unlock_process() {

        if ( !current_user_can('manage_options') ) {
            wp_die('No tienes permiso para realizar esta acción.');
        }        
        
        if (isset($_POST['type']) && ( $_POST['type'] == 'manual_curation' || $_POST['type'] == 'masive_curation') ) {
            $type = $_POST['type'];
        }else{
            return;
        }

        $log_message = "Se ejecutó un desbloqueo manual de proceso.";
        self::aicc_log_register('', 'aviso', $log_message, 'aicc_unlock_process');        

        if ($type == 'manual_curation') {

			wp_clear_scheduled_hook( 'aicc_manual_cron' );
            
			$timestamp = wp_next_scheduled( 'aicc_manual_cron' );
			wp_unschedule_event( $timestamp, 'aicc_manual_cron' );
            
			delete_site_transient('aicc_manual_process_lock');			
 
        }

        if ($type == 'masive_curation') {
           
			if(isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
			    $this->bulk->cancel();
			}
            
            sleep(1);

			wp_clear_scheduled_hook( 'aicc_bulk_cron' );
            
			$timestamp = wp_next_scheduled( 'aicc_bulk_cron' );
			wp_unschedule_event( $timestamp, 'aicc_bulk_cron' );

			delete_site_transient('aicc_bulk_process_lock');	

			$data = array('from' => 'unlock');
					
			if (is_object($this->bulk) && $this->bulk !== null)
			{
				$this->bulk->push_to_queue($data);
				$this->bulk->save()->dispatch();
			}            
         
        }

		delete_transient('aicc_is_running');		

		delete_transient('aicc_is_bulk_running');		
		delete_transient('aicc_end_bulk');
		delete_transient('aicc_bulk_discarded_count');	
		delete_transient('aicc_total_recover_posts');
		delete_transient('aicc_processed_recovery_posts');
		delete_transient('aicc_current_bulk_process_id');	
		delete_transient('aicc_is_recovery_running');
		delete_transient('aicc_error_bulk');		

		// Transients que se usaban antes de la versión 2.2.0 se eliminan
		delete_transient('aicc_bulk_processed_count');
		delete_transient('aicc_bulk_ids_processed');
		delete_transient('aicc_cancel_bulk_process');	

		// Transients que siguen usando las funciones de imágenes y metaetiquetas
		delete_transient('aicc_bulk_total_posts');

		delete_option('aicc_cancel_bulk_process');	
		delete_option('aicc_bulk_total_cost');
		delete_option('aicc_bulk_total_tokens');
		delete_option('aicc_bulk_total_posts');	
		delete_option('aicc_bulk_processed_count');
		delete_option('aicc_bulk_ids_processed');

        sleep(1);

		wp_cache_flush();

        wp_die();

    }

	/*
     * Funciones para procesar y redimensionar la imagen generada por OpenAI
     */
    public static function aicc_download_image_to_temp($url) {
        $temp_dir = sys_get_temp_dir(); 
        $temp_file = tempnam($temp_dir, 'image'); 

        $chimg = curl_init($url);
        curl_setopt($chimg, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($chimg, CURLOPT_FOLLOWLOCATION, 1);
        $data = curl_exec($chimg);
        $curlError = curl_error($chimg); 
        curl_close($chimg);

        if ($data) {
            file_put_contents($temp_file, $data);

            // Verificar el tamaño del archivo descargado
            $max_size = self::aicc_return_bytes(ini_get('upload_max_filesize')); // Obtiene el tamaño máximo permitido
            if (filesize($temp_file) > $max_size) {
                self::aicc_log_register('', 'error', "El archivo descargado excede el tamaño máximo permitido de " . ini_get('upload_max_filesize'), 'aicc_download_image_to_temp');
            }

            return $temp_file;
        } else {
            self::aicc_log_register('', 'error', "Error al descargar la imagen: $curlError", 'aicc_download_image_to_temp');
            return false;
        }
    }    

    public static function aicc_process_image($image_path, $size, $motor = 'openai', $aspect_ratio = '16:9') {
        
        if (!file_exists($image_path)) {
            self::aicc_log_register('', 'error', "Archivo no encontrado: $image_path", 'aicc_process_image');
            return false;
        }   
             
        $file_info = wp_check_filetype($image_path);
        $mime_type = $file_info['type'];

        if (extension_loaded("fileinfo")) {
            $file_info = finfo_open(FILEINFO_MIME_TYPE);
            $mime_type = finfo_file($file_info, $image_path);
            finfo_close($file_info);
        } else {
            $error_message = 'La extensión Fileinfo no está habilitada en este servidor.';
            self::aicc_log_register('', 'error', $error_message, 'aicc_process_image');
            return false;            
        }

        // Asegurarse de que el archivo es una imagen antes de continuar
        if (!in_array($mime_type, ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {
            $error_message = 'Tipo de archivo no soportado: ' . $mime_type;
            self::aicc_log_register('', 'error', $error_message, 'aicc_process_image');
            return false;
        }

        $image_editor = wp_get_image_editor($image_path);
        if (is_wp_error($image_editor)) {
            $error_message = 'Error cargando la imagen en la biblioteca de medios';
            self::aicc_log_register('', 'error', $error_message, 'aicc_process_image');
            return false;
        }

        if ($motor == 'openai') {

			$alto_nuevo = 1024;
			$ancho_nuevo = 585;	

			if ($size == '1024x1024') {
				$ancho_nuevo = 1024;
				$alto_nuevo = 1024;
			} elseif ($size == '1792x1024') {
				$ancho_nuevo = 1024;
				$alto_nuevo = 585;					            		
			} elseif ($size == '1024x1792') {
				$alto_nuevo = 1024;
				$ancho_nuevo = 585;
			}

	        // Redimensionar la imagen
	        $image_editor->resize($ancho_nuevo, $alto_nuevo, false);

	        $resized_image = $image_editor->save($image_path, 'image/webp', 80);

        } else {

			$alto_nuevo = 1024;
			$ancho_nuevo = 576;	

			if ($aspect_ratio == '1:1') {
				$ancho_nuevo = 1024;
				$alto_nuevo = 1024;
			} elseif ($aspect_ratio == '16:9') {
				$ancho_nuevo = 1024;
				$alto_nuevo = 576;					            		
			} elseif ($aspect_ratio == '9:16') {
				$alto_nuevo = 1024;
				$ancho_nuevo = 576;
			}

	        // Redimensionar la imagen
	        $image_editor->resize($ancho_nuevo, $alto_nuevo, false);

        	$resized_image = $image_editor->save($image_path);

        }

        if (is_wp_error($resized_image)) {
            $error_message = 'Error redimensionando la imagen';
            self::aicc_log_register('', 'error', $error_message, 'aicc_process_image');
            return false;
        }

        return $resized_image['path'];
    }

    
    public static function aicc_move_image_to_media($image_path, $post_id, $title_h1) {
        // Cargar los archivos necesarios de WordPress solo si las funciones no están ya cargadas
        if (!function_exists('media_handle_sideload')) {
            require_once(ABSPATH . 'wp-admin/includes/image.php');
            require_once(ABSPATH . 'wp-admin/includes/file.php');
            require_once(ABSPATH . 'wp-admin/includes/media.php');
        }

        // Limpiar y formatear el título para el nombre del archivo
        $image_name = strtolower($title_h1); // Convertir a minúsculas
        $image_name = remove_accents($image_name); // Eliminar tildes y caracteres especiales
        $image_name = preg_replace('/\s+/', '-', $image_name); // Reemplazar espacios con guiones
        $image_name = preg_replace('/[^a-z0-9\-]/', '', $image_name); // Eliminar caracteres no deseados
        $image_name = trim($image_name, '-'); // Eliminar guiones al inicio y al final            
        
        if (!file_exists($image_path)) {
            $error_message = "Archivo no encontrado en: " . $image_path;
            self::aicc_log_register($post_id, 'error', $error_message, 'aicc_move_image_to_media');
            return false;
        }

        // Verificar el tamaño del archivo procesado
        $max_size = self::aicc_return_bytes(ini_get('upload_max_filesize'));
        if (filesize($image_path) > $max_size) {
            $error_message = "El archivo procesado excede el tamaño máximo permitido de " . ini_get('upload_max_filesize');
            self::aicc_log_register($post_id, 'error', $error_message, 'aicc_move_image_to_media');
        }

        $file_ext = pathinfo($image_path, PATHINFO_EXTENSION);
        $new_image_path = dirname($image_path) . '/' . $image_name . '.' . $file_ext;

        if (!is_writable(dirname($image_path)) || !is_writable(dirname($new_image_path))) {
            $error_message = "No hay permisos de escritura en los directorios de origen o destino.";
            self::aicc_log_register($post_id, 'error', $error_message, 'aicc_move_image_to_media');
            return false;
        }
        
        if (!rename($image_path, $new_image_path)) {
            $error_message = "Error al renombrar/mover el archivo de '$image_path' a '$new_image_path'. Verifique permisos y existencia de ambos directorios.";
            self::aicc_log_register($post_id, 'error', $error_message, 'aicc_move_image_to_media');
            return false;
        }

        $file_array = array(
            'name' => basename($new_image_path),
            'tmp_name' => $new_image_path,
        );

        $attach_id = media_handle_sideload($file_array, $post_id);

        if (is_wp_error($attach_id)) {
            $error_message = "Error en media_handle_sideload: " . $attach_id->get_error_message();
            self::aicc_log_register($post_id, 'error', $error_message, 'aicc_move_image_to_media');
            return false;
        }

        return $attach_id;
    }


    // Función para convertir valores de php.ini a bytes
    public static function aicc_return_bytes($val) {
        $val = trim($val);
        if (is_numeric($val)) {
            return $val;
        }
        $number = (float) filter_var($val, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND);
        $last = strtolower($val[strlen($val) - 1]);
        switch ($last) {
            case 'g':
                $number *= 1024 * 1024 * 1024;
                break;
            case 'm':
                $number *= 1024 * 1024;
                break;
            case 'k':
                $number *= 1024;
                break;
        }
        return $number;
    }


	public function aicc_extractHeaders($content) {
	    $headers = array();
	    $headerTags = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');

	    foreach ($headerTags as $tag) {
	        $pattern = "/<$tag.*?>(.*?)<\/$tag>/";
	        preg_match_all($pattern, $content, $matches);
	        foreach ($matches[1] as $match) {
	            $headers[] = trim(strip_tags($match));
	        }
	    }

	    return $headers;
	}

	public function aicc_cancel_bulk_process() {
	    update_option('aicc_cancel_bulk_process', true);
	    wp_die();
	}
	
	
	/*
	 * Función para calcular costos de curaciones masivas
	 */
	public function aicc_calculate_total_cost($post_type, $include, $exclude_already, $type, $quality, $resolution, $excludeNoPublish, $curationType, $source, $intraType, $motor, $replace) {

    	global $wpdb;

		if ($type == 'content') {
   
		    $costoTotal = 0;

			$ajust_model = get_option('aicc_ajust_model', 1);

		    if ($ajust_model) {
		    	if ($intraType == 'improve') {
		      		$model = 'gpt-4-1106-preview'; 
		    	} else {
		      		$model = 'gpt-4o-mini'; 		    		
		    	}
		   	} else {
				$model = get_option('aicc_model', 'gpt-4o-mini');

				// Actualiza modelos antiguos del plugin a los nuevos modelos
				$models = [
				    'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
				    'gpt-4' => 'gpt-4-1106-preview',
				    'gpt-4-0125-preview' => 'gpt-4-1106-preview'
				];

				$model = $models[$model] ?? $model;
			}

	        $costsPerToken = [
			    'gpt-3.5-turbo-0125' => ['input' => 0.0000005, 'output' => 0.0000015],
			    'gpt-4-1106-preview' => ['input' => 0.00001, 'output' => 0.00003],
			    'gpt-4o' => ['input' => 0.0000025, 'output' => 0.00001],
			    'gpt-4o-mini' => ['input' => 0.00000015, 'output' => 0.0000006],
			    'gpt-4.1-nano' => ['input' => 0.00000010, 'output' => 0.0000004],
			    'gpt-4.1-mini' => ['input' => 0.00000040, 'output' => 0.0000016],
	        ];

	        // Asegurar que el modelo seleccionado esté disponible, ajustar si es necesario
	        if (!isset($costsPerToken[$model])) {
	            $model = 'gpt-3.5-turbo-0125'; // Modelo por defecto si el seleccionado no está definido
	        }

	        $costPerTokenInput = $costsPerToken[$model]['input'];
	        $costPerTokenOutput = $costsPerToken[$model]['output'];

	        // Asumiendo 0.75 palabras por token como promedio
	        $tokensPorPalabra = 1 / 0.75;

	        $exclude_ids = [];
			$include_ids = !empty($include) ? explode(',', $include) : array();

		    // Posts ya procesados
		    if ($exclude_already) {
		    	$processedPosts = get_option('aicc_bulk_processed_posts', []);
		        $exclude_ids = array_unique($processedPosts);
		    }

		    $exclude_ids = array_filter($exclude_ids, function($id) {
		        return is_numeric($id) && $id > 0;
		    });

		    // Preparar condiciones WHERE para la consulta
		    $where_conditions = ['post_type = %s', 'post_status = \'publish\''];
		    $query_args = [$post_type];

		    // Incluir y excluir IDs específicos
		    if (!empty($include_ids)) {
		        $include_placeholders = implode(', ', array_fill(0, count($include_ids), '%d'));
		        $where_conditions[] = "ID IN ($include_placeholders)";
		        $query_args = array_merge($query_args, $include_ids);
		    }

		    if (!empty($exclude_ids)) {
		        $exclude_placeholders = implode(', ', array_fill(0, count($exclude_ids), '%d'));
		        $where_conditions[] = "ID NOT IN ($exclude_placeholders)";
		        $query_args = array_merge($query_args, $exclude_ids);
		    }

			$urls_metrics = get_transient('aicc_all_urls_metrics');

			if (!$urls_metrics) {
			    $this->aicc_search_console_first();
			    $urls_metrics = get_transient('aicc_all_urls_metrics');
			}

			if ($urls_metrics !== false) {
			   	$urls_metrics_array = maybe_unserialize($urls_metrics);

			    if (is_array($urls_metrics_array)) {
			        $posts_ids_with_data = $this->aicc_get_post_ids_from_urls(array_keys($urls_metrics_array));

			    	if (!empty($posts_ids_with_data)) {
			            $posts_ids_list = implode(',', array_map('intval', $posts_ids_with_data));
			            $where_conditions[] = "ID IN ($posts_ids_list)";
			        }
			    }
			}

			$sql = "SELECT SUM(LENGTH(post_content) - LENGTH(REPLACE(post_content, ' ', '')) + 1) AS total_words, COUNT(*) AS total_posts FROM {$wpdb->posts} WHERE " . implode(' AND ', $where_conditions);

			// Ejecutar consulta y calcular el costo total
			$results = $wpdb->get_row($wpdb->prepare($sql, ...$query_args));
			$total_words = $results->total_words;
			$total_posts = $results->total_posts;

	        $totalTokens = $total_words * $tokensPorPalabra;
	        $costoInput = $totalTokens * $costPerTokenInput;
	        $costoOutput = $totalTokens * $costPerTokenOutput;
	        $costoTotal = $costoInput + $costoOutput;
	    } elseif ($type == 'image') {

			$post_statuses_array = $excludeNoPublish ? ['publish'] : ['publish', 'draft', 'pending', 'future'];
			$post_statuses_placeholders = implode(',', array_fill(0, count($post_statuses_array), '%s'));
			$include_ids = !empty($include) ? explode(',', $include) : array();


			if ($curationType == 'featured' && $source == 'ia') {
				if ($motor != 'openai') {
					$costPerImage = 0.003;
				} else {
					$resolutionKey = $resolution === '1024x1024' ? '1024x1024' : 'other';
					$imageCosts = [
					    'standard' => ['1024x1024' => 0.04, 'other' => 0.08],
					    'hd' => ['1024x1024' => 0.08, 'other' => 0.12]
					];
					$costPerImage = $imageCosts[$quality][$resolutionKey];
				}
			} else {
				$costPerImage = 0.001;		
			}

			$includeCondition = '';
			$placeholders = array_merge([$post_type], $post_statuses_array);

			if (!empty($include_ids)) {
			    $include_ids_placeholder = implode(',', array_fill(0, count($include_ids), '%d'));
			    $includeCondition = "AND p.ID IN ($include_ids_placeholder)";
			    $placeholders = array_merge($placeholders, $include_ids);
			} else {
				$includeCondition = '';
			}

			if ($curationType == 'featured') {

				if (!$replace) {
				    $sql = "
				        SELECT COUNT(*) 
				        FROM {$wpdb->posts} p
				        WHERE p.post_type = %s
				        AND p.post_status IN ($post_statuses_placeholders)
				        $includeCondition
				        AND NOT EXISTS (
				            SELECT 1 
				            FROM {$wpdb->postmeta} pm
				            WHERE pm.post_id = p.ID
				            AND pm.meta_key = '_thumbnail_id'
				        )
				    ";
				} else {
				    $sql = "
				        SELECT COUNT(*) 
				        FROM {$wpdb->posts} p
				        WHERE p.post_type = %s
				        AND p.post_status IN ($post_statuses_placeholders)
				        $includeCondition
				    ";
				}
			} else {
			    $sql = "
			        SELECT COUNT(*) 
			    FROM {$wpdb->posts} p
			    WHERE p.post_type = %s
			    AND p.post_status IN ($post_statuses_placeholders)
			    $includeCondition
			    AND p.post_content NOT REGEXP '<img[^>]*>'
			    ";
			}

			// Preparar la consulta
			$sql_prepared = $wpdb->prepare($sql, ...$placeholders);

			// Obtener el resultado
			$total_posts = $wpdb->get_var($sql_prepared);

			// Calcular el costo total
			$costoTotal = $costPerImage * $total_posts;

		} elseif ($type == 'metatags' || $type == 'video') {

		    $ajust_model = get_option('aicc_ajust_model', 1);

		    if ($ajust_model) {
		        $model = 'gpt-4o-mini'; 
		    } else {
		        $model = get_option('aicc_model', 'gpt-4o-mini');

		        // Actualiza modelos antiguos a las versiones nuevas
		        $models = [
		            'gpt-3.5-turbo' => 'gpt-3.5-turbo-0125',
		            'gpt-4' => 'gpt-4-1106-preview',
		            'gpt-4-0125-preview' => 'gpt-4-1106-preview'
		        ];

		        $model = $models[$model] ?? $model;
		    }

		    $costsPerToken = [
			    'gpt-3.5-turbo-0125' => ['input' => 0.0000005, 'output' => 0.0000015],
			    'gpt-4-1106-preview' => ['input' => 0.00001, 'output' => 0.00003],
			    'gpt-4o' => ['input' => 0.0000025, 'output' => 0.00001],
			    'gpt-4o-mini' => ['input' => 0.00000015, 'output' => 0.0000006],
			    'gpt-4.1-nano' => ['input' => 0.00000010, 'output' => 0.0000004],
			    'gpt-4.1-mini' => ['input' => 0.00000040, 'output' => 0.0000016],
		    ];

		    // Asegurar que el modelo seleccionado esté disponible
		    if (!isset($costsPerToken[$model])) {
		        $model = 'gpt-4o-mini'; // Modelo por defecto si el seleccionado no está definido
		    }

		    $costPerTokenInput = $costsPerToken[$model]['input'];
		    $costPerTokenOutput = $costsPerToken[$model]['output'];
		    $tokensPorPalabra = 1 / 0.75; // Asumiendo 0.75 palabras por token como promedio

		    // IDs a incluir y excluir
		    $exclude_ids = [];
		    $include_ids = !empty($include) ? explode(',', $include) : array();

		    if ($exclude_already) {
		        $processedPosts = get_option('aicc_bulk_processed_posts', []);
		        $exclude_ids = array_unique($processedPosts);
		    }

		    $exclude_ids = array_filter($exclude_ids, function($id) {
		        return is_numeric($id) && $id > 0;
		    });

		    if ($type == 'video') {
		    	$where_conditions = ['post_type = %s'];
		    } else {
		    	$where_conditions = ['post_type = %s', 'post_status = \'publish\''];		    	
		    }
		    $query_args = [$post_type];

		    if (!empty($include_ids)) {
		        $include_placeholders = implode(', ', array_fill(0, count($include_ids), '%d'));
		        $where_conditions[] = "ID IN ($include_placeholders)";
		        $query_args = array_merge($query_args, $include_ids);
		    }

		    if (!empty($exclude_ids)) {
		        $exclude_placeholders = implode(', ', array_fill(0, count($exclude_ids), '%d'));
		        $where_conditions[] = "ID NOT IN ($exclude_placeholders)";
		        $query_args = array_merge($query_args, $exclude_ids);
		    }

		    $sql = "SELECT COUNT(*) AS total_posts FROM {$wpdb->posts} WHERE " . implode(' AND ', $where_conditions);

		    // Ejecutar consulta y calcular el costo total
		    $total_posts = $wpdb->get_var($wpdb->prepare($sql, ...$query_args));

		    // Calcular tokens y costos
			$totalTokens = 200 * $tokensPorPalabra * $total_posts;
			$costoInput = $totalTokens * $costPerTokenInput;

			$outputTokens = ($type == 'metatags') ? 100 : 50;
			$costoOutput = ($outputTokens * $tokensPorPalabra * $total_posts) * $costPerTokenOutput;

		    // Agregar el costo fijo para ValueSERP en caso de tipo 'video'
		    $costoTotal = $costoInput + $costoOutput + ($type === 'video' ? 0.0025 * $total_posts : 0);
		}

	    if (!$total_posts || empty($total_posts)) {
	    	$total_posts = 0;
	    }

		$formatted = rtrim(rtrim(number_format($costoTotal, 5, '.', ''), '0'), '.');
		return $formatted . ' (' . $total_posts . ' contenidos)';

	}

	private function aicc_get_post_ids_from_urls($urls) {
	    global $wpdb;
	    $post_ids = [];

	    foreach ($urls as $url) {
	        $post_id = url_to_postid($url);
	        if ($post_id) {
	            $post_ids[] = $post_id;
	        }
	    }

	    return $post_ids;
	}

	public function aicc_return_total_cost() {
	    $post_type = isset($_POST['postType']) ? sanitize_text_field($_POST['postType']) : 'post';
	    $type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : 'content';
	    $quality = isset($_POST['quality']) ? sanitize_text_field($_POST['quality']) : '';
	    $resolution = isset($_POST['resolution']) ? sanitize_text_field($_POST['resolution']) : '';
		$include = isset($_POST['include']) ? $_POST['include'] : '';
       	$exclude_already = isset($_POST['excludeAlready']) && $_POST['excludeAlready'] === '1' ? true : false;
       	$excludeNoPublish = isset($_POST['excludeNoPublish']) && $_POST['excludeNoPublish'] === '1' ? true : false;
	    $curationType = isset($_POST['curationType']) ? sanitize_text_field($_POST['curationType']) : 'featured';
	    $source = isset($_POST['source']) ? sanitize_text_field($_POST['source']) : 'ia';
	    $intraType = isset($_POST['intraType']) ? sanitize_text_field($_POST['intraType']) : 'improve';
	    $motor = isset($_POST['motor']) ? sanitize_text_field($_POST['motor']) : 'openai';
       	$replace = isset($_POST['replace']) && $_POST['replace'] === '1' ? true : false;

	    echo $this->aicc_calculate_total_cost($post_type, $include, $exclude_already, $type, $quality, $resolution, $excludeNoPublish, $curationType, $source, $intraType, $motor, $replace); 
	    wp_die();
	}

	public function aicc_capture_fatal_error() 
	{
		$last_error = error_get_last();
		if ($last_error && in_array($last_error['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR])) {

			set_transient('aicc_fatal_error', true, 360);

			wp_clear_scheduled_hook( 'aicc_manual_cron' );
			wp_clear_scheduled_hook( 'aicc_bulk_cron' );

			delete_site_transient('aicc_manual_process_lock');			
			delete_site_transient('aicc_bulk_process_lock');

			delete_transient('aicc_is_bulk_running');		
			delete_transient('aicc_bulk_discarded_count');		
			delete_transient('aicc_is_recovery_running');		
			delete_transient('aicc_current_bulk_process_id');
		
			delete_option('aicc_cancel_bulk_process');	
			delete_option('aicc_bulk_total_cost');
			delete_option('aicc_bulk_total_tokens');
			delete_option('aicc_bulk_total_posts');	
			delete_option('aicc_bulk_processed_count');
			delete_option('aicc_bulk_ids_processed');

			// Transients que se usaban antes de la versión 2.2.0 se eliminan
			delete_transient('aicc_bulk_processed_count');
			delete_transient('aicc_bulk_ids_processed');
			delete_transient('aicc_cancel_bulk_process');			

			// Transients que siguen usando las funciones de imágenes y metaetiquetas
			delete_transient('aicc_bulk_total_posts');

			wp_cache_flush();

			$this->aicc_log_fatal_error($last_error);

			if(isset($this->manual) && method_exists($this->manual, 'cancel')) {
				$this->manual->cancel();
			}
			if(isset($this->bulk) && method_exists($this->bulk, 'cancel')) {
				$this->bulk->cancel();
			}
			return false;
		}
	}

	public function aicc_log_fatal_error($error) 
	{
		$id = 0; 
		$type = $error['type'];
		$message = $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'];
		$function = 'log_fatal_error'; 

		self::aicc_log_register($id, $type, $message, $function);
	}
	
	public function aicc_encrypt_api_key($key_to_encrypt) {
		$encryption_key = openssl_digest(wp_salt(), 'SHA256', TRUE);
		$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
		$encrypted = openssl_encrypt($key_to_encrypt, 'aes-256-cbc', $encryption_key, 0, $iv);
		return base64_encode($encrypted . '::' . $iv);
	}

	public static function aicc_decrypt_api_key($encrypted_key) {
		$encryption_key = openssl_digest(wp_salt(), 'SHA256', TRUE);
		list($encrypted_data, $iv) = explode('::', base64_decode($encrypted_key), 2);
		return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
	}

	public function aicc_check_canonical($post_id) {
	  
		if (class_exists('WPSEO_Frontend')) {
			$canonical_url = get_post_meta($post_id, '_yoast_wpseo_canonical', true);
			if (!empty($canonical_url)) {
				return $canonical_url;
			}
		}

		if (class_exists('RankMath')) {
			$canonical_url = get_post_meta($post_id, '_rank_math_canonical_url', true);
			if (!empty($canonical_url)) {
				return $canonical_url;
			}
		}
									   
		return wp_get_canonical_url($post_id);								   
									   
	}

	public static function aicc_handleOpenAIHttpErrors($httpcode, $response) 
	{
		if ($httpcode == 401) 
		{
			throw new Exception("Error de autenticación con OpenAI. Por favor, verifica tu API key.");
		}
		elseif ($httpcode == 429) 
		{
			throw new Exception("Has alcanzado un límite con OpenAI, ya sea por velocidad de solicitud o por cuota. Ajusta el ritmo de tus solicitudes o revisa tu plan y detalles de facturación.");
		}
		elseif ($httpcode == 500) 
		{
			throw new Exception("Hubo un problema en los servidores de OpenAI. Intenta nuevamente después de un momento.");
		}
		elseif ($httpcode == 503) 
		{
			throw new Exception("El motor de OpenAI está sobrecargado en este momento. Por favor, intenta nuevamente más tarde.");
		}
		elseif ($httpcode >= 400) 
		{
			$errorMessage = isset($response->error) ? $response->error->message : 'No se proporcionó un mensaje de error específico.';
			throw new Exception("Error desde OpenAI. Código: $httpcode. Mensaje: $errorMessage. Consulta la documentación o contacta a soporte para más información.");
		}
	}

	function aicc_ValidateKey($api_key) {
   
		$url = 'https://api.openai.com/v1/engines'; 

		$ch = curl_init($url);

		curl_setopt($ch, CURLOPT_HTTPHEADER, array(
			'Authorization: Bearer ' . $api_key,
		));
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			
		curl_setopt($ch, CURLOPT_TIMEOUT, 15);

		$response = curl_exec($ch);
		$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

		if ($response === false) {
			$error_msg = curl_error($ch);
			curl_close($ch);
			return array('status' => false, 'message' => 'CURL Error: ' . $error_msg);
		}

		curl_close($ch);

		if ($http_status == 200) {
	   		return array('status' => true, 'message' => 'Tu clave de API es correcta.');
		} 
		elseif ($http_status == 401) {
			return array('status' => false, 'message' => 'Hubo un error al conectarse con OpenAI. Verifica tu clave API.');
		} 
		elseif ($http_status == 402) {
			return array('status' => false, 'message' => 'Ha agotado sus créditos de OpenAI.');
		} 
		elseif ($http_status == 403) {
			return array('status' => false, 'message' => 'Acceso prohibido. Es posible que haya excedido los límites de tarifas.');
		}
		elseif ($http_status == 429) {
			return array('status' => false, 'message' => 'Has alcanzado un límite con OpenAI, ya sea por velocidad de solicitud o por cuota. Ajusta el ritmo de tus solicitudes o revisa tu plan y detalles de facturación.');
		}
		elseif ($http_status === 500) {
			return array('status' => false, 'message' => 'Hubo un problema en los servidores de OpenAI. Intenta nuevamente después de un momento.');
		}
		elseif ($http_status === 503) {
			return array('status' => false, 'message' => 'El motor de OpenAI está sobrecargado en este momento. Intenta nuevamente más tarde.');
		}
		else {
			return array('status' => false, 'message' => 'An unexpected error occurred.');
		}

	}


	function aicc_ValidateKey_replicate($api_key) {
	    // Endpoint de Replicate para obtener la lista de modelos
	    $url = 'https://api.replicate.com/v1/models';
	    
	    $ch = curl_init($url);
	    
	    // Replicate requiere el header "Authorization: Token <api_key>"
	    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
	        'Authorization: Token ' . $api_key,
	    ));
	    
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
	    
	    $response = curl_exec($ch);
	    $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	    
	    if ($response === false) {
	        $error_msg = curl_error($ch);
	        curl_close($ch);
	        return array('status' => false, 'message' => 'CURL Error: ' . $error_msg);
	    }
	    
	    curl_close($ch);
	    
	    if ($http_status == 200) {
	        return array('status' => true, 'message' => 'Tu clave de API de Replicate es correcta.');
	    } elseif ($http_status == 401) {
	        return array('status' => false, 'message' => 'Error al conectarse con Replicate. Verifica tu clave API.');
	    } elseif ($http_status == 429) {
	        return array('status' => false, 'message' => 'Has alcanzado un límite con Replicate, ya sea por velocidad de solicitud o por cuota.');
	    } else {
	        return array('status' => false, 'message' => 'Error inesperado al validar la clave de Replicate.');
	    }
	}

	public static function aicc_checkValueSERP($valueserp_api) {
        $query = 'test'; 

        $url = 'https://api.valueserp.com/search';
        $params = [
            'api_key' => $valueserp_api,
            'q' => $query,
            'hl' => 'es'
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, sprintf('%s?%s', $url, http_build_query($params)));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 120);

        $response = curl_exec($ch);
        $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        curl_close($ch);

        if ($response === false) {
            $error = curl_error($ch);
            return ['status' => false, 'message' => "Error en cURL: $error"];
        }

        if ($http_status == 200) {
            return ['status' => true, 'message' => "Conexión con ValueSERP exitosa."];
        } elseif ($http_status == 401) {
            return ['status' => false, 'message' => "Clave API de ValueSERP no válida o problema de autenticación."];
        } else {
            return ['status' => false, 'message' => "Error con ValueSERP, código de estado HTTP: $http_status"];
        }
    }

	public function get_order_link($field, $current_orderby, $current_order) 
	{
	    $order = ($field == $current_orderby && $current_order == 'desc') ? 'asc' : 'desc'; 
	    return add_query_arg(array('orderby' => $field, 'order' => $order));
	}	

	public function aicc_clear_log() {

	    check_ajax_referer('clear_log_nonce', 'security');

	    global $wpdb;

	    $wpdb->query("DELETE FROM " . AICC_LOG_TABLE);

	    wp_send_json_success();
	}	


	public static function aicc_process_video_results($video_results) {
	    $videos = [];
	    $hay_videos = 0;

	    if (isset($video_results['video_results'])) {
	        foreach ($video_results['video_results'] as $video) {
	            if (isset($video['link']) && (strpos($video['link'], 'youtube.com') !== false || strpos($video['link'], 'youtu.be') !== false)) {
	                $hay_videos = 1;
	                
	                // Capturamos más detalles del video
	                $videos[] = [
	                    'link' => $video['link'],
	                    'title' => $video['title'] ?? 'Título no disponible',
	                    'description' => $video['snippet'] ?? $video['title'],
	                    'thumbnail' => $video['image'] ?? '',
	                    'upload_date' => $video['date_utc'] ?? '',
	                    'duration' => $video['length'] ?? '',
	                ];
	            }

	            // Limitar a 3 videos
	            if (count($videos) >= 3) {
	                break;
	            }
	        }
	    }

	    $processed_results_video = [
	        'hay_videos' => $hay_videos,
	        'videos' => $videos
	    ];

	    return $processed_results_video;    
	}


	// Función para generar el código de inserción del video
	public static function aicc_generate_video_embed($url) {
	    // Verificar si es un enlace de YouTube y ajustar el formato
	    if (strpos($url, 'youtube.com/watch?v=') !== false) {
	        // Convertir URL a formato embed, reemplazando 'watch?v=' con 'embed/'
	        $url = str_replace('watch?v=', 'embed/', $url);
	    } elseif (strpos($url, 'youtu.be/') !== false) {
	        // Convertir URL de formato corto de YouTube
	        $url = str_replace('youtu.be/', 'youtube.com/embed/', $url);
	    }

	    // Generar el código de inserción usando el enlace formateado
	    return '<iframe width="560" height="315" src="' . esc_url($url) . '" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>';
	}

	// Función para obtener la URL de incrustación
	public static function aicc_get_youtube_embed_url($url) {
	    // Verificar si la URL es de YouTube
	    if (strpos($url, 'youtube.com') !== false || strpos($url, 'youtu.be') !== false) {
	        // Convertir la URL al formato de incrustación
	        $url_parts = parse_url($url);
	        if ($url_parts['host'] === 'youtu.be') {
	            $video_id = ltrim($url_parts['path'], '/');
	        } else {
	            parse_str($url_parts['query'], $query_vars);
	            $video_id = $query_vars['v'] ?? null;
	        }
	        if ($video_id) {
	            return 'https://www.youtube.com/embed/' . $video_id;
	        }
	    }
	    return null; // Retorna null si no es un video de YouTube válido
	}


	public static function aicc_get_youtube_thumbnail_url($youtube_link) {
	    // Extraer el ID del video
	    preg_match("/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]+)/", $youtube_link, $matches);
	    $video_id = $matches[1] ?? null;

	    if ($video_id) {
	        // Retornar la URL de la miniatura en alta resolución
	        return "https://img.youtube.com/vi/{$video_id}/hqdefault.jpg";
	    }

	    return null;
	}

	public static function aicc_convertDurationToISO8601($duration) {
	    // Asumiendo que $duration es una cadena en el formato "mm:ss"
	    list($minutes, $seconds) = explode(':', $duration);
	    return 'PT' . intval($minutes) . 'M' . intval($seconds) . 'S';
	}

	public static function aicc_process_paa_results($search_results) {
	    $paa = [];
	    $related_searches = [];
		//$selected_api = get_option('aicc_options_analysis_api', 'valueserp');
		$selected_api = 'valueserp';

	    if ($selected_api == 'valueserp' || $selected_api == 'serpapi') {
	        // Procesar PAA para ValueSerp y SerpApi
	        if (isset($search_results['related_questions'])) {
	            foreach ($search_results['related_questions'] as $question) {
	                if (isset($question['question'])) {
	                    $paa[] = $question['question'];
	                }
	            }
	        }

	        // Procesar búsquedas relacionadas para ValueSerp y SerpApi
	        if (isset($search_results['related_searches'])) {
	            foreach ($search_results['related_searches'] as $related_search) {
	                if (isset($related_search['query'])) {
	                    $related_searches[] = $related_search['query'];
	                } elseif (isset($related_search['title'])) {
	                    $related_searches[] = $related_search['title'];
	                }
	            }
	        }
	    } elseif ($selected_api == 'spaceserp') {
	        // Procesar PAA para SpaceSerp
	        if (isset($search_results['people_also_ask'])) {
	            foreach ($search_results['people_also_ask'] as $question) {
	                if (isset($question['question'])) {
	                    $paa[] = $question['question'];
	                } elseif (isset($question['title'])) {
	                    $paa[] = $question['title'];
	                }
	            }
	        }

	        // Procesar búsquedas relacionadas para SpaceSerp
	        if (isset($search_results['related_searches'])) {
	            foreach ($search_results['related_searches'] as $related_search) {
	                if (isset($related_search['query'])) {
	                    $related_searches[] = $related_search['query'];
	                } elseif (isset($related_search['title'])) {
	                    $related_searches[] = $related_search['title'];
	                }
	            }
	        }
	    }

	    // Devolver ambos resultados
	    return ['paa' => $paa, 'related_searches' => $related_searches];
	}


	public static function aicc_get_images($query, $keyword, $source) {

        // Configurar las variables para cada API
        $pexels_endpoint = "https://api.pexels.com/v1/search";
        $pixabay_endpoint = "https://pixabay.com/api/";
        $pexels_key_option = 'aicc_options_pexels';
        $pixabay_key_option = 'aicc_options_pixabay';

        // Seleccionar el endpoint y la clave API en función de la fuente
        if ($source == 'pixabay') {
            $encrypted_key = get_option($pixabay_key_option);
            if (!$encrypted_key) {
                $error_message = 'Es necesario ingresar una clave API de Pixabay';
                self::aicc_log_register(0, 'warning', $error_message, 'aicc_maker');
                return false;
            }
            $api_key = self::aicc_decrypt_api_key($encrypted_key);
            $endpoint = $pixabay_endpoint;
            $params = [
                'key' => $api_key,
                'q' => urlencode($query),
                'per_page' => 20,
                'image_type' => 'photo',
                'orientation' => 'horizontal',
                'safesearch' => 'true',
            ];
            $headers = [];
        } else {
            $encrypted_key = get_option($pexels_key_option);
            if (!$encrypted_key) {
                $error_message = 'Es necesario ingresar una clave API de Pexels.';
                self::aicc_log_register(0, 'warning', $error_message, 'aicc_maker');
                return false;
            }            
            $api_key = self::aicc_decrypt_api_key($encrypted_key);
            $endpoint = $pexels_endpoint;
            $params = [
                'query' => urlencode($query),
                'per_page' => 20,
                'orientation' => 'landscape',
                'size' => 'large',
            ];
            $headers = [
                'Authorization: ' . $api_key,
            ];
        }

        // Construir la URL de la API
        $url = $endpoint . '?' . http_build_query($params);

        // Configurar y ejecutar la solicitud cURL
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        curl_close($ch);

        if ($response === false) {
            $message = 'Hubo un error al conectarse con ' . ucfirst($source) . ' para la consulta "' . $query . '".';
            self::aicc_log_register(0, 'warning', $message, 'aicc_maker');
            return false;
        }

        $result = json_decode($response, true);

        // Procesar la respuesta de acuerdo con la fuente
        if ($source === 'pixabay') {
            $photos = $result['hits'] ?? [];
        } else {
            $photos = $result['photos'] ?? [];
        }

        if (count($photos) > 0) {
            $photos_count = count($photos);

            // Obtener los índices aleatorios sin repetición
            $random_indexes = array_rand($photos, $photos_count);

            // Si hay menos de 4 fotos, asegurarse de que $random_indexes sea un array
            if ($photos_count == 1) {
                $random_indexes = [$random_indexes];
            }

            // Seleccionar aleatoriamente entre 3 y 5 imágenes
            $num_images = rand(3, 5);

            // Obtener el índice de la imagen destacada
            $featured_image_index = $random_indexes[min(3, $photos_count - 1)];
            $featured_image = $source === 'pixabay' ? $photos[$featured_image_index]['webformatURL'] : $photos[$featured_image_index]['src']['medium'];

            // Remover el índice de la imagen destacada de los índices aleatorios
            $random_indexes = array_diff($random_indexes, [$featured_image_index]);

            // Obtener las imágenes para la primera opción y subirlas al servidor
            $post_images = [];
            $image_counter = 1;
            foreach (array_slice($random_indexes, 0, min($num_images, $photos_count - 1)) as $index) {
                $image_url = $source === 'pixabay' ? $photos[$index]['webformatURL'] : $photos[$index]['src']['medium'];
                $temp_image_path = self::aicc_download_image_to_temp($image_url);
                if ($temp_image_path) {
                    $processed_image_path = self::aicc_process_image($temp_image_path, '');
                    if ($processed_image_path) {
                        $attachment_id = self::aicc_move_image_to_media($processed_image_path, 0, $keyword . '-' . $image_counter);
                        $image_counter++;
                        if ($attachment_id) {
                            $post_images[] = wp_get_attachment_url($attachment_id);
                        }
                    }
                }
            }
            return [
                'post_images' => $post_images,
                'featured_image' => $featured_image,
            ];
        } else {
            return false;
        }
    }


	function aicc_add_faq_schema_to_head() {
	   	global $post;
		$enable_schema_faq = get_option('aicc_enable_schema_faq', true);
	    if (is_singular() && $enable_schema_faq) {
	        global $post;
	        $faqSchema = get_post_meta($post->ID, '_aicc_faq_schema', true);
	        if ($faqSchema) {
	        	echo '<!-- Schema: FAQ -->';
	            echo $faqSchema;
	        }       
	    }
		$enable_video_faq = get_option('aicc_enable_schema_video', true);
	    if (is_singular() && $enable_video_faq) {
	        $videoSchema = get_post_meta($post->ID, '_aicc_video_schema', true);
	        if ($videoSchema) {
	        	echo '<!-- Video: FAQ -->';
	            echo $videoSchema;
	        }	        
	    }	    
	}
	
	function aicc_get_posts_to_recover() {
	    $processedPostsHistory = get_option('aicc_bulk_processed_posts_history', []);
	    $selected_index = isset($_POST['selected_index']) ? $_POST['selected_index'] : null;
	    
	    $post_ids = [];
	    
	    if ($selected_index === 'recover_last') {
	        // Recuperar la última ejecución
	        $lastExecution = end($processedPostsHistory);
	        $post_ids = $lastExecution['post_ids'];
	        
	    } elseif ($selected_index === 'recover_all') {
	        // Recuperar todos los posts de todas las ejecuciones
	        foreach ($processedPostsHistory as $execution) {
	            $post_ids = array_merge($post_ids, $execution['post_ids']);
	        }
	        
	    } elseif (is_numeric($selected_index) && isset($processedPostsHistory[$selected_index])) {
	        // Recuperar una ejecución específica seleccionada por índice
	        $execution = $processedPostsHistory[$selected_index];
	        $post_ids = $execution['post_ids'];
	        
	    } else {
	        echo '<p>No se encontró la ejecución seleccionada.</p>';
	        wp_die();
	    }

	    // Construir la lista de títulos con enlaces
	    $output = '<ul>';
	    foreach ($post_ids as $post_id) {
	        $post_title = get_the_title($post_id);
	        $post_link = get_edit_post_link($post_id);
	        $output .= '<li><a href="' . esc_url($post_link) . '" target="_blank">' . esc_html($post_title) . '</a></li>';
	    }
	    $output .= '</ul>';

	    echo $output;
	    wp_die();
	}



	public static function aicc_generate_image_replicate($prompt, $style = 'flux_schnell', $aspect_ratio = '16:9', $api_key) {

 		$message = 'Motor de generación de imágenes seleccionado: Replicate';
       	self::aicc_log_register(0, 'warning', $message, 'aicc_generate_image_replicate');

	    // Mapeamos los estilos a sus respectivos model_id.
	    $models = [
	        'stable_diffusion' => [
	            'model_id' => 'stability-ai/stable-diffusion-3'
	        ],
	        'flux_schnell' => [
	            'model_id' => 'black-forest-labs/flux-schnell'
	        ],
	        'imagen' => [
	            'model_id' => 'google/imagen-3'
	        ],
	        'ideogram' => [
	            'model_id' => 'ideogram-ai/ideogram-v2a-turbo'
	        ],
	    ];

	    // Si el estilo solicitado no existe, se usa 'flux_schnell' por defecto
	    if (!isset($models[$style])) {
	        $style = 'flux_schnell';
	    }
	    $model_info = $models[$style];

	    // Configura el endpoint dinámicamente según el model_id
	    $api_url = "https://api.replicate.com/v1/models/" . $model_info['model_id'] . "/predictions";

	    // Configura los parámetros de entrada según el modelo
	    switch ($style) {
	        case 'stable_diffusion':
	            $input = [
	                "cfg"              => 3.5,
	                "steps"            => 28,
	                "prompt"           => $prompt,
	                "aspect_ratio"     => $aspect_ratio,
	                "output_format"    => "webp",
	                "output_quality"   => 90,
	                "negative_prompt"  => "",
	                "prompt_strength"  => 0.85
	            ];
	            break;
	        case 'flux_schnell':
	            $input = [
	                "prompt"         => $prompt,
	                "go_fast"        => true,
	                "num_outputs"    => 1,
	                "aspect_ratio"   => $aspect_ratio,
	                "output_format"  => "webp",
	                "output_quality" => 80
	            ];
	            break;
	        case 'imagen':
	            $input = [
	                "prompt"             => $prompt,
	                "aspect_ratio"       => $aspect_ratio,
	                "safety_filter_level"=> "block_medium_and_above"
	            ];
	            break;
	        case 'ideogram':
	            $input = [
	                "prompt"             => $prompt,
	                "resolution"         => "None",
	                "style_type"         => "None",
	                "aspect_ratio"       => $aspect_ratio,
	                "magic_prompt_option"=> "Auto"
	            ];
	            break;
	        default:
	            // Por defecto se usa la configuración para flux_schnell
	            $input = [
	                "prompt"         => $prompt,
	                "go_fast"        => true,
	                "num_outputs"    => 1,
	                "aspect_ratio"   => $aspect_ratio,
	                "output_format"  => "jpg",
	                "output_quality" => 80
	            ];
	    }

	    // Todos los modelos usan el header "Prefer: wait" para obtener respuesta sincrónica.
	    $headers = [
	        "Content-Type"  => "application/json",
	        "Authorization" => "Token " . $api_key,
	        "Prefer"        => "wait"
	    ];

	    $data = [
	        "input" => $input
	    ];
	    $args = [
	        "body"    => json_encode($data),
	        "headers" => $headers,
	        "timeout" => 60
	    ];

	    $response = wp_remote_post($api_url, $args);
	    if (is_wp_error($response)) {
			$message = "Replicate: Error: " . $response->get_error_message();
       		self::aicc_log_register(0, 'warning', $message, 'aicc_generate_image_replicate');	        
	        return "";
	    }
	    $body = wp_remote_retrieve_body($response);
	    $result = json_decode($body, true);
	    if (!isset($result['id'])) {
			$message = "Replicate: No se recibió ID de predicción.";
       		self::aicc_log_register(0, 'warning', $message, 'aicc_generate_image_replicate');		        
       		return "";
	    }
	    $prediction_id = $result['id'];
	    error_log("Replicate: ID de predicción: " . $prediction_id);

	    // Al usar "Prefer: wait", la respuesta contendrá la salida directamente
	    if (isset($result['output'][0])) {
	        $output_url = $result['output'][0];
			$message = "Replicate: Imagen generada: $output_url";
       		self::aicc_log_register(0, 'warning', $message, 'aicc_generate_image_replicate');		        
	        return $output_url;
	    } else {
			$message = "Replicate: Predicción completada pero sin URL de imagen.";
       		self::aicc_log_register(0, 'warning', $message, 'aicc_generate_image_replicate');			        
	        return "";
	    }
	}


}


new SeoCuratorPlugin();


/*
 * License Page
 */
include ( AICC_PLUGIN_DIR . 'pages/license.php');