<?php

declare(strict_types=1);



/**

 * Content Chunker — EduTrack AI Exam Generator

 *

 * cleanExtractedContent() — strips boilerplate, deduplicates paragraphs

 * chunkContent()          — splits cleaned text into topic-sized chunks

 * getPlanWordLimit()      — word ceiling for the school's current plan

 * getPlanJobQuota()       — monthly generation limit for the plan

 */



/**

 * Remove noise from extracted text and return cleaned educational content.

 */

function cleanExtractedContent(string $raw): string

{

    if (trim($raw) === '') {

        return '';

    }



    // Normalise line endings

    $text = str_replace(["\r\n", "\r"], "\n", $raw);



    $lines = explode("\n", $text);



    // Identify running headers/footers: lines that appear 3+ times verbatim

    $lineFrequency = array_count_values(array_map('trim', $lines));

    $repeatingLines = array_filter($lineFrequency, static fn(int $c) => $c >= 3);



    $cleaned = [];

    foreach ($lines as $line) {

        $trimmed = trim($line);



        if ($trimmed === '') {

            $cleaned[] = '';

            continue;

        }



        // Running header/footer

        if (isset($repeatingLines[$trimmed])) {

            continue;

        }



        // Standalone page numbers

        if (preg_match('/^\d+$/', $trimmed)) {

            continue;

        }

        if (preg_match('/^page\s+\d+(\s+of\s+\d+)?$/i', $trimmed)) {

            continue;

        }



        // Table of contents entries (text … 12)

        if (preg_match('/\.{3,}\s*\d+\s*$/', $trimmed)) {

            continue;

        }



        // Copyright / publication noise

        if (preg_match('/©|all rights reserved|isbn[\s:]/i', $trimmed)) {

            continue;

        }

        if (preg_match('/^published (by|in)\b/i', $trimmed)) {

            continue;

        }



        // Very short lines — keep only headings or formula-like content

        if (mb_strlen($trimmed) < 15) {

            $isHeading = strtoupper($trimmed) === $trimmed && preg_match('/[A-Z]/', $trimmed);

            $isFormula = (bool) preg_match('/[=\+\-×÷∑∫√πΣΔ]/', $trimmed);

            if (!$isHeading && !$isFormula) {

                continue;

            }

        }



        $cleaned[] = $line;

    }



    // Collapse excessive blank lines

    $text = implode("\n", $cleaned);

    $text = (string) preg_replace('/\n{3,}/', "\n\n", $text);

    $text = trim($text);



    // Paragraph-level deduplication

    $paragraphs  = (array) preg_split('/\n{2,}/', $text);

    $seen        = [];

    $deduped     = [];

    foreach ($paragraphs as $para) {

        $hash = md5(strtolower(trim((string) $para)));

        if (!isset($seen[$hash])) {

            $seen[$hash] = true;

            $deduped[]   = $para;

        }

    }



    return trim(implode("\n\n", $deduped));

}



/**

 * Word limit for AI content based on the school's license plan.

 */

function getPlanWordLimit(): int

{

    if (function_exists('checkLicense')) {

        $lic = checkLicense();

        return match($lic['plan'] ?? 'starter') {

            'premium' => 20000,

            'growth'  => 14000,

            default   => 8000,

        };

    }

    return 8000;

}



/**

 * Monthly generation-job quota per plan.

 * Premium uses a fair-use cap (999 = effectively unlimited within reason).

 */

function getPlanJobQuota(): int

{

    if (function_exists('checkLicense')) {

        $lic = checkLicense();

        return match($lic['plan'] ?? 'starter') {

            'premium' => 999,

            'growth'  => 20,

            default   => 10,

        };

    }

    return 10;

}



/**

 * Whether the current plan includes the AI question-generator add-on.

 * Starter and Growth require the explicit 'ai_exam' addon; Premium always has it.

 */

function planHasAIExamAddon(): bool

{

    if (function_exists('checkLicense')) {

        $lic = checkLicense();

        if (($lic['plan'] ?? '') === 'premium') {

            return true;

        }

        $addons = $lic['addons'] ?? [];

        return in_array('ai_exam', $addons, true);

    }

    return true; // default open during dev

}



/**

 * Whether the current plan supports the refinement (moderation) pass.

 * Growth and Premium only.

 */

function planSupportsRefinement(): bool

{

    if (function_exists('checkLicense')) {

        $lic = checkLicense();

        return in_array($lic['plan'] ?? 'starter', ['growth', 'premium'], true);

    }

    return true;

}



