<?php

/**
 * Exam AI Generator
 *
 * Routes exam generation through the unified RBCAIEngine
 * (Claude → Groq → Gemini → OpenAI cascading fallback) instead of
 * calling a single provider directly. This is the primary generation
 * path; the PHP template generator remains as a last-resort fallback.
 */

require_once __DIR__ . '/exam_utils.php';
require_once dirname(__DIR__, 2) . '/ai/provider_engine.php';

/**
 * Render the school's distilled Exam Format Profile (if any) into a prompt
 * block. The profile governs structure, layout, and phrasing style ONLY —
 * callers must pair this with an explicit rule that the model must not copy
 * the original sample's actual question content (see the Rules section of
 * buildExamGenerationPrompt()).
 */
function formatExamFormatProfileForPrompt(?array $profile): string {
    if (empty($profile) || empty($profile['sections'])) {
        return 'No school-specific exam format is on file for this subject/class — use standard subject-appropriate structure and wording.';
    }

    $lines = [];
    foreach ($profile['sections'] as $section) {
        if (!is_array($section)) {
            continue;
        }
        $label = trim((string) ($section['label'] ?? 'Section'));
        $type  = trim((string) ($section['type'] ?? 'subjective'));
        $count = (int) ($section['question_count'] ?? 0);
        $marks = (int) ($section['marks_per_question'] ?? 0);
        $notes = trim((string) ($section['notes'] ?? ''));

        $line = "- {$label} ({$type})";
        if ($count > 0) {
            $line .= ", {$count} questions";
        }
        if ($marks > 0) {
            $line .= ", {$marks} marks each";
        }
        if ($notes !== '') {
            $line .= " — {$notes}";
        }
        $lines[] = $line;
    }

    $text = "This school's standard exam format, learned from a real past paper:\n" . implode("\n", $lines);
    if (!empty($profile['header_instructions'])) {
        $text .= "\nHeader/instruction phrasing style to match: " . $profile['header_instructions'];
    }
    if (!empty($profile['command_words']) && is_array($profile['command_words'])) {
        $text .= "\nCommand words this school typically uses: " . implode(', ', $profile['command_words']);
    }
    if (!empty($profile['tone_notes'])) {
        $text .= "\nTone/style to match: " . $profile['tone_notes'];
    }
    if (($profile['confidence'] ?? '') === 'low') {
        $text .= "\n(Note: this format was distilled with low confidence from an unclear sample — use it as guidance, not a strict template.)";
    }
    return $text;
}

/**
 * Build the strict-JSON exam prompt sent to the AI engine.
 * Ported from the legacy python_exam_generator.build_prompt().
 */
