<?php
/**
 * ==========================
 * 🔐 IGNY8 FILE RULE HEADER
 * ==========================
 * @file         : modules-ai.php
 * @location     : /ai/modules-ai.php
 * @type         : AI Integration
 * @scope        : Global
 * @allowed      : AI service abstraction, module AI interfaces
 * @reusability  : Globally Reusable
 * @notes        : Common AI interface for all modules
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Get AI setting value
 */
function igny8_get_ai_setting($key, $default = null) {
    $settings = get_option('igny8_ai_settings', []);
    return isset($settings[$key]) ? $settings[$key] : $default;
}

/**
 * Update AI setting value
 */
function igny8_update_ai_setting($key, $value) {
    $settings = get_option('igny8_ai_settings', []);
    $settings[$key] = $value;
    update_option('igny8_ai_settings', $settings);
}

/**
 * Get Planner AI settings
 */
function igny8_get_planner_ai_settings() {
    return [
        'planner_mode' => igny8_get_ai_setting('planner_mode', 'manual'),
        'clustering' => igny8_get_ai_setting('clustering', 'enabled'),
        'ideas' => igny8_get_ai_setting('ideas', 'enabled'),
        'mapping' => igny8_get_ai_setting('mapping', 'enabled'),
        'prompts' => [
            'clustering' => igny8_get_ai_setting('clustering_prompt', igny8_get_default_clustering_prompt()),
            'ideas' => igny8_get_ai_setting('ideas_prompt', igny8_get_default_ideas_prompt())
        ]
    ];
}

/**
 * Get Writer AI settings
 */
function igny8_get_writer_ai_settings() {
    return [
        'writer_mode' => igny8_get_ai_setting('writer_mode', 'manual'),
        'content_generation' => igny8_get_ai_setting('content_generation', 'enabled'),
        'prompts' => [
            'content_generation' => get_option('igny8_content_generation_prompt', igny8_content_generation_prompt())
        ]
    ];
}


/**
 * Process AI request with prompt template
 */
function igny8_process_ai_request($action, $data, $prompt_template) {
    // Log AI processing start
    igny8_log_ai_event('AI Processing Started', 'ai', $action, 'info', 'Starting AI request processing', 'Action: ' . $action . ', Data count: ' . count($data));
    
    // Replace shortcodes with actual data
    $prompt = $prompt_template;
    
    switch ($action) {
        case 'clustering':
            $keywords_data = igny8_format_keywords_for_ai($data);
            $prompt = str_replace('[IGNY8_KEYWORDS]', $keywords_data, $prompt);
            
            // Add sector information if multiple sectors are configured
            $sector_options = igny8_get_sector_options();
            if (count($sector_options) > 1) {
                $sector_names = array_column($sector_options, 'label');
                $sector_text = "\n\nAvailable sectors: " . implode(', ', $sector_names) . "\nAssign each cluster to the most suitable sector from the above list.";
                $prompt .= $sector_text;
            }
            
            igny8_log_ai_event('Prompt Preparation', 'ai', $action, 'info', 'Keywords data formatted for prompt', 'Keywords: ' . substr($keywords_data, 0, 100) . '...');
            break;
            
        case 'ideas':
            $clusters_data = igny8_format_clusters_for_ai($data);
            $cluster_keywords_data = igny8_format_cluster_keywords_for_ai($data);
            $prompt = str_replace('[IGNY8_CLUSTERS]', $clusters_data, $prompt);
            $prompt = str_replace('[IGNY8_CLUSTER_KEYWORDS]', $cluster_keywords_data, $prompt);
            break;
            
        case 'mapping':
            $content_data = igny8_format_content_for_ai($data['content']);
            $clusters_data = igny8_format_clusters_for_ai($data['clusters']);
            $prompt = str_replace('[IGNY8_CONTENT]', $content_data, $prompt);
            $prompt = str_replace('[IGNY8_CLUSTERS]', $clusters_data, $prompt);
            break;
            
        case 'content_generation':
            $idea_data = igny8_format_idea_for_ai($data['idea']);
            $cluster_data = igny8_format_cluster_for_ai($data['cluster']);
            $keywords_data = igny8_format_keywords_for_ai($data['keywords']);
            $max_in_article_images = get_option('igny8_max_in_article_images', 1);
            
            // Safety check: Analyze outline to ensure we don't exceed available H2 sections
            $safe_max_images = igny8_calculate_safe_image_quantity($idea_data, $max_in_article_images);
            $image_prompts_data = igny8_format_image_prompts_for_ai($safe_max_images);
            
            $prompt = str_replace('[IGNY8_IDEA]', $idea_data, $prompt);
            $prompt = str_replace('[IGNY8_CLUSTER]', $cluster_data, $prompt);
            $prompt = str_replace('[IGNY8_KEYWORDS]', $keywords_data, $prompt);
            $prompt = str_replace('[IGNY8_DESKTOP_QUANTITY]', $safe_max_images, $prompt);
            $prompt = str_replace('[IMAGE_PROMPTS]', $image_prompts_data, $prompt);
            // Content generation prompt is now self-contained with 3-part structure
            igny8_log_ai_event('Prompt Preparation', 'ai', $action, 'info', 'Content generation data formatted', 'Idea: ' . substr($idea_data, 0, 100) . '..., Max In-Article Images: ' . $max_in_article_images . ', Safe Max: ' . $safe_max_images);
            break;
    }
    
    // Check if OpenAI function exists
    if (!function_exists('igny8_call_openai')) {
        if (defined('DOING_CRON') && DOING_CRON) {
            error_log("Igny8 AI Process: igny8_call_openai function not found");
        }
        igny8_log_ai_event('AI Function Missing', 'ai', $action, 'error', 'igny8_call_openai function not found', 'OpenAI integration not available');
        return false;
    }
    
    // Get API configuration
    $api_key = get_option('igny8_api_key');
    $model = get_option('igny8_model', 'gpt-4.1');
    
    if (empty($api_key)) {
        if (defined('DOING_CRON') && DOING_CRON) {
            error_log("Igny8 AI Process: API key is empty or missing");
        }
        igny8_log_ai_event('API Key Missing', 'ai', $action, 'error', 'OpenAI API key not configured', 'Please configure API key in settings');
        return false;
    }
    
    igny8_log_ai_event('OpenAI API Call', 'ai', $action, 'info', 'Calling OpenAI API', 'Model: ' . $model . ', Prompt length: ' . strlen($prompt));
    
    // Debug logging for CRON context
    if (defined('DOING_CRON') && DOING_CRON) {
        error_log("Igny8 AI Process: Making OpenAI API call - Model: " . $model . ", Prompt length: " . strlen($prompt));
    }
    
    // Call OpenAI API
    $response = igny8_call_openai($prompt, $api_key, $model);
    
    if (defined('DOING_CRON') && DOING_CRON) {
        error_log("Igny8 AI Process: OpenAI response received: " . ($response ? 'Success' : 'Failed'));
        if ($response && strlen($response) > 0) {
            error_log("Igny8 AI Process: Response length: " . strlen($response) . " characters");
        }
    }
    
    if (!$response) {
        if (defined('DOING_CRON') && DOING_CRON) {
            error_log("Igny8 AI Process: OpenAI API returned no response - this is the failure point");
        }
        igny8_log_ai_event('OpenAI API Failed', 'ai', $action, 'error', 'OpenAI API returned no response', 'Check API key and network connection');
        return false;
    }
    
    // Check if response starts with "Error:"
    if (strpos($response, 'Error:') === 0) {
        $error_details = [
            'error_message' => $response,
            'model' => $model,
            'prompt_length' => strlen($prompt),
            'api_key_configured' => !empty($api_key),
            'timestamp' => current_time('mysql')
        ];
        igny8_log_ai_event('OpenAI API Error', 'ai', $action, 'error', 'OpenAI API returned error', 'Error: ' . $response . ' | Details: ' . json_encode($error_details));
        return false;
    }
    
    // igny8_call_openai returns the content directly, not wrapped in an array
    igny8_log_ai_event('OpenAI Response Received', 'ai', $action, 'info', 'Raw response from OpenAI', 'Response length: ' . strlen($response) . ', Preview: ' . substr($response, 0, 100) . '...');
    
    // Parse JSON response - try to extract JSON from response
    $json_result = igny8_extract_json_from_response($response);
    
    if (!$json_result) {
        $error_details = [
            'raw_response' => $response,
            'response_length' => strlen($response),
            'model' => $model,
            'action' => $action,
            'timestamp' => current_time('mysql')
        ];
        igny8_log_ai_event('JSON Parse Failed', 'ai', $action, 'error', 'Failed to parse OpenAI response as JSON', 'Raw response: ' . substr($response, 0, 500) . '... | Details: ' . json_encode($error_details));
        return false;
    }
    
    igny8_log_ai_event('OpenAI Success', 'ai', $action, 'success', 'OpenAI API returned valid JSON', 'Result keys: ' . json_encode(array_keys($json_result)));
    
    // Normalize image prompts structure if needed
    // Handle case where AI returns featured_image and in_article_images at top level
    if (isset($json_result['featured_image']) && !isset($json_result['image_prompts'])) {
        $json_result['image_prompts'] = [
            'featured_image' => $json_result['featured_image'],
            'in_article_images' => $json_result['in_article_images'] ?? []
        ];
        igny8_log_ai_event('Image Prompts Normalized', 'ai', $action, 'info', 'Image prompts structure normalized from top-level fields', 'Moved featured_image and in_article_images to image_prompts object');
    }
    
    return $json_result;
}

