<?php
/**
 * Plugin Name: IELTS Task 1 Report Checker
 * Plugin URI:  https://example.com/ielts-task1-checker
 * Description: AI-powered IELTS Academic Writing Task 1 report checker. Upload a chart/graph, write your report, and get an instant band-score evaluation powered by Claude (Anthropic). Use shortcode [ielts_task1_checker].
 * Version:     2.0.0
 * Author:      Your Name
 * License:     GPL v2 or later
 * Text Domain: ielts-task1-checker
 */

declare(strict_types=1);

if (!defined('ABSPATH')) {
    exit;
}

define('ITC_VERSION', '2.0.0');
define('ITC_PLUGIN_FILE', __FILE__);
define('ITC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('ITC_PLUGIN_URL', plugin_dir_url(__FILE__));
define('ITC_DB_VERSION', '2.0');

// ============================================================
// DATABASE CLASS
// ============================================================
class ITC_DB
{
    public static function table_name(): string
    {
        global $wpdb;
        return $wpdb->prefix . 'itc_reports';
    }

    public static function install_tables(): void
    {
        global $wpdb;
        $table = self::table_name();
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            user_id BIGINT UNSIGNED DEFAULT NULL,
            topic TEXT NOT NULL,
            image_url VARCHAR(500) DEFAULT NULL,
            image_description LONGTEXT DEFAULT NULL,
            image_hash VARCHAR(64) DEFAULT NULL,
            report LONGTEXT NOT NULL,
            provider VARCHAR(20) DEFAULT NULL,
            overall_band DECIMAL(3,1) DEFAULT NULL,
            task_response DECIMAL(3,1) DEFAULT NULL,
            coherence DECIMAL(3,1) DEFAULT NULL,
            lexical_resource DECIMAL(3,1) DEFAULT NULL,
            grammar DECIMAL(3,1) DEFAULT NULL,
            grammar_mistakes_count INT DEFAULT 0,
            vocabulary_level VARCHAR(5) DEFAULT NULL,
            vocabulary_repetition LONGTEXT DEFAULT NULL,
            grammar_mistakes_detail LONGTEXT DEFAULT NULL,
            feedback LONGTEXT DEFAULT NULL,
            improved_report LONGTEXT DEFAULT NULL,
            enhanced_report LONGTEXT DEFAULT NULL,
            ip_address VARCHAR(45) DEFAULT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY created_at (created_at),
            KEY overall_band (overall_band),
            KEY image_hash (image_hash)
        ) {$charset_collate};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);

        update_option('itc_db_version', ITC_DB_VERSION);
    }

    public static function insert_report(array $data): int
    {
        global $wpdb;
        $wpdb->insert(self::table_name(), $data);
        return (int) $wpdb->insert_id;
    }

    public static function get_report(int $id): ?array
    {
        global $wpdb;
        $table = self::table_name();
        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id = %d", $id), ARRAY_A);
        return $row ?: null;
    }

    public static function get_reports(int $limit = 50, int $offset = 0): array
    {
        global $wpdb;
        $table = self::table_name();
        return $wpdb->get_results(
            $wpdb->prepare("SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d OFFSET %d", $limit, $offset),
            ARRAY_A
        ) ?: [];
    }

    public static function count_reports(): int
    {
        global $wpdb;
        $table = self::table_name();
        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
    }

    public static function average_band(): float
    {
        global $wpdb;
        $table = self::table_name();
        $avg = $wpdb->get_var("SELECT AVG(overall_band) FROM {$table} WHERE overall_band IS NOT NULL");
        return $avg !== null ? round((float) $avg, 2) : 0.0;
    }
}

// ============================================================
// AI CLIENT CLASS
// ============================================================
class ITC_AI_Exception extends RuntimeException {}

class ITC_AI_Client
{
    private array $settings;
    private const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
    private const ANTHROPIC_VERSION = '2023-06-01';

    public function __construct()
    {
        $this->settings = wp_parse_args(
            get_option('itc_settings', []),
            self::default_settings()
        );
    }

    public static function default_settings(): array
    {
        return [
            'claude_api_key' => '',
            'claude_model' => 'claude-sonnet-4-6',
            'max_upload_mb' => 10,
            'rate_limit_max' => 20,
            'rate_limit_window' => 3600,
            'request_timeout' => 90,
        ];
    }

    public function describeImage(string $imagePath, string $mime): array
    {
        $this->validateApiKey();
        $imageData = $this->prepareImageData($imagePath);

        $payload = [
            'model' => $this->settings['claude_model'],
            'max_tokens' => 800,
            'system' => $this->getImageAnalysisPrompt(),
            'messages' => [
                [
                    'role' => 'user',
                    'content' => [
                        [
                            'type' => 'image',
                            'source' => [
                                'type' => 'base64',
                                'media_type' => $mime,
                                'data' => $imageData,
                            ],
                        ],
                        [
                            'type' => 'text',
                            'text' => 'Analyze this IELTS Academic Writing Task 1 visual and provide a detailed description.',
                        ],
                    ],
                ],
            ],
        ];

        $response = $this->makeApiRequest($payload);
        $text = $this->extractClaudeText($response);
        return $this->parseImageDescription($text);
    }

    public function analyzeReport(string $topic, string $imagePath, string $mime, string $report): array
    {
        $this->validateApiKey();
        $imageData = $this->prepareImageData($imagePath);
        $wordCount = $this->countWords($report);

        $payload = [
            'model' => $this->settings['claude_model'],
            'max_tokens' => 2000,
            'system' => $this->getEvaluationPrompt(),
            'messages' => [
                [
                    'role' => 'user',
                    'content' => [
                        [
                            'type' => 'image',
                            'source' => [
                                'type' => 'base64',
                                'media_type' => $mime,
                                'data' => $imageData,
                            ],
                        ],
                        [
                            'type' => 'text',
                            'text' => $this->buildEvaluationMessage($topic, $report, $wordCount),
                        ],
                    ],
                ],
            ],
        ];

        try {
            $payload['output_config'] = [
                'format' => 'json_schema',
                'json_schema' => $this->getEvaluationSchema(),
            ];
        } catch (Exception $e) {
            // Fall back to prompt-based JSON
        }

        $response = $this->makeApiRequest($payload);
        $text = $this->extractClaudeText($response);
        $result = $this->parseJson($text);
        $result = $this->validateAndCalculateBands($result);
        $result['word_count'] = $wordCount;
        $result['below_minimum'] = $wordCount < 150;

        return $result;
    }

    public function improveNaturalness(string $report): string
    {
        $this->validateApiKey();
        
        $payload = [
            'model' => $this->settings['claude_model'],
            'max_tokens' => 1200,
            'system' => $this->getImprovementPrompt(),
            'messages' => [
                ['role' => 'user', 'content' => $report],
            ],
        ];

        $response = $this->makeApiRequest($payload);
        return trim($this->extractClaudeText($response));
    }

    public function enhanceReport(string $topic, string $report): string
    {
        $this->validateApiKey();
        
        $payload = [
            'model' => $this->settings['claude_model'],
            'max_tokens' => 1200,
            'system' => $this->getEnhancementPrompt($topic),
            'messages' => [
                ['role' => 'user', 'content' => $report],
            ],
        ];

        $response = $this->makeApiRequest($payload);
        return trim($this->extractClaudeText($response));
    }

    public function testConnection(): array
    {
        $this->validateApiKey();
        
        $payload = [
            'model' => $this->settings['claude_model'],
            'max_tokens' => 20,
            'system' => 'You are a helpful assistant.',
            'messages' => [
                ['role' => 'user', 'content' => 'Reply with exactly "OK"'],
            ],
        ];

        $response = $this->makeApiRequest($payload);
        $text = $this->extractClaudeText($response);
        
        return [
            'success' => trim($text) === 'OK',
            'model' => $this->settings['claude_model'],
            'message' => trim($text) === 'OK' ? 'Connection successful' : 'Unexpected response',
        ];
    }

    public function getModel(): string
    {
        return $this->settings['claude_model'] ?? 'claude-sonnet-4-6';
    }

    // ------------------------------------------------------------
    // Private Methods
    // ------------------------------------------------------------

    private function validateApiKey(): void
    {
        $key = trim($this->settings['claude_api_key'] ?? '');
        if (empty($key)) {
            throw new ITC_AI_Exception(
                'Claude API key is not configured. Please add your API key in IELTS Checker → Settings.'
            );
        }
        if (strlen($key) < 20) {
            throw new ITC_AI_Exception(
                'Invalid Claude API key format. Please check your API key in IELTS Checker → Settings.'
            );
        }
    }

    private function prepareImageData(string $imagePath): string
    {
        if (!file_exists($imagePath)) {
            throw new ITC_AI_Exception('Image file not found.');
        }

        $data = file_get_contents($imagePath);
        if ($data === false) {
            throw new ITC_AI_Exception('Failed to read image file.');
        }

        return base64_encode($data);
    }