function buildExamGenerationPrompt(array $requestData): string {
    $retrievalContext = $requestData['retrieval_context'] ?? [];
    $contextLines = [];
    foreach ($retrievalContext as $index => $chunk) {
        $title = $chunk['title'] ?? 'Material';
        $type  = $chunk['material_type'] ?? 'reference';
        $text  = trim((string) ($chunk['content'] ?? $chunk['text'] ?? ''));
        $contextLines[] = 'CONTEXT ' . ($index + 1) . " | {$title} | {$type}\n" . $text;
    }

    // Subject rules are a flat list of rule strings (see getSubjectGenerationRules()).
    $subjectRules = $requestData['subject_rules'] ?? [];
    if (!is_array($subjectRules)) {
        $subjectRules = [];
    }

    $requestedSections = [];
    if (($requestData['include_objective'] ?? 'false') === 'true') {
        $requestedSections[] = 'Objective section: ' . (int) ($requestData['obj_count'] ?? 20)
            . ' questions, ' . (int) ($requestData['obj_marks'] ?? 2) . ' marks each.';
    }
    if (($requestData['include_subjective'] ?? 'false') === 'true') {
        $requestedSections[] = 'Theory section: ' . (int) ($requestData['subj_count'] ?? 5)
            . ' questions, ' . (int) ($requestData['subj_marks'] ?? 8) . ' marks each.';
    }
    if (($requestData['include_comprehension'] ?? 'false') === 'true') {
        $requestedSections[] = 'Add one comprehension section with one passage and questions derived from it.';
    }

    $subjectName  = $requestData['subject_name'] ?? 'Subject';
    $className    = $requestData['class_name'] ?? 'Class';
    $term         = $requestData['term'] ?? '';
    $examTitle    = $requestData['exam_title'] ?? ($subjectName . ' Examination');
    $duration     = (int) ($requestData['duration'] ?? 60);
    $totalMarks   = (int) ($requestData['total_marks'] ?? 100);
    $examType     = $requestData['exam_type'] ?? 'mid_term';
    $aiNotes      = trim((string) ($requestData['ai_notes'] ?? '')) ?: 'None';
    $topics       = trim((string) ($requestData['topics'] ?? '')) ?: 'None';
    $sampleQs     = trim((string) ($requestData['sample_questions'] ?? '')) ?: 'None';
    $rulesText    = empty($subjectRules) ? 'Use balanced subject-appropriate wording.' : implode("\n", $subjectRules);
    $sectionsText = empty($requestedSections) ? 'At least one theory section.' : implode("\n", $requestedSections);
    $contextText  = empty($contextLines) ? 'No retrieved context supplied.' : implode("\n", $contextLines);
    $formatProfileText = formatExamFormatProfileForPrompt($requestData['format_profile'] ?? null);

    return <<<PROMPT
Generate a school examination as strict JSON.

Context:
- Subject: {$subjectName}
- Class: {$className}
- Term: {$term}
- Exam title: {$examTitle}
- Duration: {$duration} minutes
- Total marks target: {$totalMarks}
- Exam type: {$examType}

SCHOOL EXAM FORMAT & STANDARD TO FOLLOW (highest priority for structure/style/wording — see Rules below):
{$formatProfileText}

Teacher instructions:
{$aiNotes}

Topics to emphasize:
{$topics}

Sample question patterns / past question style to imitate:
{$sampleQs}

Subject-specific rules:
{$rulesText}

Requested structure:
{$sectionsText}

Top retrieved learning context:
{$contextText}

Return strict JSON with this shape:
{
  "title": "string",
  "subject": "string",
  "class": "string",
  "time": "string",
  "instructions": "string",
  "sections": [
    {
      "section": "A",
      "title": "string",
      "instructions": "string",
      "type": "objective|subjective|comprehension",
      "passage": "optional for comprehension",
      "questions": [
        {
          "number": 1,
          "text": "string",
          "marks": 2,
          "options": {"A":"", "B":"", "C":"", "D":""},
          "correct_option": "A",
          "model_answer": "string",
          "rubric": ["string"],
          "diagram": {"type":"none","title":"string","data":{},"labels":["string"],"caption":"string"}
        }
      ]
    }
  ]
}

Rules:
- If a school exam format is provided above, match its section labels/types, phrasing register, command words, and tone. Never copy or closely paraphrase its actual questions — it is a style/structure reference only, not a content source.
- The "Requested structure" below reflects the teacher's explicit choice for THIS exam; if it conflicts with the school format's section counts/marks, follow the requested structure and apply the school format's style/wording to it instead.
- Base the questions primarily on the retrieved context and teacher instructions.
- Produce genuinely subject-correct questions, not generic templated wording.
- Spread questions across different ideas instead of repeating one stem style.
- Keep objective distractors plausible and non-duplicated.
- Use wording that is classroom-ready and printable.
- For objective questions include "correct_option" using A-D.
- For theory/comprehension include "model_answer" and a short "rubric" array.
- Provide a complete answer key: every question must carry the data a teacher needs to mark it.
- Add a "diagram" ONLY when a question genuinely needs a figure (charts, geometry, number lines, labelled diagrams). Otherwise set "type":"none" or omit the field.
- Allowed diagram types: bar_chart, line_chart, pie_chart, number_line, triangle, right_triangle, rectangle, circle, angle, coordinate_plane, table.
- Diagram specs must be precise and self-consistent so they can be drawn exactly (e.g. triangle angles summing to 180, data values matching their labels).
- "data" holds type-specific values, e.g. bar_chart {"categories":["A","B"],"values":[3,5]}; number_line {"min":0,"max":10,"marks":[2,5,8]}; triangle {"angles":[60,60,60]} or {"sides":[3,4,5]}; pie_chart {"labels":["X","Y"],"values":[40,60]}.
- Do not wrap the JSON in markdown.
PROMPT;
}

/**
 * Decode the model's JSON response, tolerating markdown code fences.
 */
function decodeExamJson(string $content): ?array {
    $content = trim($content);
    if ($content === '') {
        return null;
    }

    // Strip ```json ... ``` or ``` ... ``` fences if the model added them.
    if (str_starts_with($content, '```')) {
        $content = preg_replace('/^```[a-zA-Z0-9]*\s*/', '', $content);
        $content = preg_replace('/\s*```$/', '', (string) $content);
        $content = trim((string) $content);
    }

    $decoded = json_decode($content, true);
    if (is_array($decoded)) {
        return $decoded;
    }

    // Last resort: extract the outermost { ... } block.
    $start = strpos($content, '{');
    $end   = strrpos($content, '}');
    if ($start !== false && $end !== false && $end > $start) {
        $decoded = json_decode(substr($content, $start, $end - $start + 1), true);
        if (is_array($decoded)) {
            return $decoded;
        }
    }

    return null;
}

/**
 * Generate an exam via the unified multi-provider AI engine.
 *
 * @return array {success: bool, exam?: array, provider?: string, message?: string}
 */
function generateExamUsingAIEngine(array $requestData): array {
    if (!class_exists('RBCAIEngine')) {
        return ['success' => false, 'message' => 'AI provider engine is unavailable.'];
    }

    $prompt = buildExamGenerationPrompt($requestData);
    $system = 'You create balanced, subject-accurate school examination papers and return only valid JSON.';

    $result = RBCAIEngine::generate($prompt, $system, 4000, true);
    if (empty($result['success']) || empty($result['content'])) {
        return [
            'success' => false,
            'message' => $result['error'] ?? 'All AI providers failed to generate an exam.',
        ];
    }

    $exam = decodeExamJson((string) $result['content']);
    if ($exam === null) {
        return [
            'success'  => false,
            'provider' => $result['provider'] ?? null,
            'message'  => 'AI engine returned a response that was not valid exam JSON.',
        ];
    }

    return [
        'success'  => true,
        'exam'     => $exam,
        'provider' => $result['provider'] ?? 'unknown',
    ];
}