/**
 * Parse 3-part response structure
 */
function igny8_parse_three_part_response($response) {
    $result = [];
    
    // Extract metadata JSON
    if (preg_match('/##Metadata Fields JSON##\s*(\{.*?\})/s', $response, $matches)) {
        $metadata = json_decode($matches[1], true);
        if ($metadata) {
            $result['metadata'] = $metadata;
        }
    }
    
    // Extract content-related JSON
    if (preg_match('/##Content-Related JSON##\s*(\{.*?\})/s', $response, $matches)) {
        $content_data = json_decode($matches[1], true);
        if ($content_data) {
            $result['content_data'] = $content_data;
        }
    }
    
    // Extract image prompts
    if (preg_match('/##Image Prompts Requirements:##.*?\[IMAGE_PROMPTS\]/s', $response, $matches)) {
        $result['image_prompts'] = $matches[0];
    }
    
    // If we found at least one part, return the result
    if (!empty($result)) {
        return $result;
    }
    
    return null;
}

/**
 * Extract JSON from AI response (handles cases where AI adds extra text)
 */
function igny8_extract_json_from_response($response) {
    // First, try to parse the response directly as JSON
    $json_result = json_decode($response, true);
    if ($json_result) {
        return $json_result;
    }
    
    // Try to parse 3-part structure
    $three_part_result = igny8_parse_three_part_response($response);
    if ($three_part_result) {
        return $three_part_result;
    }
    
    // If that fails, try to find JSON within the response
    // Look for content between curly braces
    if (preg_match('/\{.*\}/s', $response, $matches)) {
        $json_result = json_decode($matches[0], true);
        if ($json_result) {
            return $json_result;
        }
    }
    
    // Handle GPT-4o-mini format: ```json { ... } ``` or """json { ... } """
    if (preg_match('/```json\s*(\{.*?\})\s*```/s', $response, $matches)) {
        $json_result = json_decode($matches[1], true);
        if ($json_result) {
            return $json_result;
        }
    }
    
    if (preg_match('/"""json\s*(\{.*?\})\s*"""/s', $response, $matches)) {
        $json_result = json_decode($matches[1], true);
        if ($json_result) {
            return $json_result;
        }
    }
    
    // If still no luck, try to clean the response
    $cleaned_response = trim($response);
    
    // Remove common prefixes that AI might add
    $prefixes_to_remove = [
        'Here is the JSON response:',
        'Here\'s the JSON:',
        'JSON Response:',
        '```json',
        '```',
        '"""json',
        '"""',
        'Here is the content in JSON format:',
        'The JSON response is:'
    ];
    
    foreach ($prefixes_to_remove as $prefix) {
        if (stripos($cleaned_response, $prefix) === 0) {
            $cleaned_response = trim(substr($cleaned_response, strlen($prefix)));
        }
    }
    
    // Remove common suffixes
    $suffixes_to_remove = [
        '```',
        '"""',
        'This JSON contains all the required fields.',
        'Hope this helps!',
        'Let me know if you need any modifications.'
    ];
    
    foreach ($suffixes_to_remove as $suffix) {
        $pos = stripos($cleaned_response, $suffix);
        if ($pos !== false) {
            $cleaned_response = trim(substr($cleaned_response, 0, $pos));
        }
    }
    
    // Try parsing the cleaned response
    $json_result = json_decode($cleaned_response, true);
    if ($json_result) {
        return $json_result;
    }
    
    // Last resort: try to find and extract JSON object
    if (preg_match('/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/', $response, $matches)) {
        $json_result = json_decode($matches[0], true);
        if ($json_result) {
            return $json_result;
        }
    }
    
    return false;
}

/**
 * Set post categories from AI response
 */
function igny8_set_post_categories($post_id, $categories) {
    if (empty($categories) || !is_array($categories)) {
        return;
    }
    
    $category_ids = [];
    
    foreach ($categories as $category) {
        $category_id = igny8_get_or_create_category($category);
        if ($category_id) {
            $category_ids[] = $category_id;
        }
    }
    
    if (!empty($category_ids)) {
        wp_set_post_categories($post_id, $category_ids);
    }
}

/**
 * Get or create category from category string
 */
function igny8_get_or_create_category($category_string) {
    if (empty($category_string)) {
        return false;
    }
    
    // Handle parent > child format
    if (strpos($category_string, ' > ') !== false) {
        $parts = explode(' > ', $category_string);
        $parent_name = trim($parts[0]);
        $child_name = trim($parts[1]);
        
        // Create or get parent category
        $parent_term = get_term_by('name', $parent_name, 'category');
        if (!$parent_term) {
            $parent_result = wp_insert_term($parent_name, 'category');
            if (!is_wp_error($parent_result)) {
                $parent_id = $parent_result['term_id'];
            } else {
                return false;
            }
        } else {
            $parent_id = $parent_term->term_id;
        }
        
        // Create or get child category
        $child_term = get_term_by('name', $child_name, 'category');
        if (!$child_term) {
            $child_result = wp_insert_term($child_name, 'category', ['parent' => $parent_id]);
            if (!is_wp_error($child_result)) {
                return $child_result['term_id'];
            }
        } else {
            return $child_term->term_id;
        }
    } else {
        // Single category
        $term = get_term_by('name', $category_string, 'category');
        if (!$term) {
            $result = wp_insert_term($category_string, 'category');
            if (!is_wp_error($result)) {
                return $result['term_id'];
            }
        } else {
            return $term->term_id;
        }
    }
    
    return false;
}

/**
 * Store cluster and sector metadata
 */
function igny8_store_content_metadata($post_id, $ai_response) {
    global $wpdb;
    
    // Store cluster and sector info if available
    $cluster_id = $ai_response['cluster_id'] ?? null;
    $sector_id = $ai_response['sector_id'] ?? null;
    
    if ($cluster_id) {
        update_post_meta($post_id, '_igny8_cluster_id', $cluster_id);
    }
    
    if ($sector_id) {
        update_post_meta($post_id, '_igny8_sector_id', $sector_id);
    }
    
    // Store keywords if available
    if (!empty($ai_response['keywords_used'])) {
        update_post_meta($post_id, '_igny8_keywords_used', wp_json_encode($ai_response['keywords_used']));
    }
}

/**
 * Log AI event for debugging
 */
function igny8_log_ai_event($event, $module, $action, $status = 'info', $message = '', $details = '') {
    $ai_logs = get_option('igny8_ai_logs', []);
    
    $log_entry = [
        'timestamp' => current_time('mysql'),
        'event' => $event,
        'module' => $module,
        'action' => $action,
        'status' => $status,
        'message' => $message,
        'details' => $details
    ];
    
    // Add to beginning of array (newest first)
    array_unshift($ai_logs, $log_entry);
    
    // Keep only last 100 events
    $ai_logs = array_slice($ai_logs, 0, 100);
    
    update_option('igny8_ai_logs', $ai_logs);
}

/**
 * Format keywords data for AI processing
 */
function igny8_format_keywords_for_ai($keywords) {
    $formatted = [];
    foreach ($keywords as $keyword) {
        $formatted[] = [
            'id' => isset($keyword->id) ? $keyword->id : null,
            'keyword' => isset($keyword->keyword) ? $keyword->keyword : '',
            'search_volume' => isset($keyword->search_volume) ? $keyword->search_volume : 0,
            'difficulty' => isset($keyword->difficulty) ? $keyword->difficulty : 0
        ];
    }
    return json_encode($formatted, JSON_PRETTY_PRINT);
}

/**
 * Format clusters data for AI processing
 */