    private function makeApiRequest(array $payload): array
    {
        $headers = [
            'Content-Type' => 'application/json',
            'x-api-key' => $this->settings['claude_api_key'],
            'anthropic-version' => self::ANTHROPIC_VERSION,
        ];

        $timeout = (int) ($this->settings['request_timeout'] ?? 90);

        $response = wp_remote_post(self::ANTHROPIC_API_URL, [
            'headers' => $headers,
            'body' => wp_json_encode($payload),
            'timeout' => $timeout,
            'data_format' => 'body',
        ]);

        if (is_wp_error($response)) {
            $this->logError('Network error: ' . $response->get_error_message());
            throw new ITC_AI_Exception(
                'Could not connect to Claude API. Please check your internet connection and try again.'
            );
        }

        $statusCode = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $decoded = json_decode($body, true);

        if (!is_array($decoded)) {
            $this->logError('Invalid response: ' . substr($body, 0, 500));
            throw new ITC_AI_Exception(
                'Claude API returned an invalid response. Please try again later.'
            );
        }

        if ($statusCode >= 400) {
            $errorMsg = $decoded['error']['message'] ?? 'Unknown error';
            $errorType = $decoded['error']['type'] ?? '';

            $this->logError("API Error (HTTP {$statusCode}): {$errorType} - {$errorMsg}");

            switch ($statusCode) {
                case 401:
                    throw new ITC_AI_Exception(
                        'Claude API authentication failed. Please check your API key in IELTS Checker → Settings.'
                    );
                case 403:
                    throw new ITC_AI_Exception(
                        'Claude API access forbidden. Please check your API key permissions.'
                    );
                case 404:
                    throw new ITC_AI_Exception(
                        'Claude model not found. Please check your model setting in IELTS Checker → Settings.'
                    );
                case 413:
                    throw new ITC_AI_Exception(
                        'The image is too large. Please upload a smaller image (max ' . 
                        $this->settings['max_upload_mb'] . 'MB).'
                    );
                case 429:
                    throw new ITC_AI_Exception(
                        'Claude API rate limit reached. Please wait a moment and try again.'
                    );
                case 500:
                case 502:
                case 503:
                    throw new ITC_AI_Exception(
                        'Claude API is temporarily unavailable. Please try again later.'
                    );
                default:
                    throw new ITC_AI_Exception(
                        'Claude API error: ' . $errorMsg . ' Please try again or contact support.'
                    );
            }
        }

        return $decoded;
    }

    private function extractClaudeText(array $response): string
    {
        $texts = [];
        
        if (isset($response['content']) && is_array($response['content'])) {
            foreach ($response['content'] as $block) {
                if (isset($block['type']) && $block['type'] === 'text') {
                    $texts[] = $block['text'] ?? '';
                }
            }
        }

        $result = trim(implode("\n", $texts));
        
        if (empty($result)) {
            $this->logError('Empty response from Claude');
            throw new ITC_AI_Exception(
                'Claude returned an empty response. Please try again.'
            );
        }

        return $result;
    }

    private function parseJson(string $text): array
    {
        $text = preg_replace('/^```(?:json)?\s*/i', '', $text);
        $text = preg_replace('/\s*```$/', '', $text);
        $text = trim($text);

        if (!str_starts_with($text, '{')) {
            if (preg_match('/\{[\s\S]*\}/', $text, $matches)) {
                $text = $matches[0];
            }
        }

        $decoded = json_decode($text, true);
        
        if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
            $this->logError('JSON parse error: ' . json_last_error_msg() . ' - Response: ' . substr($text, 0, 500));
            throw new ITC_AI_Exception(
                'Claude returned an invalid response format. Please try again.'
            );
        }

