<?php

declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) { session_start(); }

require_once '../../config.php';

require_once dirname(__DIR__) . '/includes/file_extractor.php';

require_once dirname(__DIR__) . '/lib/content_chunker.php';



// Catch any fatal error and return it as JSON rather than an empty 500

register_shutdown_function(static function (): void {

    $e = error_get_last();

    if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {

        if (!headers_sent()) {

            http_response_code(500);

            header('Content-Type: application/json');

        }

        echo json_encode(['success' => false, 'message' => 'Server error: ' . $e['message']]);

    }

});



header('Content-Type: application/json');



if (!isset($_SESSION['user_id'])) {

    http_response_code(401);

    echo json_encode(['success' => false, 'message' => 'Unauthorized']);

    exit;

}



$conn = getDBConnection();

if (!$conn instanceof mysqli) {

    echo json_encode(['success' => false, 'message' => 'Database connection failed']);

    exit;

}





// ── Ensure tables exist — schema DDL must never throw ─────────────────────

mysqli_report(MYSQLI_REPORT_OFF);

$conn->query("CREATE TABLE IF NOT EXISTS ai_study_materials (

    id                INT AUTO_INCREMENT PRIMARY KEY,

    subject_id        INT NOT NULL,

    class_name        VARCHAR(100) NOT NULL,

    original_filename VARCHAR(255) NOT NULL,

    stored_filename   VARCHAR(255) NOT NULL,

    file_type         VARCHAR(20)  NOT NULL DEFAULT 'pdf',

    file_size         INT          NOT NULL DEFAULT 0,

    page_count        INT          NOT NULL DEFAULT 0,

    extracted_content LONGTEXT DEFAULT NULL,

    cleaned_content   LONGTEXT DEFAULT NULL,

    cleaned_word_count INT         NOT NULL DEFAULT 0,

    chunk_count       INT          NOT NULL DEFAULT 0,

    extraction_status VARCHAR(20)  NOT NULL DEFAULT 'processing',

    word_count        INT          NOT NULL DEFAULT 0,

    uploaded_by       INT          NOT NULL,

    uploaded_at       TIMESTAMP DEFAULT CURRENT_TIMESTAMP

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");



// Add new columns to existing installs (AFTER clause omitted — order is cosmetic)

foreach ([

    'word_count'         => 'ADD COLUMN word_count INT NOT NULL DEFAULT 0',

    'page_count'         => 'ADD COLUMN page_count INT NOT NULL DEFAULT 0',

    'extracted_content'  => 'ADD COLUMN extracted_content LONGTEXT DEFAULT NULL',

    'cleaned_content'    => 'ADD COLUMN cleaned_content LONGTEXT DEFAULT NULL',

    'cleaned_word_count' => 'ADD COLUMN cleaned_word_count INT NOT NULL DEFAULT 0',

    'chunk_count'        => 'ADD COLUMN chunk_count INT NOT NULL DEFAULT 0',

    'extraction_status'  => "ADD COLUMN extraction_status VARCHAR(20) NOT NULL DEFAULT 'processing'",

] as $col => $ddl) {

    $chk = $conn->query("SHOW COLUMNS FROM ai_study_materials LIKE '{$col}'");

    if ($chk && $chk->num_rows === 0) {

        $conn->query("ALTER TABLE ai_study_materials {$ddl}");

    }

}

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);



// Quota table — also guarded from throwing

mysqli_report(MYSQLI_REPORT_OFF);

$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");

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);



// ── Plan-based limits ─────────────────────────────────────────────────────

if (!planHasAIExamAddon()) {

    echo json_encode([

        'success' => false,

        'message' => 'AI Exam Generator is not included in your current plan. Please upgrade or contact your administrator.',

    ]);

    exit;

}



$lic  = function_exists('checkLicense') ? checkLicense() : ['plan' => 'starter'];

$plan = $lic['plan'] ?? 'starter';



// File size limit per plan (bytes)

$maxFileSizeBytes = match($plan) {

    'premium' => 50 * 1024 * 1024,

    'growth'  => 25 * 1024 * 1024,

    default   => 10 * 1024 * 1024,

};



// PDF page limit per plan (0 = skip check when pdfinfo unavailable)

$maxPages = match($plan) {

    'premium' => 100,

    'growth'  => 50,

    default   => 20,

};



// Max files in a single upload session per plan

