package latencylog

import (
	"bufio"
	"fmt"
	"html/template"
	"os"
	"path/filepath"
	"strings"

	"omnisocketgo/cmd/internal/protocol"
)

const summaryChartHTMLTemplate = `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Latency Summary Chart</title>
  <style>
    :root {
      color-scheme: light;
      --bg: #f6f7fb;
      --panel: #ffffff;
      --text: #172033;
      --muted: #60708a;
      --border: #d9dfeb;
      --track: #e8edf5;
      --a-proc: #3b82f6;
      --a-queue: #14b8a6;
      --transport: #f59e0b;
      --b-proc: #22c55e;
      --unknown: #94a3b8;
    }

    * { box-sizing: border-box; }
    body {
      margin: 0;
      font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
      background: linear-gradient(180deg, #eef3ff 0%, var(--bg) 220px);
      color: var(--text);
    }
    main {
      max-width: 1120px;
      margin: 0 auto;
      padding: 32px 20px 48px;
    }
    h1 {
      margin: 0 0 8px;
      font-size: 32px;
      line-height: 1.1;
    }
    .intro {
      color: var(--muted);
      margin: 0 0 24px;
      font-size: 15px;
    }
    .stats {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 12px;
      margin-bottom: 24px;
    }
    .stat {
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 14px 16px;
      box-shadow: 0 10px 30px rgba(23, 32, 51, 0.06);
    }
    .stat-label {
      color: var(--muted);
      font-size: 12px;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      margin-bottom: 6px;
    }
    .stat-value {
      font-size: 22px;
      font-weight: 700;
    }
    .legend {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-bottom: 20px;
      color: var(--muted);
      font-size: 13px;
    }
    .legend-item {
      display: inline-flex;
      align-items: center;
      gap: 8px;
    }
    .swatch {
      width: 12px;
      height: 12px;
      border-radius: 999px;
      flex: 0 0 auto;
    }
    .rows {
      display: grid;
      gap: 14px;
    }
    .row {
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 16px;
      padding: 16px;
      box-shadow: 0 12px 30px rgba(23, 32, 51, 0.05);
    }
    .row-head {
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      gap: 12px;
      margin-bottom: 8px;
    }
    .row-title {
      font-size: 18px;
      font-weight: 700;
      margin: 0;
    }
    .row-e2e {
      font-size: 16px;
      font-weight: 700;
      white-space: nowrap;
    }
    .row-meta {
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 12px;
    }
    .bar {
      height: 24px;
      display: flex;
      overflow: hidden;
      background: var(--track);
      border-radius: 999px;
    }
    .segment {
      height: 100%;
    }
    .segment:first-child {
      border-top-left-radius: 999px;
      border-bottom-left-radius: 999px;
    }
    .segment:last-child {
      border-top-right-radius: 999px;
      border-bottom-right-radius: 999px;
    }
    .row-legend {
      display: flex;
      flex-wrap: wrap;
      gap: 10px 14px;
      margin-top: 10px;
      color: var(--muted);
      font-size: 12px;
    }
    .missing {
      margin-top: 10px;
      color: #a16207;
      font-size: 12px;
    }
    .empty {
      color: var(--muted);
      font-style: italic;
      padding: 24px 16px;
      background: var(--panel);
      border: 1px dashed var(--border);
      border-radius: 16px;
      text-align: center;
    }
  </style>
</head>
<body>
  <main>
    <h1>Latency Summary</h1>
    <p class="intro">A simple per-message end-to-end latency chart generated from summarized JSONL records.</p>

    <section class="stats">
      <div class="stat">
        <div class="stat-label">Messages</div>
        <div class="stat-value">{{.TotalMessages}}</div>
      </div>
      <div class="stat">
        <div class="stat-label">With End-To-End</div>
        <div class="stat-value">{{.MessagesWithEndToEnd}}</div>
      </div>
      <div class="stat">
        <div class="stat-label">Average End-To-End</div>
        <div class="stat-value">{{.AverageEndToEnd}}</div>
      </div>
      <div class="stat">
        <div class="stat-label">Max End-To-End</div>
        <div class="stat-value">{{.MaxEndToEnd}}</div>
      </div>
    </section>

    <section class="legend">
      {{range .Legend}}
      <span class="legend-item">
        <span class="swatch" style="background: {{.Color}}"></span>
        <span>{{.Label}}</span>
      </span>
      {{end}}
    </section>

    {{if .Rows}}
    <section class="rows">
      {{range .Rows}}
      <article class="row">
        <div class="row-head">
          <h2 class="row-title">{{.Title}}</h2>
          <div class="row-e2e">{{.EndToEnd}}</div>
        </div>
        <div class="row-meta">{{.Subtitle}}</div>
        <div class="bar">
          {{range .Segments}}
          <div class="segment" style="width: {{printf "%.4f" .WidthPercent}}%; background: {{.Color}}" title="{{.Label}}: {{.Value}}"></div>
          {{end}}
        </div>
        {{if .Segments}}
        <div class="row-legend">
          {{range .Segments}}
          <span class="legend-item">
            <span class="swatch" style="background: {{.Color}}"></span>
            <span>{{.Label}} {{.Value}}</span>
          </span>
          {{end}}
        </div>
        {{end}}
        {{if .MissingTimestamps}}
        <div class="missing">Missing timestamps: {{.MissingTimestamps}}</div>
        {{end}}
      </article>
      {{end}}
    </section>
    {{else}}
    <section class="empty">No summarized messages were available for chart rendering.</section>
    {{end}}
  </main>
</body>
</html>
`

