<?php
declare(strict_types=1);
require_once __DIR__ . '/_json_guard.php';
session_start();
require_once '../../config.php';
require_once dirname(__DIR__) . '/includes/question_builder.php';
require_once dirname(__DIR__) . '/lib/content_chunker.php';
require_once dirname(__DIR__) . '/lib/question_refiner.php';
header('Content-Type: application/json');

if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    echo json_encode(['success' => false]);
    exit;
}

$conn = getDBConnection();
if (!$conn instanceof mysqli) {
    echo json_encode(['success' => false, 'message' => 'Database connection failed']);
    exit;
}

// ── Ensure tables / columns exist ─────────────────────────────────────────
$conn->query("CREATE TABLE IF NOT EXISTS ai_generation_jobs (
    id               INT AUTO_INCREMENT PRIMARY KEY,
    material_id      INT NOT NULL,
    subject_id       INT NOT NULL,
    class_name       VARCHAR(100) NOT NULL,
    term             VARCHAR(50)  DEFAULT NULL,
    academic_year    VARCHAR(20)  DEFAULT NULL,
    difficulty       VARCHAR(20)  DEFAULT 'medium',
    mcq_count        INT          DEFAULT 0,
    theory_count     INT          DEFAULT 0,
    fill_blank_count INT          DEFAULT 0,
    diagram_count    INT          DEFAULT 0,
    chunk_count      INT          DEFAULT 0,
    status           VARCHAR(20)  DEFAULT 'processing',
    result_summary   TEXT         DEFAULT NULL,
    engine_used      VARCHAR(50)  DEFAULT NULL,
    refined          TINYINT(1)   NOT NULL DEFAULT 0,
    refiner_engine   VARCHAR(50)  DEFAULT NULL,
    error_message    TEXT         DEFAULT NULL,
    created_by       INT          NOT NULL,
    created_at       TIMESTAMP    DEFAULT CURRENT_TIMESTAMP,
    completed_at     TIMESTAMP    NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

// Add refinement columns to existing installs
foreach ([
    'chunk_count'   => 'ADD COLUMN chunk_count INT DEFAULT 0 AFTER diagram_count',
    'refined'       => 'ADD COLUMN refined TINYINT(1) NOT NULL DEFAULT 0 AFTER result_summary',
    'refiner_engine'=> 'ADD COLUMN refiner_engine VARCHAR(50) DEFAULT NULL AFTER refined',
] as $col => $ddl) {
    $chk = $conn->query("SHOW COLUMNS FROM ai_generation_jobs LIKE '{$col}'");
    if ($chk && $chk->num_rows === 0) {
        $conn->query("ALTER TABLE ai_generation_jobs {$ddl}");
    }
}

$conn->query("CREATE TABLE IF NOT EXISTS ai_generation_chunks (
    id              INT AUTO_INCREMENT PRIMARY KEY,
    job_id          INT NOT NULL,
    chunk_index     INT NOT NULL,
    topic           VARCHAR(255) DEFAULT NULL,
    word_count      INT NOT NULL DEFAULT 0,
    status          VARCHAR(20)  NOT NULL DEFAULT 'pending',
    questions_saved INT NOT NULL DEFAULT 0,
    error_message   TEXT DEFAULT NULL,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

$conn->query("CREATE TABLE IF NOT EXISTS ai_generation_quota (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    school_code VARCHAR(50)  NOT NULL,
    month_year  CHAR(7)      NOT NULL,
    jobs_used   INT          NOT NULL DEFAULT 0,
    word_count  INT          NOT NULL DEFAULT 0,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uq_school_month (school_code, month_year)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

// Ensure exam_questions exists with all columns the INSERT needs
$conn->query("CREATE TABLE IF NOT EXISTS exam_questions (
    id                INT AUTO_INCREMENT PRIMARY KEY,
    subject_id        INT DEFAULT NULL,
    class_name        VARCHAR(100) NOT NULL DEFAULT '',
    term              VARCHAR(50)  DEFAULT NULL,
    academic_year     VARCHAR(20)  DEFAULT NULL,
    question_type     ENUM('mcq','theory','fill_blank') NOT NULL DEFAULT 'mcq',
    question          TEXT NOT NULL,
    option_a          VARCHAR(500) DEFAULT NULL,
    option_b          VARCHAR(500) DEFAULT NULL,
    option_c          VARCHAR(500) DEFAULT NULL,
    option_d          VARCHAR(500) DEFAULT NULL,
    correct_answer    ENUM('A','B','C','D') DEFAULT NULL,
    marks             INT NOT NULL DEFAULT 1,
    difficulty        ENUM('easy','medium','hard') NOT NULL DEFAULT 'medium',
    diagram_required  TINYINT(1) NOT NULL DEFAULT 0,
    diagram_type      VARCHAR(100) DEFAULT NULL,
    diagram_spec      TEXT DEFAULT NULL,
    ai_generated      TINYINT(1) NOT NULL DEFAULT 0,
    generation_job_id INT DEFAULT NULL,
    chunk_topic       VARCHAR(255) DEFAULT NULL,
    moderation_note   TEXT DEFAULT NULL,
    refined           TINYINT(1) NOT NULL DEFAULT 0,
    created_by        INT DEFAULT NULL,
    created_at        TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at        TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_subject_id  (subject_id),
    INDEX idx_job_id      (generation_job_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

// Add new columns to pre-existing installs that had the table before these columns
foreach ([
    'diagram_required'  => 'ADD COLUMN diagram_required TINYINT(1) NOT NULL DEFAULT 0',
    'diagram_type'      => 'ADD COLUMN diagram_type VARCHAR(100) DEFAULT NULL',
    'diagram_spec'      => 'ADD COLUMN diagram_spec TEXT DEFAULT NULL',
    'ai_generated'      => 'ADD COLUMN ai_generated TINYINT(1) NOT NULL DEFAULT 0',
    'generation_job_id' => 'ADD COLUMN generation_job_id INT DEFAULT NULL',
    'chunk_topic'       => 'ADD COLUMN chunk_topic VARCHAR(255) DEFAULT NULL',
    'moderation_note'   => 'ADD COLUMN moderation_note TEXT DEFAULT NULL',
    'refined'           => 'ADD COLUMN refined TINYINT(1) NOT NULL DEFAULT 0',
    'model_answer'      => 'ADD COLUMN model_answer TEXT DEFAULT NULL',
    'marking_guide'     => 'ADD COLUMN marking_guide TEXT DEFAULT NULL',
] as $col => $ddl) {
    $chk = $conn->query("SHOW COLUMNS FROM exam_questions LIKE '{$col}'");
    if ($chk && $chk->num_rows === 0) {
        $conn->query("ALTER TABLE exam_questions {$ddl}");
    }
}

// ── Plan & quota check ────────────────────────────────────────────────────
if (!planHasAIExamAddon()) {
    echo json_encode([
        'success' => false,
        'message' => 'AI Exam Generator is not included in your current plan.',
    ]);
    exit;
}

$monthYear  = date('Y-m');
$schoolCode = defined('SCHOOL_CODE') ? SCHOOL_CODE : 'DEMO';
$jobQuota   = getPlanJobQuota();

$qRow = $conn->query(
    "SELECT jobs_used FROM ai_generation_quota
     WHERE school_code = '" . $conn->real_escape_string($schoolCode) . "'
       AND month_year  = '" . $conn->real_escape_string($monthYear) . "'"
);
$qData    = $qRow ? $qRow->fetch_assoc() : null;
$jobsUsed = (int) ($qData['jobs_used'] ?? 0);

if ($jobsUsed >= $jobQuota) {
    echo json_encode([
        'success' => false,
        'message' => "Monthly AI generation quota reached ({$jobsUsed}/{$jobQuota}). "
                   . 'Resets on the 1st of next month.',
    ]);
    exit;
}

// ── Parse request ─────────────────────────────────────────────────────────
$materialIds    = json_decode((string) ($_POST['material_ids'] ?? '[]'), true);
$subjectId      = (int)   ($_POST['subject_id']      ?? 0);
$className      = trim((string) ($_POST['class_name'] ?? ''));
$term           = trim((string) ($_POST['term']       ?? ''));
$academicYear   = trim((string) ($_POST['academic_year'] ?? ''));
$difficulty     = trim((string) ($_POST['difficulty']    ?? 'medium'));
$mcqCount       = max(0, (int) ($_POST['mcq_count']        ?? 0));
$theoryCount    = max(0, (int) ($_POST['theory_count']     ?? 0));
$fillBlankCount = max(0, (int) ($_POST['fill_blank_count'] ?? 0));
$trueFalseCount = max(0, (int) ($_POST['true_false_count'] ?? 0));
$matchingCount  = max(0, (int) ($_POST['matching_count']   ?? 0));
$comprehensionCount = max(0, (int) ($_POST['comprehension_count'] ?? 0));
$diagramCount   = max(0, (int) ($_POST['diagram_count']    ?? 0));

if (!is_array($materialIds) || $materialIds === [] || $subjectId <= 0 || $className === '') {
    echo json_encode(['success' => false, 'message' => 'Missing generation settings']);
    exit;
}

$cleanIds = array_values(array_unique(
    array_filter(array_map('intval', $materialIds), static fn($v) => $v > 0)
));
if ($cleanIds === []) {
    echo json_encode(['success' => false, 'message' => 'No valid materials selected']);
    exit;
}

// ── Load material content ─────────────────────────────────────────────────
$placeholders = implode(',', array_fill(0, count($cleanIds), '?'));
$types        = str_repeat('i', count($cleanIds)) . 'i';
$sql = "SELECT id, cleaned_content, extracted_content
        FROM ai_study_materials
        WHERE id IN ({$placeholders}) AND uploaded_by = ?";
$stmt = $conn->prepare($sql);
$params = [...$cleanIds, (int) $_SESSION['user_id']];
$stmt->bind_param($types, ...$params);
$stmt->execute();
$result       = $stmt->get_result();
$contentParts = [];
$firstMatId   = $cleanIds[0];
while ($row = $result->fetch_assoc()) {
    // Prefer cleaned_content; fall back to raw extracted_content
    $text = !empty($row['cleaned_content'])
        ? (string) $row['cleaned_content']
        : (string) ($row['extracted_content'] ?? '');
    if (trim($text) !== '') {
        $contentParts[] = $text;
    }
}
$stmt->close();

$combined = trim(implode("\n\n", $contentParts));
if ($combined === '') {
    echo json_encode(['success' => false, 'message' => 'No extracted content available in selected materials']);
    exit;
}

// ── Fetch subject info ────────────────────────────────────────────────────
$subjectStmt = $conn->prepare("SELECT subject_code, subject_name FROM subjects WHERE id = ? LIMIT 1");
$subjectStmt->bind_param('i', $subjectId);
$subjectStmt->execute();
$subject = $subjectStmt->get_result()->fetch_assoc();
$subjectStmt->close();
if (!$subject) {
    echo json_encode(['success' => false, 'message' => 'Subject not found']);
    exit;
}
$subjectCode = (string) ($subject['subject_code'] ?? '');
$subjectName = (string) ($subject['subject_name'] ?? '');

// ── Clean (if not already cleaned) + chunk ────────────────────────────────
$wordLimit = getPlanWordLimit();
// If content came from raw extracted_content it wasn't pre-cleaned — clean it now
$cleanedCombined = cleanExtractedContent($combined);
if (str_word_count($cleanedCombined) < 50) {
    // Cleaning stripped too much — fall back to raw
    $cleanedCombined = $combined;
}

$chunks = chunkContent($cleanedCombined, $wordLimit);
if (empty($chunks)) {
    echo json_encode(['success' => false, 'message' => 'No content chunks could be created from this material']);
    exit;
}

$chunkCount  = count($chunks);
$totalQuestions = $mcqCount + $theoryCount + $fillBlankCount + $trueFalseCount + $matchingCount + $comprehensionCount;

// ── Create job record ─────────────────────────────────────────────────────
$userId = (int) $_SESSION['user_id'];
$insertJob = $conn->prepare(
    "INSERT INTO ai_generation_jobs
      (material_id, subject_id, class_name, term, academic_year, difficulty,
       mcq_count, theory_count, fill_blank_count, diagram_count, chunk_count,
       status, created_by)
     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'processing', ?)"
);
$insertJob->bind_param(
    'iissssiiiiii',
    $firstMatId, $subjectId, $className, $term, $academicYear, $difficulty,
    $mcqCount, $theoryCount, $fillBlankCount, $diagramCount, $chunkCount,
    $userId
);
$insertJob->execute();
$jobId = (int) $insertJob->insert_id;
$insertJob->close();

// ── Distribute question counts across chunks ──────────────────────────────
/**
 * Distribute $total questions across $n chunks proportionally by word count.
 * Returns an array of $n integers that sum to $total.
 */
function distributeQuestions(int $total, int $n, array $chunks): array
{
    if ($n === 1 || $total === 0) {
        return [$total] + array_fill(1, $n - 1, 0);
    }
    $totalWords = array_sum(array_column($chunks, 'words')) ?: 1;
    $counts     = [];
    $assigned   = 0;
    for ($i = 0; $i < $n - 1; $i++) {
        $share      = (int) round($total * $chunks[$i]['words'] / $totalWords);
        $share      = max(0, $share);
        $counts[]   = $share;
        $assigned  += $share;
    }
    $counts[] = max(0, $total - $assigned); // remainder goes to last chunk
    return $counts;
}

$mcqPerChunk    = distributeQuestions($mcqCount,    $chunkCount, $chunks);
$theoryPerChunk = distributeQuestions($theoryCount, $chunkCount, $chunks);
$fillPerChunk   = distributeQuestions($fillBlankCount, $chunkCount, $chunks);
$tfPerChunk     = distributeQuestions($trueFalseCount, $chunkCount, $chunks);
$matchPerChunk  = distributeQuestions($matchingCount, $chunkCount, $chunks);
$compPerChunk   = distributeQuestions($comprehensionCount, $chunkCount, $chunks);
// Diagrams: assign to chunks that have the most questions
$diagPerChunk   = array_fill(0, $chunkCount, 0);
$remaining      = $diagramCount;
for ($i = 0; $i < $chunkCount && $remaining > 0; $i++) {
    $chunkTotal = $mcqPerChunk[$i] + $theoryPerChunk[$i] + $fillPerChunk[$i];
    $assign     = min($remaining, max(0, (int) ceil($chunkTotal * 0.15)));
    $diagPerChunk[$i] = $assign;
    $remaining -= $assign;
}
if ($remaining > 0) {
    $diagPerChunk[0] += $remaining; // dump remainder in first chunk
}

// ── Insert chunk tracking rows ────────────────────────────────────────────
$chunkInsert = $conn->prepare(
    "INSERT INTO ai_generation_chunks (job_id, chunk_index, topic, word_count, status)
     VALUES (?, ?, ?, ?, 'pending')"
);
$chunkIds = [];
foreach ($chunks as $idx => $chunk) {
    $chunkInsert->bind_param('iisi', $jobId, $idx, $chunk['topic'], $chunk['words']);
    $chunkInsert->execute();
    $chunkIds[$idx] = (int) $conn->insert_id;
}
$chunkInsert->close();

// ── Generate questions per chunk ──────────────────────────────────────────
$allInsertedIds = [];
$allQuestions   = [];
$engineUsed     = '';
$errors         = [];

set_time_limit(300); // allow up to 5 minutes for multi-chunk generation

foreach ($chunks as $idx => $chunk) {
    $chunkMcq    = $mcqPerChunk[$idx];
    $chunkTheory = $theoryPerChunk[$idx];
    $chunkFill   = $fillPerChunk[$idx];
    $chunkTf     = $tfPerChunk[$idx];
    $chunkMatch  = $matchPerChunk[$idx];
    $chunkComp   = $compPerChunk[$idx];
    $chunkDiag   = $diagPerChunk[$idx];

    if ($chunkMcq + $chunkTheory + $chunkFill + $chunkTf + $chunkMatch + $chunkComp === 0) {
        // Nothing to generate for this chunk
        $conn->query("UPDATE ai_generation_chunks SET status='done', questions_saved=0 WHERE id=" . $chunkIds[$idx]);
        continue;
    }

    // Mark chunk as processing
    $conn->query("UPDATE ai_generation_chunks SET status='processing' WHERE id=" . $chunkIds[$idx]);

    try {
        $result = generateChunkQuestions(
            $conn,
            $subjectId,
            $subjectCode,
            $subjectName,
            $className,
            $term,
            $academicYear,
            $difficulty,
            $chunkMcq,
            $chunkTheory,
            $chunkFill,
            $chunkDiag,
            $chunk['content'],
            $chunk['topic'],
            $jobId,
            $userId,
            false,
            $chunkTf,
            $chunkMatch,
            $chunkComp
        );

        $saved  = count($result['inserted_ids']);
        $engine = (string) ($result['engine'] ?? '');
        if ($engine !== '' && $engineUsed === '') {
            $engineUsed = $engine;
        }

        $allInsertedIds = array_merge($allInsertedIds, $result['inserted_ids']);
        $allQuestions   = array_merge($allQuestions,   $result['questions']);

        $conn->query(
            "UPDATE ai_generation_chunks
             SET status='done', questions_saved={$saved}
             WHERE id=" . $chunkIds[$idx]
        );

    } catch (Throwable $e) {
        $errMsg = $e->getMessage();
        $errors[] = "Chunk {$idx} ({$chunk['topic']}): {$errMsg}";
        error_log("[EduTrack] Chunk {$idx} generation failed: {$errMsg}");

        $safeErr = $conn->real_escape_string(substr($errMsg, 0, 500));
        $conn->query(
            "UPDATE ai_generation_chunks
             SET status='failed', error_message='{$safeErr}'
             WHERE id=" . $chunkIds[$idx]
        );
    }
}

// If ALL chunks failed — abort
if (empty($allInsertedIds)) {
    $errSummary = implode(' | ', $errors) ?: 'All chunks failed to generate.';
    $conn->prepare("UPDATE ai_generation_jobs SET status='failed', error_message=?, completed_at=NOW() WHERE id=?")
         ->execute() ?: null;
    $fail = $conn->prepare("UPDATE ai_generation_jobs SET status='failed', error_message=?, completed_at=NOW() WHERE id=?");
    $fail->bind_param('si', $errSummary, $jobId);
    $fail->execute();
    $fail->close();
    echo json_encode(['success' => false, 'message' => $errSummary]);
    exit;
}

// ── Refinement pass (Growth / Premium only) ───────────────────────────────
$refined       = false;
$refinerEngine = '';

if (planSupportsRefinement() && !empty($allQuestions)) {
    $refineResult = refineQuestions($allQuestions, $subjectName, $className, $difficulty, $engineUsed);
    if ($refineResult['refined'] && !empty($refineResult['questions'])) {
        $refined       = true;
        $refinerEngine = (string) $refineResult['engine'];
        $refinedQs     = $refineResult['questions'];

        // Update the saved questions with any corrections + mark as refined
        $updateQ = $conn->prepare(
            "UPDATE exam_questions
             SET question         = ?,
                 option_a         = ?,
                 option_b         = ?,
                 option_c         = ?,
                 option_d         = ?,
                 correct_answer   = ?,
                 marks            = ?,
                 moderation_note  = ?,
                 refined          = 1
             WHERE id = ?"
        );

        foreach ($refinedQs as $i => $rq) {
            $id = $allInsertedIds[$i] ?? 0;
            if ($id <= 0) {
                continue;
            }
            $rQuestion    = trim((string) ($rq['question']        ?? ''));
            $rA           = (string) ($rq['option_a']             ?? '');
            $rB           = (string) ($rq['option_b']             ?? '');
            $rC           = (string) ($rq['option_c']             ?? '');
            $rD           = (string) ($rq['option_d']             ?? '');
            $rCorrect     = (string) ($rq['correct_answer']       ?? '');
            $rMarks       = max(1, (int) ($rq['marks']            ?? 1));
            $rNote        = (string) ($rq['moderation_note']      ?? '');
            $updateQ->bind_param('ssssssiis',
                $rQuestion, $rA, $rB, $rC, $rD, $rCorrect, $rMarks, $rNote, $id);
            $updateQ->execute();
        }
        $updateQ->close();
    }
}

// ── Update quota ──────────────────────────────────────────────────────────
$safeCode = $conn->real_escape_string($schoolCode);
$safeMonth = $conn->real_escape_string($monthYear);
$conn->query(
    "INSERT INTO ai_generation_quota (school_code, month_year, jobs_used, word_count)
     VALUES ('{$safeCode}', '{$safeMonth}', 1, " . str_word_count($cleanedCombined) . ")
     ON DUPLICATE KEY UPDATE
       jobs_used  = jobs_used + 1,
       word_count = word_count + " . str_word_count($cleanedCombined)
);

// ── Finalise job record ───────────────────────────────────────────────────
$savedCount = count($allInsertedIds);
$summary    = "Generated {$savedCount} questions from {$chunkCount} topic chunk(s): "
            . "{$mcqCount} MCQ, {$theoryCount} Theory, {$fillBlankCount} Fill in Blank, {$trueFalseCount} True/False, {$matchingCount} Matching, {$comprehensionCount} Comprehension";
if (!empty($errors)) {
    $summary .= ' (some chunks failed: ' . count($errors) . ')';
}

$doneStmt = $conn->prepare(
    "UPDATE ai_generation_jobs
     SET status='done', result_summary=?, engine_used=?,
         refined=?, refiner_engine=?, completed_at=NOW()
     WHERE id=?"
);
$refinedInt = $refined ? 1 : 0;
$doneStmt->bind_param('ssisi', $summary, $engineUsed, $refinedInt, $refinerEngine, $jobId);
$doneStmt->execute();
$doneStmt->close();

// ── Fetch saved question rows for the response ────────────────────────────
$questionRows = [];
if (!empty($allInsertedIds)) {
    $idList = implode(',', array_map('intval', $allInsertedIds));
    $res    = $conn->query("SELECT * FROM exam_questions WHERE id IN ({$idList}) ORDER BY id ASC");
    while ($res && $row = $res->fetch_assoc()) {
        $questionRows[] = $row;
    }
}

echo json_encode([
    'success'        => true,
    'job_id'         => $jobId,
    'question_count' => $savedCount,
    'chunk_count'    => $chunkCount,
    'summary'        => $summary,
    'questions'      => $questionRows,
    'engine'         => $engineUsed,
    'refined'        => $refined,
    'refiner_engine' => $refinerEngine,
    'quota_used'     => $jobsUsed + 1,
    'quota_limit'    => $jobQuota,
    'partial_errors' => $errors,
]);
