<?php
/**
 * TalkGenAI Job Manager
 * Generic job management for async tasks (articles, apps, etc.)
 */

if (!defined('ABSPATH')) {
    exit('Direct access not allowed.');
}

class TalkGenAI_Job_Manager {
    
    private $api_url;
    private $api_key;
    
    /**
     * Constructor
     */
    public function __construct() {
        // Prefer new consolidated settings structure
        $settings = get_option('talkgenai_settings', array());
        $server_mode = isset($settings['server_mode']) ? $settings['server_mode'] : 'local';
        $local_url = isset($settings['local_server_url']) ? $settings['local_server_url'] : 'http://localhost:8000';
        $remote_url = isset($settings['remote_server_url']) ? $settings['remote_server_url'] : '';

        // Choose base URL based on mode, fallback to local if remote not set
        $base_url = ($server_mode === 'remote' && !empty($remote_url)) ? $remote_url : $local_url;
        $this->api_url = rtrim($base_url, '/');

        // API key (primarily used for remote)
        $this->api_key = isset($settings['remote_api_key']) ? $settings['remote_api_key'] : get_option('talkgenai_api_key', '');
    }
    
    /**
     * Create a new job
     * 
     * @param string $job_type Job type ('article', 'app', etc.)
     * @param int $user_id WordPress user ID
     * @param array $input_data Job-specific input data
     * @return array Result with job_id or error
     */
    public function create_job($job_type, $user_id, $input_data) {
        // Normalize input for known job types to avoid backend 400s
        if ($job_type === 'article') {
            if (!is_array($input_data)) { $input_data = array(); }
            $input_data = $this->normalize_article_input($input_data);
        } elseif ($job_type === 'standalone_article') {
            // Normalize standalone article input
            if (!is_array($input_data)) { $input_data = array(); }
            $input_data = $this->normalize_standalone_article_input($input_data);
        } elseif ($job_type === 'app') {
            // Add ai_provider for app generation/modification
            if (!is_array($input_data)) { $input_data = array(); }
            if (!isset($input_data['ai_provider'])) {
                $input_data['ai_provider'] = 'anthropic';  // Default AI provider
            }
        }

        $response = $this->send_api_request(
            '/api/jobs/create',
            'POST',
            array(
                'job_type' => $job_type,
                'user_id' => $user_id,
                'input_data' => $input_data
            )
        );
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        return $response;
    }

    /**
     * Normalize article job input data to required keys
     */
    private function normalize_article_input($data) {
        // Accept common aliases from various UI sources
        $title = isset($data['app_title']) ? $data['app_title'] : (isset($data['title']) ? $data['title'] : (isset($data['name']) ? $data['name'] : ''));
        $description = isset($data['app_description']) ? $data['app_description'] : (isset($data['description']) ? $data['description'] : (isset($data['summary']) ? $data['summary'] : ''));
        $length = isset($data['article_length']) ? $data['article_length'] : (isset($data['length']) ? $data['length'] : 'medium');
        $instructions = isset($data['additional_instructions']) ? $data['additional_instructions'] : (isset($data['instructions']) ? $data['instructions'] : '');
        $app_spec = isset($data['app_spec']) ? $data['app_spec'] : (isset($data['json_spec']) ? $data['json_spec'] : null);

        // New enhanced fields (unified form)
        $article_title = isset($data['article_title']) ? $data['article_title'] : '';
        $topic = isset($data['topic']) ? $data['topic'] : '';
        $internal_urls = isset($data['internal_urls']) ? $data['internal_urls'] : array();
        $include_faq = isset($data['include_faq']) ? (bool) $data['include_faq'] : false;
        $auto_internal_links = isset($data['auto_internal_links']) ? (bool) $data['auto_internal_links'] : false;
        $include_external_link = isset($data['include_external_link']) ? (bool) $data['include_external_link'] : false;
        $create_image = isset($data['create_image']) ? (bool) $data['create_image'] : false;

        // Trim strings
        $title = is_string($title) ? trim($title) : '';
        $description = is_string($description) ? trim($description) : '';
        $length = is_string($length) ? trim($length) : 'medium';
        $instructions = is_string($instructions) ? trim($instructions) : '';
        $article_title = is_string($article_title) ? trim($article_title) : '';
        $topic = is_string($topic) ? trim($topic) : '';

        // Normalize internal_urls (same logic as standalone)
        if (!is_array($internal_urls)) {
            $internal_urls = array();
        }
        $normalized_urls = array();
        foreach ($internal_urls as $item) {
            if (is_array($item) && isset($item['url'])) {
                $normalized_urls[] = array(
                    'url' => trim($item['url']),
                    'anchor' => isset($item['anchor']) ? trim($item['anchor']) : '',
                );
            } elseif (is_string($item)) {
                $normalized_urls[] = array(
                    'url' => trim($item),
                    'anchor' => '',
                );
            }
        }

        // Rebuild with required keys
        $normalized = array(
            'app_title' => $title,
            'app_description' => $description,
            'article_length' => $length,
        );
        if ($instructions !== '') {
            $normalized['additional_instructions'] = $instructions;
        }
        // Include app_spec for AI customization (full app specification)
        if ($app_spec !== null) {
            $normalized['app_spec'] = $app_spec;
        }

        // Include enhanced fields when provided (unified form sends these)
        if ($article_title !== '') {
            $normalized['article_title'] = $article_title;
        }
        if ($topic !== '') {
            $normalized['topic'] = $topic;
        }
        if (!empty($normalized_urls)) {
            $normalized['internal_urls'] = $normalized_urls;
        }
        if ($include_faq) {
            $normalized['include_faq'] = true;
        }
        if ($include_external_link) {
            $normalized['include_external_link'] = true;
        }
        if ($auto_internal_links) {
            $normalized['auto_internal_links'] = true;
        }
        if ($create_image) {
            $normalized['create_image'] = true;
        }

        // If still missing, keep originals as best-effort (for debugging)
        foreach ($data as $k => $v) {
            if (!isset($normalized[$k])) {
                $normalized[$k] = $v;
            }
        }

        return $normalized;
    }

