<?php
// +----------------------------------------------------------------------------------------+
// © 2002-2006 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
//
// Fichier en UTF-8
// +----------------------------------------------------------------------------------------+
// $Id: index.php,v 1.3 2026/08/14 10:16:07 dbellamy Exp $

header('Content-Type: text/html; charset=UTF-8');
include 'SchemeParser.php';

$doc = new SchemeParser();
$doc->parseJsonFile();
$t_table   = $doc->get_table();
$t_parcours = $doc->get_parcours();
$t_relation = $doc->get_relation();

// Titre depuis le XML
preg_match('/<h3>(.*?)<\/h3>/', $doc->getTitre(), $m);
$titre = $m[1] ?? 'Base de données PMB';
?>
<!DOCTYPE html>
<html lang="fr">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?= htmlspecialchars($titre) ?></title>
    <style>
        *,
        *::before,
        *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        :root {
            --sidebar-w: 280px;
            --topbar-h: 52px;
            --bg: #f4f6f9;
            --surface: #ffffff;
            --sidebar-bg: #1e2330;
            --sidebar-hover: #2c3347;
            --sidebar-active: #3b82f6;
            --text: #1a1f2e;
            --text-muted: #6b7280;
            --border: #e5e7eb;
            --pk-color: #f59e0b;
            --fk-color: #3b82f6;
            --tag-bg: #eff6ff;
            --tag-text: #1d4ed8;
            --mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
            --sans: system-ui, -apple-system, sans-serif;
        }

        html,
        body {
            height: 100%;
            overflow: hidden;
            font-family: var(--sans);
            color: var(--text);
            background: var(--bg);
        }

        /* -- Lien d'évitement -- */
        .skip-link {
            position: fixed;
            top: -100px;
            left: 8px;
            z-index: 9999;
            background: #fff;
            color: var(--text);
            padding: 8px 16px;
            border-radius: 4px;
            font-size: 14px;
            font-weight: 600;
            border: 2px solid var(--sidebar-active);
            text-decoration: none;
        }

        .skip-link:focus {
            top: 8px;
        }

        /* -- Focus visible global -- */
        :focus-visible {
            outline: 3px solid var(--sidebar-active);
            outline-offset: 2px;
        }

        /* -- Topbar -- */
        .topbar {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            height: var(--topbar-h);
            background: var(--sidebar-bg);
            color: #fff;
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 0 20px;
            z-index: 100;
            border-bottom: 1px solid rgba(255, 255, 255, .08);
        }

        .topbar-logo {
            font-size: 18px;
            font-weight: 700;
            letter-spacing: -.3px;
            white-space: nowrap;
        }

        .topbar-logo span {
            color: var(--sidebar-active);
        }

        /* Contraste AA : blanc pur sur #1e2330 = ratio 10.6:1 */
        .topbar-count {
            margin-left: auto;
            font-size: 14px;
            color: #ffffff;
            white-space: nowrap;
        }

        /* -- Export buttons -- */
        .export-group {
            display: flex;
            align-items: center;
            gap: 6px;
        }

        /* "Exporter" est décoratif, contraste abaissé intentionnellement (non porteur d'info) */
        .export-group-label {
            font-size: 12px;
            color: rgba(255, 255, 255, .7);
            text-transform: uppercase;
            letter-spacing: .05em;
            margin-right: 2px;
        }

        .export-btn {
            display: flex;
            align-items: center;
            gap: 5px;
            padding: 6px 14px;
            border-radius: 5px;
            border: 1px solid rgba(59, 130, 246, .4);
            background: rgba(255, 255, 255, .07);
            color: rgba(255, 255, 255, .9);
            font-size: 14px;
            cursor: pointer;
            transition: background .15s, border-color .15s;
            white-space: nowrap;
        }

        .export-btn:hover {
            background: rgba(255, 255, 255, .15);
            border-color: rgba(255, 255, 255, .4);
            color: #fff;
        }

        /* -- Layout -- */
        .layout {
            display: flex;
            height: 100vh;
            padding-top: var(--topbar-h);
        }

        /* -- Sidebar -- */
        .sidebar {
            width: var(--sidebar-w);
            flex-shrink: 0;
            background: var(--sidebar-bg);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .si {
            background: rgba(255, 255, 255, .07);
            border: 1px solid rgba(255, 255, 255, .12);
            border-radius: 6px;
            color: #fff;
            padding: 5px 10px 5px 28px;
            font-size: 12px;
            width: 200px;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: 8px center;
        }

        .si::placeholder {
            color: rgba(255, 255, 255, .35);
        }

        .si:focus {
            border-color: var(--sidebar-active);
            outline: none;
        }

        .table-list {
            overflow-y: auto;
            flex: 1;
            padding: 4px 0;
        }

        .table-list::-webkit-scrollbar {
            width: 4px;
        }

        .table-list::-webkit-scrollbar-track {
            background: transparent;
        }

        .table-list::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, .15);
            border-radius: 2px;
        }

        .table-item {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 8px 14px;
            width: 100%;
            cursor: pointer;
            background: none;
            border: none;
            border-left: 2px solid transparent;
            color: rgba(255, 255, 255, .85);
            font-size: 14px;
            font-family: var(--sans);
            text-align: left;
            transition: background .12s, color .12s, border-color .12s;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .table-item:hover {
            background: var(--sidebar-hover);
            color: #fff;
        }

        .table-item.active {
            background: rgba(59, 130, 246, .15);
            color: #fff;
            border-left-color: var(--sidebar-active);
        }

        .table-item[aria-current="true"] {
            background: rgba(59, 130, 246, .15);
            color: #fff;
            border-left-color: var(--sidebar-active);
        }

        .table-item .icon {
            opacity: 1;
            font-size: 11px;
            flex-shrink: 0;
        }

        .table-item.hidden {
            display: none;
        }

        /* -- Main -- */
        .main {
            flex: 1;
            overflow-y: auto;
            padding: 28px 32px;
            background: var(--bg);
        }

        .main::-webkit-scrollbar {
            width: 6px;
        }

        .main::-webkit-scrollbar-track {
            background: transparent;
        }

        .main::-webkit-scrollbar-thumb {
            background: var(--border);
            border-radius: 3px;
        }

        .welcome {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
            color: var(--text-muted);
            text-align: center;
            gap: 12px;
        }

        .welcome .big-icon {
            font-size: 48px;
            opacity: .3;
        }

        .welcome p {
            font-size: 15px;
        }

        /* -- Table panel -- */
        .panel {
            display: none;
        }

        .panel.active {
            display: block;
        }

        .panel-header {
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: 10px;
            padding: 20px 24px;
            margin-bottom: 20px;
        }

        .panel-header h2 {
            font-size: 20px;
            font-weight: 700;
            font-family: var(--mono);
            color: var(--text);
            margin-bottom: 4px;
        }

        .panel-header .desc {
            font-size: 13px;
            color: var(--text-muted);
            line-height: 1.5;
        }

        .panel-header .meta {
            margin-top: 12px;
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
        }

        .badge {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            background: var(--tag-bg);
            color: var(--tag-text);
            font-size: 11px;
            font-weight: 600;
            padding: 3px 8px;
            border-radius: 20px;
        }

        .badge.green {
            background: #f0fdf4;
            color: #166534;
        }

        .badge.amber {
            background: #fffbeb;
            color: #92400e;
        }

        .badge.gray {
            background: #f9fafb;
            color: #374151;
        }

        /* -- Fields table -- */
        .section-title {
            font-size: 13px;
            font-weight: 700;
            letter-spacing: .05em;
            text-transform: uppercase;
            color: #4b5563;
            margin-bottom: 8px;
        }

        .fields-table {
            width: 100%;
            border-collapse: collapse;
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: 10px;
            overflow: hidden;
            font-size: 13px;
            margin-bottom: 24px;
        }

        .fields-table th {
            background: #f9fafb;
            text-align: left;
            padding: 10px 14px;
            font-size: 11px;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: .04em;
            color: var(--text-muted);
            border-bottom: 1px solid var(--border);
        }

        .fields-table td {
            padding: 9px 14px;
            border-bottom: 1px solid #f3f4f6;
            vertical-align: middle;
        }

        .fields-table tr:last-child td {
            border-bottom: none;
        }

        .fields-table tr:hover td {
            background: #fafafa;
        }

        .field-name {
            font-family: var(--mono);
            font-weight: 600;
            font-size: 12.5px;
        }

        .field-type {
            font-family: var(--mono);
            font-size: 12px;
            color: #7c3aed;
        }

        /* #4b5563 sur blanc = ratio 7.1:1 (AAA) */
        .field-desc {
            color: #4b5563;
            font-style: italic;
            font-size: 12px;
        }

        /* #047857 sur blanc = ratio 4.54:1 (AA) */
        .field-default {
            font-family: var(--mono);
            font-size: 11px;
            color: #047857;
        }

        .key-badge {
            display: inline-block;
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 10px;
            font-weight: 700;
            white-space: nowrap;
        }

        .key-pk {
            background: #fef3c7;
            color: #92400e;
        }

        .key-fk {
            background: #dbeafe;
            color: #1e40af;
        }

        /* -- Relations -- */
        .relations-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
            gap: 10px;
            margin-bottom: 24px;
        }

        .relation-card {
            background: var(--surface);
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 12px 14px;
            cursor: pointer;
            transition: border-color .15s, box-shadow .15s;
            display: flex;
            align-items: center;
            gap: 10px;
            width: 100%;
            text-align: left;
            font-family: var(--sans);
        }

        .relation-card:hover,
        .relation-card:focus-visible {
            border-color: var(--sidebar-active);
            box-shadow: 0 0 0 3px rgba(59, 130, 246, .1);
        }

        .relation-card .rel-icon {
            opacity: .4;
            flex-shrink: 0;
            display: flex;
            align-items: center;
        }

        .relation-card .rel-body {
            flex: 1;
            min-width: 0;
            overflow: hidden;
        }

        .relation-card .rel-join {
            display: flex;
            flex-direction: column;
            gap: 2px;
            font-family: var(--mono);
            font-size: 11.5px;
        }

        .relation-card .rel-join>div {
            word-break: break-all;
        }

        .rel-table {
            font-weight: 700;
            color: var(--text);
        }

        .rel-field {
            color: #7c3aed;
        }

        .rel-arrow {
            color: var(--text-muted);
            font-size: 18px;
            line-height: 1;
        }

        .relation-card .rel-desc {
            font-size: 10px;
            color: var(--text-muted);
            margin-top: 4px;
        }

        /* -- Accessibilité -- */
        .sr-only {
            position: absolute;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            white-space: nowrap;
            border: 0;
        }

        ul.table-list {
            list-style: none;
            padding: 0;
            margin: 0;
        }
    </style>