function igny8_format_clusters_for_ai($clusters) {
    $formatted = [];
    foreach ($clusters as $cluster) {
        $formatted[] = [
            'id' => $cluster->id,
            'name' => $cluster->cluster_name,
            'sector_id' => $cluster->sector_id,
            'keyword_count' => $cluster->keyword_count,
            'keywords' => $cluster->keywords_list ?? ''
        ];
    }
    return json_encode($formatted, JSON_PRETTY_PRINT);
}

/**
 * Format cluster keywords data for AI processing
 */
function igny8_format_cluster_keywords_for_ai($clusters) {
    $formatted = [];
    foreach ($clusters as $cluster) {
        $formatted[] = [
            'cluster_id' => $cluster->id,
            'cluster_name' => $cluster->cluster_name,
            'keywords' => $cluster->keywords_list ? explode(', ', $cluster->keywords_list) : []
        ];
    }
    return json_encode($formatted, JSON_PRETTY_PRINT);
}

/**
 * Format content data for AI processing
 */
function igny8_format_content_for_ai($content) {
    $formatted = [];
    foreach ($content as $item) {
        $formatted[] = [
            'id' => $item->ID,
            'title' => $item->post_title,
            'content' => wp_strip_all_tags($item->post_content),
            'type' => $item->post_type,
            'excerpt' => $item->post_excerpt
        ];
    }
    return json_encode($formatted, JSON_PRETTY_PRINT);
}

/**
 * Add AI processing task to queue
 */
function igny8_add_ai_queue_task($action, $data, $user_id = null) {
    global $wpdb;
    
    $user_id = $user_id ?: get_current_user_id();
    
    $result = $wpdb->insert(
        $wpdb->prefix . 'igny8_ai_queue',
        [
            'action' => $action,
            'data' => json_encode($data),
            'user_id' => $user_id,
            'status' => 'pending',
            'created_at' => current_time('mysql'),
            'processed_at' => null,
            'result' => null,
            'error_message' => null
        ],
        ['%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s']
    );
    
    return $result ? $wpdb->insert_id : false;
}

/**
 * Process AI queue tasks
 */