    /**
     * Normalize standalone article job input data to required keys
     */
    private function normalize_standalone_article_input($data) {
        // Extract required fields
        $title = isset($data['article_title']) ? $data['article_title'] : '';
        $topic = isset($data['topic']) ? $data['topic'] : '';
        $length = isset($data['article_length']) ? $data['article_length'] : 'medium';
        $instructions = isset($data['additional_instructions']) ? $data['additional_instructions'] : '';
        $internal_urls = isset($data['internal_urls']) ? $data['internal_urls'] : array();
        $include_faq = isset($data['include_faq']) ? (bool) $data['include_faq'] : true;
        $auto_internal_links = isset($data['auto_internal_links']) ? (bool) $data['auto_internal_links'] : true;
        $include_external_link = isset($data['include_external_link']) ? (bool) $data['include_external_link'] : true;
        $internal_link_candidates = isset($data['internal_link_candidates']) ? $data['internal_link_candidates'] : array();
        $create_image = isset($data['create_image']) ? (bool) $data['create_image'] : false;
        $writing_style_id = isset($data['writing_style_id']) ? sanitize_text_field($data['writing_style_id']) : '';

        // Trim strings
        $title = is_string($title) ? trim($title) : '';
        $topic = is_string($topic) ? trim($topic) : '';
        $length = is_string($length) ? trim($length) : 'medium';
        $instructions = is_string($instructions) ? trim($instructions) : '';

        // Ensure internal_urls is an array of objects with {url, anchor}
        // New format from JS: array of {url: "...", anchor: "..."}
        if (!is_array($internal_urls)) {
            $internal_urls = array();
        }
        // Normalize: ensure each entry has 'url' and 'anchor' keys
        $normalized_urls = array();
        foreach ($internal_urls as $item) {
            if (is_array($item) && isset($item['url'])) {
                $normalized_urls[] = array(
                    'url' => trim($item['url']),
                    'anchor' => isset($item['anchor']) ? trim($item['anchor']) : '',
                );
            } elseif (is_string($item)) {
                // Fallback: if old format (plain string URL), convert to new format
                $normalized_urls[] = array(
                    'url' => trim($item),
                    'anchor' => '',
                );
            }
        }

        // Rebuild with required keys
        $normalized = array(
            'article_title' => $title,
            'topic' => $topic,
            'article_length' => $length,
            'include_faq' => $include_faq,
            'internal_urls' => $normalized_urls,
            'auto_internal_links' => $auto_internal_links,
            'include_external_link' => $include_external_link,
        );

        // Include auto-found internal link candidates
        if (!empty($internal_link_candidates)) {
            $normalized['internal_link_candidates'] = $internal_link_candidates;
        }

        if ($instructions !== '') {
            $normalized['additional_instructions'] = $instructions;
        }
        if ($create_image) {
            $normalized['create_image'] = true;
        }
        if (!empty($writing_style_id)) {
            $normalized['writing_style_id'] = $writing_style_id;
        }

        return $normalized;
    }