type summaryChartPage struct {
	TotalMessages        int
	MessagesWithEndToEnd int
	AverageEndToEnd      string
	MaxEndToEnd          string
	Legend               []summaryChartLegendItem
	Rows                 []summaryChartRow
}

type summaryChartLegendItem struct {
	Label string
	Color string
}

type summaryChartRow struct {
	Title             string
	Subtitle          string
	EndToEnd          string
	MissingTimestamps string
	Segments          []summaryChartSegment
}

type summaryChartSegment struct {
	Label        string
	Value        string
	Color        string
	WidthPercent float64
}

type summaryChartSegmentMetric struct {
	label string
	value *int64
	color string
}

// WriteSummariesHTMLChart 将整理结果写成一个可直接在浏览器中打开的简单 HTML 图表。
func WriteSummariesHTMLChart(path string, summaries []Summary) error {
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return fmt.Errorf("latencylog: create chart dir for %s: %w", path, err)
	}

	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
	if err != nil {
		return fmt.Errorf("latencylog: open chart file %s: %w", path, err)
	}
	defer file.Close()

	page := buildSummaryChartPage(summaries)
	tmpl, err := template.New("summary-chart").Parse(summaryChartHTMLTemplate)
	if err != nil {
		return fmt.Errorf("latencylog: parse chart template: %w", err)
	}

	writer := bufio.NewWriter(file)
	if err := tmpl.Execute(writer, page); err != nil {
		return fmt.Errorf("latencylog: render chart %s: %w", path, err)
	}
	if err := writer.Flush(); err != nil {
		return fmt.Errorf("latencylog: flush chart %s: %w", path, err)
	}

	return nil
}

