"""Render the v5 app shell around a validated, non-executable JSON bundle."""
from __future__ import annotations

import html
from typing import Any

from dashboard_v5.contracts import safe_json

VIEW_LABELS = (
    ("today", "Heute", "Heute"), ("cockpit", "Cockpit", "Trend"), ("explorer", "Explorer", "Daten"),
    ("nutrition", "Ernährung", "Essen"), ("doctor", "Arzt", "Arzt"),
)


def render(bundle: dict[str, Any], *, echarts_prototype: bool = False) -> str:
    payload = safe_json(bundle)
    nav = "".join(
        f"<button type='button' class='nav-item' data-dashboard-view='{key}' role='tab' aria-label='{html.escape(label)}' aria-controls='view-{key}' aria-selected='{'true' if key == 'today' else 'false'}'><span class='nav-label-full'>{html.escape(label)}</span><span class='nav-label-short' aria-hidden='true'>{html.escape(short_label)}</span></button>"
        for key, label, short_label in VIEW_LABELS
    )
    periods = "".join(
        f"<button type='button' data-dashboard-period='{value}' aria-pressed='{'true' if value == '30' else 'false'}'>{label}</button>"
        for value, label in (("7", "7 T"), ("30", "30 T"), ("90", "90 T"), ("180", "180 T"), ("all", "Alle"))
    )
    symptom_fields = "".join(
        f"<label><span>{label}</span><select name='{key}' required><option value=''>–</option>"
        + "".join(f"<option value='{score}'>{score}</option>" for score in range(4))
        + "</select></label>"
        for key, label in (
            ("aphthen", "Aphthen"), ("gi", "GI/Darm"), ("fatigue", "Fatigue"),
            ("skin", "Haut"), ("eyes", "Augen"), ("joints", "Gelenke"), ("vascular", "Vaskulär"),
        )
    )
    prototype_controls = "".join(
        f"<div class='prototype-series-control'><button type='button' data-prototype-series-toggle data-series='Synthetische Reihe {index}' aria-pressed='true'>Reihe {index}</button><button type='button' data-prototype-series-solo data-series='Synthetische Reihe {index}' aria-label='Nur synthetische Reihe {index} anzeigen'>solo</button></div>"
        for index in range(1, 9)
    )
    prototype_markup = f"""
<article class='v5-card echarts-prototype' data-echarts-prototype data-prototype-ready='false' data-pan-active='false' data-brush-active='false'>
  <div class='panel-heading'><div><span class='v5-badge'>Sprint 6A · nur synthetisch</span><h3>Apache-ECharts-Interaktionsprototyp</h3></div><p>365 Tage, acht deterministische Reihen; keine produktiven Gesundheitsdaten.</p></div>
  <div class='prototype-toolbar' role='group' aria-label='Synthetische Chartinteraktionen'><button type='button' data-prototype-action='zoom-90'>Letzte 90 Tage</button><button type='button' data-prototype-action='zoom-all'>Gesamtzeitraum</button><button type='button' data-prototype-action='pan'>Drag/Pan</button><button type='button' data-prototype-action='brush'>Bereich auswählen</button><button type='button' data-prototype-action='clear-brush'>Auswahl löschen</button><button type='button' data-prototype-action='all'>Reihen zurücksetzen</button></div>
  <div class='prototype-series-controls' aria-label='Synthetische Reihen ein- oder ausblenden'>{prototype_controls}</div>
  <p class='prototype-legend-note'><span class='reference-swatch' aria-hidden='true'></span>Synthetisches Referenzband: 40–60. <span class='event-swatch' aria-hidden='true'>◆</span>E1, E2 und E3 markieren synthetische Ereignisse. Muster, Text und Form ergänzen Farbe.</p>
  <div class='prototype-chart' data-prototype-chart role='img' aria-label='Synthetischer Verlauf mit acht Reihen über 365 Tage'></div>
  <p class='v5-meta'>Ausgewählter Tag: <time data-prototype-selected-day datetime='2025-07-14'>noch keiner</time></p><p class='sr-summary' data-prototype-status aria-live='polite'></p>
  <details class='prototype-table-wrap'><summary>Zugängliche synthetische Datentabelle</summary><div class='table-scroll'><table><caption>365 synthetische Tage und acht synthetische Reihen; Datumsschaltflächen lösen denselben dokumentierten ISO-Callback wie Chartpunkte aus.</caption><thead><tr><th>Datum</th>{''.join(f'<th>Reihe {index}</th>' for index in range(1, 9))}</tr></thead><tbody></tbody></table></div></details>
</article>
""" if echarts_prototype else ""
    prototype_scripts = (
        "<script src='/health-assets/echarts-6.1.0.min.js'></script>"
        "<script src='/health-assets/dashboard-v5-echarts-prototype.js'></script>"
        if echarts_prototype else ""
    )
    return f"""<!doctype html>
<html lang='de'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'>
<title>JARVIS Health Dashboard v5</title>
<link rel='icon' href='data:,'>
<link rel='stylesheet' href='/health-assets/dashboard-v5.css'>
</head><body><div data-dashboard-v5>
<a class='skip-link' href='#main'>Zum Inhalt</a>
<header class='app-header v5-header'><div><span class='eyebrow'>JARVIS HealthManager</span><h1>Gesundheit</h1></div>
<div class='header-actions'><span id='active-period-label'>Letzte 30 Tage</span><span id='header-data-status' class='v5-badge'>Datenstand wird geprüft</span><button type='button' id='privacy-toggle' aria-pressed='false'>Privacy</button><button type='button' id='more-toggle' aria-controls='view-more' aria-expanded='false'>Mehr</button></div></header>
<div class='global-controls' aria-label='Globaler Zeitraum'>{periods}</div>
<nav class='bottom-nav' data-dashboard-nav role='tablist' aria-label='Hauptnavigation'>{nav}</nav>
<main id='main' tabindex='-1'>
<section id='view-today' class='app-view' data-dashboard-panel='today' aria-labelledby='title-today'><div class='view-heading'><div><span class='eyebrow'>Tagesübersicht</span><h2 id='title-today'>Heute</h2></div><p class='context-note'>Das Wichtigste aus den zuletzt dokumentierten Daten. Fehlende Werte bleiben unbekannt.</p></div>
<div class='today-grid'><article class='hero-card today-snapshot-card'><h3>Momentaufnahme</h3><div id='today-snapshot'></div></article><article><h3>Aufgaben</h3><div id='today-tasks'></div></article><article><h3>Medikation</h3><div id='today-medication'></div></article></div>
<p id='capture-status' class='capture-status v5-meta' role='status' aria-live='polite' data-queued='__CAPTURE_QUEUED__' hidden></p>
<button type='button' class='primary-action mobile-capture-action' id='open-checkin' aria-describedby='capture-helper'><span aria-hidden='true'>＋</span> Beschwerden erfassen</button>
<p id='capture-helper' class='v5-meta'>Alle sieben Werte sind für einen vollständigen Tag erforderlich. Fehlende Angaben werden nicht als 0 gespeichert.</p>
<dialog id='checkin-dialog' aria-labelledby='checkin-title'><form method='post' action='/health-actions/symptom-checkin'><input type='hidden' name='csrf_token' value='__CSRF_TOKEN__'><input type='hidden' name='return_to' value='v5'><input type='hidden' name='date' value='__CAPTURE_DATE__'><h2 id='checkin-title'>Beschwerden erfassen</h2><p class='v5-meta'>Erfassung für <time id='capture-date' datetime='__CAPTURE_DATE__'>__CAPTURE_DATE_LABEL__</time></p><div class='score-grid'>{symptom_fields}</div><label>Notiz, optional<textarea name='notes' maxlength='300'></textarea></label><div class='dialog-actions'><button type='button' id='close-checkin'>Abbrechen</button><button type='submit'>Sicher vormerken</button></div></form></dialog>
</section>
<section id='view-cockpit' class='app-view' data-dashboard-panel='cockpit' aria-labelledby='title-cockpit' hidden><div class='view-heading'><div><span class='eyebrow'>Persönliche Trends</span><h2 id='title-cockpit'>Cockpit</h2></div><p class='context-note'>Dokumentierte Werte im persönlichen Verlauf; keine generische medizinische Bewertung.</p></div><div id='metric-cards' class='metric-grid'></div><article class='chart-panel cockpit-chart-panel'><div class='panel-heading'><div><span class='v5-badge'>Verlauf</span><h3 id='cockpit-chart-title'>Gesundheitsverlauf</h3></div><p id='cockpit-chart-context' class='v5-meta'></p></div><canvas id='cockpit-chart' role='img' aria-label='Gesundheitsverlauf'></canvas><p id='cockpit-chart-empty' class='v5-empty' hidden>Keine dokumentierten Beobachtungen im gewählten Zeitraum.</p><div id='cockpit-chart-summary' class='sr-summary'></div></article><div id='event-track' class='event-track' aria-label='Ereignisspur'></div></section>
<section id='view-explorer' class='app-view' data-explorer-shell='advanced' data-dashboard-panel='explorer' aria-labelledby='title-explorer' hidden><div class='explorer-heading'><div><span class='eyebrow'>Advanced Explorer</span><h2 id='title-explorer'>Explorer</h2></div><p class='context-note'>Kontrollierte Verlaufsansicht. Explorative Darstellung, keine Kausalitäts-, Diagnose- oder Therapieaussage.</p></div>
<div class='explorer-controls v5-card' aria-label='Explorer konfigurieren'>
<fieldset class='mode-selector'><legend>Darstellung</legend><button type='button' data-explorer-mode='raw' aria-pressed='true'>Rohwerte</button><button type='button' data-explorer-mode='baseline' aria-pressed='false'>Persönliche Basislinie</button></fieldset>
<fieldset class='mode-selector'><legend>Auflösung</legend><button type='button' data-explorer-cadence='daily' aria-pressed='true'>Täglich</button><button type='button' data-explorer-cadence='weekly' aria-pressed='false'>Wöchentlich</button></fieldset>
<div class='preset-controls' aria-label='Vorauswahl'><span class='v5-label'>Vorauswahl</span><div id='explorer-presets' class='preset-list'></div><button type='button' data-explorer-preset='clear'>Auswahl leeren</button></div>
<p id='explorer-cadence-status' class='v5-meta'>Tageswerte sind dokumentierte Beobachtungen; fehlende Tage bleiben Lücken.</p>
<div><h3 id='metric-picker-title'>Freigegebene Metriken</h3><p id='metric-picker-help' class='v5-meta'>Rohwerte: höchstens zwei Metriken mit derselben Einheit. Basislinie: höchstens vier Metriken.</p><div id='metric-picker' class='metric-picker' role='group' aria-labelledby='metric-picker-title' aria-describedby='metric-picker-help metric-picker-status'></div><p id='metric-picker-status' class='v5-meta' role='status' aria-live='polite'></p></div>
</div>
{prototype_markup}
<p class='v5-meta' id='explorer-aggregate-scope'>Lag und Phasen zeigen ausschließlich serverseitig validierte Aggregate. Deren Start-/Endzeitraum ist im Bundle nicht ausgewiesen; Zeitraum sowie Tages-/Wochenmodus gelten nur für Verlauf, Ereignisspur und Scatter.</p>
<div class='explorer-panels'>
<article class='chart-panel explorer-panel' data-explorer-panel='trend'><div class='panel-heading'><div><span class='v5-badge'>Verlauf</span><h3 id='explorer-trend-title'>Trend · Täglich</h3></div><p>Zeigt ausschließlich dokumentierte Werte; fehlende Kalendertage bleiben sichtbare Lücken.</p></div><div class='explorer-chart-frame'><canvas id='explorer-chart' role='img' aria-label='Ausgewählte Gesundheitsverläufe'></canvas></div><p id='explorer-chart-empty' class='v5-empty' hidden>Für diese Auswahl sind im Zeitraum keine dokumentierten Beobachtungen verfügbar.</p><div id='explorer-summary' class='sr-summary' aria-live='polite'></div></article>
<div id='explorer-event-track' class='event-track explorer-event-track' data-explorer-panel='events' aria-label='Explorer-Ereignisspur'></div>
<article class='v5-card explorer-panel' data-explorer-panel='lag'><div class='panel-heading'><div><span class='v5-badge'>Explorativ</span><h3>Lag-Heatmap</h3></div><p>Zeitversetzte Beschreibungen aus vorhandenen Explorer-Ergebnissen. Keine Ursache-Wirkung-Aussage.</p></div><div id='explorer-lag-heatmap' data-explorer-hook='lag-heatmap'></div></article>
<article class='v5-card explorer-panel' data-explorer-panel='scatter'><div class='panel-heading'><div><span class='v5-badge'>Explorativ</span><h3>Scatter</h3></div><p>Paarweise Darstellung ist nur möglich, wenn passende Beobachtungen im Bundle vorliegen.</p></div><div id='explorer-scatter' data-explorer-hook='scatter'></div></article>
<article class='v5-card explorer-panel' data-explorer-panel='phase'><div class='panel-heading'><div><span class='v5-badge'>Explorativ</span><h3>Phasen</h3></div><p>Vorhandene Medikation-Phasen werden getrennt beschrieben, nicht medizinisch bewertet.</p></div><div id='explorer-phase' data-explorer-hook='phase'></div></article>
</div><details class='v5-card explorer-results'><summary>Vorhandene Ergebnisdetails</summary><div id='correlation-list'></div></details></section>
<section id='view-nutrition' class='app-view' data-dashboard-panel='nutrition' aria-labelledby='title-nutrition' hidden><h2 id='title-nutrition'>Ernährung</h2><p class='context-note'>SIGHi-Zuordnungen dokumentieren Quellen und Unsicherheit; sie sind keine allgemeine Lebensmittelsicherheitsbewertung.</p><div id='nutrition-summary' class='metric-grid'></div><article class='chart-panel'><canvas id='nutrition-chart' role='img' aria-label='Dokumentierter Histamin-Score'></canvas><p id='nutrition-chart-empty' class='v5-empty' hidden>Keine vollständig klassifizierten Ernährungstage im gewählten Zeitraum.</p></article><h3>Mapping prüfen</h3><div id='mapping-review'></div></section>
<section id='view-doctor' class='app-view' data-dashboard-panel='doctor' aria-labelledby='title-doctor' hidden><h2 id='title-doctor'>Arzt</h2><p class='context-note'>Nur gegen das Original verifizierte, kanonisch freigegebene Laborwerte. Fehlende oder unvollständige Daten bleiben unbekannt. Keine Diagnose, Kausalitätsaussage oder Therapieempfehlung.</p><div id='doctor-medication'></div><h3>Verifizierte Labore</h3><div id='verified-labs'></div><button type='button' id='print-view'>Drucken</button></section>
<section id='view-more' class='app-view' data-dashboard-panel='more' aria-labelledby='title-more' hidden><h2 id='title-more'>Daten & System</h2><div id='data-status'></div><p>Technische Datenqualität und sekundäre Arbeitsansichten sind bewusst von den primären Gesundheitsansichten getrennt.</p></section>
</main>
<script type='application/json' id='health-dashboard-bundle'>{payload}</script>
<script src='/health-assets/chart.umd.min.js'></script><script src='/health-assets/dashboard-v5.js'></script>{prototype_scripts}
<noscript>Dashboard v5 benötigt lokales JavaScript. Dashboard v4 bleibt als Fallback verfügbar.</noscript>
</div></body></html>"""