        return $decoded;
    }

    private function validateAndCalculateBands(array $result): array
    {
        $taskResponse = $this->extractBand($result, 'task_response');
        $coherence = $this->extractBand($result, 'coherence');
        $lexical = $this->extractBand($result, 'lexical_resource');
        $grammar = $this->extractBand($result, 'grammar');

        $rawOverall = ($taskResponse + $coherence + $lexical + $grammar) / 4;
        $overallBand = $this->roundToHalfBand($rawOverall);

        return [
            'overall_band' => $overallBand,
            'task_response' => $taskResponse,
            'coherence' => $coherence,
            'lexical_resource' => $lexical,
            'grammar' => $grammar,
            'grammar_mistakes' => (int) ($result['grammar_mistakes'] ?? 0),
            'vocabulary_level' => $this->normalizeVocabularyLevel($result['vocabulary_level'] ?? 'B2'),
            'vocabulary_repetition' => $this->normalizeRepetition($result['vocabulary_repetition'] ?? []),
            'grammar_mistakes_detail' => $this->normalizeGrammarMistakes($result['grammar_mistakes_detail'] ?? []),
            'feedback' => $this->normalizeFeedback($result['feedback'] ?? []),
            'improved_report' => trim($result['improved_report'] ?? ''),
            'enhanced_report' => trim($result['enhanced_report'] ?? ''),
        ];
    }

    private function extractBand(array $result, string $key): float
    {
        $value = $result[$key]['band'] ?? $result[$key] ?? 0;
        $value = (float) $value;
        $value = max(0, min(9, $value));
        return round($value * 2) / 2;
    }

    private function roundToHalfBand(float $value): float
    {
        $value = max(0, min(9, $value));
        return round($value * 2) / 2;
    }

    private function normalizeVocabularyLevel(?string $level): string
    {
        $valid = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'];
        $level = strtoupper(trim($level ?? 'B2'));
        return in_array($level, $valid, true) ? $level : 'B2';
    }

    private function normalizeRepetition(array $repetition): array
    {
        $result = [];
        $stopwords = [
            'the', 'a', 'an', 'and', 'is', 'are', 'to', 'of', 'in', 'on',
            'it', 'this', 'that', 'was', 'were', 'for', 'with', 'as', 'at',
            'by', 'from', 'into', 'through', 'during', 'including', 'etc'
        ];

        foreach ($repetition as $item) {
            $word = strtolower(trim($item['word'] ?? ''));
            if (empty($word) || in_array($word, $stopwords, true)) {
                continue;
            }
            $count = (int) ($item['count'] ?? 1);
            if ($count >= 3) {
                $result[] = [
                    'word' => $word,
                    'count' => $count,
                    'suggestions' => array_slice((array) ($item['suggestions'] ?? []), 0, 3),
                ];
            }
        }

        return $result;
    }

    private function normalizeGrammarMistakes(array $mistakes): array
    {
        $result = [];
        foreach ($mistakes as $m) {
            $sentence = trim($m['sentence'] ?? '');
            $correction = trim($m['correction'] ?? '');
            $explanation = trim($m['explanation'] ?? '');
            
            if (!empty($sentence) && !empty($correction)) {
                $result[] = [
                    'sentence' => $sentence,
                    'correction' => $correction,
                    'explanation' => $explanation ?: 'Grammar correction',
                ];
            }
        }
        return $result;
    }

    private function normalizeFeedback(array $feedback): array
    {
        return [
            'task_response' => trim($feedback['task_response'] ?? $feedback['task'] ?? ''),
            'coherence' => trim($feedback['coherence'] ?? $feedback['cohesion'] ?? ''),
            'lexical' => trim($feedback['lexical'] ?? $feedback['lexical_resource'] ?? ''),
            'grammar' => trim($feedback['grammar'] ?? ''),
        ];
    }

    private function countWords(string $text): int
    {
        $text = preg_replace('/\s+/', ' ', trim($text));
        return empty($text) ? 0 : count(explode(' ', $text));
    }

    private function logError(string $message): void
    {
        error_log('ITC: ' . $message);
    }

    // ------------------------------------------------------------
    // Prompts
    // ------------------------------------------------------------

    private function getImageAnalysisPrompt(): string
    {
        return <<<PROMPT
You are an expert IELTS Academic Writing Task 1 visual data analyst. You will be shown an image containing a chart, graph, table, map, process diagram, or combination of visuals from an IELTS Task 1 question.

Analyze the image and return a JSON object with the following structure:

{
  "visual_type": "bar_chart|line_graph|pie_chart|table|map|process|multiple_visuals|mixed|unknown",
  "title": "Title of the visual if visible",
  "description": "A clear, detailed description of what the visual shows",
  "axes": {
    "x": "X-axis label and values",
    "y": "Y-axis label and values"
  },
  "units": "Units used (percentage, millions, etc.)",
  "categories": ["List of categories or groups shown"],
  "data_points": [
    {"label": "Item 1", "value": "value or range", "description": "additional context"}
  ],
  "key_trends": ["Key trends observed"],
  "key_comparisons": ["Important comparisons"],
  "uncertain_information": ["Any values or details that are unclear from the image"]
}

Rules:
- Only include information you can clearly see in the image.
- If a value is uncertain, note it in "uncertain_information".
- Never invent or guess values.
- For maps, describe changes (additions, removals, relocations).
- For processes, describe stages and sequence.
- Return ONLY the JSON object. No other text.
PROMPT;
    }

    private function getEvaluationPrompt(): string
    {
        return <<<PROMPT
You are a certified IELTS examiner with 15+ years of experience marking Academic Writing Task 1 scripts. You evaluate strictly against the official IELTS band descriptors.

You will be shown:
1. An IELTS Task 1 visual (chart/graph/table/map/process)
2. The task topic
3. The candidate's report

CRITICAL: You MUST compare the candidate's report against the ACTUAL VISUAL. Check:
- Whether the candidate correctly understood the chart
- Whether numerical data is accurate
- Whether comparisons are accurate
- Whether trends are accurately reported
- Whether important features were omitted
- Whether the overview is appropriate
- Whether the report contains fabricated information

Return a JSON object with this exact structure:

{
  "task_response": {
    "band": 0,
    "summary": "Brief summary of task achievement",
    "strengths": ["Strength 1", "Strength 2"],
    "weaknesses": ["Weakness 1", "Weakness 2"],
    "data_accuracy": "accurate|mostly_accurate|inaccurate",
    "data_errors": ["Error description"]
  },
  "coherence": {
    "band": 0,
    "summary": "Brief summary of coherence and cohesion",
    "strengths": ["Strength 1"],
    "weaknesses": ["Weakness 1"]
  },
  "lexical_resource": {
    "band": 0,
    "summary": "Brief summary of vocabulary use",
    "strengths": ["Strength 1"],
    "weaknesses": ["Weakness 1"]
  },
  "grammar": {
    "band": 0,
    "summary": "Brief summary of grammar",
    "strengths": ["Strength 1"],
    "weaknesses": ["Weakness 1"]
  },
  "grammar_mistakes": 0,
  "vocabulary_level": "A1|A2|B1|B2|C1|C2",
  "vocabulary_repetition": [
    {"word": "example", "count": 3, "suggestions": ["synonym1", "synonym2", "synonym3"]}
  ],
  "grammar_mistakes_detail": [
    {"sentence": "original", "correction": "corrected", "explanation": "why"}
  ],
  "feedback": {
    "task_response": "Detailed feedback on task achievement",
    "coherence": "Detailed feedback on coherence and cohesion",
    "lexical": "Detailed feedback on vocabulary",
    "grammar": "Detailed feedback on grammar"
  },
  "improved_report": "Candidate's report with grammar fixed and naturalness improved, preserving meaning and structure",
  "enhanced_report": "Band 9 model version of the report with sophisticated vocabulary and complex grammar"
}

Band Scoring Rules:
- All band scores must be between 0.0 and 9.0 in 0.5 increments
- Task Achievement: Penalize for inaccurate data, missing key features, weak overview, under-length (<150 words)
- Coherence: Assess organization, paragraphing, logical flow, linking devices
- Lexical Resource: Assess range, precision, appropriateness, avoid repetition
- Grammar: Assess range, accuracy, variety of structures

Return ONLY the JSON object. No other text.
PROMPT;
    }

    private function getImprovementPrompt(): string
    {
        return <<<PROMPT
You are an IELTS writing coach. Rewrite the candidate's IELTS Task 1 report to sound more natural and idiomatic in standard academic English.

Rules:
- Preserve the candidate's meaning and reported data
- Preserve the original structure where reasonable
- Fix grammar and punctuation errors
- Improve awkward phrasing
- Improve word choice
- Avoid unnecessary sophistication (don't turn it into a Band 9)
- Keep roughly the same length
- Return ONLY the rewritten report text. No markdown, no explanations, no headings.

Candidate's report:
PROMPT;
    }

    private function getEnhancementPrompt(string $topic): string
    {
        return <<<PROMPT
You are an IELTS examiner-trainer who writes Band 9 model answers. Rewrite the candidate's IELTS Academic Writing Task 1 report into a Band 9 model response.

Task Topic: {$topic}

Rules:
- Preserve the actual data/trends reported by the candidate
- Do NOT invent data or change numerical values
- Do NOT introduce trends that don't exist
- Include a clear overview paragraph
- Use sophisticated vocabulary and varied grammatical structures
- Make comparisons accurately
- Keep approximately 150-190 words
- Follow IELTS Academic Task 1 conventions (overview + detailed paragraphs)
- Return ONLY the rewritten report text. No markdown, no explanations, no headings.

Candidate's report:
PROMPT;
    }

    private function getEvaluationSchema(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'task_response' => [
                    'type' => 'object',
                    'properties' => [
                        'band' => ['type' => 'number', 'minimum' => 0, 'maximum' => 9],
                        'summary' => ['type' => 'string'],
                        'strengths' => ['type' => 'array', 'items' => ['type' => 'string']],
                        'weaknesses' => ['type' => 'array', 'items' => ['type' => 'string']],
                        'data_accuracy' => ['type' => 'string', 'enum' => ['accurate', 'mostly_accurate', 'inaccurate']],
                        'data_errors' => ['type' => 'array', 'items' => ['type' => 'string']],
                    ],
                    'required' => ['band', 'summary'],
                ],
                'coherence' => [
                    'type' => 'object',
                    'properties' => [
                        'band' => ['type' => 'number', 'minimum' => 0, 'maximum' => 9],
                        'summary' => ['type' => 'string'],
                        'strengths' => ['type' => 'array', 'items' => ['type' => 'string']],
                        'weaknesses' => ['type' => 'array', 'items' => ['type' => 'string']],
                    ],
                    'required' => ['band', 'summary'],
                ],
                'lexical_resource' => [
                    'type' => 'object',
                    'properties' => [
                        'band' => ['type' => 'number', 'minimum' => 0, 'maximum' => 9],
                        'summary' => ['type' => 'string'],
                        'strengths' => ['type' => 'array', 'items' => ['type' => 'string']],
                        'weaknesses' => ['type' => 'array', 'items' => ['type' => 'string']],
                    ],
                    'required' => ['band', 'summary'],
                ],
                'grammar' => [
                    'type' => 'object',
                    'properties' => [
                        'band' => ['type' => 'number', 'minimum' => 0, 'maximum' => 9],
                        'summary' => ['type' => 'string'],
                        'strengths' => ['type' => 'array', 'items' => ['type' => 'string']],
                        'weaknesses' => ['type' => 'array', 'items' => ['type' => 'string']],
                    ],
                    'required' => ['band', 'summary'],
                ],
                'grammar_mistakes' => ['type' => 'integer', 'minimum' => 0],
                'vocabulary_level' => ['type' => 'string', 'enum' => ['A1', 'A2', 'B1', 'B2', 'C1', 'C2']],
                'vocabulary_repetition' => [
                    'type' => 'array',
                    'items' => [
                        'type' => 'object',
                        'properties' => [
                            'word' => ['type' => 'string'],
                            'count' => ['type' => 'integer', 'minimum' => 0],
                            'suggestions' => ['type' => 'array', 'items' => ['type' => 'string']],
                        ],
                        'required' => ['word', 'count'],
                    ],
                ],
                'grammar_mistakes_detail' => [
                    'type' => 'array',
                    'items' => [
                        'type' => 'object',
                        'properties' => [
                            'sentence' => ['type' => 'string'],
                            'correction' => ['type' => 'string'],
                            'explanation' => ['type' => 'string'],
                        ],
                        'required' => ['sentence', 'correction'],
                    ],
                ],
                'feedback' => [
                    'type' => 'object',
                    'properties' => [
                        'task_response' => ['type' => 'string'],
                        'coherence' => ['type' => 'string'],
                        'lexical' => ['type' => 'string'],
                        'grammar' => ['type' => 'string'],
                    ],
                ],
                'improved_report' => ['type' => 'string'],
                'enhanced_report' => ['type' => 'string'],
            ],
            'required' => ['task_response', 'coherence', 'lexical_resource', 'grammar'],
        ];
    }

    private function buildEvaluationMessage(string $topic, string $report, int $wordCount): string
    {
        $message = "TOPIC:\n{$topic}\n\n";
        
        if ($wordCount < 150) {
            $message .= "NOTE: This report is only {$wordCount} words (IELTS minimum is 150 words).\n\n";
        }
        
        $message .= "CANDIDATE REPORT:\n{$report}\n\n";
        $message .= "Evaluate this report against the actual visual. Check data accuracy, task achievement, coherence, vocabulary, and grammar.";
        
        return $message;
    }

    private function parseImageDescription(string $text): array
    {
        try {
            return $this->parseJson($text);
        } catch (ITC_AI_Exception $e) {
            return [
                'visual_type' => 'unknown',
                'title' => '',
                'description' => $text,
                'axes' => ['x' => '', 'y' => ''],
                'units' => '',
                'categories' => [],
                'data_points' => [],
                'key_trends' => [],
                'key_comparisons' => [],
                'uncertain_information' => [],
            ];
        }
    }
}

