<?php
if (!defined('ABSPATH')) exit;

class WPBS_Media_Indexer {

    private static $instance = null;
    private $index_file;
    private $batch_size = 5;

    public static function instance() {
        if (null === self::$instance) self::$instance = new self();
        return self::$instance;
    }

    private function __construct() {
        $this->index_file = WP_CONTENT_DIR . '/uploads/pagespark-media-index.json';

        // Index new uploads immediately
        add_action('add_attachment', [$this, 'on_attachment_added']);

        // Remove deleted images from the index
        add_action('delete_attachment', [$this, 'on_attachment_deleted']);

        // WP cron handler for batch backfill
        add_action('pagespark_media_backfill', [$this, 'run_backfill_batch']);

        // Admin notices for indexing progress
        add_action('admin_notices', [$this, 'render_progress_notice']);

        // AJAX: trigger re-index from settings page
        add_action('wp_ajax_wpbs_reindex_media', [$this, 'ajax_reindex_media']);

        // AJAX: check indexing progress
        add_action('wp_ajax_wpbs_index_progress', [$this, 'ajax_index_progress']);

        // AJAX: test Anthropic API key
        add_action('wp_ajax_wpbs_test_anthropic', [$this, 'ajax_test_anthropic']);

        // AJAX: save Anthropic key from settings page (before re-index)
        add_action('wp_ajax_wpbs_save_anthropic_key', [$this, 'ajax_save_anthropic_key']);
    }

    // ------------------------------------------------------------------
    // JSON index file helpers
    // ------------------------------------------------------------------

    private function read_index() {
        if (!file_exists($this->index_file)) return [];
        $contents = file_get_contents($this->index_file);
        if (!$contents) return [];
        $data = json_decode($contents, true);
        return is_array($data) ? $data : [];
    }