func buildSummaryChartPage(summaries []Summary) summaryChartPage {
	page := summaryChartPage{
		TotalMessages: len(summaries),
		Legend: []summaryChartLegendItem{
			{Label: "A processing", Color: "var(--a-proc)"},
			{Label: "A queue", Color: "var(--a-queue)"},
			{Label: "Transport + B queue", Color: "var(--transport)"},
			{Label: "B processing", Color: "var(--b-proc)"},
			{Label: "Unknown / missing", Color: "var(--unknown)"},
		},
		Rows: make([]summaryChartRow, 0, len(summaries)),
	}

	var (
		endToEndValues []int64
		totalEndToEnd  int64
		maxEndToEnd    int64
	)

	for _, summary := range summaries {
		page.Rows = append(page.Rows, buildSummaryChartRow(summary))

		if summary.EndToEndLatencyNS == nil {
			continue
		}
		endToEnd := *summary.EndToEndLatencyNS
		endToEndValues = append(endToEndValues, endToEnd)
		totalEndToEnd += endToEnd
		if endToEnd > maxEndToEnd {
			maxEndToEnd = endToEnd
		}
	}

	page.MessagesWithEndToEnd = len(endToEndValues)
	page.AverageEndToEnd = "n/a"
	page.MaxEndToEnd = "n/a"
	if len(endToEndValues) > 0 {
		page.AverageEndToEnd = formatLatencyNS(totalEndToEnd / int64(len(endToEndValues)))
		page.MaxEndToEnd = formatLatencyNS(maxEndToEnd)
	}

	return page
}

func buildSummaryChartRow(summary Summary) summaryChartRow {
	row := summaryChartRow{
		Title:             buildSummaryChartTitle(summary),
		Subtitle:          buildSummaryChartSubtitle(summary),
		EndToEnd:          "End-to-end: n/a",
		MissingTimestamps: strings.Join(summary.MissingTimestamps, ", "),
	}

	if summary.EndToEndLatencyNS == nil || *summary.EndToEndLatencyNS <= 0 {
		return row
	}

	total := *summary.EndToEndLatencyNS
	row.EndToEnd = fmt.Sprintf("End-to-end: %s", formatLatencyNS(total))

	metrics := []summaryChartSegmentMetric{
		{label: "A processing", value: summary.AProcessingLatencyNS, color: "var(--a-proc)"},
		{label: "A queue", value: summary.AQueueLatencyNS, color: "var(--a-queue)"},
		{label: "Transport + B queue", value: summary.ABTransportPropagationBQueueLatencyNS, color: "var(--transport)"},
		{label: "B processing", value: summary.BProcessingLatencyNS, color: "var(--b-proc)"},
	}

	var knownTotal int64
	for _, metric := range metrics {
		if metric.value == nil || *metric.value <= 0 {
			continue
		}
		knownTotal += *metric.value
	}

	scaleTotal := total
	if knownTotal > scaleTotal {
		scaleTotal = knownTotal
	}
	if scaleTotal <= 0 {
		return row
	}

	for _, metric := range metrics {
		if metric.value == nil || *metric.value <= 0 {
			continue
		}
		row.Segments = append(row.Segments, summaryChartSegment{
			Label:        metric.label,
			Value:        formatLatencyNS(*metric.value),
			Color:        metric.color,
			WidthPercent: float64(*metric.value) * 100 / float64(scaleTotal),
		})
	}

	if remaining := total - knownTotal; remaining > 0 {
		row.Segments = append(row.Segments, summaryChartSegment{
			Label:        "Unknown / missing",
			Value:        formatLatencyNS(remaining),
			Color:        "var(--unknown)",
			WidthPercent: float64(remaining) * 100 / float64(scaleTotal),
		})
	}

	return row
}

func buildSummaryChartTitle(summary Summary) string {
	if summary.MessageType == protocol.MessageTypeFile && summary.FileName != "" {
		return fmt.Sprintf("%s #%d (%s)", summary.MessageType, summary.MessageID, summary.FileName)
	}

	return fmt.Sprintf("%s #%d", summary.MessageType, summary.MessageID)
}

func buildSummaryChartSubtitle(summary Summary) string {
	parts := []string{
		fmt.Sprintf("%s -> %s", summary.From, summary.To),
		fmt.Sprintf("%d bytes", summary.BodySize),
	}

	if summary.MessageType == protocol.MessageTypeFile && summary.FileName != "" {
		parts = append(parts, fmt.Sprintf("file: %s", summary.FileName))
	}

	return strings.Join(parts, " | ")
}

func formatLatencyNS(ns int64) string {
	return fmt.Sprintf("%.3f ms", float64(ns)/1_000_000)
}
