<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
//   Renders the repo's own markdown — READMEs, Manuals, design notes — inside the WebGUI,
//   substituting live conf values into `$VAR` markers so documentation shows what this host
//   is actually configured to do rather than a generic example.
//
// STATUS
//   Not currently wired. Nothing requires this file and there is no docs page or endpoint
//   yet. It is kept because the per-folder README/Manual corpus is exactly what it exists to
//   surface. Written to be safe on the day it is connected — see OPERATIONAL SAFEGUARDS.
//
// DESIGN PRINCIPLES
//   Documentation is discovered, not enumerated.
//       vv_docs_tree() walks SCRIPTS_DIR for *.md. A new folder README appears in the UI
//       with no registration step, which is what keeps the docs from drifting out of the
//       navigation.
//
//   Live values, not example values.
//       `$VAR_NAME` in a markdown file is replaced with that variable's current value from
//       conf. Unresolved names render in a distinct class rather than being left as-is, so a
//       stale variable reference in a doc is visible instead of looking like prose.
//
//   Degrades to readable text without Parsedown.
//       If the bundled renderer is absent the raw markdown is emitted in a <pre> block.
//       Missing a formatter reduces presentation; it never hides the content.
//
// OPERATIONAL SAFEGUARDS
//   Paths are contained to SCRIPTS_DIR.
//       $rel is resolved with realpath() and required to remain under SCRIPTS_DIR, be a
//       regular file, and carry a .md extension. This is deliberate defence for a parameter
//       that will arrive from a request the moment this is wired up — without it, a
//       traversal sequence reaches any file the web user can read.
//
//   Markdown is rendered in safe mode.
//       Parsedown runs with setSafeMode(true), and the <pre> fallback escapes everything.
//       These files are trusted today, but they are also synced between hosts.
//
//   Substituted conf values are escaped.
//       htmlspecialchars() is applied to both the value and the variable name, so a conf
//       value containing markup cannot inject into the rendered page.
//
//   Read-only. Discovers and renders; never writes a doc.
//
// EXPORTS
//   vv_docs_tree()     every *.md under SCRIPTS_DIR, relative paths, sorted
//   vv_docs_render()   one file to HTML with conf substitution applied
//
// CONFIGURATION
//   SCRIPTS_DIR       the containment root and the discovery root
//   PARSEDOWN_PATH    /usr/local/emhttp/plugins/varaverk/lib/Parsedown.php — optional
// ═══════════════════════════════════════════════════════════════════════════════════════════════

require_once __DIR__ . '/config.php';

define('PARSEDOWN_PATH', '/usr/local/emhttp/plugins/varaverk/lib/Parsedown.php');

function vv_docs_tree(): array {
    $base  = SCRIPTS_DIR;
    $tree  = [];
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($files as $f) {
        if ($f->isFile() && strtolower($f->getExtension()) === 'md') {
            $rel    = ltrim(str_replace($base, '', $f->getPathname()), '/');
            $tree[] = $rel;
        }
    }
    sort($tree);
    return $tree;
}

function vv_docs_render(string $rel, array $vars): string {
    // Containment check — $rel is expected to come from a request parameter once this is
    // wired to a page. Resolve it and require the result to stay inside SCRIPTS_DIR and to
    // still be a .md file, so a traversal sequence cannot reach arbitrary files.
    $base = realpath(SCRIPTS_DIR);
    $path = realpath(SCRIPTS_DIR . '/' . $rel);
    if ($base === false || $path === false)             return '<p>File not found.</p>';
    if (!str_starts_with($path, $base . '/'))           return '<p>File not found.</p>';
    if (strtolower(pathinfo($path, PATHINFO_EXTENSION)) !== 'md') return '<p>File not found.</p>';
    if (!is_file($path))                                return '<p>File not found.</p>';

    $md = file_get_contents($path);

    // Substitute `$VAR_NAME` markers with live conf values
    $md = preg_replace_callback('/`\$([A-Z0-9_]+)`/', function($m) use ($vars) {
        $key = $m[1];
        return isset($vars[$key])
            ? '<code class="vv-live-var">' . htmlspecialchars($vars[$key]) . '</code>'
            : '<code class="vv-unknown-var">$' . htmlspecialchars($key) . '</code>';
    }, $md);

    // Render markdown
    if (file_exists(PARSEDOWN_PATH)) {
        require_once PARSEDOWN_PATH;
        $pd = new Parsedown();
        $pd->setSafeMode(true);
        return $pd->text($md);
    }

    // Fallback: plain preformatted text
    return '<pre>' . htmlspecialchars($md) . '</pre>';
}