$maxFilesPerUpload = match($plan) {

    'premium' => 10,

    'growth'  => 5,

    default   => 3,

};



// ── Quota pre-check ───────────────────────────────────────────────────────

$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);



// We warn at upload time if approaching limit; hard block happens at generation time

$quotaWarning = null;

if ($jobsUsed >= $jobQuota) {

    echo json_encode([

        'success' => false,

        'message' => "Monthly AI generation quota reached ({$jobsUsed}/{$jobQuota}). "

                   . 'Resets on the 1st of next month. Upgrade your plan for a higher limit.',

    ]);

    exit;

}

if ($jobsUsed >= $jobQuota - 2) {

    $quotaWarning = "Quota notice: {$jobsUsed} of {$jobQuota} AI generations used this month.";

}



// ── Basic request validation ──────────────────────────────────────────────

$subjectId = (int) ($_POST['subject_id'] ?? 0);

$className = trim((string) ($_POST['class_name'] ?? ''));

if ($subjectId <= 0 || $className === '') {

    echo json_encode(['success' => false, 'message' => 'Subject and class are required']);

    exit;

}

if (!isset($_FILES['file'])) {

    echo json_encode(['success' => false, 'message' => 'No file received']);

    exit;

}



$uploadError = (int) $_FILES['file']['error'];

if ($uploadError !== UPLOAD_ERR_OK) {

    $msg = match($uploadError) {

        UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE =>

            'File too large (PHP limit: ' . ini_get('upload_max_filesize') . '). '

            . 'Ask your admin to raise upload_max_filesize in php.ini.',

        UPLOAD_ERR_PARTIAL => 'Upload was interrupted — please try again.',

        UPLOAD_ERR_NO_FILE => 'No file was selected.',

        default            => 'Upload error code ' . $uploadError,

    };

    echo json_encode(['success' => false, 'message' => $msg]);

    exit;

}



$file = $_FILES['file'];



// ── File size check ───────────────────────────────────────────────────────

if ((int) $file['size'] > $maxFileSizeBytes) {

    $limitMB = $maxFileSizeBytes / 1024 / 1024;

    echo json_encode(['success' => false, 'message' => "File exceeds {$limitMB}MB limit for your plan."]);

    exit;

}



// ── File type check (extension + finfo MIME) ──────────────────────────────

$ext = strtolower((string) pathinfo((string) $file['name'], PATHINFO_EXTENSION));



$allowedExtensions = [

    'pdf'  => 'application/pdf',

    'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',

    'doc'  => 'application/msword',

    'txt'  => 'text/plain',

    'jpg'  => 'image/jpeg',

    'jpeg' => 'image/jpeg',

    'png'  => 'image/png',

    'webp' => 'image/webp',

];



if (!isset($allowedExtensions[$ext])) {

    echo json_encode([

        'success' => false,

        'message' => "Unsupported file type '.{$ext}'. Allowed: PDF, Word (.docx), TXT, JPG, PNG, WEBP.",

    ]);

    exit;

}



// Verify MIME via finfo (more reliable than trusting extension)

$expectedMime = $allowedExtensions[$ext];

if (function_exists('finfo_open')) {

    $finfo    = finfo_open(FILEINFO_MIME_TYPE);

    $realMime = finfo_file($finfo, (string) $file['tmp_name']);

    finfo_close($finfo);



    // Allow application/zip as alias for docx (they're ZIP archives)

    if ($realMime === 'application/zip' && $ext === 'docx') {

        $realMime = $expectedMime;

    }



    $safeMimes = [

        'application/pdf',

        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',

        'application/msword',

        'text/plain',

        'image/jpeg',

        'image/png',

        'image/webp',

        'application/zip', // .docx is a ZIP

    ];

    if (!in_array($realMime, $safeMimes, true)) {

        echo json_encode([

            'success' => false,

            'message' => "File content does not match its extension (detected: {$realMime}).",

        ]);

        exit;

    }

    $mimeType = ($realMime === 'application/zip') ? $expectedMime : $realMime;

} else {

    $mimeType = $expectedMime;

}



// ── Save file to disk ─────────────────────────────────────────────────────

if (!is_dir(EXAM_UPLOAD_DIR)) {

    @mkdir(EXAM_UPLOAD_DIR, 0777, true);

}



$storedFilename = 'mat_' . (int) $_SESSION['user_id'] . '_' . time() . '_' . uniqid('', true) . '.' . $ext;