    /**
     * Check job status
     *
     * @param string $job_id Job ID
     * @return array Status information
     */
    public function check_job_status($job_id) {
        $response = $this->send_api_request(
            '/api/jobs/' . $job_id . '/status',
            'GET'
        );

        if (is_wp_error($response)) {
            return array(
                'status' => 'failed',
                'error' => $response->get_error_message()
            );
        }

        return $response;
    }

    /**
     * Strip the base64 image data from a job after it has been uploaded to WordPress.
     * Fire-and-forget — failure is non-fatal (image is already in WP Media Library).
     *
     * @param string $job_id Job identifier
     * @return void
     */
    public function clear_job_image_data($job_id) {
        $this->send_api_request('/api/jobs/' . $job_id . '/image-data', 'DELETE');
    }

    /**
     * Get user's job results/history
     * 
     * @param int $user_id User ID
     * @param string $job_type Optional: filter by job type
     * @param int $limit Optional: number of results
     * @return array List of results
     */
    public function get_user_results($user_id, $job_type = null, $limit = 50) {
        // Current backend exposes article history at /api/articles/user/{user_id}
        $endpoint = '/api/articles/user/' . $user_id . '?' . http_build_query(array('limit' => $limit));
        $response = $this->send_api_request($endpoint, 'GET');
        
        // Debug logging removed for WordPress.org submission
        // error_log("📊 [get_user_results] Endpoint: $endpoint");
        // error_log("📊 [get_user_results] Raw response: " . print_r($response, true));
        
        if (is_wp_error($response)) {
            // Debug logging removed for WordPress.org submission
            // error_log("❌ [get_user_results] WP_Error: " . $response->get_error_message());
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        // Normalize to generic shape expected by JS (results array)
        if (isset($response['success']) && $response['success'] && isset($response['articles'])) {
            $normalized = array(
                'success' => true,
                'count' => isset($response['count']) ? $response['count'] : count($response['articles']),
                'results' => array_map(function($a) {
                    return array(
                        'id' => $a['id'],
                        'job_type' => 'article',
                        'app_title' => isset($a['app_title']) ? $a['app_title'] : '',
                        'article_length' => isset($a['article_length']) ? $a['article_length'] : '',
                        'word_count' => isset($a['word_count']) ? $a['word_count'] : 0,
                        'ai_provider' => isset($a['ai_provider']) ? $a['ai_provider'] : '',
                        'created_at' => isset($a['created_at']) ? $a['created_at'] : ''
                    );
                }, $response['articles'])
            );
            // Debug logging removed for WordPress.org submission
            // error_log("✅ [get_user_results] Normalized response with " . count($normalized['results']) . " articles");
            // error_log("📄 [get_user_results] First article: " . print_r($normalized['results'][0] ?? 'none', true));
            return $normalized;
        }
        
        // Debug logging removed for WordPress.org submission
        // error_log("⚠️ [get_user_results] Response doesn't match expected format, returning as-is");
        return $response;
    }
    
    /**
     * Get specific result
     * 
     * @param int $result_id Result ID
     * @return array Result data
     */
    public function get_result($result_id) {
        // Current backend exposes single article at /api/articles/{id}
        $response = $this->send_api_request('/api/articles/' . $result_id, 'GET');
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        // Normalize to generic shape expected by JS (result.result_data)
        if (isset($response['success']) && $response['success'] && isset($response['article'])) {
            return array(
                'success' => true,
                'result' => array(
                    'result_data' => array(
                        'article' => isset($response['article']['article_html']) ? $response['article']['article_html'] : '',
                        'article_html' => isset($response['article']['article_html']) ? $response['article']['article_html'] : '',
                        'meta_description' => isset($response['article']['meta_description']) ? $response['article']['meta_description'] : '',
                        'word_count' => isset($response['article']['word_count']) ? $response['article']['word_count'] : 0,
                        'provider' => isset($response['article']['ai_provider']) ? $response['article']['ai_provider'] : '',
                        'generation_time' => isset($response['article']['generation_time']) ? $response['article']['generation_time'] : 0
                    )
                )
            );
        }
        return $response;
    }
    
    /**
     * Delete result
     * 
     * @param int $result_id Result ID
     * @param int $user_id User ID (for ownership verification)
     * @return array Success or error
     */
    public function delete_result($result_id, $user_id) {
        // Current backend exposes delete at /api/articles/{id}/user/{user_id}
        $response = $this->send_api_request('/api/articles/' . $result_id . '/user/' . $user_id, 'DELETE');
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        return $response;
    }
    
    /**
     * Send API request
     * 
     * @param string $endpoint API endpoint
     * @param string $method HTTP method
     * @param array $data Request data
     * @return array|WP_Error Response or error
     */
    private function send_api_request($endpoint, $method = 'GET', $data = null) {
        $url = rtrim($this->api_url, '/') . $endpoint;
        
        $args = array(
            'method' => $method,
            'timeout' => 90,
            'headers' => array(
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
                'Authorization' => 'Bearer ' . $this->api_key,
                // Attach caller identity for ownership checks server-side
                'X-User-Id' => strval(get_current_user_id())
            )
        );
        
        if ($data && in_array($method, array('POST', 'PUT', 'PATCH'))) {
            $args['body'] = wp_json_encode($data);
        }
        
        $response = wp_remote_request($url, $args);
        
        if (is_wp_error($response)) {
            // Debug logging removed for WordPress.org submission
            // error_log('TalkGenAI Job Manager API Error: ' . $response->get_error_message());
            return $response;
        }
        
        $body = wp_remote_retrieve_body($response);
        $status_code = wp_remote_retrieve_response_code($response);
        
        if ($status_code >= 400) {
            $error_msg = 'API Error (Status ' . $status_code . ')';
            $decoded = $this->decode_maybe_escaped_json($body);
            
            // Special handling for 402/403 errors (payment/credits required)
            if (($status_code === 402 || $status_code === 403) && $decoded) {
                // Check if this is an insufficient credits error
                if (isset($decoded['detail']) && is_array($decoded['detail'])) {
                    // Generic passthrough: if backend provides a ready-to-render message,
                    // return it as ai_message so the admin chat can display it.
                    if (isset($decoded['detail']['ai_message']) && is_string($decoded['detail']['ai_message']) && $decoded['detail']['ai_message'] !== '') {
                        $user_message = isset($decoded['detail']['message']) && is_string($decoded['detail']['message'])
                            ? $decoded['detail']['message']
                            : 'Request blocked by account limits. Please upgrade your plan.';
                        $error_code = isset($decoded['detail']['code']) && is_string($decoded['detail']['code'])
                            ? $decoded['detail']['code']
                            : 'api_limit';

                        return array(
                            'success' => false,
                            'error' => $user_message,
                            'error_code' => $error_code,
                            'ai_message' => $decoded['detail']['ai_message'],
                            'is_html' => true
                        );
                    }

                    if (isset($decoded['detail']['code']) && $decoded['detail']['code'] === 'insufficient_credits') {
                        // Return structured error with ai_message for better UX
                        $user_message = isset($decoded['detail']['message']) 
                            ? $decoded['detail']['message'] 
                            : 'You don\'t have enough credits to generate content. Please upgrade your plan.';
                        
                        $credits_remaining = isset($decoded['detail']['credits_remaining']) 
                            ? $decoded['detail']['credits_remaining'] 
                            : 0;
                        
                        $plan = isset($decoded['detail']['plan']) 
                            ? $decoded['detail']['plan'] 
                            : 'free';
                        
                        $upgrade_url = isset($decoded['detail']['upgrade_url']) 
                            ? $decoded['detail']['upgrade_url'] 
                            : 'https://app.talkgen.ai/';
                        
                        // Create a structured error response with ai_message for the frontend
                        return array(
                            'success' => false,
                            'error' => $user_message,
                            'error_code' => 'insufficient_credits',
                            'ai_message' => '<div class="talkgenai-error-notice" style="font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif; padding: 16px; background: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107; max-width: 500px;">
                                <div style="display: flex; align-items: center; margin-bottom: 12px;">
                                    <span style="font-size: 22px; margin-right: 8px;">⚠️</span>
                                    <strong style="color: #856404; font-size: 1.1em;">Out of Credits</strong>
                                </div>
                                <div style="color: #856404; margin-bottom: 12px;">
                                    <p style="margin: 0 0 8px 0;">' . esc_html($user_message) . '</p>
                                    <p style="margin: 0; font-size: 0.9em;">Credits Remaining: <strong>' . esc_html($credits_remaining) . '</strong></p>
                                    <p style="margin: 0; font-size: 0.9em;">Current Plan: <strong>' . esc_html(ucfirst($plan)) . '</strong></p>
                                </div>
                                <div style="text-align: center; margin-top: 16px;">
                                    <a href="' . esc_url($upgrade_url) . '" target="_blank" style="display: inline-block; padding: 12px 24px; background: linear-gradient(135deg, #e67e22 0%, #d35400 100%); color: white; text-decoration: none; border-radius: 6px; font-weight: 600; transition: transform 0.2s;">
                                        Upgrade to Starter Plan →
                                    </a>
                                </div>
                            </div>',
                            'is_html' => true
                        );
                    }
                    
                    // Check if this is a domain restriction error (free tier limitation)
                    if (isset($decoded['detail']['code']) && $decoded['detail']['code'] === 'domain_restriction') {
                        $user_message = isset($decoded['detail']['message']) 
                            ? $decoded['detail']['message'] 
                            : 'Free tier is limited to one website. Upgrade to Starter plan to use your API key on multiple websites.';
                        
                        $registered_domain = isset($decoded['detail']['registered_domain']) 
                            ? $decoded['detail']['registered_domain'] 
                            : 'your registered website';
                        
                        $current_domain = isset($decoded['detail']['current_domain']) 
                            ? $decoded['detail']['current_domain'] 
                            : 'this website';
                        
                        $upgrade_url = isset($decoded['detail']['upgrade_url']) 
                            ? $decoded['detail']['upgrade_url'] 
                            : 'https://app.talkgen.ai/dashboard';
                        
                        // Create a structured error response with ai_message for the frontend
                        return array(
                            'success' => false,
                            'error' => $user_message,
                            'error_code' => 'domain_restriction',
                            'ai_message' => '<div class="talkgenai-error-notice">
                                <h3>🚫 Domain Restriction (Free Tier)</h3>
                                <p>' . esc_html($user_message) . '</p>
                                <p><strong>Registered Domain:</strong> ' . esc_html($registered_domain) . '</p>
                                <p><strong>Current Domain:</strong> ' . esc_html($current_domain) . '</p>
                                <p><strong>💡 Solution:</strong> Upgrade to Starter plan ($20/month) to use your API key on unlimited websites!</p>
                                <p><a href="' . esc_url($upgrade_url) . '" target="_blank" class="button button-primary">Upgrade to Starter Plan</a></p>
                            </div>',
                            'is_html' => true
                        );
                    }
                }
            }
            
            if ($decoded && isset($decoded['detail'])) {
                if (is_string($decoded['detail'])) {
                    $error_msg .= ': ' . $decoded['detail'];
                } elseif (is_array($decoded['detail']) && isset($decoded['detail']['message'])) {
                    $error_msg .= ': ' . $decoded['detail']['message'];
                }
            }
            // Debug logging removed for WordPress.org submission
            // error_log('TalkGenAI Job Manager: ' . $error_msg);
            return new WP_Error('api_error', $error_msg);
        }
        
        $decoded = $this->decode_maybe_escaped_json($body);
        if ($decoded === null) {
            // Debug logging removed for WordPress.org submission
            // error_log('TalkGenAI Job Manager: Invalid JSON response from API. Raw body: ' . substr($body, 0, 300));
            return new WP_Error('invalid_json', 'Invalid JSON response from API');
        }
        
        return $decoded;
    }

    /**
     * Decode JSON that may be double-encoded or escaped as a string
     */
    private function decode_maybe_escaped_json($raw) {
        if ($raw === null || $raw === '') {
            return null;
        }
        // First attempt
        $decoded = json_decode($raw, true);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded;
        }
        // Trim quotes and unescape common patterns
        $txt = trim($raw);
        if ((substr($txt, 0, 1) === '"' && substr($txt, -1) === '"') || (substr($txt, 0, 1) === "'" && substr($txt, -1) === "'")) {
            $txt = substr($txt, 1, -1);
        }
        $txt = str_replace(array('\\"', '\\\\'), array('"', '\\'), $txt);
        $decoded = json_decode($txt, true);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded;
        }
        return null;
    }
}