// ============================================================
// ADMIN SETTINGS CLASS
// ============================================================
class ITC_Admin_Settings
{
    public function __construct()
    {
        add_action('admin_menu', [$this, 'add_menu']);
        add_action('admin_init', [$this, 'register_settings']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
    }

    public function add_menu(): void
    {
        add_menu_page(
            'IELTS Checker Settings',
            'IELTS Checker',
            'manage_options',
            'itc-settings',
            [$this, 'render_settings_page'],
            'dashicons-editor-spellcheck',
            58
        );
    }

    public function register_settings(): void
    {
        register_setting('itc_settings_group', 'itc_settings', [
            'sanitize_callback' => [$this, 'sanitize_settings'],
        ]);
    }

    public function sanitize_settings(array $input): array
    {
        $clean = ITC_AI_Client::default_settings();

        $clean['claude_api_key'] = sanitize_text_field($input['claude_api_key'] ?? '');
        $clean['claude_model'] = sanitize_text_field($input['claude_model'] ?? 'claude-sonnet-4-6');
        $clean['max_upload_mb'] = max(1, (int) ($input['max_upload_mb'] ?? 10));
        $clean['rate_limit_max'] = max(1, (int) ($input['rate_limit_max'] ?? 20));
        $clean['rate_limit_window'] = max(60, (int) ($input['rate_limit_window'] ?? 3600));
        $clean['request_timeout'] = max(30, (int) ($input['request_timeout'] ?? 90));

        return $clean;
    }

    public function enqueue_admin_scripts(string $hook): void
    {
        if (!in_array($hook, ['toplevel_page_itc-settings', 'ielts-checker_page_itc-history'], true)) {
            return;
        }
        wp_add_inline_script('jquery', $this->get_admin_js(), 'after');
    }

    public function render_settings_page(): void
    {
        if (!current_user_can('manage_options')) {
            return;
        }
        
        $settings = wp_parse_args(get_option('itc_settings', []), ITC_AI_Client::default_settings());
        ?>
        <div class="wrap">
            <h1>IELTS Task 1 Checker &mdash; Settings</h1>
            <p>Configure the Claude AI settings for the IELTS Academic Writing Task 1 report checker.</p>

            <div id="itc-test-result" class="notice" style="display:none;"></div>

            <form method="post" action="options.php">
                <?php settings_fields('itc_settings_group'); ?>

                <table class="form-table" role="presentation">
                    <tr>
                        <th colspan="2"><h2 class="title">Claude (Anthropic) Configuration</h2></th>
                    </tr>
                    <tr>
                        <th scope="row"><label for="claude_api_key">Claude API Key</label></th>
                        <td>
                            <input type="password" id="claude_api_key" name="itc_settings[claude_api_key]" 
                                   value="<?php echo esc_attr($settings['claude_api_key']); ?>" 
                                   class="regular-text" autocomplete="off">
                            <p class="description">Get your API key from the <a href="https://console.anthropic.com/" target="_blank" rel="noopener">Anthropic Console</a>.</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="claude_model">Claude Model</label></th>
                        <td>
                            <input type="text" id="claude_model" name="itc_settings[claude_model]" 
                                   value="<?php echo esc_attr($settings['claude_model']); ?>" 
                                   class="regular-text">
                            <p class="description">
                                Recommended: <code>claude-sonnet-4-6</code> (fast, cost-effective). 
                                <a href="https://docs.anthropic.com/claude/docs/models" target="_blank" rel="noopener">View all models</a>
                            </p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"></th>
                        <td>
                            <button type="button" id="itc-test-connection" class="button button-secondary">
                                <span class="dashicons dashicons-yes" style="vertical-align:middle;"></span> Test Claude Connection
                            </button>
                            <span id="itc-test-loading" style="display:none; margin-left:10px;">
                                <span class="spinner is-active" style="float:none;"></span> Testing...
                            </span>
                        </td>
                    </tr>

                    <tr>
                        <th colspan="2"><h2 class="title">Limits &amp; Performance</h2></th>
                    </tr>
                    <tr>
                        <th scope="row"><label for="max_upload_mb">Max Image Size (MB)</label></th>
                        <td>
                            <input type="number" min="1" max="50" id="max_upload_mb" 
                                   name="itc_settings[max_upload_mb]" 
                                   value="<?php echo esc_attr((string) $settings['max_upload_mb']); ?>" 
                                   class="small-text">
                            <p class="description">Maximum allowed image upload size. Smaller images process faster.</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="request_timeout">Request Timeout (seconds)</label></th>
                        <td>
                            <input type="number" min="30" max="300" id="request_timeout" 
                                   name="itc_settings[request_timeout]" 
                                   value="<?php echo esc_attr((string) $settings['request_timeout']); ?>" 
                                   class="small-text">
                            <p class="description">Maximum time to wait for Claude API response.</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="rate_limit_max">Rate Limit</label></th>
                        <td>
                            <input type="number" min="1" id="rate_limit_max" 
                                   name="itc_settings[rate_limit_max]" 
                                   value="<?php echo esc_attr((string) $settings['rate_limit_max']); ?>" 
                                   class="small-text">
                            requests per
                            <input type="number" min="60" name="itc_settings[rate_limit_window]" 
                                   value="<?php echo esc_attr((string) $settings['rate_limit_window']); ?>" 
                                   class="small-text">
                            seconds, per IP.
                        </td>
                    </tr>
                </table>

                <?php submit_button('Save Settings'); ?>
            </form>

            <hr>

            <h2>Shortcode</h2>
            <p>Add this shortcode to any page or post to display the checker:</p>
            <code>[ielts_task1_checker]</code>

            <hr>

            <h2>System Diagnostics</h2>
            <table class="widefat" style="max-width:600px;">
                <tr><th>PHP Version</th><td><?php echo esc_html(PHP_VERSION); ?></td></tr>
                <tr><th>WordPress HTTP API</th><td><?php echo function_exists('wp_remote_post') ? '✓ Available' : '✗ Not available'; ?></td></tr>
                <tr><th>Upload Max Size</th><td><?php echo esc_html(ini_get('upload_max_filesize')); ?></td></tr>
                <tr><th>Max Execution Time</th><td><?php echo esc_html(ini_get('max_execution_time')); ?>s</td></tr>
                <tr><th>Memory Limit</th><td><?php echo esc_html(ini_get('memory_limit')); ?></td></tr>
                <tr><th>Claude Model</th><td><code><?php echo esc_html($settings['claude_model']); ?></code></td></tr>
                <tr><th>API Key Configured</th><td><?php echo !empty($settings['claude_api_key']) ? '✓ Yes' : '✗ No'; ?></td></tr>
            </table>
        </div>
        <?php
    }

    private function get_admin_js(): string
    {
        $nonce = wp_create_nonce('itc_nonce');
        return <<<JS
        jQuery(document).ready(function($) {
            $('#itc-test-connection').on('click', function() {
                var $btn = $(this);
                var $result = $('#itc-test-result');
                var $loading = $('#itc-test-loading');
                
                $btn.prop('disabled', true);
                $loading.show();
                $result.hide();
                
                $.post(ajaxurl, {
                    action: 'itc_test_connection',
                    nonce: '{$nonce}'
                })
                .done(function(res) {
                    if (res.success) {
                        $result
                            .removeClass('notice-error')
                            .addClass('notice-success')
                            .html('<p>✓ Claude API connection successful! Model: ' + res.data.model + '</p>')
                            .show();
                    } else {
                        $result
                            .removeClass('notice-success')
                            .addClass('notice-error')
                            .html('<p>✗ ' + (res.data.message || 'Connection failed') + '</p>')
                            .show();
                    }
                })
                .fail(function() {
                    $result
                        .removeClass('notice-success')
                        .addClass('notice-error')
                        .html('<p>✗ Network error while testing connection.</p>')
                        .show();
                })
                .always(function() {
                    $btn.prop('disabled', false);
                    $loading.hide();
                });
            });
        });
JS;
    }
}

// ============================================================
// ADMIN HISTORY CLASS
// ============================================================
class ITC_Admin_History
{
    public function __construct()
    {
        add_action('admin_menu', [$this, 'add_menu']);
    }

    public function add_menu(): void
    {
        add_submenu_page(
            'itc-settings',
            'Report History',
            'History & Dashboard',
            'manage_options',
            'itc-history',
            [$this, 'render_page']
        );
    }

    public function render_page(): void
    {
        if (!current_user_can('manage_options')) {
            return;
        }

        $total = ITC_DB::count_reports();
        $avg   = ITC_DB::average_band();
        $reports = ITC_DB::get_reports(50, 0);

        $wordCounts = [];
        foreach ($reports as $r) {
            $rep = json_decode($r['vocabulary_repetition'] ?? '[]', true) ?: [];
            foreach ($rep as $item) {
                $word = strtolower($item['word'] ?? '');
                if ($word === '') continue;
                $wordCounts[$word] = ($wordCounts[$word] ?? 0) + (int) ($item['count'] ?? 1);
            }
        }
        arsort($wordCounts);
        $topWords = array_slice($wordCounts, 0, 10, true);
        ?>
        <div class="wrap">
            <h1>IELTS Checker &mdash; History &amp; Dashboard</h1>

            <div style="display:flex; gap:20px; margin:20px 0; flex-wrap:wrap;">
                <div class="card" style="padding:16px 24px; background:#fff; border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.1);">
                    <h2 style="margin:0;font-size:28px;"><?php echo esc_html((string) $total); ?></h2>
                    <p style="margin:0;color:#666;">Total Reports</p>
                </div>
                <div class="card" style="padding:16px 24px; background:#fff; border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.1);">
                    <h2 style="margin:0;font-size:28px;"><?php echo esc_html(number_format($avg, 1)); ?></h2>
                    <p style="margin:0;color:#666;">Average Overall Band</p>
                </div>
                <div class="card" style="padding:16px 24px;min-width:260px; background:#fff; border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.1);">
                    <p style="margin:0 0 6px;color:#666;">Most Repeated Words</p>
                    <?php if (empty($topWords)): ?>
                        <p style="margin:0;">No data yet.</p>
                    <?php else: ?>
                        <?php foreach ($topWords as $word => $count): ?>
                            <span style="display:inline-block;background:#ece3fb;border-radius:10px;padding:2px 10px;margin:2px;font-size:12px;">
                                <?php echo esc_html($word) . ' (' . esc_html((string) $count) . ')'; ?>
                            </span>
                        <?php endforeach; ?>
                    <?php endif; ?>
                </div>
            </div>

            <h2>Recent Reports</h2>
            <table class="wp-list-table widefat fixed striped">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Topic</th>
                        <th>Provider</th>
                        <th>Overall Band</th>
                        <th>Vocab Level</th>
                        <th>Grammar Mistakes</th>
                        <th>Date</th>
                    </tr>
                </thead>
                <tbody>
                    <?php if (empty($reports)): ?>
                        <tr><td colspan="7">No reports yet.</td></tr>
                    <?php endif; ?>
                    <?php foreach ($reports as $r): ?>
                        <tr>
                            <td>#<?php echo esc_html((string) $r['id']); ?></td>
                            <td><?php echo esc_html(wp_trim_words($r['topic'], 12)); ?></td>
                            <td><?php echo esc_html(ucfirst($r['provider'] ?? '')); ?></td>
                            <td><?php echo esc_html($r['overall_band'] ?? 'N/A'); ?></td>
                            <td><?php echo esc_html($r['vocabulary_level'] ?? 'N/A'); ?></td>
                            <td><?php echo esc_html((string) ($r['grammar_mistakes_count'] ?? 0)); ?></td>
                            <td><?php echo esc_html($r['created_at']); ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php
    }
}

// ============================================================
// AJAX CLASS
// ============================================================
class ITC_Ajax
{
    public function __construct()
    {
        foreach (['itc_analyze_image', 'itc_check_report', 'itc_improve', 'itc_enhance', 'itc_test_connection'] as $action) {
            add_action('wp_ajax_' . $action, [$this, $action]);
            add_action('wp_ajax_nopriv_' . $action, [$this, $action]);
        }
    }