$targetPath     = rtrim(EXAM_UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $storedFilename;



if (!move_uploaded_file((string) $file['tmp_name'], $targetPath)) {

    echo json_encode(['success' => false, 'message' => 'Could not save upload — check server permissions.']);

    exit;

}



// ── PDF page count check (after saving so we can use the real path) ────────

$pageCount = 0;

if ($ext === 'pdf') {

    $pageCount = getPdfPageCount($targetPath);

    if ($pageCount > 0 && $pageCount > $maxPages) {

        @unlink($targetPath);

        echo json_encode([

            'success' => false,

            'message' => "PDF has {$pageCount} pages but your plan allows a maximum of {$maxPages} pages. "

                       . 'Split the document or upgrade your plan.',

        ]);

        exit;

    }

}



// ── Determine file_type label ──────────────────────────────────────────────

$fileTypeLabel = match(true) {

    $ext === 'pdf'              => 'pdf',

    in_array($ext, ['docx','doc']) => 'word',

    $ext === 'txt'              => 'text',

    default                     => 'image',

};



// ── Insert placeholder row ────────────────────────────────────────────────

$insert = $conn->prepare(

    "INSERT INTO ai_study_materials

      (subject_id, class_name, original_filename, stored_filename,

       file_type, file_size, page_count, extraction_status, uploaded_by)

     VALUES (?, ?, ?, ?, ?, ?, ?, 'processing', ?)"

);

$size         = (int) $file['size'];

$userId       = (int) $_SESSION['user_id'];

$originalName = (string) $file['name'];

$insert->bind_param('issssiii', $subjectId, $className, $originalName, $storedFilename,

                    $fileTypeLabel, $size, $pageCount, $userId);

$insert->execute();

$materialId = (int) $insert->insert_id;

$insert->close();



// ── Extract text ──────────────────────────────────────────────────────────

$extractionWarning = null;

try {

    $extracted = extractTextFromFile($targetPath, $mimeType);

    $status    = 'done';

} catch (Exception $e) {

    $extracted = '';

    $status    = 'failed';

    $extractionWarning = $e->getMessage();

    error_log('[EduTrack] Extraction failed: ' . $e->getMessage());

}



$rawContent = trim($extracted);



// ── Clean the extracted content ────────────────────────────────────────────

$cleanedContent   = '';

$cleanedWordCount = 0;

$chunkCount       = 0;



if ($status === 'done' && $rawContent !== '') {

    $cleanedContent   = cleanExtractedContent($rawContent);

    $cleanedWordCount = str_word_count($cleanedContent);



    // Pre-compute chunk count so the UI can show it

    $wordLimit  = getPlanWordLimit();

    $chunks     = chunkContent($cleanedContent, $wordLimit);

    $chunkCount = count($chunks);

}



$wordCount = $status === 'done' ? str_word_count($rawContent) : 0;



// ── Update row with results ────────────────────────────────────────────────

$stmt = $conn->prepare(

    "UPDATE ai_study_materials

     SET extracted_content = ?, cleaned_content = ?, extraction_status = ?,

         word_count = ?, cleaned_word_count = ?, chunk_count = ?

     WHERE id = ?"

);

$stmt->bind_param('sssiiii', $rawContent, $cleanedContent, $status,

                  $wordCount, $cleanedWordCount, $chunkCount, $materialId);

$stmt->execute();

$stmt->close();



// ── Build warning string ───────────────────────────────────────────────────

$warning = null;

if ($status === 'failed') {

    $warning = $extractionWarning

        ?: 'File saved but text extraction failed. '

         . 'If this is a scanned image PDF, ensure an AI API key is configured in Settings.';

} elseif ($cleanedWordCount < 100 && $status === 'done') {

    $warning = 'Very little educational content was found in this file ('

             . $cleanedWordCount . ' words after cleaning). '

             . 'The AI may produce poor-quality questions. Try uploading a more detailed document.';

} elseif ($quotaWarning) {

    $warning = $quotaWarning;

}



echo json_encode([

    'success'              => true,

    'material_id'          => $materialId,

    'filename'             => $originalName,

    'word_count'           => $wordCount,

    'cleaned_word_count'   => $cleanedWordCount,

    'chunk_count'          => $chunkCount,

    'page_count'           => $pageCount,

    'extraction_status'    => $status,

    'warning'              => $warning,

    'quota_used'           => $jobsUsed,

    'quota_limit'          => $jobQuota,

]);

