<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta property="og:title" content="LiB - API Документация" />
        <meta property="og:type" content="website" />
        <meta property="og:description" content="Ваша персональная библиотека книг" />
        <meta property="og:url" content="{{ request.url.scheme }}://{{ domain }}/" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/dagre/0.8.5/dagre.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jsPlumb/2.15.6/js/jsplumb.min.js"></script>
        <title id="pageTitle">Loading...</title>
        <style>
            body {
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                max-width: 1200px;
                margin: 0 auto;
                padding: 20px;
                color: #333;
            }
            h1 {
                color: #2c3e50;
                border-bottom: 2px solid #3498db;
                padding-bottom: 10px;
            }
            ul {
                list-style: none;
                display: flex;
                padding: 0;
                margin: 0;
                gap: 20px;
                flex-wrap: wrap;
            }
            li { margin: 10px 0; }
            a {
                display: inline-block;
                padding: 8px 15px;
                background-color: #3498db;
                color: white;
                text-decoration: none;
                border-radius: 4px;
                transition: background-color 0.3s;
                font-size: 14px;
            }
            a:hover { background-color: #2980b9; }
            p { margin: 5px 0; }
            .status-ok { color: #27ae60; }
            .status-error { color: #e74c3c; }
            .server-time { color: #7f8c8d; font-size: 12px; }
            #erDiagram {
                position: relative;
                width: 100%; height: 700px;
                border: 1px solid #ddd;
                border-radius: 8px;
                margin-top: 30px;
                background: #fcfcfc;
                background-image:
                    linear-gradient(#eee 1px, transparent 1px),
                    linear-gradient(90deg, #eee 1px, transparent 1px);
                background-size: 20px 20px;
                background-position: -1px -1px;
                overflow: hidden;
                cursor: grab;
            }
            #erDiagram:active { cursor: grabbing; }
            .er-table {
                position: absolute;
                width: 200px;
                background: #fff;
                border: 1px solid #bdc3c7;
                border-radius: 5px;
                box-shadow: 0 4px 10px rgba(0,0,0,0.1);
                font-size: 12px;
                z-index: 10;
                display: flex;
                flex-direction: column;
            }
            .er-table-header {
                background: #3498db;
                color: #ecf0f1;
                padding: 8px;
                font-weight: bold;
                text-align: center;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            .er-table-body {
                background: #fff;
                padding: 4px 0;
                border-bottom-left-radius: 4px;
                border-bottom-right-radius: 4px;
            }
            .er-field {
                padding: 4px 10px;
                position: relative;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                cursor: pointer;
            }
            .er-field:hover {
                background-color: #ecf0f1;
                color: #2980b9;
            }
            .relation-label {
                font-size: 10px;
                font-weight: bold;
                background: white;
                padding: 2px 4px;
                border: 1px solid #bdc3c7;
                border-radius: 3px;
                color: #7f8c8d;
                z-index: 20;
            }
            .jtk-connector { z-index: 5; }
            .jtk-endpoint { z-index: 5; }
        </style>
    </head>
    <body>
        <img src="/favicon.ico" />
        <h1 id="mainTitle">Загрузка...</h1>
        <p>Версия: <span id="appVersion">-</span></p>
        <p>Описание: <span id="appDescription">-</span></p>
        <p>Статус: <span id="appStatus">-</span></p>
        <p class="server-time">Время сервера: <span id="serverTime">-</span></p>
        <ul>
            <li><a href="/">Главная</a></li>
            <li><a href="/docs">Swagger UI</a></li>
            <li><a href="/redoc">ReDoc</a></li>
            <li><a href="https://github.com/wowlikon/LiB">Исходный код</a></li>
        </ul>
        <h2>Интерактивная ER диаграмма</h2>
        <div id="erDiagram"></div>
        <script>
        async function fetchInfo() {
            try {
                const response = await fetch('/api/info');
                const data = await response.json();

                document.getElementById('pageTitle').textContent = data.app_info.title;
                document.getElementById('mainTitle').textContent = `Добро пожаловать в ${data.app_info.title} API!`;
                document.getElementById('appVersion').textContent = data.app_info.version;
                document.getElementById('appDescription').textContent = data.app_info.description;

                const statusEl = document.getElementById('appStatus');
                statusEl.textContent = data.status;
                statusEl.className = data.status === 'ok' ? 'status-ok' : 'status-error';

                document.getElementById('serverTime').textContent = new Date(data.server_time).toLocaleString();
            } catch (error) {
                console.error('Ошибка загрузки info:', error);
                document.getElementById('appStatus').textContent = 'Ошибка соединения';
                document.getElementById('appStatus').className = 'status-error';
            }
        }

        async function fetchSchemaAndRender() {
            try {
                const response = await fetch('/api/schema');
                const diagramData = await response.json();
                renderDiagram(diagramData);
            } catch (error) {
                console.error('Ошибка загрузки схемы:', error);
                document.getElementById('erDiagram').innerHTML = '<p style="padding:20px;color:#e74c3c;">Ошибка загрузки схемы</p>';
            }
        }

        function renderDiagram(diagramData) {
            jsPlumb.ready(function () {
                const instance = jsPlumb.getInstance({
                    Container: "erDiagram",
                    Endpoint: "Blank",
                    Connector: ["Flowchart", { stub: 30, gap: 0, cornerRadius: 5, alwaysRespectStubs: true }]
                });

                const container = document.getElementById("erDiagram");
                const tableWidth = 200;

                const g = new dagre.graphlib.Graph();
                g.setGraph({
                    nodesep: 60, ranksep: 80,
                    marginx: 20, marginy: 20,
                    rankdir: 'LR',
                });
                g.setDefaultEdgeLabel(() => ({}));

                const fieldIndexByEntity = {};
                diagramData.entities.forEach(entity => {
                    const idxMap = {};
                    entity.fields.forEach((field, idx) => { idxMap[field.id] = idx; });
                    fieldIndexByEntity[entity.id] = idxMap;
                });

                diagramData.entities.forEach(entity => {
                    const table = document.createElement("div");
                    table.className = "er-table";
                    table.id = "table-" + entity.id;

                    const header = document.createElement("div");
                    header.className = "er-table-header";
                    header.textContent = entity.title || entity.id;

                    const body = document.createElement("div");
                    body.className = "er-table-body";

                    entity.fields.forEach(field => {
                        const row = document.createElement("div");
                        row.className = "er-field";
                        row.id = `field-${entity.id}-${field.id}`;
                        row.style.display = "flex";
                        row.style.alignItems = "center";

                        const labelSpan = document.createElement("span");
                        labelSpan.textContent = field.label || field.id;
                        row.appendChild(labelSpan);

                        if (field.tooltip) {
                            row.title = field.tooltip;

                            const tip = document.createElement("span");
                            tip.textContent = "ⓘ";
                            tip.title = field.tooltip;
                            tip.style.marginLeft = "4px";
                            tip.style.marginRight = "0";
                            tip.style.fontSize = "10px";
                            tip.style.cursor = "help";
                            tip.style.marginLeft = "auto";
                            row.appendChild(tip);
                        }

                        body.appendChild(row);
                    });

                    table.appendChild(header);
                    table.appendChild(body);
                    container.appendChild(table);

                    const estimatedHeight = 20 + (entity.fields.length * 26);
                    g.setNode(entity.id, { width: tableWidth, height: estimatedHeight });
                });

                const layoutEdges = [];
                const m2oGroups = {};

                diagramData.relations.forEach(rel => {
                    const isManyToOne = (rel.fromMultiplicity === 'N' || rel.fromMultiplicity === '*') && (rel.toMultiplicity === '1');

                    if (isManyToOne) {
                        const fi = (fieldIndexByEntity[rel.fromEntity] || {})[rel.fromField] ?? 0;
                        if (!m2oGroups[rel.fromEntity]) m2oGroups[rel.fromEntity] = [];
                        m2oGroups[rel.fromEntity].push({ rel, fieldIndex: fi });
                    } else {
                        layoutEdges.push({ source: rel.fromEntity, target: rel.toEntity });
                    }
                });

                Object.keys(m2oGroups).forEach(fromEntity => {
                    const arr = m2oGroups[fromEntity];
                    arr.sort((a, b) => a.fieldIndex - b.fieldIndex);

                    arr.forEach((item, idx) => {
                        const rel = item.rel;
                        if (idx % 2 === 0) { layoutEdges.push({ source: rel.toEntity, target: rel.fromEntity });
                        } else { layoutEdges.push({ source: rel.fromEntity, target: rel.toEntity }); }
                    });
                });

                layoutEdges.forEach(e => g.setEdge(e.source, e.target));
                dagre.layout(g);

                g.nodes().forEach(function(v) {
                    const node = g.node(v);
                    const el = document.getElementById("table-" + v);
                    el.style.left = (node.x - (tableWidth / 2)) + "px";
                    el.style.top = (node.y - (node.height / 2)) + "px";
                });

                diagramData.relations.forEach(rel => {
                    const overlays = [];

                    if (rel.fromMultiplicity === '1') {
                        overlays.push(["Arrow", {
                            location: 8, width: 14, length: 1, foldback: 1, direction: 1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                        overlays.push(["Arrow", {
                            location: 14, width: 14, length: 1, foldback: 1, direction: 1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                    } else if (rel.fromMultiplicity === 'N' || rel.fromMultiplicity === '*') {
                        overlays.push(["Arrow", {
                            location: 8, width: 14, length: 1, foldback: 1, direction: 1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                        overlays.push(["Arrow", {
                            location: 10, width: 14, length: 10, foldback: 0.1, direction: 1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                    }

                    if (rel.toMultiplicity === '1') {
                        overlays.push(["Arrow", {
                            location: -8, width: 14, length: 1, foldback: 1, direction: -1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                        overlays.push(["Arrow", {
                            location: -14, width: 14, length: 1, foldback: 1, direction: -1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                    } else if (rel.toMultiplicity === 'N' || rel.toMultiplicity === '*') {
                        overlays.push(["Arrow", {
                            location: -8, width: 14, length: 1, foldback: 1, direction: -1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                        overlays.push(["Arrow", {
                            location: -10, width: 14, length: 10, foldback: 0.1, direction: -1,
                            paintStyle: { stroke: "#7f8c8d", strokeWidth: 2, fill: "transparent" }
                        }]);
                    }

                    instance.connect({
                        source: `field-${rel.fromEntity}-${rel.fromField}`,
                        target: `field-${rel.toEntity}-${rel.toField}`,
                        anchor: ["Continuous", { faces: ["left", "right"] }],
                        paintStyle: { stroke: "#7f8c8d", strokeWidth: 2 },
                        hoverPaintStyle: { stroke: "#3498db", strokeWidth: 3 },
                        overlays: overlays
                    });
                });

                const tableIds = diagramData.entities.map(e => "table-" + e.id);
                instance.draggable(tableIds, {containment: "parent", stop: instance.repaintEverything});
            });
        }

        fetchInfo();
        setInterval(fetchInfo, 60000);

        fetchSchemaAndRender();
        </script>
    </body>
</html>
