<?php
/**
 * Markdown — fine façade au-dessus de Parsedown / ParsedownExtra (embarqués
 * dans lib/, licence MIT). Centralise le rendu Markdown → HTML utilisé par la
 * documentation in-app (cf. DocCatalog / DocController).
 *
 * - Mode sûr activé (setSafeMode) : le HTML brut éventuel des .md est échappé.
 * - Les titres (h1..h6) reçoivent un `id` slugifié façon GitHub afin que les
 *   tables des matières des docs existantes (liens ancres `#1-...`) fonctionnent.
 *
 * Classe sans état : un seul point d'entrée statique `Markdown::toHtml()`.
 */
class Markdown
{
    /** Convertit du Markdown en HTML prêt à afficher dans `.doc-content`. */
    public static function toHtml(string $markdown): string
    {
        $parser = new ParsedownExtra();
        $parser->setSafeMode(true);          // échappe le HTML brut (defense-in-depth)
        $parser->setBreaksEnabled(false);

        $html = $parser->text($markdown);
        $html = self::injectHeadingIds($html);

        return self::convertMermaid($html);
    }

    /**
     * Transforme les blocs ```mermaid (rendus par Parsedown en
     * `<pre><code class="language-mermaid">…</code></pre>`) en conteneurs
     * `<div class="mermaid">…</div>` que mermaid.js sait rendre côté client.
     * Le contenu reste échappé : `textContent` du div restitue le texte brut
     * attendu par mermaid.
     */
    private static function convertMermaid(string $html): string
    {
        return preg_replace(
            '#<pre><code class="language-mermaid">(.*?)</code></pre>#s',
            '<div class="mermaid">$1</div>',
            $html
        );
    }

    /** Extrait le titre d'un document = premier titre `# H1` (ou nom de fichier). */
    public static function extractTitle(string $markdown, string $fallback = ''): string
    {
        foreach (preg_split('/\r\n|\r|\n/', $markdown) as $line) {
            if (preg_match('/^\s*#\s+(.+?)\s*#*\s*$/', $line, $m)) {
                return trim($m[1]);
            }
        }
        return $fallback;
    }

    /**
     * Ajoute un attribut id slugifié aux titres qui n'en ont pas déjà
     * (ParsedownExtra peut en poser un via la syntaxe `{#id}`).
     */
    private static function injectHeadingIds(string $html): string
    {
        return preg_replace_callback(
            '/<h([1-6])>(.*?)<\/h\1>/s',
            static function (array $m): string {
                $level = $m[1];
                $inner = $m[2];
                $text  = html_entity_decode(strip_tags($inner), ENT_QUOTES, 'UTF-8');
                $slug  = self::slug($text);
                if ($slug === '') {
                    return $m[0];
                }
                return "<h{$level} id=\"{$slug}\">{$inner}</h{$level}>";
            },
            $html
        );
    }

    /** Slug façon GitHub : minuscules, ponctuation retirée, espaces → tirets. */
    private static function slug(string $text): string
    {
        $text = mb_strtolower(trim($text), 'UTF-8');
        // Translittération simple des diacritiques courants
        $text = strtr($text, [
            'à' => 'a', 'â' => 'a', 'ä' => 'a', 'á' => 'a', 'ã' => 'a',
            'ç' => 'c', 'é' => 'e', 'è' => 'e', 'ê' => 'e', 'ë' => 'e',
            'î' => 'i', 'ï' => 'i', 'í' => 'i', 'ì' => 'i',
            'ô' => 'o', 'ö' => 'o', 'ó' => 'o', 'õ' => 'o',
            'û' => 'u', 'ü' => 'u', 'ú' => 'u', 'ù' => 'u', 'ñ' => 'n',
        ]);
        $text = preg_replace('/[^a-z0-9\s-]/u', '', $text);
        $text = preg_replace('/[\s-]+/', '-', $text);
        return trim($text, '-');
    }
}
