package main

import (
	"encoding/json"
	"net/http"
	"strings"
)

func NewDashboardHandler(m *Metrics, cfg Config, collectorID string) http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		_, _ = w.Write([]byte(dashboardHTML(collectorID)))
	})

	mux.HandleFunc("/api/metrics", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		host, _ := osHostname()
		snap := m.Snapshot(collectorID, host, cfg.Endpoint, 8)
		_ = json.NewEncoder(w).Encode(snap)
	})

	mux.HandleFunc("/api/preview", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		host, _ := osHostname()
		_, _ = w.Write(m.JSONPreviewBatch(collectorID, host, cfg.Endpoint))
	})

	mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		host, _ := osHostname()
		_, _ = w.Write([]byte(m.PromText(collectorID, host, cfg.Endpoint)))
	})

	return mux
}

func dashboardHTML(collectorID string) string {
	// Inline HTML+JS (no external dependencies).
	// NOTE: raw-string literal cannot contain backticks, so JS avoids template literals.
	return `<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Concurrent Log Collector</title>
  <style>
    body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial; margin: 24px; background:#0b1020; color:#e7eaf3; }
    .row { display:flex; gap:16px; flex-wrap:wrap; }
    .card { background:#111a33; border:1px solid #1b2a55; border-radius:14px; padding:16px; min-width: 260px; flex:1; box-shadow: 0 10px 28px rgba(0,0,0,.25); }
    .muted { color:#aab3d6; font-size: 13px; }
    .kpi { font-size: 28px; font-weight: 700; margin-top:8px; }
    .mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; }
    a { color:#9bd1ff; }
    button { background:#1a2f66; color:#e7eaf3; border:1px solid #24428f; border-radius:10px; padding:10px 12px; cursor:pointer; }
    button:hover{ background:#203a7e; }
    .grid2 { display:grid; grid-template-columns: 1fr 1fr; gap: 10px; }
    pre { background:#0d1633; border:1px solid #1b2a55; border-radius:14px; padding:12px; overflow:auto; }
    svg { width:100%; height: 120px; background:#0d1633; border:1px solid #1b2a55; border-radius:14px; }
    .tag { display:inline-block; padding:4px 8px; border-radius:999px; background:#0d1633; border:1px solid #1b2a55; font-size:12px; margin-right:6px; }
  </style>
</head>
<body>
  <div class="row">
    <div class="card">
      <div class="muted">Collector ID</div>
      <div class="mono">` + htmlEscape(collectorID) + `</div>
      <div style="height:10px"></div>
      <div class="muted">Endpoints</div>
      <div class="mono"><a href="/api/metrics" target="_blank">/api/metrics</a> · <a href="/api/preview" target="_blank">/api/preview</a> · <a href="/metrics" target="_blank">/metrics</a></div>
    </div>
    <div class="card">
      <div class="muted">Lines Total</div>
      <div id="linesTotal" class="kpi">0</div>
      <div class="muted">Dropped Lines (backpressure)</div>
      <div id="droppedLines" class="mono">0</div>
    </div>
    <div class="card">
      <div class="muted">Batches</div>
      <div class="grid2">
        <div>
          <div class="muted">Sent</div>
          <div id="bSent" class="kpi">0</div>
        </div>
        <div>
          <div class="muted">Failed</div>
          <div id="bFail" class="kpi">0</div>
        </div>
      </div>
      <div class="muted">Dropped</div>
      <div id="bDrop" class="mono">0</div>
    </div>
  </div>

  <div style="height:16px"></div>

  <div class="row">
    <div class="card" style="flex:2">
      <div class="muted">Levels</div>
      <div id="levels"></div>
      <div style="height:10px"></div>
      <div class="muted">Time Series (last ~20 minutes)</div>
      <svg id="chart" viewBox="0 0 1000 120" preserveAspectRatio="none">
        <path id="pathTotal" d="" fill="none" stroke="#9bd1ff" stroke-width="2"></path>
        <path id="pathErr" d="" fill="none" stroke="#ff9b9b" stroke-width="2"></path>
      </svg>
      <div class="muted">Blue=Total per interval · Red=ERROR per interval</div>
    </div>

    <div class="card" style="flex:1">
      <div class="muted">Top Errors (keywords)</div>
      <div id="topErrors" class="mono"></div>
      <div style="height:12px"></div>
      <button id="btnPreview">Preview payload</button>
      <pre id="preview" class="mono"></pre>
    </div>
  </div>

<script>
const el = (id)=>document.getElementById(id);

function asNum(x){ return (x===undefined||x===null) ? 0 : Number(x); }

function renderTags(map){
  const keys = Object.keys(map||{}).sort();
  return keys.map(function(k){
    return '<span class="tag mono">' + k + ': ' + map[k] + '</span>';
  }).join(' ');
}

function buildPath(values){
  const w = 1000, h = 120, pad=6;
  if(!values || values.length===0) return '';
  const maxV = Math.max(1, ...values);
  const step = (w-2*pad) / Math.max(1, values.length-1);
  let d='';
  for(let i=0;i<values.length;i++){
    const x = pad + i*step;
    const y = (h-pad) - (values[i]/maxV) * (h-2*pad);
    d += (i===0 ? 'M' : ' L') + x.toFixed(2) + ' ' + y.toFixed(2);
  }
  return d;
}

async function tick(){
  const r = await fetch('/api/metrics');
  const s = await r.json();

  const c = s.counters || {};
  el('linesTotal').textContent = asNum(c.lines_total);
  el('droppedLines').textContent = asNum(c.dropped_lines_total);

  el('bSent').textContent = asNum(c.batches_sent_total);
  el('bFail').textContent = asNum(c.batches_failed_total);
  el('bDrop').textContent = asNum(c.batches_dropped_total);

  el('levels').innerHTML = renderTags(s.levels || {});

  const top = s.top_errors || [];
  el('topErrors').innerHTML = top.length ? top.map(function(x){ return x.key + ': ' + x.value; }).join('<br/>') : '(none)';

  const series = s.series || [];
  const total = series.map(function(pt){
    const cc = pt.counts || {};
    let sum=0;
    Object.keys(cc).forEach(function(k){ sum += asNum(cc[k]); });
    return sum;
  });
  const err = series.map(function(pt){ return asNum((pt.counts||{}).ERROR); });

  el('pathTotal').setAttribute('d', buildPath(total));
  el('pathErr').setAttribute('d', buildPath(err));
}

el('btnPreview').addEventListener('click', async function(){
  const r = await fetch('/api/preview');
  el('preview').textContent = await r.text();
});

tick();
setInterval(tick, 1500);
</script>

</body>
</html>`
}

func htmlEscape(s string) string {
	replacer := strings.NewReplacer(
		`&`, "&amp;",
		`<`, "&lt;",
		`>`, "&gt;",
		`"`, "&quot;",
		`'`, "&#39;",
	)
	return replacer.Replace(s)
}