/**

 * Split cleaned educational text into topic-based chunks for individual AI calls.

 *

 * Target chunk size: 1,500–3,000 words.

 *

 * @param string $cleanedText  Output of cleanExtractedContent()

 * @param int    $wordLimit    Total word budget (from getPlanWordLimit())

 * @return array<int,array{topic:string,words:int,content:string}>

 */

function chunkContent(string $cleanedText, int $wordLimit = 8000): array

{

    if (trim($cleanedText) === '') {

        return [];

    }



    // Enforce plan word limit

    $allWords   = (array) preg_split('/\s+/', $cleanedText, -1, PREG_SPLIT_NO_EMPTY);

    $totalWords = count($allWords);

    if ($totalWords > $wordLimit) {

        $cleanedText = implode(' ', array_slice($allWords, 0, $wordLimit));

        $cleanedText .= "\n\n[Content trimmed to plan word limit. Upload additional files to cover remaining topics.]";

    }



    // Detect headings to use as split points.

    // Uses ~ as delimiter to avoid colliding with / inside the pattern.

    // Matches: ALL-CAPS lines ≥3 chars, Chapter/Lesson/… N, or "1.2 Forces"-style numbers.

    $headingPattern = '~(?:^|\n)(?='

        . '[A-Z][A-Z\s\d\:\-]{2,}[A-Z][ \t]*$'

        . '|(?i:(?:Chapter|Lesson|Unit|Section|Topic|Part|Module))\s+[\dIVXivx\.:+]'

        . '|\d+[\.:]\d*\s+[A-Z]'

        . ')~m';



    $splitResult = preg_split($headingPattern, $cleanedText, -1, PREG_SPLIT_NO_EMPTY);

    $sections    = (is_array($splitResult) && $splitResult !== []) ? $splitResult : [$cleanedText];



    // Tag each section with its topic name (first line)

    $rawSections = [];

    foreach ($sections as $sec) {

        $sec = trim((string) $sec);

        if ($sec === '') {

            continue;

        }

        $firstLine = trim(explode("\n", $sec)[0] ?? '');

        $topic     = mb_strlen($firstLine) > 120

            ? mb_substr($firstLine, 0, 120) . '…'

            : ($firstLine ?: 'General');

        $rawSections[] = [

            'topic'   => $topic,

            'words'   => str_word_count($sec),

            'content' => $sec,

        ];

    }



    // Merge tiny (<200 words) sections forward; split oversize (>3000 words)

    $chunks = [];

    $buffer = null;



    foreach ($rawSections as $sec) {

        if ($buffer === null) {

            $buffer = $sec;

            continue;

        }



        if ($buffer['words'] < 200) {

            // Tiny buffer — absorb into it

            $buffer['content'] .= "\n\n" . $sec['content'];

            $buffer['words']   += $sec['words'];

            continue;

        }



        // Flush buffer

        $chunks = array_merge($chunks, _splitOversized($buffer));

        $buffer = $sec;

    }

    if ($buffer !== null) {

        $chunks = array_merge($chunks, _splitOversized($buffer));

    }



    // Last-resort: if splitting produced nothing, treat the whole text as one chunk

    if (empty($chunks)) {

        $chunks = [[

            'topic'   => mb_substr(trim(explode("\n", $cleanedText)[0] ?? 'General'), 0, 120),

            'words'   => str_word_count($cleanedText),

            'content' => $cleanedText,

        ]];

    }



    return $chunks;

}



/**

 * Split a single section that exceeds 3,000 words at paragraph boundaries.

 *

 * @param array{topic:string,words:int,content:string} $sec

 * @return array<int,array{topic:string,words:int,content:string}>

 */

function _splitOversized(array $sec): array

{

    if ($sec['words'] <= 3000) {

        return [$sec];

    }



    $paragraphs   = (array) preg_split('/\n{2,}/', $sec['content'], -1, PREG_SPLIT_NO_EMPTY);

    $chunks       = [];

    $current      = '';

    $currentWords = 0;

    $partNum      = 1;



    foreach ($paragraphs as $para) {

        $paraWords = str_word_count((string) $para);

        if ($currentWords + $paraWords > 3000 && $currentWords > 0) {

            $chunks[] = [

                'topic'   => $sec['topic'] . " (Part $partNum)",

                'words'   => $currentWords,

                'content' => trim($current),

            ];

            $current      = (string) $para;

            $currentWords = $paraWords;

            $partNum++;

        } else {

            $current      .= ($current === '' ? '' : "\n\n") . $para;

            $currentWords += $paraWords;

        }

    }



    if (trim($current) !== '') {

        $chunks[] = [

            'topic'   => $partNum > 1 ? $sec['topic'] . " (Part $partNum)" : $sec['topic'],

            'words'   => $currentWords,

            'content' => trim($current),

        ];

    }



    return $chunks ?: [$sec];

}