</head>

<body>

    <a class="skip-link" href="#main">Aller au contenu principal</a>

    <header class="topbar" role="banner">
        <p class="topbar-logo" aria-label="Schéma de la base de données PMB version 8.0.1">Schéma de la base de données PMB <span aria-hidden="true">v8.0.1</span></p>
        <input type="search" class="si" id="search" placeholder="Rechercher une table…" autocomplete="off" aria-label="Rechercher une table">
        <div class="export-group" style="margin-left:auto; gap:0;">
            <?php include_once 'schema_selector.inc.php';
            echo schema_graphique_selector('', 'class="export-btn full" style="cursor:pointer"', '⚙ Schéma graphique…', 'schemas/'); ?>
            <?php include_once 'export_selector.inc.php';
            echo export_selector('class="export-btn full" style="cursor:pointer;margin-left:12px"'); ?>
        </div>
        <p class="topbar-count" style="margin-left:16px" aria-live="polite"><?= count($t_parcours) ?> tables</p>
    </header>

    <div class="layout">

        <!-- Sidebar -->
        <aside class="sidebar" aria-label="Navigation des tables">
            <ul class="table-list" id="tableList" role="list" aria-label="Liste des tables">
                <?php foreach ($t_parcours as $id => $name): ?>
                    <li role="none">
                        <button class="table-item" data-id="<?= $id ?>" data-name="<?= strtolower(htmlspecialchars($name)) ?>"
                            onclick="showTable('<?= $id ?>')"
                            aria-current="false">
                            <span class="icon" aria-hidden="true">▤</span>
                            <?= htmlspecialchars($name) ?>
                        </button>
                    </li>
                <?php endforeach; ?>
            </ul>
        </aside>

        <!-- Main content -->
        <main class="main" id="main" aria-label="Détail de la table sélectionnée" aria-live="polite">

            <div class="welcome" id="welcome">
                <p class="big-icon" aria-hidden="true">🗄</p>
                <p>Sélectionnez une table dans le panneau de gauche.</p>
            </div>


            <?php foreach ($t_table as $id => $table):
                $attrs   = $table['ATTRS'] ?? [];
                $liens   = $table['LIENS'] ?? [];
                $pkids   = array_filter(explode(',', $table['KEY']));
                $nbFields = count($attrs);
                $nbRel   = count($liens);
            ?>
                <div class="panel" id="panel-<?= $id ?>">

                    <!-- Header -->
                    <div class="panel-header">
                        <h2><?= htmlspecialchars($table['NAME']) ?></h2>
                        <?php if ($table['DESC']): ?>
                            <div class="desc"><?= htmlspecialchars($table['DESC']) ?></div>
                        <?php endif; ?>
                        <div class="meta">
                            <span class="badge gray"><?= $nbFields ?> champ<?= $nbFields > 1 ? 's' : '' ?></span>
                            <?php if ($nbRel): ?>
                                <span class="badge"><?= $nbRel ?> relation<?= $nbRel > 1 ? 's' : '' ?></span>
                            <?php endif; ?>
                            <?php if ($pkids): ?>
                                <span class="badge amber">PK : <?= implode(', ', array_map(fn($k) => $attrs[$k]['NAME'] ?? $k, $pkids)) ?></span>
                            <?php endif; ?>
                        </div>
                    </div>


                    <!-- Champs -->
                    <?php if ($attrs): ?>
                        <h3 class="section-title">Champs</h3>
                        <table class="fields-table">
                            <caption class="sr-only">Champs de la table <?= htmlspecialchars($table['NAME']) ?></caption>
                            <thead>
                                <tr>
                                    <th scope="col">Champ</th>
                                    <th scope="col">Type</th>
                                    <th scope="col">Description</th>
                                    <th scope="col">Clé</th>
                                    <th scope="col">Référence</th>
                                    <th scope="col">Défaut</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php foreach ($attrs as $fid => $field): ?>
                                    <?php
                                    $keyLabel = '';
                                    if ($field['KEY'] === "Cl&eacute; primaire") $keyLabel = '<span class="key-badge key-pk">PK</span>';
                                    elseif ($field['KEY'] === "Cl&eacute; &eacute;trang&egrave;re") $keyLabel = '<span class="key-badge key-fk">FK</span>';
                                    elseif ($field['KEY']) $keyLabel = '<span class="key-badge key-pk">PK</span><span class="key-badge key-fk">FK</span>';
                                    $refId  = $field['REF'] ?? '';
                                    $refName = $refId && isset($t_table[$refId]) ? $t_table[$refId]['NAME'] : '';
                                    ?>
                                    <tr>
                                        <td class="field-name"><?= htmlspecialchars($field['NAME']) ?></td>
                                        <td class="field-type"><?= htmlspecialchars($field['TYPE']) ?></td>
                                        <td class="field-desc"><?= htmlspecialchars($field['DESC'] ?? '') ?></td>
                                        <td><?= $keyLabel ?></td>
                                        <td><?php if ($refName): ?><button class="key-badge key-fk" style="cursor:pointer;border:none;font:inherit;font-size:10px;font-weight:700;" onclick="showTable('<?= $refId ?>')" aria-label="Voir la table <?= htmlspecialchars($refName) ?>"><?= htmlspecialchars($refName) ?></button><?php endif; ?></td>
                                        <td class="field-default"><?= htmlspecialchars($field['DEFVAL'] ?? '') ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            </tbody>
                        </table>
                    <?php endif; ?>

                    <!-- Relations -->
                    <?php if ($liens): ?>
                        <h3 class="section-title">Tables liées</h3>
                        <div class="relations-grid">
                            <?php foreach ($liens as $lienId):
                                $rel = $t_relation[$lienId] ?? null;
                                if (!$rel) continue;
                                $isParent  = ($rel['T_PERE'] == $id);
                                $otherId   = $isParent ? $rel['T_FILS']  : $rel['T_PERE'];
                                $otherName = $t_table[$otherId]['NAME'] ?? '';
                                // Champs de la jointure
                                $curFieldId   = $isParent ? $rel['F_PERE']  : $rel['F_FILS'];
                                $otherFieldId = $isParent ? $rel['F_FILS']  : $rel['F_PERE'];
                                $curFieldName   = $t_table[$id]['ATTRS'][$curFieldId]['NAME']       ?? $curFieldId;
                                $otherFieldName = $t_table[$otherId]['ATTRS'][$otherFieldId]['NAME'] ?? $otherFieldId;
                            ?>
                                <button class="relation-card" onclick="showTable('<?= $otherId ?>')"
                                    aria-label="Naviguer vers la table <?= htmlspecialchars($otherName) ?>">
                                    <span class="rel-icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                            <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
                                            <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
                                        </svg></span>
                                    <div class="rel-body">
                                        <div class="rel-join">
                                            <div><span class="rel-field"><?= htmlspecialchars($curFieldName) ?></span> <span class="rel-arrow">→</span></div>
                                            <div><span class="rel-table"><?= htmlspecialchars($otherName) ?></span><span class="rel-field">.<?= htmlspecialchars($otherFieldName) ?></span></div>
                                        </div>
                                        <?php if ($rel['DESC']): ?><div class="rel-desc"><?= htmlspecialchars($rel['DESC']) ?></div><?php endif; ?>
                                    </div>
                                </button>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>

                </div><!-- /panel -->
            <?php endforeach; ?>

        </main>
    </div>

    <script>
        // -- Données PHP -> JS --
        const DB = <?php
                    $export = [];
                    foreach ($t_table as $id => $table) {
                        $fields = [];
                        foreach (($table['ATTRS'] ?? []) as $fid => $f) {
                            $fields[] = [
                                'id'      => $fid,
                                'name'    => $f['NAME'],
                                'type'    => $f['TYPE'],
                                'desc'    => $f['DESC'] ?? '',
                                'key'     => html_entity_decode($f['KEY'] ?? ''),
                                'defval'  => $f['DEFVAL'] ?? '',
                                'ref'     => $f['REF'] ?? '',
                            ];
                        }
                        $liens = [];
                        foreach (($table['LIENS'] ?? []) as $lid) {
                            $rel = $t_relation[$lid] ?? null;
                            if (!$rel) continue;
                            $isParent = ($rel['T_PERE'] == $id);
                            $otherId  = $isParent ? $rel['T_FILS'] : $rel['T_PERE'];
                            $curFid   = $isParent ? $rel['F_PERE'] : $rel['F_FILS'];
                            $othFid   = $isParent ? $rel['F_FILS'] : $rel['F_PERE'];
                            $liens[] = [
                                'otherId'       => $otherId,
                                'otherName'     => $t_table[$otherId]['NAME'] ?? '',
                                'curField'      => $t_table[$id]['ATTRS'][$curFid]['NAME'] ?? $curFid,
                                'otherField'    => $t_table[$otherId]['ATTRS'][$othFid]['NAME'] ?? $othFid,
                                'isParent'      => $isParent,
                                'desc'          => $rel['DESC'] ?? '',
                            ];
                        }
                        $export[$id] = [
                            'id'     => $id,
                            'name'   => $table['NAME'],
                            'desc'   => $table['DESC'] ?? '',
                            'fields' => $fields,
                            'liens'  => $liens,
                        ];
                    }
                    echo json_encode($export, JSON_UNESCAPED_UNICODE);
                    ?>;


        // -- Navigation --
        const items = document.querySelectorAll('.table-item');
        const panels = document.querySelectorAll('.panel');
        const welcome = document.getElementById('welcome');
        const main = document.getElementById('main');
        const search = document.getElementById('search');
        let currentId = null;

        function showTable(id) {
            currentId = id;
            items.forEach(el => {
                const isActive = el.dataset.id === id;
                el.classList.toggle('active', isActive);
                el.setAttribute('aria-current', isActive ? 'true' : 'false');
            });
            panels.forEach(el => el.classList.toggle('active', el.id === 'panel-' + id));
            welcome.style.display = 'none';
            const active = document.querySelector('.table-item.active');
            if (active) active.scrollIntoView({
                block: 'nearest'
            });
            main.scrollTop = 0;
            // Déplacer le focus sur le titre de la table pour les AT
            const heading = document.querySelector('#panel-' + id + ' h2');
            if (heading) {
                heading.setAttribute('tabindex', '-1');
                heading.focus();
            }
            history.replaceState(null, '', '#' + id);
        }

        // Recherche
        search.addEventListener('input', () => {
            const q = search.value.toLowerCase().trim();
            items.forEach(el => {
                el.classList.toggle('hidden', q !== '' && !el.dataset.name.includes(q));
            });
        });

        // Hash au chargement
        const hash = location.hash.replace('#', '');
        if (hash) showTable(hash);

        // -- Export --
        function download(content, filename, mime) {
            const blob = new Blob([content], {
                type: mime + ';charset=utf-8'
            });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            setTimeout(() => URL.revokeObjectURL(url), 100);
        }


        function toText(t) {
            // Calcul des largeurs max par colonne
            const wName = Math.max(5, ...t.fields.map(f => (f.name || '').length));
            const wType = Math.max(4, ...t.fields.map(f => (f.type || '').length));
            const wKey = Math.max(3, ...t.fields.map(f => (f.key || '').length));
            const wDefval = Math.max(6, ...t.fields.map(f => (f.defval || '').length));
            const wDesc = Math.max(11, ...t.fields.map(f => (f.desc || '').length));
            const total = wName + 1 + wType + 1 + wKey + 1 + wDefval + 1 + wDesc;
            const hr = '-'.repeat(total);

            let s = `TABLE : ${t.name}\n`;
            if (t.desc) s += `Description : ${t.desc}\n`;
            s += `${hr}\n`;
            s += `${'Champ'.padEnd(wName)} ${'Type'.padEnd(wType)} ${'Cle'.padEnd(wKey)} ${'Defaut'.padEnd(wDefval)} Description\n`;
            s += `${hr}\n`;
            for (const f of t.fields) {
                const desc = f.desc ? `  ${f.desc}` : '';
                s += `${(f.name||'').padEnd(wName)} ${(f.type||'').padEnd(wType)} ${(f.key||'').padEnd(wKey)} ${(f.defval||'').padEnd(wDefval)}${desc}\n`;
            }
            if (t.liens && t.liens.length) {
                s += `\nRELATIONS\n${hr}\n`;
                for (const l of t.liens) {
                    s += `${t.name}.${l.curField} -> ${l.otherName}.${l.otherField}\n`;
                }
            }
            return s;
        }

        // -- Export schéma complet --
        function exportAll(fmt) {
            if (fmt === 'text') download(allToText(), 'schema_pmb.txt', 'text/plain');
            if (fmt === 'markdown') download(allToMarkdown(), 'schema_pmb.md', 'text/markdown');
        }

        function allToText() {
            const hr = '═'.repeat(70);
            let s = `SCHÉMA BASE DE DONNÉES PMB v8.0.1\n${hr}\n\n`;
            for (const id of Object.keys(DB)) {
                s += toText(DB[id]) + '\n\n';
            }
            return s;
        }

        function toMarkdown(t) {
            const esc = s => (s || '').replace(/\|/g, '\\|').replace(/\n/g, ' ');
            let s = `## ${t.name}\n\n`;
            if (t.desc) s += `_${esc(t.desc)}_\n\n`;
            s += `| Champ | Type | Clé | Défaut | Description |\n`;
            s += `|-------|------|-----|--------|-------------|\n`;
            for (const f of t.fields) {
                s += `| \`${esc(f.name)}\` | \`${esc(f.type)}\` | ${esc(f.key)} | ${esc(f.defval)} | ${esc(f.desc)} |\n`;
            }
            if (t.liens && t.liens.length) {
                s += `\n**Relations**\n\n`;
                for (const l of t.liens) {
                    s += `- \`${t.name}.${l.curField}\` → \`${l.otherName}.${l.otherField}\``;
                    if (l.desc) s += ` — ${esc(l.desc)}`;
                    s += `\n`;
                }
            }
            return s;
        }

        function allToMarkdown() {
            let s = `# Schéma base de données PMB v8.0.1\n\n`;
            for (const id of Object.keys(DB)) {
                s += toMarkdown(DB[id]) + '\n---\n\n';
            }
            return s;
        }

        function allToPlantUML() {
            let s = "@startuml schema_pmb\n";
            s += "' Schéma base de données PMB v8.0.1\n\n";
            s += "hide empty methods\n";
            s += "skinparam linetype ortho\n";
            s += "skinparam entity {\n";
            s += "  BackgroundColor White\n";
            s += "  BorderColor #1e2330\n";
            s += "  HeaderBackgroundColor #1e2330\n";
            s += "  FontColorHeader White\n";
            s += "  FontColor #1a1f2e\n";
            s += "}\n\n";

            // Entités
            for (const id of Object.keys(DB)) {
                const t = DB[id];
                s += `entity "${t.name}" as E${id} {\n`;
                if (t.desc) s += `  ' ${t.desc}\n`;
                const pkFields = t.fields.filter(f => f.key.includes('primaire'));
                const otherFields = t.fields.filter(f => !f.key.includes('primaire'));
                for (const f of pkFields) {
                    s += `  + ${f.name} : ${f.type} <<PK>>\n`;
                }
                if (pkFields.length && otherFields.length) s += '  --\n';
                for (const f of otherFields) {
                    const tag = f.key.includes('trang') ? ' <<FK>>' : '';
                    s += `  ${f.name} : ${f.type}${tag}\n`;
                }
                s += '}\n\n';
            }

            // Relations (dédoublonnées)
            s += "' Relations\n";
            const seen = new Set();
            for (const id of Object.keys(DB)) {
                const t = DB[id];
                for (const l of t.liens) {
                    const key = l.isParent ?
                        `${id}-${l.otherId}-${l.curField}-${l.otherField}` :
                        `${l.otherId}-${id}-${l.otherField}-${l.curField}`;
                    if (seen.has(key)) continue;
                    seen.add(key);
                    const [parentId, childId, pField, cField] = l.isParent ? [id, l.otherId, l.curField, l.otherField] : [l.otherId, id, l.otherField, l.curField];
                    s += `E${parentId} ||--o{ E${childId} : "${pField} → ${cField}"\n`;
                }
            }

            s += '\n@enduml\n';
            return s;
        }

        showTable('notices');
    </script>
</body>

</html>