function igny8_process_ai_queue($limit = null) {
    if ($limit === null) {
        error_log('Igny8 AI Queue: No limit provided');
        return 0;
    }
    global $wpdb;
    
    // Get pending tasks
    $tasks = $wpdb->get_results($wpdb->prepare("
        SELECT * FROM {$wpdb->prefix}igny8_ai_queue 
        WHERE status = 'pending' 
        ORDER BY created_at ASC 
        LIMIT %d
    ", $limit));
    
    $processed = 0;
    
    foreach ($tasks as $task) {
        // Mark as processing
        $wpdb->update(
            $wpdb->prefix . 'igny8_ai_queue',
            ['status' => 'processing'],
            ['id' => $task->id],
            ['%s'],
            ['%d']
        );
        
        try {
            $data = json_decode($task->data, true);
            $result = igny8_process_ai_request($task->action, $data, igny8_get_ai_prompt_for_action($task->action));
            
            if ($result) {
                // Mark as completed
                $wpdb->update(
                    $wpdb->prefix . 'igny8_ai_queue',
                    [
                        'status' => 'completed',
                        'processed_at' => current_time('mysql'),
                        'result' => json_encode($result)
                    ],
                    ['id' => $task->id],
                    ['%s', '%s', '%s'],
                    ['%d']
                );
                
                // Process the result based on action
                igny8_process_ai_queue_result($task->action, $result);
                
            } else {
                // Mark as failed
                $wpdb->update(
                    $wpdb->prefix . 'igny8_ai_queue',
                    [
                        'status' => 'failed',
                        'processed_at' => current_time('mysql'),
                        'error_message' => 'AI processing returned no result'
                    ],
                    ['id' => $task->id],
                    ['%s', '%s', '%s'],
                    ['%d']
                );
            }
            
        } catch (Exception $e) {
            // Mark as failed with error
            $wpdb->update(
                $wpdb->prefix . 'igny8_ai_queue',
                [
                    'status' => 'failed',
                    'processed_at' => current_time('mysql'),
                    'error_message' => $e->getMessage()
                ],
                ['id' => $task->id],
                ['%s', '%s', '%s'],
                ['%d']
            );
        }
        
        $processed++;
    }
    
    return $processed;
}

/**
 * Get AI prompt for specific action
 */
function igny8_get_ai_prompt_for_action($action) {
    switch ($action) {
        case 'clustering':
            return igny8_get_ai_setting('clustering_prompt', igny8_get_default_clustering_prompt());
        case 'ideas':
            return igny8_get_ai_setting('ideas_prompt', igny8_get_default_ideas_prompt());
        case 'content_generation':
            return get_option('igny8_content_generation_prompt', igny8_content_generation_prompt());
        default:
            return '';
    }
}

/**
 * Format idea data for AI processing
 */
function igny8_format_idea_for_ai($idea) {
    if (is_object($idea)) {
        // Handle structured description (JSON) vs plain text
        $description = $idea->idea_description ?? '';
        
        // Check if description is JSON and format it for AI
        if (!empty($description)) {
            $decoded = json_decode($description, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                // Format structured description for AI
                $formatted_description = igny8_format_structured_description_for_ai($decoded);
            } else {
                // Use as plain text
                $formatted_description = $description;
            }
        } else {
            $formatted_description = '';
        }
        
        return sprintf(
            "Title: %s\nDescription: %s\nStructure: %s\nType: %s\nPriority: %s\nEstimated Word Count: %s\nStatus: %s",
            $idea->idea_title ?? '',
            $formatted_description,
            $idea->content_structure ?? '',
            $idea->content_type ?? '',
            $idea->priority ?? '',
            $idea->estimated_word_count ?? '',
            $idea->status ?? ''
        );
    }
    return 'Idea data not available';
}

/**
 * Format structured description for AI processing
 */
function igny8_format_structured_description_for_ai($structured_description) {
    if (!is_array($structured_description) || empty($structured_description['H2'])) {
        return 'No structured outline available';
    }
    
    $formatted = "Content Outline:\n\n";
    
    foreach ($structured_description['H2'] as $h2_section) {
        $formatted .= "## " . $h2_section['heading'] . "\n";
        
        if (!empty($h2_section['subsections'])) {
            foreach ($h2_section['subsections'] as $h3_section) {
                $formatted .= "### " . $h3_section['subheading'] . "\n";
                $formatted .= "Content Type: " . $h3_section['content_type'] . "\n";
                $formatted .= "Details: " . $h3_section['details'] . "\n\n";
            }
        }
    }
    
    return $formatted;
}


/**
 * Format cluster data for AI processing
 */
function igny8_format_cluster_for_ai($cluster) {
    if (is_object($cluster)) {
        return sprintf(
            "Cluster Name: %s\nDescription: %s\nStatus: %s\nKeyword Count: %s\nTotal Volume: %s\nAverage Difficulty: %s",
            $cluster->cluster_name ?? '',
            $cluster->description ?? '',
            $cluster->status ?? '',
            $cluster->keyword_count ?? 0,
            $cluster->total_volume ?? 0,
            $cluster->avg_difficulty ?? 0
        );
    }
    return 'Cluster data not available';
}

/**
 * Format image prompts structure for AI content generation
 * 
 * @param int $max_in_article_images Number of in-article images to generate
 * @return string JSON structure for image prompts
 */
function igny8_format_image_prompts_for_ai($max_in_article_images = 1) {
    $image_prompts = [
        'featured_image' => '[Detailed prompt for featured/hero image based on the article title and main topic]',
        'in_article_images' => []
    ];
    
    // Generate in-article image prompts based on quantity
    // Each prompt corresponds to H2 sections: 1st H2, 2nd H2, 3rd H2, etc.
    for ($i = 1; $i <= $max_in_article_images; $i++) {
        $section_number = $i; // Start from 1st H2 (section 1)
        $image_prompts['in_article_images'][] = [
            'prompt-img-' . $i => '[Detailed image prompt based on topics of section ' . $section_number . '(' . $section_number . getOrdinalSuffix($section_number) . ' H2 in outline)]'
        ];
    }
    
    return wp_json_encode($image_prompts, JSON_PRETTY_PRINT);
}

/**
 * Get ordinal suffix for numbers (1st, 2nd, 3rd, 4th, etc.)
 * 
 * @param int $number The number to get ordinal suffix for
 * @return string The ordinal suffix
 */
function getOrdinalSuffix($number) {
    $ends = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'];
    if ((($number % 100) >= 11) && (($number % 100) <= 13)) {
        return 'th';
    }
    return $ends[$number % 10];
}











/**
 * Create responsive image data for image tracking
 * 
 * @param int $post_id WordPress post ID
 * @return void
 */
function igny8_create_responsive_image_data($post_id) {
    // Get article images data
    $article_images_data = get_post_meta($post_id, '_igny8_article_images_data', true);
    if (empty($article_images_data)) {
        return;
    }
    
    $article_images_data = json_decode($article_images_data, true);
    if (!is_array($article_images_data) || empty($article_images_data)) {
        return;
    }
    
    // Create responsive data structure
    $responsive_data = [];
    
    foreach ($article_images_data as $index => $image_data) {
        // Find the prompt key (prompt-img-X)
        $prompt_key = null;
        $prompt_value = null;
        
        foreach ($image_data as $key => $value) {
            if (strpos($key, 'prompt-img-') === 0) {
                $prompt_key = $key;
                $prompt_value = $value;
                break;
            }
        }
        
        if (!$prompt_key || !$prompt_value) {
            continue;
        }
        
        // Extract image number from prompt key
        $image_number = str_replace('prompt-img-', '', $prompt_key);
        $section_number = intval($image_number) + 1; // 2nd, 3rd, 4th H2, etc.
        
        // Create responsive data entry
        $responsive_data[] = [
            'desktop' => [
                'attachment_id' => null, // Will be filled when images are generated
                'url' => null
            ],
            'mobile' => [
                'attachment_id' => null, // Will be filled when images are generated  
                'url' => null
            ],
            'section' => 'Section ' . $section_number,
            'prompt' => $prompt_value
        ];
    }
    
    // Save responsive data
    update_post_meta($post_id, '_igny8_article_images_responsive', wp_json_encode($responsive_data));
}

/**
 * Calculate safe image quantity based on outline analysis
 * 
 * @param string $idea_data The idea data containing the outline
 * @param int $max_in_article_images Maximum images requested
 * @return int Safe maximum images (max 1 less than total H2 sections)
 */
function igny8_calculate_safe_image_quantity($idea_data, $max_in_article_images) {
    // Count H2 sections in the outline
    $h2_count = igny8_count_h2_sections_in_outline($idea_data);
    
    // Safety rule: Max images = H2 count - 1 (since we start from 2nd H2)
    $max_possible_images = max(0, $h2_count - 1);
    
    // Use the smaller of requested max or possible max
    $safe_max = min($max_in_article_images, $max_possible_images);
    
    // Log the safety calculation
    igny8_log_ai_event('Image Quantity Safety Check', 'ai', 'content_generation', 'info', 
        'Calculated safe image quantity', 
        'H2 Sections: ' . $h2_count . ', Requested: ' . $max_in_article_images . ', Safe Max: ' . $safe_max
    );
    
    return $safe_max;
}

/**
 * Count H2 sections in the outline
 * 
 * @param string $idea_data The idea data containing the outline
 * @return int Number of H2 sections found
 */
function igny8_count_h2_sections_in_outline($idea_data) {
    // Look for H2 patterns in the outline
    // Common patterns: "## ", "**", "H2:", "Section", etc.
    $patterns = [
        '/##\s+/',                    // Markdown H2: ## Section
        '/\*\*[^*]+\*\*/',            // Bold text: **Section**
        '/H2:\s*[^\n]+/',            // H2: Section
        '/Section\s+\d+/i',         // Section 1, Section 2, etc.
        '/\d+\.\s+[A-Z][^\.]+\./',   // Numbered sections: 1. Section Title.
        '/^[A-Z][^\.]+\.$/m'          // Title case sections ending with period
    ];
    
    $max_count = 0;
    
    foreach ($patterns as $pattern) {
        preg_match_all($pattern, $idea_data, $matches);
        $count = count($matches[0]);
        if ($count > $max_count) {
            $max_count = $count;
        }
    }
    
    // If no clear H2 patterns found, estimate based on content length
    if ($max_count === 0) {
        // Estimate: roughly 1 H2 per 200-300 words in outline
        $word_count = str_word_count($idea_data);
        $max_count = max(3, floor($word_count / 250)); // Minimum 3 sections
    }
    
    // Ensure we have at least 2 sections (1 for featured, 1 for in-article)
    return max(2, $max_count);
}


/**
 * Add in-article image to post meta for meta box integration
 * 
 * @param int $post_id WordPress post ID
 * @param int $attachment_id WordPress attachment ID
 * @param string $label Image label (e.g., 'desktop-1', 'mobile-2')
 * @param string $device Device type ('desktop' or 'mobile')
 * @param int|null $section Section number (optional)
 * @return bool Success status
 */
function igny8_add_inarticle_image_meta($post_id, $attachment_id, $label, $device = 'desktop', $section = null) {
    error_log("[IGNY8 DEBUG] igny8_add_inarticle_image_meta called with post_id: $post_id, attachment_id: $attachment_id, label: $label, device: $device, section: $section");
    
    $url = wp_get_attachment_url($attachment_id);
    if (!$url) {
        error_log("[IGNY8 DEBUG] Failed to get attachment URL for attachment ID: $attachment_id");
        return false;
    }

    $images = get_post_meta($post_id, '_igny8_inarticle_images', true);
    if (!is_array($images)) {
        $images = [];
    }

    $images[$label] = [
        'label'         => $label,
        'attachment_id' => $attachment_id,
        'url'           => $url,
        'device'        => $device,
        'section'       => $section,
    ];

    error_log("[IGNY8 DEBUG] About to save images meta for post $post_id: " . print_r($images, true));

    $result = update_post_meta($post_id, '_igny8_inarticle_images', $images);

    error_log("[IGNY8 DEBUG] update_post_meta result: " . ($result ? 'SUCCESS' : 'FAILED'));

    return $result !== false;
}
/**
 * Process AI queue result and save to database
 */
function igny8_process_ai_queue_result($action, $result) {
    global $wpdb;
    
    switch ($action) {
        case 'clustering':
            if (isset($result['clusters'])) {
                // Get sector options for assignment logic
                $sector_options = igny8_get_sector_options();
                $sector_count = count($sector_options);
                
                foreach ($result['clusters'] as $cluster_data) {
                    // Determine sector_id based on sector count
                    $sector_id = 1; // Default fallback
                    
                    if ($sector_count == 1) {
                        // Only 1 sector: assign all clusters to that sector
                        $sector_id = $sector_options[0]['value'];
                    } elseif ($sector_count > 1) {
                        // Multiple sectors: use AI response sector assignment
                        if (isset($cluster_data['sector']) && !empty($cluster_data['sector'])) {
                            // Find sector ID by matching sector name from AI response
                            foreach ($sector_options as $sector) {
                                if (strtolower(trim($sector['label'])) === strtolower(trim($cluster_data['sector']))) {
                                    $sector_id = $sector['value'];
                                    break;
                                }
                            }
                        }
                        // If no match found or no sector in AI response, use first sector as fallback
                        if ($sector_id == 1 && !isset($cluster_data['sector'])) {
                            $sector_id = $sector_options[0]['value'];
                        }
                    }
                    
                    $wpdb->insert(
                        $wpdb->prefix . 'igny8_clusters',
                        [
                            'cluster_name' => sanitize_text_field($cluster_data['name']),
                            'sector_id' => $sector_id,
                            'status' => 'active',
                            'keyword_count' => count($cluster_data['keywords']),
                            'total_volume' => 0,
                            'avg_difficulty' => 0,
                            'mapped_pages_count' => 0,
                            'created_at' => current_time('mysql')
                        ],
                        ['%s', '%d', '%s', '%d', '%d', '%f', '%d', '%s']
                    );
                    
                    $cluster_id = $wpdb->insert_id;
                    
                    // Trigger taxonomy term creation for AI-generated cluster
                    do_action('igny8_cluster_added', $cluster_id);
                    
                    // Update keywords with cluster_id
                    foreach ($cluster_data['keywords'] as $keyword_name) {
                        $wpdb->update(
                            $wpdb->prefix . 'igny8_keywords',
                            ['cluster_id' => $cluster_id],
                            ['keyword' => $keyword_name],
                            ['%d'],
                            ['%s']
                        );
                    }
                    
                    igny8_update_cluster_metrics($cluster_id);
                }
            }
            break;
            
        case 'ideas':
            if (isset($result['ideas'])) {
                foreach ($result['ideas'] as $idea_data) {
                    $wpdb->insert(
                        $wpdb->prefix . 'igny8_content_ideas',
                        [
                            'idea_title' => sanitize_text_field($idea_data['title']),
                            'idea_description' => sanitize_textarea_field($idea_data['description']),
                            'content_structure' => sanitize_text_field($idea_data['type']),
                            'content_type' => 'post', // Default to post for AI generated ideas
                            'keyword_cluster_id' => intval($idea_data['cluster_id']),
                            'priority' => sanitize_text_field($idea_data['priority']),
                            'status' => 'draft',
                            'estimated_word_count' => intval($idea_data['estimated_word_count']),
                            'ai_generated' => 1,
                            'created_at' => current_time('mysql')
                        ],
                        ['%s', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%s']
                    );
                }
            }
            break;
            
        case 'mapping':
            if (isset($result['mappings'])) {
                foreach ($result['mappings'] as $mapping_data) {
                    if ($mapping_data['relevance_score'] >= 0.7) {
                        $cluster_term_id = $wpdb->get_var($wpdb->prepare("
                            SELECT cluster_term_id FROM {$wpdb->prefix}igny8_clusters WHERE id = %d
                        ", $mapping_data['cluster_id']));
                        
                        if ($cluster_term_id) {
                            wp_set_object_terms(
                                $mapping_data['content_id'],
                                $cluster_term_id,
                                'clusters',
                                true
                            );
                        }
                    }
                }
            }
            break;
            
        case 'content_generation':
            if (isset($result['title']) && isset($result['content'])) {
                // Pass task_id through the AI response for proper task updating
                if (isset($data['task_id'])) {
                    $result['task_id'] = $data['task_id'];
                }
                
                // Pass cluster and sector data from original task
                if (isset($data['cluster']) && $data['cluster']) {
                    $result['cluster_id'] = $data['cluster']->id;
                    $result['sector_id'] = $data['cluster']->sector_id;
                }
                
                // Create WordPress post from AI response
                $post_id = igny8_create_post_from_ai_response($result);
                
                if ($post_id) {
                    // Log successful content generation
                    igny8_log_ai_event('content_created', 'writer', 'content_generation', 'success', 
                        'Post created successfully', "Post ID: {$post_id}");
                } else {
                    // Log failure
                    igny8_log_ai_event('content_failed', 'writer', 'content_generation', 'error', 
                        'Failed to create post from AI response');
                }
            } else {
                // Log invalid response format
                igny8_log_ai_event('invalid_response', 'writer', 'content_generation', 'error', 
                    'AI response missing required fields (title, content)');
            }
            break;
    }
}



/**
 * Create WordPress post from AI response
 */
function igny8_create_post_from_ai_response($ai_response) {
    global $wpdb;
    
    try {
        // Get cluster and sector data from the task, not from AI response
        $cluster_id = null;
        $sector_id = null;
        
        if (!empty($ai_response['task_id'])) {
            error_log('Igny8: Looking up task_id: ' . intval($ai_response['task_id']));
            echo "<strong>Igny8 DEBUG: Looking up task_id: " . intval($ai_response['task_id']) . "</strong><br>";
            
            $task = $wpdb->get_row($wpdb->prepare(
                "SELECT cluster_id, content_structure, content_type FROM {$wpdb->prefix}igny8_tasks WHERE id = %d",
                intval($ai_response['task_id'])
            ));
            
            error_log('Igny8: Task lookup result: ' . ($task ? 'Found task' : 'Task not found'));
            echo "<strong>Igny8 DEBUG: Task lookup result: " . ($task ? 'Found task' : 'Task not found') . "</strong><br>";
            
            if ($task) {
                error_log('Igny8: Task cluster_id: ' . ($task->cluster_id ?: 'NULL'));
                echo "<strong>Igny8 DEBUG: Task cluster_id: " . ($task->cluster_id ?: 'NULL') . "</strong><br>";
            }
            
            if ($task && $task->cluster_id) {
                $cluster_id = $task->cluster_id;
                // Get sector_id from cluster (this is the sector taxonomy term ID)
                $cluster_data = $wpdb->get_row($wpdb->prepare(
                    "SELECT sector_id FROM {$wpdb->prefix}igny8_clusters WHERE id = %d",
                    intval($cluster_id)
                ));
                if ($cluster_data) {
                    $sector_id = $cluster_data->sector_id; // This is already the taxonomy term ID
                    error_log('Igny8: Found sector_id (term ID): ' . $sector_id);
                    echo "<strong>Igny8 DEBUG: Found sector_id (term ID): " . $sector_id . "</strong><br>";
                }
            }
        } else {
            error_log('Igny8: No task_id in AI response');
            echo "<strong>Igny8 DEBUG: No task_id in AI response</strong><br>";
        }
        
        // Get content structure and type from task (not from AI response)
        $content_structure = $task->content_structure ?? 'cluster_hub';
        $content_type = $task->content_type ?? 'post';
        $post_type = igny8_map_content_type_to_post_type($content_structure);
        
        // Prepare content for processing
        $content = $ai_response['content'] ?? '';
        $editor_type = get_option('igny8_editor_type', 'block');
        error_log("IGNY8 DEBUG - EDITOR TYPE FROM DB: " . $editor_type);
        
        // Content is now direct HTML from the new prompt format
        // No need to check for nested structures or convert from JSON
        igny8_log_ai_event('Content Format Detection', 'writer', 'content_generation', 'info', 'Using direct HTML content from AI response', 'Editor type: ' . $editor_type);
        
        // NEW PIPELINE: Process content through integrated pipeline
        // Step 1: Convert to Gutenberg blocks if using block editor
        if ($editor_type === 'block') {
            error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - Block editor path selected");
            $final_block_content = igny8_convert_to_wp_blocks($content);
            error_log("IGNY8 DEBUG - Conversion Completed");
            
            // Step 1.5: Validate and fix block structure
            $final_block_content = igny8_validate_and_fix_blocks($final_block_content);
            
            error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - About to call insert_igny8_shortcode_blocks_into_blocks()");
            error_log("IGNY8 DEBUG: CALL LOCATION - igny8_create_post_from_ai_response() -> Block Editor Path -> Line 1186");
            $final_block_content = insert_igny8_shortcode_blocks_into_blocks($final_block_content);
            
            // Check if shortcodes were successfully injected
            $has_shortcode = false;
            foreach (parse_blocks($final_block_content) as $block) {
                if (
                    $block['blockName'] === 'core/shortcode' &&
                    isset($block['innerContent']) &&
                    is_array($block['innerContent']) &&
                    preg_match('/\[igny8-image.*?\]/', implode('', $block['innerContent']))
                ) {
                    $has_shortcode = true;
                    break;
                }
            }

            if (!$has_shortcode) {
                error_log("IGNY8 DEBUG - Shortcode injection failed: No shortcodes found in parsed blocks");
                igny8_log_ai_event('Shortcode Injection Failed', 'writer', 'content_generation', 'warning', 'No shortcodes found after injection - proceeding without shortcodes', 'Editor type: ' . $editor_type);
                // FALLBACK: Continue with post creation without shortcodes
                $content = $final_block_content;
            } else {
                $content = $final_block_content;
            }
            
            igny8_log_ai_event('Content Wrapped as Blocks', 'writer', 'content_generation', 'info', 'HTML content wrapped as Gutenberg blocks', 'Editor type: ' . $editor_type);
        } else {
            // For classic editor, use plain shortcode logic
            error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - Classic editor path selected");
            error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - About to call insert_igny8_image_shortcodes_classic()");
            error_log("IGNY8 DEBUG: CALL LOCATION - igny8_create_post_from_ai_response() -> Classic Editor Path -> Line 1214");
            $content = insert_igny8_image_shortcodes_classic($content);
            
            // Check if shortcodes were successfully injected
            if (strpos($content, '[igny8-image') === false) {
                error_log("IGNY8 DEBUG - Shortcode injection failed: No shortcodes found in content");
                igny8_log_ai_event('Shortcode Injection Failed', 'writer', 'content_generation', 'warning', 'No shortcodes found after injection - proceeding without shortcodes', 'Editor type: ' . $editor_type);
                // FALLBACK: Continue with post creation without shortcodes
            }
            
            igny8_log_ai_event('Content Format Detection', 'writer', 'content_generation', 'info', 'Using HTML content with shortcodes for Classic Editor', 'Editor type: ' . $editor_type);
        }
        
        // Get new content decision setting AFTER content processing
        $new_content_action = get_option('igny8_new_content_action', 'draft');
        $post_status = ($new_content_action === 'publish') ? 'publish' : 'draft';
        
        // Debug logging
        error_log('Igny8 DEBUG: New content action setting: ' . $new_content_action);
        error_log('Igny8 DEBUG: Post status will be: ' . $post_status);
        error_log('Igny8 DEBUG: All options with igny8_new_content_action: ' . print_r(get_option('igny8_new_content_action'), true));
        echo "<strong>Igny8 DEBUG: New content action setting: " . $new_content_action . "</strong><br>";
        echo "<strong>Igny8 DEBUG: Post status will be: " . $post_status . "</strong><br>";
        
        $post_data = [
            'post_title' => sanitize_text_field($ai_response['title'] ?? 'AI Generated Content'),
            'post_content' => $content,
            'post_excerpt' => sanitize_textarea_field($ai_response['meta_description'] ?? ''),
            'post_status' => $post_status, // Use setting from New Content Decision
            'post_type' => $post_type,
            'post_author' => get_current_user_id(),
            'post_date' => current_time('mysql'),
            'meta_input' => [
                '_igny8_ai_generated' => 1,
                '_igny8_content_type' => $content_structure,
                '_igny8_word_count' => intval($ai_response['word_count'] ?? 0),
                '_igny8_keywords_used' => wp_json_encode($ai_response['keywords_used'] ?? []),
                '_igny8_internal_links' => wp_json_encode($ai_response['internal_link_opportunities'] ?? [])
            ]
        ];
        
        error_log("IGNY8 DEBUG - POST CONTENT ABOUT TO SAVE:\n" . $post_data['post_content']);
        
        // Optional debug file write
        if (defined('IGNY8_DEBUG_BLOCKS') && IGNY8_DEBUG_BLOCKS === true) {
            file_put_contents(WP_CONTENT_DIR . '/igny8-block-output.html', $content);
        }
        
        // Create the post
        $post_id = wp_insert_post($post_data);
        
            if (is_wp_error($post_id)) {
                error_log('Igny8: Failed to create post - ' . $post_id->get_error_message());
                igny8_log_ai_event('WordPress Post Creation Failed', 'writer', 'content_generation', 'error', 'Failed to create WordPress post', 'Error: ' . $post_id->get_error_message());
                return false;
            }
        
        igny8_log_ai_event('WordPress Post Created', 'writer', 'content_generation', 'success', 'WordPress post created successfully', 'Post ID: ' . $post_id . ', Title: ' . $ai_response['title']);
        
        // Note: Task record updating is handled by the AJAX handler
        // This function only creates the WordPress post and links it to the task
        
        // Save AI-generated meta fields to post meta
        if (!empty($ai_response['meta_title'])) {
            update_post_meta($post_id, '_igny8_meta_title', sanitize_text_field($ai_response['meta_title']));
            igny8_log_ai_event('SEO Meta Title Saved', 'writer', 'content_generation', 'success', 'Meta title saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_meta_title');
        }
        if (!empty($ai_response['meta_description'])) {
            update_post_meta($post_id, '_igny8_meta_description', sanitize_textarea_field($ai_response['meta_description']));
            igny8_log_ai_event('SEO Meta Description Saved', 'writer', 'content_generation', 'success', 'Meta description saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_meta_description');
        }
        // === Igny8 Keyword Meta ===
        if (!empty($ai_response['primary_keyword'])) {
            update_post_meta($post_id, '_igny8_primary_keywords', sanitize_text_field($ai_response['primary_keyword']));
            igny8_log_ai_event('Primary Keywords Saved', 'writer', 'content_generation', 'success', 'Primary keywords saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_primary_keywords');
        }
        if (!empty($ai_response['keywords'])) {
            update_post_meta($post_id, '_igny8_primary_keywords', sanitize_text_field($ai_response['keywords']));
            igny8_log_ai_event('Primary Keywords Saved', 'writer', 'content_generation', 'success', 'Primary keywords saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_primary_keywords');
        }

        if (!empty($ai_response['secondary_keywords'])) {
            $secondary = is_array($ai_response['secondary_keywords'])
                ? implode(', ', array_map('sanitize_text_field', $ai_response['secondary_keywords']))
                : sanitize_text_field($ai_response['secondary_keywords']);
            update_post_meta($post_id, '_igny8_secondary_keywords', $secondary);
            igny8_log_ai_event('Secondary Keywords Saved', 'writer', 'content_generation', 'success', 'Secondary keywords saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_secondary_keywords');
        }
        if (!empty($ai_response['word_count'])) {
            update_post_meta($post_id, '_igny8_word_count', intval($ai_response['word_count']));
        }
        
        // === Save Image Prompts ===
        // Handle featured image prompt (direct field in new format)
        if (!empty($ai_response['featured_image'])) {
            update_post_meta($post_id, '_igny8_featured_image_prompt', sanitize_textarea_field($ai_response['featured_image']));
            igny8_log_ai_event('Featured Image Prompt Saved', 'writer', 'content_generation', 'success', 'Featured image prompt saved to post meta', 'Post ID: ' . $post_id . ', Field: _igny8_featured_image_prompt');
        }
        
        // Handle in-article image prompts (direct field in new format)
        if (!empty($ai_response['in_article_images']) && is_array($ai_response['in_article_images'])) {
            $article_images_data = [];
            foreach ($ai_response['in_article_images'] as $index => $image_data) {
                // Handle both formats: array of strings or array of objects
                if (is_string($image_data)) {
                    // Old format: array of strings
                    $clean_value = wp_strip_all_tags($image_data);
                    $article_images_data[] = [
                        'prompt-img-' . ($index + 1) => sanitize_textarea_field($clean_value)
                    ];
                } elseif (is_array($image_data)) {
                    // New format: array of objects with prompt-img-X keys
                    $sanitized_data = [];
                    foreach ($image_data as $key => $value) {
                        if (strpos($key, 'prompt-img-') === 0) {
                            // Strip HTML tags and sanitize to ensure only plain text
                            $clean_value = wp_strip_all_tags($value);
                            $sanitized_data[$key] = sanitize_textarea_field($clean_value);
                        }
                    }
                    if (!empty($sanitized_data)) {
                        $article_images_data[] = $sanitized_data;
                    }
                }
            }
            
            if (!empty($article_images_data)) {
                update_post_meta($post_id, '_igny8_article_images_data', wp_json_encode($article_images_data));
                igny8_log_ai_event('In-Article Image Prompts Saved', 'writer', 'content_generation', 'success', 'In-article image prompts saved to post meta', 'Post ID: ' . $post_id . ', Count: ' . count($article_images_data) . ', Field: _igny8_article_images_data');
            }
        }
        
        // Handle legacy image_prompts format for backward compatibility
        // DISABLED: No longer saving to _igny8_image_prompts field
        // if (!empty($ai_response['image_prompts'])) {
        //     update_post_meta($post_id, '_igny8_image_prompts', wp_json_encode($ai_response['image_prompts']));
        //     igny8_log_ai_event('Legacy Image Prompts Saved', 'writer', 'content_generation', 'info', 'Legacy image prompts format saved for backward compatibility', 'Post ID: ' . $post_id . ', Field: _igny8_image_prompts');
        // }
        
        // === Associate Cluster Term ===
        $cluster_success = false;
        if (!empty($cluster_id)) {
            global $wpdb;
            error_log('Igny8: Attempting to associate cluster_id: ' . intval($cluster_id));
            echo "<strong>Igny8 DEBUG: Attempting to associate cluster_id: " . intval($cluster_id) . "</strong><br>";
            
            $cluster_term_id = $wpdb->get_var($wpdb->prepare("
                SELECT cluster_term_id FROM {$wpdb->prefix}igny8_clusters WHERE id = %d
            ", intval($cluster_id)));
            
            error_log('Igny8: Found cluster_term_id: ' . ($cluster_term_id ?: 'NULL'));
            echo "<strong>Igny8 DEBUG: Found cluster_term_id: " . ($cluster_term_id ?: 'NULL') . "</strong><br>";
            
            if ($cluster_term_id) {
                // Check if taxonomy exists
                if (!taxonomy_exists('clusters')) {
                    error_log('Igny8: ERROR - clusters taxonomy does not exist!');
                    echo "<strong>Igny8 DEBUG: ERROR - clusters taxonomy does not exist!</strong><br>";
                    igny8_log_ai_event('Cluster Association Failed', 'writer', 'content_generation', 'error', 'Clusters taxonomy does not exist', 'Post ID: ' . $post_id . ', Cluster ID: ' . $cluster_id);
                } else {
                    error_log('Igny8: clusters taxonomy exists, attempting association...');
                    echo "<strong>Igny8 DEBUG: clusters taxonomy exists, attempting association...</strong><br>";
                    $cluster_result = wp_set_object_terms($post_id, intval($cluster_term_id), 'clusters', false);
                    $cluster_success = !is_wp_error($cluster_result);
                    error_log('Igny8: Cluster association result: ' . ($cluster_success ? 'SUCCESS' : 'FAILED - ' . ($cluster_result->get_error_message() ?? 'Unknown error')));
                    echo "<strong>Igny8 DEBUG: Cluster association result: " . ($cluster_success ? 'SUCCESS' : 'FAILED - ' . ($cluster_result->get_error_message() ?? 'Unknown error')) . "</strong><br>";
                    
                    if ($cluster_success) {
                        igny8_log_ai_event('Cluster Associated', 'writer', 'content_generation', 'success', 'Post associated with cluster taxonomy', 'Post ID: ' . $post_id . ', Cluster ID: ' . $cluster_id . ', Term ID: ' . $cluster_term_id);
                    } else {
                        igny8_log_ai_event('Cluster Association Failed', 'writer', 'content_generation', 'error', 'Failed to associate cluster', 'Post ID: ' . $post_id . ', Error: ' . ($cluster_result->get_error_message() ?? 'Unknown'));
                    }
                }
            } else {
                error_log('Igny8: Cluster term not found for cluster_id ' . intval($cluster_id));
                echo "<strong>Igny8 DEBUG: Cluster term not found for cluster_id " . intval($cluster_id) . "</strong><br>";
                igny8_log_ai_event('Cluster Term Not Found', 'writer', 'content_generation', 'warning', 'Cluster term not found in database', 'Post ID: ' . $post_id . ', Cluster ID: ' . $cluster_id);
            }
        } else {
            error_log('Igny8: No cluster_id found in task');
            echo "<strong>Igny8 DEBUG: No cluster_id found in task</strong><br>";
        }

        // === Associate Sector Term ===
        $sector_success = false;
        if (!empty($sector_id)) {
            error_log('Igny8: Attempting to associate sector_id: ' . intval($sector_id));
            echo "<strong>Igny8 DEBUG: Attempting to associate sector_id: " . intval($sector_id) . "</strong><br>";
            
            // sector_id is already the taxonomy term ID, no need to look it up
            $sector_term_id = intval($sector_id);
            
            error_log('Igny8: Using sector_term_id directly: ' . $sector_term_id);
            echo "<strong>Igny8 DEBUG: Using sector_term_id directly: " . $sector_term_id . "</strong><br>";
            
            // Check if taxonomy exists
            if (!taxonomy_exists('sectors')) {
                error_log('Igny8: ERROR - sectors taxonomy does not exist!');
                echo "<strong>Igny8 DEBUG: ERROR - sectors taxonomy does not exist!</strong><br>";
            } else {
                error_log('Igny8: sectors taxonomy exists, attempting association...');
                echo "<strong>Igny8 DEBUG: sectors taxonomy exists, attempting association...</strong><br>";
                $sector_result = wp_set_object_terms($post_id, $sector_term_id, 'sectors', false);
                $sector_success = !is_wp_error($sector_result);
                error_log('Igny8: Sector association result: ' . ($sector_success ? 'SUCCESS' : 'FAILED - ' . ($sector_result->get_error_message() ?? 'Unknown error')));
                echo "<strong>Igny8 DEBUG: Sector association result: " . ($sector_success ? 'SUCCESS' : 'FAILED - ' . ($sector_result->get_error_message() ?? 'Unknown error')) . "</strong><br>";
            }
        } else {
            error_log('Igny8: No sector_id found in task');
            echo "<strong>Igny8 DEBUG: No sector_id found in task</strong><br>";
        }
        
        // Handle tags if content type supports them
        if (in_array($post_type, ['post', 'product']) && !empty($ai_response['tags'])) {
            $tags = array_map('trim', $ai_response['tags']);
            wp_set_post_tags($post_id, $tags);
            igny8_log_ai_event('Tags Added', 'writer', 'content_generation', 'success', 'Post tags added', 'Post ID: ' . $post_id . ', Tags: ' . implode(', ', $tags));
        }

        // Handle categories
        if (!empty($ai_response['categories'])) {
            igny8_set_post_categories($post_id, $ai_response['categories']);
            igny8_log_ai_event('Categories Added', 'writer', 'content_generation', 'success', 'Post categories added', 'Post ID: ' . $post_id);
        }
        
        // Store cluster and sector metadata
        igny8_store_content_metadata($post_id, $ai_response);
        
        // Add meta description if available
        if (!empty($ai_response['meta_description'])) {
            update_post_meta($post_id, '_yoast_wpseo_metadesc', $ai_response['meta_description']);
        }
        
        // Final summary log
        igny8_log_ai_event('Content Generation Complete', 'writer', 'content_generation', 'success', 'All content components saved successfully', 'Post ID: ' . $post_id . ', Status: ' . $post_status . ', Type: ' . $post_type);
        
        
        
        return $post_id;
        
    } catch (Exception $e) {
        error_log('Igny8: Exception creating post - ' . $e->getMessage());
        return false;
    }
}

/**
 * Map AI content type to WordPress post type
 */
function igny8_map_content_type_to_post_type($content_type) {
    $mapping = [
        'blog_post' => 'post',
        'landing_page' => 'page',
        'product_page' => 'product',
        'guide_tutorial' => 'post',
        'news_article' => 'post',
        'review' => 'post',
        'comparison' => 'post',
        'email' => 'post',
        'social_media' => 'post',
        'page' => 'page',
        'product' => 'product',
        'guide' => 'post',
        'tutorial' => 'post'
    ];
    
    return $mapping[$content_type] ?? 'post';
}

/**
 * Get available models for content generation
 */
function igny8_get_available_models() {
    return [
        'gpt-4.1' => 'GPT-4.1 (Content creation, coding, analysis, high-quality content generation)',
        'gpt-4o-mini' => 'GPT-4o mini (Bulk tasks, lightweight AI, cost-effective for high-volume operations)',
        'gpt-4o' => 'GPT-4o (Advanced AI, better general performance, multimodal)'
    ];
}

/**
 * Get queue status for user
 */
function igny8_get_ai_queue_status($user_id = null) {
    global $wpdb;
    
    $user_id = $user_id ?: get_current_user_id();
    
    $status = $wpdb->get_row($wpdb->prepare("
        SELECT 
            COUNT(*) as total,
            SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
            SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) as processing,
            SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
            SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
        FROM {$wpdb->prefix}igny8_ai_queue 
        WHERE user_id = %d
    ", $user_id));
    
    return $status;
}

/**
 * Get image dimensions based on size preset and provider
 * 
 * @param string $size_preset Size preset (featured, desktop, mobile)
 * @param string $provider Image provider (runware, openai, dalle)
 * @return array ['width' => int, 'height' => int]
 */
function igny8_get_image_dimensions($size_preset = 'featured', $provider = 'runware') {
    // Size presets for different image types
    $size_presets = [
        'runware' => [
            'featured' => ['width' => 1280, 'height' => 832],
            'desktop' => ['width' => 1024, 'height' => 1024],
            'mobile' => ['width' => 960, 'height' => 1280]
        ],
        'openai' => [
            'featured' => ['width' => 1024, 'height' => 1024],  // OpenAI only supports square
            'desktop' => ['width' => 1024, 'height' => 1024],
            'mobile' => ['width' => 1024, 'height' => 1024]
        ],
        'dalle' => [
            'featured' => ['width' => 1024, 'height' => 1024],  // Placeholder for DALL-E
            'desktop' => ['width' => 1024, 'height' => 1024],
            'mobile' => ['width' => 1024, 'height' => 1024]
        ]
    ];
    
    // Get dimensions for the provider and size
    if (isset($size_presets[$provider][$size_preset])) {
        return $size_presets[$provider][$size_preset];
    }
    
    // Fallback to featured size for the provider
    if (isset($size_presets[$provider]['featured'])) {
        return $size_presets[$provider]['featured'];
    }
    
    // Ultimate fallback
    return ['width' => 1280, 'height' => 832];
}



/**
 * DEPRECATED: Generate featured image for post from post meta prompt
 * 
 * This function has been moved to ai/writer/images/image-generation.php
 * and is now included directly in the plugin bootstrap process.
 * 
 * @deprecated 5.2.0 Function moved to ai/writer/images/image-generation.php
 */

/**
 * Validate and fix Gutenberg block structure
 * Ensures all heading blocks have proper level attributes
 */
function igny8_validate_and_fix_blocks($block_content) {
    if (empty($block_content)) {
        return $block_content;
    }
    
    $blocks = parse_blocks($block_content);
    $fixed_blocks = [];
    
    foreach ($blocks as $index => $block) {
        // Fix heading blocks missing level attribute
        if (($block['blockName'] ?? null) === 'core/heading') {
            $level = $block['attrs']['level'] ?? null;
            
            if ($level === null) {
                // Try to extract level from innerHTML
                $inner_html = $block['innerHTML'] ?? '';
                if (preg_match('/<h([1-6])[^>]*>/i', $inner_html, $matches)) {
                    $detected_level = intval($matches[1]);
                    $block['attrs']['level'] = $detected_level;
                    error_log("IGNY8 BLOCKS: Fixed heading block #$index - detected level $detected_level from innerHTML");
                } else {
                    // Default to H2 if we can't detect
                    $block['attrs']['level'] = 2;
                    error_log("IGNY8 BLOCKS: Fixed heading block #$index - defaulted to level 2");
                }
            }
        }
        
        $fixed_blocks[] = $block;
    }
    
    return serialize_blocks($fixed_blocks);
}

/**
 * Inject plain Igny8 shortcodes after H2 for Classic Editor only (no block markup).
 */
function insert_igny8_image_shortcodes_classic($html_content) {
    error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - insert_igny8_image_shortcodes_classic()");
    error_log("IGNY8 DEBUG: CALLED FROM - igny8_create_post_from_ai_response() function in ai/modules-ai.php");
    error_log("IGNY8 DEBUG - CLASSIC: Starting shortcode injection");
    error_log("IGNY8 DEBUG - CLASSIC: Input content length: " . strlen($html_content));
    
    if (empty($html_content)) {
        error_log("IGNY8 DEBUG - CLASSIC: Content is empty, returning");
        return $html_content;
    }

    $pattern = '/(<h2[^>]*>.*?<\/h2>)/i';
    $matches = [];
    preg_match_all($pattern, $html_content, $matches, PREG_OFFSET_CAPTURE);

    error_log("IGNY8 DEBUG - CLASSIC: Found " . count($matches[0]) . " H2 headings");

    if (empty($matches[0])) {
        error_log("IGNY8 DEBUG - CLASSIC: No H2 headings found, returning original content");
        return $html_content;
    }

    $offset = 0;
    $image_index = 0;

    foreach (array_reverse($matches[0]) as $match) {
        $image_index++;
        error_log("IGNY8 DEBUG - CLASSIC: Processing H2 #{$image_index}");

        // Skip first H2
        if ($image_index === count($matches[0])) {
            error_log("IGNY8 DEBUG - CLASSIC: Skipping first H2");
            continue;
        }

        // Inject plain shortcodes (no Gutenberg markup)
        $shortcode = "\n\n[igny8-image id=\"desktop-{$image_index}\"] [igny8-image id=\"mobile-{$image_index}\"]\n\n";
        error_log("IGNY8 DEBUG - CLASSIC: Injecting shortcode: " . trim($shortcode));

        $insert_pos = $match[1] + strlen($match[0]) + $offset;
        $html_content = substr_replace($html_content, $shortcode, $insert_pos, 0);

        $offset += strlen($shortcode);
    }

    error_log("IGNY8 DEBUG - CLASSIC: Final content length: " . strlen($html_content));
    error_log("IGNY8 DEBUG - CLASSIC: Shortcodes in final content: " . (strpos($html_content, '[igny8-image') !== false ? 'YES' : 'NO'));

    return $html_content;
}

/**
 * Inject Gutenberg shortcode blocks after each <h2> heading block (core/heading, level 2)
 * Adds minimal, meaningful logs. Silences irrelevant debug spam.
 *
 * @param string $block_content Serialized Gutenberg block content
 * @return string|false Modified content or false if injection fails
 */
function insert_igny8_shortcode_blocks_into_blocks($block_content) {
    error_log("IGNY8 DEBUG: I AM ACTIVE AND RUNNING IN MODULE-AI.PHP - insert_igny8_shortcode_blocks_into_blocks()");
    error_log("IGNY8 DEBUG: CALLED FROM - igny8_create_post_from_ai_response() function in ai/modules-ai.php");
    
    if (empty($block_content)) {
        error_log("IGNY8 BLOCKS: No content passed to shortcode injector");
        return $block_content;
    }

    $blocks = parse_blocks($block_content);
    $output = [];
    $h2_count = 0;
    $injected = 0;
    $heading_blocks_found = 0;
    $valid_h2_blocks = 0;

    error_log("IGNY8 BLOCKS: Parsed " . count($blocks) . " total blocks");

    foreach ($blocks as $index => $block) {
        $output[] = $block;

        if (($block['blockName'] ?? null) === 'core/heading') {
            $heading_blocks_found++;
            $level = $block['attrs']['level'] ?? null;
            
            error_log("IGNY8 BLOCKS: Heading block #$index - level: " . ($level ?? 'NULL') . ", innerHTML: " . substr($block['innerHTML'] ?? '', 0, 50) . "...");

            if ($level !== 2) {
                if ($level === null) {
                    error_log("IGNY8 BLOCKS: Skipping heading block #$index — missing 'level' attribute");
                } else {
                    error_log("IGNY8 BLOCKS: Skipping heading block #$index — level $level (not H2)");
                }
                continue;
            }

            $valid_h2_blocks++;
            $h2_count++;
            
            if ($h2_count === 1) {
                error_log("IGNY8 BLOCKS: Skipping first H2 (no shortcode)");
                continue;
            }

            $shortcode = "[igny8-image id=\"desktop-{$h2_count}\"] [igny8-image id=\"mobile-{$h2_count}\"]";
            error_log("IGNY8 BLOCKS: Injecting shortcode after H2 #{$h2_count}: " . $shortcode);
            
            $output[] = [
                'blockName'     => 'core/shortcode',
                'attrs'         => [],
                'innerBlocks'   => [],
                'innerHTML'     => $shortcode,
                'innerContent'  => [$shortcode]
            ];
            $injected++;
        }
    }

    error_log("IGNY8 BLOCKS: Summary - Total headings: $heading_blocks_found, Valid H2s: $valid_h2_blocks, Shortcodes injected: $injected");

    $result = serialize_blocks($output);
    $parsed_result = parse_blocks($result);
    $confirmed = false;

    foreach ($parsed_result as $b) {
        if (
            ($b['blockName'] ?? '') === 'core/shortcode' &&
            strpos($b['innerContent'][0] ?? '', '[igny8-image') !== false
        ) {
            $confirmed = true;
            break;
        }
    }

    if (!$confirmed) {
        error_log("IGNY8 BLOCKS: ❌ Shortcode injection failed — no blocks found after serialization");
        igny8_log_ai_event(
            'Shortcode Injection Failed',
            'writer',
            'content_generation',
            'error',
            'No shortcodes found after injection (post-parse)',
            'Editor type: block'
        );
        return false;
    }

    error_log("IGNY8 BLOCKS: ✅ Injected {$injected} shortcode blocks after H2 headings");
    return $result;
}

/**
 * Wrap plain HTML content as Gutenberg blocks
 * 
 * @param string $html_content Plain HTML content
 * @return string Gutenberg block markup
 */
function wrap_html_as_blocks($html_content) {
    if (empty($html_content)) {
        return $html_content;
    }
    
    // Split content into lines for processing
    $lines = explode("\n", $html_content);
    $block_content = [];
    
    foreach ($lines as $line) {
        $line = trim($line);
        if (empty($line)) {
            continue;
        }
        
        // Wrap different HTML elements as Gutenberg blocks
        if (preg_match('/^<h2[^>]*>(.*?)<\/h2>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:heading {"level":2} -->' . $line . '<!-- /wp:heading -->';
        } elseif (preg_match('/^<h3[^>]*>(.*?)<\/h3>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:heading {"level":3} -->' . $line . '<!-- /wp:heading -->';
        } elseif (preg_match('/^<p[^>]*>(.*?)<\/p>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:paragraph -->' . $line . '<!-- /wp:paragraph -->';
        } elseif (preg_match('/^<ul[^>]*>(.*?)<\/ul>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:list -->' . $line . '<!-- /wp:list -->';
        } elseif (preg_match('/^<ol[^>]*>(.*?)<\/ol>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:list {"ordered":true} -->' . $line . '<!-- /wp:list -->';
        } elseif (preg_match('/^<blockquote[^>]*>(.*?)<\/blockquote>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:quote -->' . $line . '<!-- /wp:quote -->';
        } elseif (preg_match('/^<table[^>]*>(.*?)<\/table>$/i', $line, $matches)) {
            $block_content[] = '<!-- wp:table -->' . $line . '<!-- /wp:table -->';
        } elseif (preg_match('/^\[igny8-image[^\]]*\]/', $line)) {
            // Handle shortcodes - wrap in shortcode block
            $block_content[] = '<!-- wp:shortcode -->' . $line . '<!-- /wp:shortcode -->';
        } else {
            // For any other content, wrap as paragraph
            $block_content[] = '<!-- wp:paragraph --><p>' . $line . '</p><!-- /wp:paragraph -->';
        }
    }
    
    return implode("\n", $block_content);
}


