<?php
declare(strict_types=1);

namespace Celena\Plugin\Handbook\Support;

/**
 * Резолвит и рендерит .md-страницу документации по языку.
 * Защита от path traversal: итоговый realpath обязан лежать внутри content/
 * и иметь расширение .md. Если перевода на запрошенный язык нет — возвращает
 * fallback-язык с флагом fallback=true.
 */
final class DocPage
{
    public function __construct(private readonly string $contentDir)
    {
    }

    /**
     * @return array{html:string, toc:list<array{level:int,text:string,id:string}>, fallback:bool}|null
     */
    public function load(string $lang, string $file, string $fallbackLang = 'ru'): ?array
    {
        $path = $this->resolve($lang, $file);
        $fallback = false;
        if ($path === null && $lang !== $fallbackLang) {
            $path = $this->resolve($fallbackLang, $file);
            $fallback = $path !== null;
        }
        if ($path === null) {
            return null;
        }
        $md = (string) file_get_contents($path);
        $rendered = DocMarkdown::render($md);
        return [
            'html'     => $rendered['html'],
            'toc'      => $rendered['toc'],
            'fallback' => $fallback,
        ];
    }

    private function resolve(string $lang, string $file): ?string
    {
        $base = realpath($this->contentDir);
        if ($base === false) {
            return null;
        }
        // Нормализуем разделитель и режем любые попытки выхода.
        $rel = str_replace('\\', '/', $lang . '/' . $file);
        if (str_contains($rel, '..')) {
            return null;
        }
        $real = realpath($this->contentDir . '/' . $rel);
        if ($real === false) {
            return null;
        }
        $real = str_replace('\\', '/', $real);
        $base = str_replace('\\', '/', $base);
        if (!str_starts_with($real, rtrim($base, '/') . '/')) {
            return null;
        }
        if (!str_ends_with(strtolower($real), '.md')) {
            return null;
        }
        return $real;
    }
}
