//! Self-contained HTML graph viewer assembly (C9: read-only render path).
//!
//! `render_html` walks the blueprint into the deterministic JSON model
//! (`aura_engine::model_to_json`) and inlines it, the vendored Graphviz-WASM, the
//! pan-zoom helper, and the ported viewer JS into one standalone `.html`. The
//! emitted page fetches nothing at view time. No `eval`/build is on this path
//! (C9); the model is hand-rolled JSON (C14, owned by `model_to_json`).

use aura_engine::Composite;

/// The prototype's `<head>` shell + body skeleton (header, stage, tooltip, error
/// pane), verbatim from `docs/design/prototypes/graph-render/index.html` lines
/// 1-38 — minus the two `<script src="./…">` tags, since `render_html` inlines
/// the assets instead.
const SHELL_HEAD: &str = r#"<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>aura graph</title>
<style>
  html, body { margin: 0; height: 100%; background: #16161a; color: #cdd6f4;
    font-family: ui-monospace, monospace; }
  header { padding: 8px 14px; border-bottom: 1px solid #313244; font-size: 13px;
    display: flex; gap: 16px; align-items: baseline; flex-wrap: nowrap;
    white-space: nowrap; overflow: hidden; }
  header b { color: #f5e0dc; } .sub { color: #6c7086; }
  #crumb a { color: #89b4fa; cursor: pointer; text-decoration: none; }
  #crumb a:hover { text-decoration: underline; }
  #crumb .sep { color: #6c7086; }
  #status { color: #6c7086; margin-left: auto; }
  #stage { width: 100%; height: calc(100% - 44px); }
  #stage svg { width: 100%; height: 100%; cursor: default; }
  #stage svg text { cursor: inherit; user-select: none; -webkit-user-select: none; }
  #stage svg a { cursor: inherit; }
  #err { padding: 14px; color: #f38ba8; white-space: pre-wrap; }
  #tip { position: fixed; display: none; pointer-events: none; z-index: 10;
    background: #1e1e2e; border: 1px solid #585b70; border-radius: 6px; padding: 6px 9px;
    font-family: ui-monospace, monospace; font-size: 12px; color: #cdd6f4;
    max-width: 420px; box-shadow: 0 6px 18px rgba(0,0,0,.55); line-height: 1.5; }
  #tip b { color: #f5e0dc; } #tip code { color: #89b4fa; } #tip .dim { color: #7f849c; }
</style>
</head>
<body>
<header>
  <b>aura graph</b>
  <span id="crumb"></span>
  <span class="sub">hover · [+] expand · body drill</span>
</header>
<div id="stage"></div>
<div id="tip"></div>
<pre id="err"></pre>
"#;

const VIZ_JS: &str = include_str!("../assets/viz-standalone.js");
const PANZOOM_JS: &str = include_str!("../assets/svg-pan-zoom.min.js");
const VIEWER_JS: &str = include_str!("../assets/graph-viewer.js");

/// Assemble the self-contained interactive graph viewer page for `root`.
///
/// Order is load-bearing: the Graphviz-WASM and pan-zoom globals (`Viz`,
/// `svgPanZoom`) and the model (`window.AURA_MODEL`) must be defined before the
/// viewer script runs.
pub fn render_html(root: &Composite) -> String {
    let model = aura_engine::model_to_json(root);
    let mut html = String::with_capacity(
        SHELL_HEAD.len() + VIZ_JS.len() + PANZOOM_JS.len() + VIEWER_JS.len() + model.len() + 256,
    );
    html.push_str(SHELL_HEAD);
    html.push_str("<script>");
    html.push_str(VIZ_JS);
    html.push_str("</script>\n<script>");
    html.push_str(PANZOOM_JS);
    html.push_str("</script>\n<script>window.AURA_MODEL = ");
    html.push_str(&model);
    html.push_str(";</script>\n<script>");
    html.push_str(VIEWER_JS);
    html.push_str("</script>\n</body>\n</html>\n");
    html
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::sample_blueprint;

    /// `aura graph`'s page is self-contained (no remote fetch), embeds the
    /// deterministic model as the viewer's data source, carries the Graphviz-WASM
    /// bootstrap, and keeps the C4 four-colour palette. Structural, not pixel:
    /// the DOT/SVG are Graphviz-version-dependent and are exercised by hand
    /// against the prototype (spec Testing strategy).
    #[test]
    fn render_html_is_self_contained_and_embeds_the_model() {
        let html = render_html(&sample_blueprint());
        // a complete HTML document
        assert!(html.starts_with("<!doctype html>"), "not an HTML doc");
        assert!(html.trim_end().ends_with("</html>"), "HTML doc not closed");
        // self-contained: every asset is inlined, no remote <script src>
        assert!(!html.contains("<script src"), "assets must be inlined, found a remote script tag");
        // the deterministic model is injected as the viewer's data source
        assert!(html.contains("window.AURA_MODEL = {\"root\":"), "model JSON not injected");
        // a known node from the sample harness round-trips via the model
        assert!(html.contains("\"type\":\"Exposure\""), "sample model content missing");
        // the vendored Graphviz-WASM is present (its bootstrap global)
        assert!(html.contains("Viz.instance"), "viewer bootstrap (Viz.instance) missing");
        // C4: the four-scalar palette, no fifth colour
        assert!(
            html.contains("i64: \"#f9e2af\"") && html.contains("timestamp: \"#cba6f7\""),
            "C4 four-colour palette missing"
        );
    }
}
