package webui

import (
	"database/sql"
	"path/filepath"
	"testing"
	"time"

	"bee/audit/internal/platform"
	_ "modernc.org/sqlite"
)

func TestMetricsDBLoadSamplesKeepsChronologicalRangeForGPUs(t *testing.T) {
	db, err := openMetricsDB(filepath.Join(t.TempDir(), "metrics.db"))
	if err != nil {
		t.Fatalf("openMetricsDB: %v", err)
	}
	defer db.Close()

	base := time.Unix(1_700_000_000, 0).UTC()
	for i := 0; i < 3; i++ {
		err := db.Write(platform.LiveMetricSample{
			Timestamp:  base.Add(time.Duration(i) * time.Second),
			CPULoadPct: float64(10 + i),
			MemLoadPct: float64(20 + i),
			PowerW:     float64(300 + i),
			GPUs: []platform.GPUMetricRow{
				{GPUIndex: 0, PowerW: float64(100 + i)},
				{GPUIndex: 2, PowerW: float64(200 + i)},
			},
		})
		if err != nil {
			t.Fatalf("Write(%d): %v", i, err)
		}
	}

	all, err := db.LoadAll()
	if err != nil {
		t.Fatalf("LoadAll: %v", err)
	}
	if len(all) != 3 {
		t.Fatalf("LoadAll len=%d want 3", len(all))
	}
	for i, sample := range all {
		if len(sample.GPUs) != 2 {
			t.Fatalf("LoadAll sample %d GPUs=%v want 2 rows", i, sample.GPUs)
		}
		if sample.GPUs[0].GPUIndex != 0 || sample.GPUs[0].PowerW != float64(100+i) {
			t.Fatalf("LoadAll sample %d GPU0=%+v", i, sample.GPUs[0])
		}
		if sample.GPUs[1].GPUIndex != 2 || sample.GPUs[1].PowerW != float64(200+i) {
			t.Fatalf("LoadAll sample %d GPU1=%+v", i, sample.GPUs[1])
		}
	}

	recent, err := db.LoadRecent(2)
	if err != nil {
		t.Fatalf("LoadRecent: %v", err)
	}
	if len(recent) != 2 {
		t.Fatalf("LoadRecent len=%d want 2", len(recent))
	}
	if !recent[0].Timestamp.Before(recent[1].Timestamp) {
		t.Fatalf("LoadRecent timestamps not ascending: %v >= %v", recent[0].Timestamp, recent[1].Timestamp)
	}
	for i, sample := range recent {
		if len(sample.GPUs) != 2 {
			t.Fatalf("LoadRecent sample %d GPUs=%v want 2 rows", i, sample.GPUs)
		}
	}
}

func TestMetricsDBMigratesLegacyGPUSchema(t *testing.T) {
	path := filepath.Join(t.TempDir(), "metrics.db")
	raw, err := sql.Open("sqlite", path)
	if err != nil {
		t.Fatalf("sql.Open: %v", err)
	}
	_, err = raw.Exec(`
CREATE TABLE gpu_metrics (
  ts            INTEGER NOT NULL,
  gpu_index     INTEGER NOT NULL,
  temp_c        REAL,
  usage_pct     REAL,
  mem_usage_pct REAL,
  power_w       REAL,
  PRIMARY KEY (ts, gpu_index)
);
CREATE TABLE sys_metrics (
  ts           INTEGER NOT NULL,
  cpu_load_pct REAL,
  mem_load_pct REAL,
  power_w      REAL,
  PRIMARY KEY (ts)
);
CREATE TABLE fan_metrics (
  ts   INTEGER NOT NULL,
  name TEXT NOT NULL,
  rpm  REAL,
  PRIMARY KEY (ts, name)
);
CREATE TABLE temp_metrics (
  ts      INTEGER NOT NULL,
  name    TEXT NOT NULL,
  grp     TEXT NOT NULL,
  celsius REAL,
  PRIMARY KEY (ts, name)
);
`)
	if err != nil {
		t.Fatalf("create legacy schema: %v", err)
	}
	_ = raw.Close()

	db, err := openMetricsDB(path)
	if err != nil {
		t.Fatalf("openMetricsDB: %v", err)
	}
	defer db.Close()

	now := time.Unix(1_700_000_100, 0).UTC()
	err = db.Write(platform.LiveMetricSample{
		Timestamp: now,
		GPUs: []platform.GPUMetricRow{
			{GPUIndex: 0, ClockMHz: 1410, MemClockMHz: 2600},
		},
	})
	if err != nil {
		t.Fatalf("Write: %v", err)
	}

	samples, err := db.LoadAll()
	if err != nil {
		t.Fatalf("LoadAll: %v", err)
	}
	if len(samples) != 1 || len(samples[0].GPUs) != 1 {
		t.Fatalf("samples=%+v", samples)
	}
	if got := samples[0].GPUs[0].ClockMHz; got != 1410 {
		t.Fatalf("ClockMHz=%v want 1410", got)
	}
	if got := samples[0].GPUs[0].MemClockMHz; got != 2600 {
		t.Fatalf("MemClockMHz=%v want 2600", got)
	}
}

func TestMetricsDBLoadBetweenFiltersWindow(t *testing.T) {
	db, err := openMetricsDB(filepath.Join(t.TempDir(), "metrics.db"))
	if err != nil {
		t.Fatalf("openMetricsDB: %v", err)
	}
	defer db.Close()

	base := time.Unix(1_700_000_000, 0).UTC()
	for i := 0; i < 5; i++ {
		if err := db.Write(platform.LiveMetricSample{
			Timestamp:  base.Add(time.Duration(i) * time.Minute),
			CPULoadPct: float64(i),
		}); err != nil {
			t.Fatalf("Write(%d): %v", i, err)
		}
	}

	got, err := db.LoadBetween(base.Add(1*time.Minute), base.Add(3*time.Minute))
	if err != nil {
		t.Fatalf("LoadBetween: %v", err)
	}
	if len(got) != 3 {
		t.Fatalf("LoadBetween len=%d want 3", len(got))
	}
	if !got[0].Timestamp.Equal(base.Add(1*time.Minute)) || !got[2].Timestamp.Equal(base.Add(3*time.Minute)) {
		t.Fatalf("window=%v..%v", got[0].Timestamp, got[2].Timestamp)
	}
}
