use crate::error::PanlabelError;
use crate::stats::StatsReport;
const CHART_JS: &str = include_str!("assets/chart.min.js");
pub fn render_html(report: &StatsReport) -> Result<String, PanlabelError> {
let stats_json = serde_json::to_string(report)
.map_err(|source| PanlabelError::ReportJsonWrite { source })?
.replace("</", "<\\/");
let html = format!(
r#"<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>panlabel stats</title>
<style>
:root {{ color-scheme: light dark; }}
body {{ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 1rem auto; max-width: 1200px; padding: 0 1rem 2rem; line-height: 1.5; }}
h1, h2 {{ margin: 0.5rem 0; }}
.grid {{ display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }}
.card {{ border: 1px solid #9993; border-radius: 10px; padding: 0.8rem; background: #fff2; }}
canvas {{ width: 100%; height: 260px; }}
pre {{ overflow-x: auto; background: #0001; border-radius: 8px; padding: 0.8rem; }}
.summary {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0.5rem; }}
.metric {{ padding: 0.5rem; border: 1px solid #9993; border-radius: 8px; }}
.metric .label {{ font-size: 0.8rem; opacity: 0.8; }}
.metric .value {{ font-size: 1.1rem; font-weight: 600; }}
</style>
<script>{chart_js}</script>
</head>
<body>
<h1>panlabel stats</h1>
<p>Self-contained report generated by <code>panlabel stats --output html</code>.</p>
<div class="card">
<h2>Summary</h2>
<div class="summary">
<div class="metric"><div class="label">Images</div><div class="value" id="m-images"></div></div>
<div class="metric"><div class="label">Categories</div><div class="value" id="m-categories"></div></div>
<div class="metric"><div class="label">Annotations</div><div class="value" id="m-annotations"></div></div>
<div class="metric"><div class="label">Annotated images</div><div class="value" id="m-annotated"></div></div>
</div>
</div>
<div class="grid">
<div class="card"><h2>Labels</h2><canvas id="labels-chart"></canvas></div>
<div class="card"><h2>Area buckets</h2><canvas id="areas-chart"></canvas></div>
<div class="card"><h2>Aspect ratios</h2><canvas id="aspect-chart"></canvas></div>
<div class="card"><h2>Image resolutions</h2><canvas id="resolutions-chart"></canvas></div>
</div>
<div class="card">
<h2>Raw JSON</h2>
<pre id="raw-json"></pre>
</div>
<script type="application/json" id="stats-data">{data}</script>
<script>
const data = JSON.parse(document.getElementById("stats-data").textContent);
document.getElementById("m-images").textContent = String(data.summary.images);
document.getElementById("m-categories").textContent = String(data.summary.categories);
document.getElementById("m-annotations").textContent = String(data.summary.annotations);
document.getElementById("m-annotated").textContent = String(data.summary.annotated_images);
document.getElementById("raw-json").textContent = JSON.stringify(data, null, 2);
const labelLabels = (data.labels.entries || []).map(x => x.label);
const labelValues = (data.labels.entries || []).map(x => x.count);
new Chart(document.getElementById("labels-chart"), {{
type: "bar",
data: {{ labels: labelLabels, datasets: [{{ label: "count", data: labelValues }}] }},
options: {{ responsive: true, maintainAspectRatio: false }}
}});
const areaLabels = ["small", "medium", "large", "invalid"];
const areaValues = [
data.area_distribution.small,
data.area_distribution.medium,
data.area_distribution.large,
data.area_distribution.invalid,
];
new Chart(document.getElementById("areas-chart"), {{
type: "bar",
data: {{ labels: areaLabels, datasets: [{{ label: "count", data: areaValues }}] }},
options: {{ responsive: true, maintainAspectRatio: false }}
}});
const aspectLabels = (data.aspect_ratios.buckets || []).map(x => x.name).concat(["invalid"]);
const aspectValues = (data.aspect_ratios.buckets || []).map(x => x.count).concat([data.aspect_ratios.invalid]);
new Chart(document.getElementById("aspect-chart"), {{
type: "bar",
data: {{ labels: aspectLabels, datasets: [{{ label: "count", data: aspectValues }}] }},
options: {{ responsive: true, maintainAspectRatio: false }}
}});
new Chart(document.getElementById("resolutions-chart"), {{
type: "scatter",
data: {{ datasets: [{{
label: "resolutions",
data: [{{ x: data.image_resolutions.mean_w, y: data.image_resolutions.mean_h }}],
}}] }},
options: {{
responsive: true,
maintainAspectRatio: false,
scales: {{ x: {{ title: {{ display: true, text: "width" }} }}, y: {{ title: {{ display: true, text: "height" }} }} }},
}},
}});
</script>
</body>
</html>
"#,
chart_js = CHART_JS,
data = stats_json,
);
Ok(html)
}