    public function itc_analyze_image(): void
    {
        $this->verifyRequest('analyze_image');

        if (empty($_FILES['image'])) {
            wp_send_json_error(['message' => 'No image uploaded.'], 422);
        }

        $settings = wp_parse_args(get_option('itc_settings', []), ITC_AI_Client::default_settings());
        $maxBytes = (int) $settings['max_upload_mb'] * 1024 * 1024;

        $file = $_FILES['image'];
        if ($file['error'] !== UPLOAD_ERR_OK) {
            wp_send_json_error(['message' => 'Upload failed. Error code: ' . $file['error']], 422);
        }
        if ($file['size'] > $maxBytes) {
            wp_send_json_error([
                'message' => 'File exceeds the ' . $settings['max_upload_mb'] . 'MB limit.'
            ], 422);
        }

        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $mime = $finfo->file($file['tmp_name']);
        $allowed = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
        if (!in_array($mime, $allowed, true)) {
            wp_send_json_error(['message' => 'Unsupported file type. Use JPEG, PNG, WEBP, or GIF.'], 422);
        }

        if (!function_exists('wp_handle_upload')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $upload = wp_handle_upload($file, ['test_form' => false]);
        if (isset($upload['error'])) {
            wp_send_json_error(['message' => $upload['error']], 422);
        }

        try {
            $client = new ITC_AI_Client();
            $result = $client->describeImage($upload['file'], $mime);
            
            $imageData = base64_encode((string) file_get_contents($upload['file']));
            
            wp_send_json_success([
                'visual_type' => $result['visual_type'] ?? 'unknown',
                'title' => $result['title'] ?? '',
                'description' => $result['description'] ?? '',
                'axes' => $result['axes'] ?? ['x' => '', 'y' => ''],
                'units' => $result['units'] ?? '',
                'categories' => $result['categories'] ?? [],
                'data_points' => $result['data_points'] ?? [],
                'key_trends' => $result['key_trends'] ?? [],
                'key_comparisons' => $result['key_comparisons'] ?? [],
                'uncertain_information' => $result['uncertain_information'] ?? [],
                'image_url' => $upload['url'],
                'image_path' => $upload['file'],
                'image_mime' => $mime,
                'image_data' => $imageData,
            ]);
        } catch (ITC_AI_Exception $e) {
            error_log('ITC analyze_image failed: ' . $e->getMessage());
            wp_send_json_error(['message' => $e->getMessage()], 502);
        } catch (Exception $e) {
            error_log('ITC analyze_image unexpected error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'An unexpected error occurred. Please try again.'], 500);
        }
    }

    public function itc_check_report(): void
    {
        $this->verifyRequest('check_report');

        $topic = $this->clean($_POST['topic'] ?? '', 2000);
        $report = $this->clean($_POST['report'] ?? '', 20000);
        $imagePath = $this->clean($_POST['image_path'] ?? '', 500);
        $imageMime = $this->clean($_POST['image_mime'] ?? '', 100);
        $imageData = $_POST['image_data'] ?? '';

        if (empty($topic) || empty($report)) {
            wp_send_json_error(['message' => 'Topic and report are both required.'], 422);
        }

        $wordCount = $this->countWords($report);
        if ($wordCount < 20) {
            wp_send_json_error([
                'message' => 'Report is too short to evaluate meaningfully (minimum ~20 words).'
            ], 422);
        }

        if (empty($imageData) && empty($imagePath)) {
            wp_send_json_error([
                'message' => 'Please upload and analyze an image of the Task 1 visual first.'
            ], 422);
        }

        if (!empty($imageData) && empty($imagePath)) {
            $tempFile = $this->createTempImage($imageData, $imageMime);
            if ($tempFile) {
                $imagePath = $tempFile;
            } else {
                wp_send_json_error([
                    'message' => 'Failed to process image. Please try uploading again.'
                ], 422);
            }
        }

        $cacheKey = $this->getCacheKey($topic, $report, $imageData);
        $cached = get_transient($cacheKey);
        if ($cached !== false) {
            $cached['cached'] = true;
            wp_send_json_success($cached);
            return;
        }

        try {
            $client = new ITC_AI_Client();
            $result = $client->analyzeReport($topic, $imagePath, $imageMime, $report);
            
            set_transient($cacheKey, $result, DAY_IN_SECONDS * 30);
            
            $result['report_id'] = $this->saveToDatabase($topic, $imagePath, $report, $result);
            $result['cached'] = false;
            $result['word_count'] = $wordCount;
            $result['below_minimum'] = $wordCount < 150;
            
            wp_send_json_success($result);
        } catch (ITC_AI_Exception $e) {
            error_log('ITC check_report failed: ' . $e->getMessage());
            wp_send_json_error(['message' => $e->getMessage()], 502);
        } catch (Exception $e) {
            error_log('ITC check_report unexpected error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'An unexpected error occurred. Please try again.'], 500);
        } finally {
            if (isset($tempFile) && file_exists($tempFile)) {
                @unlink($tempFile);
            }
        }
    }

    public function itc_improve(): void
    {
        $this->verifyRequest('improve');
        $report = $this->clean($_POST['report'] ?? '', 20000);
        
        if (empty($report)) {
            wp_send_json_error(['message' => 'Report text is required.'], 422);
        }

        try {
            $client = new ITC_AI_Client();
            $improved = $client->improveNaturalness($report);
            wp_send_json_success(['improved_report' => $improved]);
        } catch (ITC_AI_Exception $e) {
            error_log('ITC improve failed: ' . $e->getMessage());
            wp_send_json_error(['message' => $e->getMessage()], 502);
        } catch (Exception $e) {
            error_log('ITC improve unexpected error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'An unexpected error occurred. Please try again.'], 500);
        }
    }

    public function itc_enhance(): void
    {
        $this->verifyRequest('enhance');
        $topic = $this->clean($_POST['topic'] ?? '', 2000);
        $report = $this->clean($_POST['report'] ?? '', 20000);
        
        if (empty($report)) {
            wp_send_json_error(['message' => 'Report text is required.'], 422);
        }

        try {
            $client = new ITC_AI_Client();
            $enhanced = $client->enhanceReport($topic, $report);
            wp_send_json_success(['enhanced_report' => $enhanced]);
        } catch (ITC_AI_Exception $e) {
            error_log('ITC enhance failed: ' . $e->getMessage());
            wp_send_json_error(['message' => $e->getMessage()], 502);
        } catch (Exception $e) {
            error_log('ITC enhance unexpected error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'An unexpected error occurred. Please try again.'], 500);
        }
    }

    public function itc_test_connection(): void
    {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        try {
            $client = new ITC_AI_Client();
            $result = $client->testConnection();
            wp_send_json_success($result);
        } catch (ITC_AI_Exception $e) {
            wp_send_json_error(['message' => $e->getMessage()], 502);
        } catch (Exception $e) {
            wp_send_json_error(['message' => 'An unexpected error occurred: ' . $e->getMessage()], 500);
        }
    }

    private function verifyRequest(string $action): void
    {
        check_ajax_referer('itc_nonce', 'nonce');
        $this->enforceRateLimit($action);
    }

    private function enforceRateLimit(string $action): void
    {
        $settings = wp_parse_args(get_option('itc_settings', []), ITC_AI_Client::default_settings());
        $ip = $this->clientIp();
        $key = 'itc_rl_' . md5($action . '_' . $ip);

        $count = (int) get_transient($key);
        $max = (int) ($settings['rate_limit_max'] ?? 20);
        $window = (int) ($settings['rate_limit_window'] ?? 3600);

        if ($count >= $max) {
            wp_send_json_error(['message' => 'Rate limit exceeded. Please try again later.'], 429);
        }

        if ($count === 0) {
            set_transient($key, 1, $window);
        } else {
            set_transient($key, $count + 1, $window);
        }
    }

    private function clientIp(): string
    {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = trim($ips[0]);
        } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        }
        return sanitize_text_field($ip);
    }

    private function clean(string $value, int $maxLength = 20000): string
    {
        $value = trim(wp_strip_all_tags($value));
        return mb_substr($value, 0, $maxLength);
    }

    private function countWords(string $text): int
    {
        $text = preg_replace('/\s+/', ' ', trim($text));
        return empty($text) ? 0 : count(explode(' ', $text));
    }

    private function createTempImage(string $imageData, string $mime): ?string
    {
        $data = base64_decode($imageData);
        if ($data === false) {
            return null;
        }

        $extension = $this->mimeToExtension($mime);
        if (!$extension) {
            return null;
        }

        $tempFile = tempnam(sys_get_temp_dir(), 'itc_img_') . '.' . $extension;
        if (file_put_contents($tempFile, $data) === false) {
            return null;
        }

        return $tempFile;
    }

    private function mimeToExtension(string $mime): string
    {
        return match ($mime) {
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/webp' => 'webp',
            'image/gif' => 'gif',
            default => '',
        };
    }

    private function getCacheKey(string $topic, string $report, string $imageData): string
    {
        $settings = wp_parse_args(get_option('itc_settings', []), ITC_AI_Client::default_settings());
        $model = $settings['claude_model'] ?? 'claude-sonnet-4-6';
        $imageHash = hash('md5', $imageData);
        $contentHash = hash('md5', $model . '|' . $topic . '|' . $report);
        return 'itc_result_' . $contentHash . '_' . substr($imageHash, 0, 12);
    }

    private function saveToDatabase(string $topic, string $imagePath, string $report, array $result): int
    {
        $imageData = file_exists($imagePath) ? hash('md5', (string) file_get_contents($imagePath)) : '';
        
        return ITC_DB::insert_report([
            'user_id' => get_current_user_id() ?: null,
            'topic' => $topic,
            'image_url' => '',
            'image_description' => '',
            'image_hash' => $imageData,
            'report' => $report,
            'provider' => 'claude',
            'overall_band' => $result['overall_band'] ?? null,
            'task_response' => $result['task_response'] ?? null,
            'coherence' => $result['coherence'] ?? null,
            'lexical_resource' => $result['lexical_resource'] ?? null,
            'grammar' => $result['grammar'] ?? null,
            'grammar_mistakes_count' => $result['grammar_mistakes'] ?? 0,
            'vocabulary_level' => $result['vocabulary_level'] ?? null,
            'vocabulary_repetition' => wp_json_encode($result['vocabulary_repetition'] ?? []),
            'grammar_mistakes_detail' => wp_json_encode($result['grammar_mistakes_detail'] ?? []),
            'feedback' => wp_json_encode($result['feedback'] ?? []),
            'improved_report' => $result['improved_report'] ?? null,
            'enhanced_report' => $result['enhanced_report'] ?? null,
            'ip_address' => $this->clientIp(),
        ]);
    }
}

// ============================================================
// SHORTCODE CLASS
// ============================================================
class ITC_Shortcode
{
    public function __construct()
    {
        add_shortcode('ielts_task1_checker', [$this, 'render']);
    }

    public function render(): string
    {
        $settings = wp_parse_args(get_option('itc_settings', []), ITC_AI_Client::default_settings());
        ob_start();
        ?>
        <div class="itc-app">
            <style>
            .itc-app { --itc-red: #d9302f; font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; }
            .itc-shell { max-width: 1400px; margin: 0 auto; }
            .itc-title { color: var(--itc-red); font-weight: 800; font-size: 2.2rem; }
            .itc-subtitle { color: #555; max-width: 800px; margin: 0 auto; font-size: 1.1rem; }
            .itc-app .card { border: none; border-radius: 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: box-shadow 0.2s ease; }
            .itc-app .card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.12); }
            .itc-app .card-body { padding: 1.25rem 1.5rem; }
            @keyframes itcFadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
            .itc-app .card { animation: itcFadeIn 0.35s ease-in-out; }
            .itc-band-card { background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); border-bottom: 4px solid #198754; }
            .itc-band-card .display-3 { font-size: 4rem; font-weight: 800; letter-spacing: -1px; }
            .itc-card-vocab { background: #fff8d6; border-left: 4px solid #ffc107; }
            .itc-card-grammar { background: #fde3e3; border-left: 4px solid #dc3545; }
            .itc-card-repetition { background: #ece3fb; border-left: 4px solid #6f42c1; }
            .itc-app .score-item { background: #fff; border-radius: 10px; padding: 0.75rem 1rem; margin-bottom: 0.6rem; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }
            .itc-app .score-item .progress { height: 0.4rem; border-radius: 1rem; }
            .itc-app .score-item .progress-bar { border-radius: 1rem; transition: width 0.6s ease; }
            #itcImagePreview { max-height: 280px; object-fit: contain; width: auto; max-width: 100%; background: #f8f9fa; border-radius: 8px; padding: 4px; }
            .itc-app .btn { border-radius: 10px; font-weight: 600; padding: 0.6rem 1.2rem; transition: all 0.2s ease; }
            .itc-app .btn-lg { padding: 0.8rem 1.5rem; font-size: 1.1rem; }
            .itc-app .btn .spinner-border { margin-left: 0.4rem; width: 1rem; height: 1rem; }
            .itc-app .btn .btn-label { transition: opacity 0.2s ease; }
            .itc-app .btn .btn-label.opacity-50 { opacity: 0.5; }
            #itcToastContainer .toast { border-radius: 10px; box-shadow: 0 4px 16px rgba(0,0,0,0.15); }
            #itcToastContainer .toast-body { padding: 0.75rem 1rem; }
            .itc-app .nav-tabs .nav-link { border-radius: 8px 8px 0 0; font-weight: 600; color: #555; padding: 0.6rem 1.2rem; }
            .itc-app .nav-tabs .nav-link.active { color: var(--itc-red); border-color: #dee2e6 #dee2e6 #fff; }
            .itc-app .tab-content { border-radius: 0 0 10px 10px; background: #fff; max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-size: 0.95rem; line-height: 1.7; }
            .itc-app .form-control { border-radius: 10px; border: 1px solid #ddd; padding: 0.75rem 1rem; font-size: 0.95rem; }
            .itc-app .form-control:focus { border-color: var(--itc-red); box-shadow: 0 0 0 3px rgba(217,48,47,0.15); }
            .itc-app textarea.form-control { resize: vertical; min-height: 100px; }
            #itcWordWarning { color: #856404; font-weight: 600; }
            @keyframes itcPulse { 0%,100%{opacity:1} 50%{opacity:0.5} }
            #itcCheckBtn.loading .btn-label { animation: itcPulse 1.2s ease-in-out infinite; }
            @media (max-width:991px) { .itc-title { font-size:1.6rem; } .itc-app .card-body { padding:1rem; } .itc-band-card .display-3 { font-size:3rem; } #itcImagePreview { max-height:200px; } }
            @media (max-width:576px) { .itc-title { font-size:1.3rem; } .itc-subtitle { font-size:0.9rem; } .itc-app .btn { font-size:0.85rem; padding:0.5rem 0.8rem; } .itc-app .btn-lg { font-size:0.95rem; padding:0.6rem 1rem; } .itc-band-card .display-3 { font-size:2.5rem; } }
            </style>

            <div class="toast-container position-fixed top-0 end-0 p-3" id="itcToastContainer" style="z-index:1080"></div>

            <div class="container-fluid itc-shell py-4">
                <header class="text-center mb-4">
                    <h1 class="itc-title">IELTS Academic Writing Task 1 Report Checker</h1>
                    <p class="itc-subtitle">Upload your Task 1 visual, write your report, and get instant AI feedback with band scores.</p>
                    <p class="small text-muted">Powered by Claude AI</p>
                </header>

                <div class="row g-4">
                    <div class="col-lg-7">
                        <div class="card shadow-sm mb-3">
                            <div class="card-body">
                                <label class="form-label fw-semibold">Topic</label>
                                <textarea id="itcTopic" class="form-control" rows="2" placeholder="Enter the IELTS Task 1 question / topic..."></textarea>
                            </div>
                        </div>

                        <div class="card shadow-sm mb-3">
                            <div class="card-body">
                                <label class="form-label fw-semibold">Topic Image</label>
                                <p class="text-muted small mb-2">Upload a clear image of the graph/chart/table/map (JPEG, PNG, WEBP, max <?php echo (int) $settings['max_upload_mb']; ?>MB).</p>
                                <div class="d-flex gap-2 flex-wrap">
                                    <input type="file" id="itcImageInput" class="form-control" accept="image/jpeg,image/png,image/webp" style="width:auto;flex:1;">
                                    <button id="itcAnalyzeImageBtn" type="button" class="btn btn-outline-secondary">
                                        <span class="btn-label"><i class="bi bi-magic"></i> Analyze Image</span>
                                        <span class="spinner-border spinner-border-sm d-none" role="status"></span>
                                    </button>
                                    <button id="itcRemoveImageBtn" type="button" class="btn btn-outline-danger d-none">
                                        <i class="bi bi-x-lg"></i>
                                    </button>
                                </div>
                                <div id="itcImagePreviewContainer" class="mt-2 d-none">
                                    <img id="itcImagePreview" class="img-fluid rounded border" alt="Preview">
                                </div>
                                <div id="itcVisualAnalysis" class="mt-2 d-none">
                                    <div class="alert alert-info small mb-0">
                                        <strong>AI Visual Analysis</strong>
                                        <div id="itcVisualType" class="text-muted"></div>
                                        <div id="itcVisualDescription" class="mt-1"></div>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="card shadow-sm mb-3">
                            <div class="card-body">
                                <label class="form-label fw-semibold">Report</label>
                                <textarea id="itcReport" class="form-control" rows="10" placeholder="Enter your IELTS Academic Writing Task 1 report..."></textarea>
                                <div class="d-flex justify-content-between align-items-center mt-2">
                                    <span class="text-muted small">Word Count: <span id="itcWordCount">0</span></span>
                                    <span id="itcWordWarning" class="text-warning small d-none">
                                        <i class="bi bi-exclamation-triangle"></i> Below 150 words
                                    </span>
                                </div>
                            </div>
                        </div>

                        <div class="d-flex flex-wrap gap-2">
                            <button id="itcCheckBtn" type="button" class="btn btn-danger btn-lg flex-grow-1">
                                <span class="btn-label"><i class="bi bi-check2-circle"></i> Check Report</span>
                                <span class="spinner-border spinner-border-sm d-none" role="status"></span>
                            </button>
                            <button id="itcImproveBtn" type="button" class="btn btn-success" disabled>
                                <span class="btn-label">Improve Naturalness</span>
                                <span class="spinner-border spinner-border-sm d-none" role="status"></span>
                            </button>
                            <button id="itcEnhanceBtn" type="button" class="btn btn-primary" disabled>
                                <span class="btn-label">Enhance Report</span>
                                <span class="spinner-border spinner-border-sm d-none" role="status"></span>
                            </button>
                        </div>

                        <p class="text-muted small mt-3">This tool is an AI guide, not a definitive score. Use it to complement your IELTS preparation.</p>
                    </div>

                    <div class="col-lg-5">
                        <div class="card shadow-sm text-center mb-3 itc-band-card">
                            <div class="card-body">
                                <h6 class="text-uppercase text-muted">Overall Band Score</h6>
                                <div class="display-3 fw-bold text-success" id="itcOverallBand">—</div>
                                <p class="small text-muted">(± 0.5 AI estimation)</p>
                            </div>
                        </div>

                        <div class="card shadow-sm mb-3 itc-card-vocab">
                            <div class="card-body">
                                <h6 class="fw-semibold">Vocabulary Complexity: <span id="itcVocabLevel">—</span></h6>
                                <p class="small mb-0 text-muted" id="itcVocabDescription">AI-estimated CEFR level</p>
                            </div>
                        </div>

                        <div class="card shadow-sm mb-3 itc-card-grammar">
                            <div class="card-body">
                                <h6 class="fw-semibold mb-2">Grammar Mistakes: <span id="itcGrammarCount">0</span></h6>
                                <ul id="itcGrammarList" class="small mb-0 ps-3"></ul>
                            </div>
                        </div>

                        <div class="card shadow-sm mb-3 itc-card-repetition">
                            <div class="card-body">
                                <h6 class="fw-semibold mb-2">Vocabulary Repetition</h6>
                                <div id="itcRepetitionList" class="small text-muted">No evaluation yet.</div>
                            </div>
                        </div>

                        <div id="itcScoreBreakdown"></div>

                        <div id="itcFeedbackSection" class="d-none">
                            <h6 class="fw-semibold mt-3">Detailed Feedback</h6>
                            <div id="itcFeedbackText" class="small"></div>
                        </div>

                        <div id="itcRewriteSection" class="d-none mt-3">
                            <ul class="nav nav-tabs" role="tablist">
                                <li class="nav-item">
                                    <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#itcImprovedTab" type="button">
                                        Improved
                                    </button>
                                </li>
                                <li class="nav-item">
                                    <button class="nav-link" data-bs-toggle="tab" data-bs-target="#itcEnhancedTab" type="button">
                                        Enhanced (Band 9)
                                    </button>
                                </li>
                            </ul>
                            <div class="tab-content border border-top-0 p-3 bg-white rounded-bottom">
                                <div class="tab-pane fade show active" id="itcImprovedTab"></div>
                                <div class="tab-pane fade" id="itcEnhancedTab"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            var imageData = { data: '', mime: '', path: '', url: '', analysis: null };
            var lastResult = null;
            var progressInterval = null;

            function toast(message, type) {
                type = type || 'info';
                var id = 'itc-toast-' + Date.now();
                var bgClass = 'text-bg-' + (type === 'error' ? 'danger' : type === 'warning' ? 'warning' : type === 'success' ? 'success' : 'primary');
                var html = '<div id="' + id + '" class="toast align-items-center ' + bgClass + ' border-0" role="alert"><div class="d-flex"><div class="toast-body">' + message + '</div><button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button></div></div>';
                $('#itcToastContainer').append(html);
                var el = document.getElementById(id);
                var t = new bootstrap.Toast(el, { delay: 5000 });
                t.show();
                el.addEventListener('hidden.bs.toast', function() { el.remove(); });
            }

            function setLoading($btn, loading) {
                $btn.prop('disabled', loading);
                $btn.find('.spinner-border').toggleClass('d-none', !loading);
                $btn.find('.btn-label').toggleClass('opacity-50', loading);
            }

            function setGlobalLoading(loading) {
                var $btn = $('#itcCheckBtn');
                if (loading) {
                    $btn.prop('disabled', true);
                    $btn.find('.spinner-border').removeClass('d-none');
                    $btn.find('.btn-label').addClass('opacity-50');
                    var messages = ['Analyzing your report...', 'Claude is evaluating your Task Achievement...', 'Checking grammar and vocabulary...', 'Comparing your report with the chart...', 'Calculating your IELTS band...'];
                    var idx = 0;
                    if (!progressInterval) {
                        progressInterval = setInterval(function() {
                            if (idx < messages.length) {
                                toast(messages[idx], 'info');
                                idx++;
                            } else {
                                clearInterval(progressInterval);
                                progressInterval = null;
                            }
                        }, 1500);
                    }
                } else {
                    $btn.prop('disabled', false);
                    $btn.find('.spinner-border').addClass('d-none');
                    $btn.find('.btn-label').removeClass('opacity-50');
                    if (progressInterval) {
                        clearInterval(progressInterval);
                        progressInterval = null;
                    }
                }
            }

            function updateWordCount() {
                var text = $('#itcReport').val().trim();
                var count = text === '' ? 0 : text.split(/\s+/).length;
                $('#itcWordCount').text(count);
                if (count > 0 && count < 150) {
                    $('#itcWordWarning').removeClass('d-none');
                } else {
                    $('#itcWordWarning').addClass('d-none');
                }
                return count;
            }

            $('#itcReport').on('input', updateWordCount);

            $('#itcImageInput').on('change', function() {
                var file = this.files[0];
                if (!file) return;
                var maxBytes = (ITC.maxUploadMb || 10) * 1024 * 1024;
                if (file.size > maxBytes) {
                    toast('File exceeds the ' + (ITC.maxUploadMb || 10) + 'MB limit.', 'error');
                    $(this).val('');
                    return;
                }
                var validTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
                if (!validTypes.includes(file.type)) {
                    toast('Unsupported file type. Use JPEG, PNG, WEBP, or GIF.', 'error');
                    $(this).val('');
                    return;
                }
                var reader = new FileReader();
                reader.onload = function(e) {
                    $('#itcImagePreview').attr('src', e.target.result);
                    $('#itcImagePreviewContainer').removeClass('d-none');
                    $('#itcRemoveImageBtn').removeClass('d-none');
                    imageData.data = e.target.result.split(',')[1];
                    imageData.mime = file.type;
                    imageData.analysis = null;
                    $('#itcVisualAnalysis').addClass('d-none');
                    toast('Image loaded. Click "Analyze Image" for AI visual analysis.', 'success');
                };
                reader.readAsDataURL(file);
            });

            $('#itcRemoveImageBtn').on('click', function() {
                $('#itcImageInput').val('');
                $('#itcImagePreviewContainer').addClass('d-none');
                $('#itcRemoveImageBtn').addClass('d-none');
                $('#itcVisualAnalysis').addClass('d-none');
                imageData = { data: '', mime: '', path: '', url: '', analysis: null };
                toast('Image removed.', 'info');
            });

            $('#itcAnalyzeImageBtn').on('click', function() {
                var $btn = $(this);
                var fileInput = document.getElementById('itcImageInput');
                if (!fileInput.files.length) {
                    toast('Please choose an image first.', 'warning');
                    return;
                }
                var formData = new FormData();
                formData.append('action', 'itc_analyze_image');
                formData.append('nonce', ITC.nonce);
                formData.append('image', fileInput.files[0]);
                setLoading($btn, true);
                toast('Analyzing your Task 1 image...', 'info');
                $.ajax({
                    url: ITC.ajaxUrl,
                    method: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    timeout: 120000,
                })
                .done(function(res) {
                    if (!res.success) {
                        toast(res.data?.message || 'Could not analyze the image.', 'error');
                        return;
                    }
                    imageData.analysis = res.data;
                    imageData.path = res.data.image_path || '';
                    imageData.url = res.data.image_url || '';
                    var typeLabels = { bar_chart: 'Bar Chart', line_graph: 'Line Graph', pie_chart: 'Pie Chart', table: 'Table', map: 'Map', process: 'Process Diagram', multiple_visuals: 'Multiple Visuals', mixed: 'Mixed', unknown: 'Unknown' };
                    var type = res.data.visual_type || 'unknown';
                    $('#itcVisualType').text('Type: ' + (typeLabels[type] || type));
                    var desc = res.data.description || 'No description available.';
                    if (res.data.uncertain_information && res.data.uncertain_information.length > 0) {
                        desc += '<br><span class="text-warning">Note: ' + res.data.uncertain_information.join('; ') + '</span>';
                    }
                    $('#itcVisualDescription').html(desc);
                    $('#itcVisualAnalysis').removeClass('d-none');
                    toast('Image analysis complete.', 'success');
                })
                .fail(function(xhr) {
                    var msg = xhr.responseJSON?.data?.message || 'Network error while analyzing the image.';
                    toast(msg, 'error');
                })
                .always(function() {
                    setLoading($btn, false);
                });
            });

            $('#itcCheckBtn').on('click', function() {
                var $btn = $(this);
                var topic = $('#itcTopic').val().trim();
                var report = $('#itcReport').val().trim();
                if (!topic) { toast('Please enter the topic.', 'warning'); return; }
                if (!report) { toast('Please enter your report.', 'warning'); return; }
                if (!imageData.data) { toast('Please upload and analyze an image of the Task 1 visual.', 'warning'); return; }
                var wordCount = updateWordCount();
                if (wordCount < 150) {
                    toast('Your report is below the recommended IELTS minimum of 150 words.', 'warning');
                }
                setGlobalLoading(true);
                toast('Starting evaluation...', 'info');
                $.post(ITC.ajaxUrl, {
                    action: 'itc_check_report',
                    nonce: ITC.nonce,
                    topic: topic,
                    report: report,
                    image_data: imageData.data,
                    image_mime: imageData.mime,
                    image_path: imageData.path,
                })
                .done(function(res) {
                    if (!res.success) {
                        toast(res.data?.message || 'Could not evaluate the report.', 'error');
                        return;
                    }
                    lastResult = res.data;
                    renderResult(res.data);
                    $('#itcImproveBtn, #itcEnhanceBtn').prop('disabled', false);
                    if (res.data.below_minimum) {
                        toast('Your report is below 150 words. Task Achievement has been penalized.', 'warning');
                    } else {
                        toast('Evaluation complete!', 'success');
                    }
                })
                .fail(function(xhr) {
                    var msg = xhr.responseJSON?.data?.message || 'Network error while evaluating the report.';
                    toast(msg, 'error');
                })
                .always(function() {
                    setGlobalLoading(false);
                });
            });

            function renderResult(data) {
                var band = data.overall_band || 0;
                $('#itcOverallBand').text(band.toFixed(1));
                var levelMap = { A1: 'A1 - Beginner', A2: 'A2 - Elementary', B1: 'B1 - Intermediate', B2: 'B2 - Upper Intermediate', C1: 'C1 - Advanced', C2: 'C2 - Proficient' };
                var level = data.vocabulary_level || 'B2';
                $('#itcVocabLevel').text(levelMap[level] || level);
                var mistakes = data.grammar_mistakes_detail || [];
                $('#itcGrammarCount').text(data.grammar_mistakes || 0);
                var $gList = $('#itcGrammarList').empty();
                if (mistakes.length === 0) {
                    $gList.html('<li class="text-muted">No grammar mistakes detected.</li>');
                } else {
                    mistakes.forEach(function(m) {
                        $gList.append('<li><strong>' + escapeHtml(m.sentence) + '</strong> → ' + escapeHtml(m.correction) + '<br><span class="text-muted">' + escapeHtml(m.explanation || '') + '</span></li>');
                    });
                }
                var $rep = $('#itcRepetitionList').empty();
                var repetition = data.vocabulary_repetition || [];
                if (repetition.length === 0) {
                    $rep.text('No significant repetition detected.');
                } else {
                    repetition.forEach(function(w) {
                        $rep.append('<div class="mb-1"><strong>' + escapeHtml(w.word) + '</strong>: ' + w.count + ' &mdash; try: ' + escapeHtml((w.suggestions || []).join(', ')) + '</div>');
                    });
                }
                var scores = [
                    ['Task Achievement', data.task_response],
                    ['Coherence & Cohesion', data.coherence],
                    ['Lexical Resource', data.lexical_resource],
                    ['Grammatical Range & Accuracy', data.grammar]
                ];
                var $breakdown = $('#itcScoreBreakdown').empty();
                scores.forEach(function(pair) {
                    var label = pair[0];
                    var val = pair[1] || 0;
                    var pct = (val / 9) * 100;
                    var color = val >= 7 ? 'bg-success' : val >= 5 ? 'bg-warning' : 'bg-danger';
                    $breakdown.append('<div class="score-item"><div class="d-flex justify-content-between"><span class="fw-semibold">' + label + '</span><span class="fw-bold">' + val.toFixed(1) + '</span></div><div class="progress mt-1"><div class="progress-bar ' + color + '" style="width:' + pct + '%"></div></div></div>');
                });
                var fb = data.feedback || {};
                var $fbText = $('#itcFeedbackText').empty();
                var fbLabels = { task_response: 'Task Achievement', coherence: 'Coherence & Cohesion', lexical: 'Lexical Resource', grammar: 'Grammatical Range & Accuracy' };
                var hasFeedback = false;
                Object.keys(fbLabels).forEach(function(key) {
                    if (fb[key]) {
                        hasFeedback = true;
                        $fbText.append('<p><strong>' + fbLabels[key] + ':</strong> ' + escapeHtml(fb[key]) + '</p>');
                    }
                });
                if (hasFeedback) {
                    $('#itcFeedbackSection').removeClass('d-none');
                } else {
                    $('#itcFeedbackSection').addClass('d-none');
                }
                if (data.improved_report) {
                    $('#itcImprovedTab').text(data.improved_report);
                }
                if (data.enhanced_report) {
                    $('#itcEnhancedTab').text(data.enhanced_report);
                }
                if (data.improved_report || data.enhanced_report) {
                    $('#itcRewriteSection').removeClass('d-none');
                }
            }

            function escapeHtml(str) {
                if (!str) return '';
                return $('<div>').text(str).html();
            }

            $('#itcImproveBtn').on('click', function() {
                var $btn = $(this);
                var report = $('#itcReport').val().trim();
                if (!report) { toast('No report to improve.', 'warning'); return; }
                setLoading($btn, true);
                toast('Improving naturalness...', 'info');
                $.post(ITC.ajaxUrl, {
                    action: 'itc_improve',
                    nonce: ITC.nonce,
                    report: report
                })
                .done(function(res) {
                    if (!res.success) {
                        toast(res.data?.message || 'Could not improve the report.', 'error');
                        return;
                    }
                    $('#itcImprovedTab').text(res.data.improved_report);
                    $('#itcRewriteSection').removeClass('d-none');
                    toast('Naturalness improved.', 'success');
                })
                .fail(function(xhr) {
                    var msg = xhr.responseJSON?.data?.message || 'Network error.';
                    toast(msg, 'error');
                })
                .always(function() {
                    setLoading($btn, false);
                });
            });

            $('#itcEnhanceBtn').on('click', function() {
                var $btn = $(this);
                var topic = $('#itcTopic').val().trim();
                var report = $('#itcReport').val().trim();
                if (!report) { toast('No report to enhance.', 'warning'); return; }
                setLoading($btn, true);
                toast('Creating Band 9 enhancement...', 'info');
                $.post(ITC.ajaxUrl, {
                    action: 'itc_enhance',
                    nonce: ITC.nonce,
                    topic: topic,
                    report: report
                })
                .done(function(res) {
                    if (!res.success) {
                        toast(res.data?.message || 'Could not enhance the report.', 'error');
                        return;
                    }
                    $('#itcEnhancedTab').text(res.data.enhanced_report);
                    $('#itcRewriteSection').removeClass('d-none');
                    toast('Band 9 rewrite ready.', 'success');
                })
                .fail(function(xhr) {
                    var msg = xhr.responseJSON?.data?.message || 'Network error.';
                    toast(msg, 'error');
                })
                .always(function() {
                    setLoading($btn, false);
                });
            });

            $('#itcReport').on('keydown', function(e) {
                if (e.ctrlKey && e.key === 'Enter') {
                    e.preventDefault();
                    $('#itcCheckBtn').click();
                }
            });
        });
        </script>
        <?php
        return (string) ob_get_clean();
    }
}

// ============================================================
// PLUGIN BOOTSTRAP
// ============================================================

function itc_activate(): void
{
    ITC_DB::install_tables();
    if (false === get_option('itc_settings')) {
        add_option('itc_settings', ITC_AI_Client::default_settings());
    }
}
register_activation_hook(ITC_PLUGIN_FILE, 'itc_activate');

function itc_bootstrap(): void
{
    new ITC_Admin_Settings();
    new ITC_Admin_History();
    new ITC_Ajax();
    new ITC_Shortcode();
}
add_action('plugins_loaded', 'itc_bootstrap');

function itc_enqueue_frontend_assets(): void
{
    global $post;
    if (!is_a($post, 'WP_Post') || !has_shortcode($post->post_content, 'ielts_task1_checker')) {
        return;
    }

    wp_enqueue_style('bootstrap-5', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css', [], '5.3.3');
    wp_enqueue_style('bootstrap-icons', 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css', [], '1.11.3');

    wp_enqueue_script('bootstrap-5-js', 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js', [], '5.3.3', true);
    wp_enqueue_script('itc-app', ITC_PLUGIN_URL . 'assets/js/app.js', ['jquery'], ITC_VERSION, true);

    wp_localize_script('itc-app', 'ITC', [
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('itc_nonce'),
        'maxUploadMb' => (int) apply_filters('itc_max_upload_mb', 10),
    ]);
}
add_action('wp_enqueue_scripts', 'itc_enqueue_frontend_assets');