    private function write_index($data) {
        $dir = dirname($this->index_file);
        if (!is_dir($dir)) {
            wp_mkdir_p($dir);
        }
        $tmp = $this->index_file . '.tmp';
        file_put_contents($tmp, wp_json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        rename($tmp, $this->index_file);
    }

    // ------------------------------------------------------------------
    // Haiku vision — alt text + description
    // ------------------------------------------------------------------

    private function describe_image($attachment_id) {
        $api_key = get_option('wpbs_anthropic_key', '');
        $url = wp_get_attachment_url($attachment_id);
        $filename = basename($url);
        $title = get_the_title($attachment_id);

        // Fallback when no API key is configured
        if (empty($api_key)) {
            return [
                'alt'         => $title ?: $filename,
                'description' => $title ?: $filename,
            ];
        }

        $prompt = 'Look at this image. Return a JSON object with exactly two fields: '
            . 'alt: a concise alt text under 125 characters describing the image for accessibility and SEO. '
            . 'description: a 1-2 sentence visual description of what the image shows, suitable for an AI to understand what this image contains so it can place it appropriately on a web page. '
            . 'Return only valid JSON. No preamble, no markdown, no backticks.';

        // Build the image content block — base64 from local file, URL as fallback
        $image_content = $this->build_image_content($attachment_id, $url);
        if (!$image_content) {
            return [
                'alt'         => $title ?: $filename,
                'description' => $title ?: $filename,
            ];
        }

        $messages = [
            [
                'role'    => 'user',
                'content' => [
                    $image_content,
                    ['type' => 'text', 'text' => $prompt],
                ],
            ],
        ];

        $request_body = wp_json_encode([
            'model'      => 'claude-haiku-4-5-20251001',
            'max_tokens' => 300,
            'messages'   => $messages,
        ]);

        $max_retries = 3;
        $response = null;
        $code = 0;
        $body = null;

        for ($attempt = 0; $attempt < $max_retries; $attempt++) {
            if ($attempt > 0) {
                $delay = min(5 * pow(2, $attempt), 30); // 10s, 20s, 30s
                error_log('[PageSpark Media] Retry ' . $attempt . ' after ' . $delay . 's for ' . $filename);
                sleep($delay);
            }

            $response = wp_remote_post('https://api.anthropic.com/v1/messages', [
                'timeout' => 60,
                'headers' => [
                    'Content-Type'      => 'application/json',
                    'x-api-key'         => $api_key,
                    'anthropic-version'  => '2023-06-01',
                ],
                'body' => $request_body,
            ]);

            if (is_wp_error($response)) {
                continue;
            }

            $code = wp_remote_retrieve_response_code($response);

            if ($code === 529 || $code === 429) {
                continue;
            }

            break; // Success or non-retryable error
        }

        if (is_wp_error($response)) {
            return [
                'alt'         => $title ?: $filename,
                'description' => $title ?: $filename,
            ];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if ($code !== 200 || empty($body['content'][0]['text'])) {
            return [
                'alt'         => $title ?: $filename,
                'description' => $title ?: $filename,
            ];
        }

        $text = trim($body['content'][0]['text']);
        // Strip markdown code fences if Haiku wraps its response
        $text = preg_replace('/^```(?:json)?\s*/i', '', $text);
        $text = preg_replace('/\s*```\s*$/', '', $text);
        $text = trim($text);
        $parsed = json_decode($text, true);

        if (!is_array($parsed) || empty($parsed['alt'])) {
            return [
                'alt'         => $title ?: $filename,
                'description' => $title ?: $filename,
            ];
        }

        return [
            'alt'         => sanitize_text_field($parsed['alt']),
            'description' => sanitize_text_field($parsed['description'] ?? ''),
        ];
    }

    private function build_image_content($attachment_id, $url) {
        $mime = get_post_mime_type($attachment_id);
        $supported_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

        if (!in_array($mime, $supported_types, true)) {
            return null;
        }

        // Prefer base64 from local file — always works, no loopback issues
        $file_path = get_attached_file($attachment_id);
        if ($file_path && file_exists($file_path)) {
            $size = filesize($file_path);
            if ($size > 5 * 1024 * 1024) {
                // Skip oversized files
            } else {
                return [
                    'type'   => 'image',
                    'source' => [
                        'type'       => 'base64',
                        'media_type' => $mime,
                        'data'       => base64_encode(file_get_contents($file_path)),
                    ],
                ];
            }
        }

        // Fall back to public URL if local file not available
        return [
            'type'   => 'image',
            'source' => [
                'type' => 'url',
                'url'  => $url,
            ],
        ];
    }

    // ------------------------------------------------------------------
    // Build a single index entry for an attachment
    // ------------------------------------------------------------------

    private function build_entry($attachment_id) {
        $url = wp_get_attachment_url($attachment_id);
        if (!$url) return null;

        $result = $this->describe_image($attachment_id);

        // Save alt text back to WP attachment meta
        update_post_meta($attachment_id, '_wp_attachment_image_alt', $result['alt']);

        return [
            'id'          => (int) $attachment_id,
            'url'         => $url,
            'filename'    => basename($url),
            'alt'         => $result['alt'],
            'description' => $result['description'],
        ];
    }

    // ------------------------------------------------------------------
    // Hook: new upload
    // ------------------------------------------------------------------

    public function on_attachment_added($attachment_id) {
        // Only process images
        if (!wp_attachment_is_image($attachment_id)) return;

        $entry = $this->build_entry($attachment_id);
        if (!$entry) return;

        $index = $this->read_index();
        $index[] = $entry;
        $this->write_index($index);
    }

    // ------------------------------------------------------------------
    // Hook: delete attachment
    // ------------------------------------------------------------------

    public function on_attachment_deleted($attachment_id) {
        $index = $this->read_index();
        $index = array_values(array_filter($index, function ($img) use ($attachment_id) {
            return (int) $img['id'] !== (int) $attachment_id;
        }));
        $this->write_index($index);
    }

    // ------------------------------------------------------------------
    // Backfill — cron-based batch processing
    // ------------------------------------------------------------------

    public function schedule_backfill() {
        // Reset progress tracking
        update_option('wpbs_media_index_total', 0);
        update_option('wpbs_media_index_done', 0);
        delete_option('wpbs_media_index_complete');

        // Clear any existing scheduled event then fire immediately
        wp_clear_scheduled_hook('pagespark_media_backfill');
        wp_schedule_single_event(time(), 'pagespark_media_backfill');
        spawn_cron();
    }

    public function run_backfill_batch() {
        $index = $this->read_index();
        $indexed_ids = array_column($index, 'id');

        // All image attachments in the library
        $all_ids = get_posts([
            'post_type'      => 'attachment',
            'post_mime_type' => 'image',
            'post_status'    => 'inherit',
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'orderby'        => 'ID',
            'order'          => 'ASC',
        ]);

        $missing = array_diff($all_ids, $indexed_ids);
        $total = count($all_ids);

        update_option('wpbs_media_index_total', $total);
        update_option('wpbs_media_index_done', $total - count($missing));

        if (empty($missing)) {
            update_option('wpbs_media_index_complete', true);
            return;
        }

        // Process one batch
        $batch = array_slice($missing, 0, $this->batch_size);

        foreach ($batch as $attachment_id) {
            $entry = $this->build_entry($attachment_id);
            if ($entry) {
                $index[] = $entry;
            }
        }

        $this->write_index($index);

        $remaining = count($missing) - count($batch);
        update_option('wpbs_media_index_done', $total - $remaining);

        if ($remaining > 0) {
            // Schedule next batch
            wp_schedule_single_event(time() + 5, 'pagespark_media_backfill');
            spawn_cron();
        } else {
            update_option('wpbs_media_index_complete', true);
        }
    }

    // ------------------------------------------------------------------
    // Admin notice — indexing progress
    // ------------------------------------------------------------------

    public function render_progress_notice() {
        if (!current_user_can('manage_options')) return;

        $total = (int) get_option('wpbs_media_index_total', 0);
        if ($total === 0) return;

        $done = (int) get_option('wpbs_media_index_done', 0);
        $complete = get_option('wpbs_media_index_complete', false);

        if ($complete) {
            echo '<div class="notice notice-success is-dismissible"><p>';
            echo 'PageSpark: Media library index complete. ' . esc_html($total) . ' images indexed.';
            echo '</p></div>';
            // Clear so the notice only shows once
            delete_option('wpbs_media_index_total');
            delete_option('wpbs_media_index_done');
            delete_option('wpbs_media_index_complete');
        } else {
            echo '<div class="notice notice-info"><p>';
            echo 'PageSpark: Indexing media library&hellip; ' . esc_html($done) . '/' . esc_html($total) . ' images complete.';
            echo '</p></div>';
        }
    }

    // ------------------------------------------------------------------
    // AJAX: Re-index from settings page
    // ------------------------------------------------------------------

    public function ajax_reindex_media() {
        check_ajax_referer('wpbs_settings', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
        }

        // Clear existing index and start fresh
        $this->write_index([]);
        $this->schedule_backfill();

        wp_send_json_success(['message' => 'Re-indexing started.']);
    }

    public function ajax_index_progress() {
        check_ajax_referer('wpbs_settings', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
        }

        $total = (int) get_option('wpbs_media_index_total', 0);
        $done = (int) get_option('wpbs_media_index_done', 0);
        $complete = (bool) get_option('wpbs_media_index_complete', false);

        wp_send_json_success([
            'total'    => $total,
            'done'     => $done,
            'complete' => $complete,
        ]);
    }

    public function ajax_test_anthropic() {
        check_ajax_referer('wpbs_settings', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
        }

        // Use the key from the form (may not be saved yet)
        $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
        if (empty($api_key)) {
            $api_key = get_option('wpbs_anthropic_key', '');
        }
        if (empty($api_key)) {
            wp_send_json_error('No API key provided.');
        }

        // Make a minimal Haiku call to verify the key works — retry on overload
        $max_retries = 3;
        $response = null;
        $code = 0;

        for ($attempt = 0; $attempt < $max_retries; $attempt++) {
            if ($attempt > 0) sleep(3);

            $response = wp_remote_post('https://api.anthropic.com/v1/messages', [
                'timeout' => 20,
                'headers' => [
                    'Content-Type'      => 'application/json',
                    'x-api-key'         => $api_key,
                    'anthropic-version'  => '2023-06-01',
                ],
                'body' => wp_json_encode([
                    'model'      => 'claude-haiku-4-5-20251001',
                    'max_tokens' => 10,
                    'messages'   => [
                        ['role' => 'user', 'content' => 'Say "ok"'],
                    ],
                ]),
            ]);

            if (is_wp_error($response)) {
                wp_send_json_error('Connection failed: ' . $response->get_error_message());
            }

            $code = wp_remote_retrieve_response_code($response);

            if ($code === 529 || $code === 429) {
                continue; // Retry on overload
            }
            break;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if ($code === 401) {
            wp_send_json_error('Invalid API key.');
        }
        if ($code === 403) {
            wp_send_json_error('API key lacks permission. Check your Anthropic account.');
        }
        if ($code === 529 || $code === 429) {
            // Still overloaded after retries — key is probably fine, save it anyway
            update_option('wpbs_anthropic_key', $api_key);
            wp_send_json_success('Key saved. Haiku is overloaded right now — re-index will retry automatically.');
        }
        if ($code !== 200) {
            $msg = isset($body['error']['message']) ? $body['error']['message'] : 'API error (status ' . $code . ')';
            wp_send_json_error($msg);
        }

        // Key works — save it
        update_option('wpbs_anthropic_key', $api_key);
        wp_send_json_success('Connected to Claude Haiku.');
    }

    public function ajax_save_anthropic_key() {
        check_ajax_referer('wpbs_settings', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
        }

        $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
        update_option('wpbs_anthropic_key', $api_key);
        wp_send_json_success('Key saved.');
    }

    // ------------------------------------------------------------------
    // Activation — trigger initial backfill
    // ------------------------------------------------------------------

    public function activate() {
        $this->schedule_backfill();
    }
}
