<?php
declare(strict_types=1);

require_once __DIR__ . '/claude_api.php';

/**
 * Extract raw text from an uploaded study-material file.
 *
 * Supported mime types:
 *   application/pdf                               — pdftotext → ImageMagick → vision cascade
 *   application/vnd.openxmlformats-officedocument.wordprocessingml.document (.docx)
 *                                                 — ZipArchive XML parse
 *   application/msword (.doc)                     — LibreOffice headless convert (if available)
 *   text/plain                                    — direct read
 *   image/jpeg, image/png, image/webp             — vision cascade
 */
function extractTextFromFile(string $filePath, string $mimeType): string
{
    // ── PDF ───────────────────────────────────────────────────────────────
    if ($mimeType === 'application/pdf') {
        // 1. pdftotext — fast, no AI needed for text-based PDFs
        $text = @shell_exec('pdftotext ' . escapeshellarg($filePath) . ' -');
        if (!empty(trim((string) $text))) {
            return trim((string) $text);
        }

        // 2. Scanned PDF — convert page 0 to image via ImageMagick, then vision
        $tmpImg = sys_get_temp_dir() . '/pdf_page_' . uniqid('', true) . '.jpg';
        @shell_exec('convert -density 150 ' . escapeshellarg($filePath) . '[0] -quality 90 ' . escapeshellarg($tmpImg));
        if (is_file($tmpImg)) {
            $base64 = base64_encode((string) file_get_contents($tmpImg));
            @unlink($tmpImg);
            return callVisionCascade($base64, 'image/jpeg');
        }

        // 3. No ImageMagick — send raw PDF bytes to vision cascade
        //    Claude + Gemini accept PDF inline; Groq is skipped gracefully inside callVisionCascade()
        $base64 = base64_encode((string) file_get_contents($filePath));
        return callVisionCascade($base64, 'application/pdf');
    }

    // ── Word .docx ────────────────────────────────────────────────────────
    if ($mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
        return _extractDocx($filePath);
    }

    // ── Word .doc (legacy binary) ─────────────────────────────────────────
    if ($mimeType === 'application/msword') {
        return _extractDoc($filePath);
    }

    // ── Plain text ────────────────────────────────────────────────────────
    if (in_array($mimeType, ['text/plain', 'text/csv'], true)) {
        return (string) file_get_contents($filePath);
    }

    // ── Images ────────────────────────────────────────────────────────────
    if (in_array($mimeType, ['image/jpeg', 'image/png', 'image/webp', 'image/gif'], true)) {
        $base64 = base64_encode((string) file_get_contents($filePath));
        return callVisionCascade($base64, $mimeType);
    }

    throw new Exception("Unsupported file type: {$mimeType}");
}

/**
 * Extract plain text from a .docx file using ZipArchive + XML parsing.
 * No external dependencies required.
 */
function _extractDocx(string $filePath): string
{
    if (!class_exists('ZipArchive')) {
        throw new Exception('ZipArchive extension is required to read .docx files. Enable zip in php.ini.');
    }

    $zip = new ZipArchive();
    if ($zip->open($filePath) !== true) {
        throw new Exception('Could not open .docx file — it may be corrupted or password-protected.');
    }

    $xml = $zip->getFromName('word/document.xml');
    $zip->close();

    if ($xml === false) {
        throw new Exception('Invalid .docx file — word/document.xml not found inside archive.');
    }

    // Suppress XML parse errors for malformed docs; extract what we can
    $prev = libxml_use_internal_errors(true);
    $dom  = new DOMDocument();
    $dom->loadXML($xml);
    libxml_use_internal_errors($prev);

    // Each <w:p> is a paragraph; <w:t> nodes inside hold the text runs
    $paragraphs = $dom->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'p');
    $lines = [];
    foreach ($paragraphs as $para) {
        $runs  = $para->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 't');
        $parts = [];
        foreach ($runs as $run) {
            $parts[] = $run->textContent;
        }
        $line = implode('', $parts);
        if (trim($line) !== '') {
            $lines[] = $line;
        }
    }

    if (empty($lines)) {
        throw new Exception('No text found in .docx — the document may be empty or contain only images.');
    }

    return implode("\n", $lines);
}

/**
 * Extract plain text from a legacy .doc file using LibreOffice headless.
 * Falls back to a helpful error if LibreOffice is not installed.
 */
function _extractDoc(string $filePath): string
{
    // Try LibreOffice headless conversion to text
    $tmpDir = sys_get_temp_dir() . '/lo_' . uniqid('', true);
    @mkdir($tmpDir, 0777, true);

    $cmd    = 'libreoffice --headless --convert-to txt:Text --outdir '
            . escapeshellarg($tmpDir) . ' ' . escapeshellarg($filePath) . ' 2>&1';
    $output = @shell_exec($cmd);

    $txtFile = $tmpDir . '/' . pathinfo($filePath, PATHINFO_FILENAME) . '.txt';
    if (is_file($txtFile)) {
        $text = (string) file_get_contents($txtFile);
        @unlink($txtFile);
        @rmdir($tmpDir);
        if (trim($text) !== '') {
            return $text;
        }
    }

    // Clean up and report
    @rmdir($tmpDir);
    throw new Exception(
        'Cannot read legacy .doc files: LibreOffice is not installed on this server. '
        . 'Please save the file as .docx or export it as a PDF and upload that instead.'
    );
}

/**
 * Count pages in a PDF using pdfinfo (poppler-utils).
 * Returns 0 if pdfinfo is not available (caller should skip the page check).
 */
function getPdfPageCount(string $filePath): int
{
    $output = @shell_exec('pdfinfo ' . escapeshellarg($filePath) . ' 2>/dev/null');
    if (empty($output)) {
        return 0; // pdfinfo not available — skip page check
    }
    if (preg_match('/Pages:\s+(\d+)/i', (string) $output, $m)) {
        return (int) $m[1];
    }
    return 0;
}
