package webui

import (
	"encoding/json"
	"net/http/httptest"
	"strings"
	"testing"

	"bee/audit/internal/app"
	"bee/audit/internal/platform"
)

func TestHandleAPISATRunDecodesBodyWithoutContentLength(t *testing.T) {
	globalQueue.mu.Lock()
	originalTasks := globalQueue.tasks
	globalQueue.tasks = nil
	globalQueue.mu.Unlock()
	t.Cleanup(func() {
		globalQueue.mu.Lock()
		globalQueue.tasks = originalTasks
		globalQueue.mu.Unlock()
	})

	h := &handler{opts: HandlerOptions{App: &app.App{}}}
	req := httptest.NewRequest("POST", "/api/sat/cpu/run", strings.NewReader(`{"profile":"smoke"}`))
	req.ContentLength = -1
	rec := httptest.NewRecorder()

	h.handleAPISATRun("cpu").ServeHTTP(rec, req)

	if rec.Code != 200 {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	globalQueue.mu.Lock()
	defer globalQueue.mu.Unlock()
	if len(globalQueue.tasks) != 1 {
		t.Fatalf("tasks=%d want 1", len(globalQueue.tasks))
	}
	if got := globalQueue.tasks[0].params.BurnProfile; got != "smoke" {
		t.Fatalf("burn profile=%q want smoke", got)
	}
	if got := globalQueue.tasks[0].Priority; got != taskPriorityValidate {
		t.Fatalf("priority=%d want %d", got, taskPriorityValidate)
	}
}

func TestHandleAPIBenchmarkNvidiaRunQueuesSelectedGPUs(t *testing.T) {
	globalQueue.mu.Lock()
	originalTasks := globalQueue.tasks
	globalQueue.tasks = nil
	globalQueue.mu.Unlock()
	t.Cleanup(func() {
		globalQueue.mu.Lock()
		globalQueue.tasks = originalTasks
		globalQueue.mu.Unlock()
	})
	prevList := apiListNvidiaGPUs
	apiListNvidiaGPUs = func(_ *app.App) ([]platform.NvidiaGPU, error) {
		return []platform.NvidiaGPU{
			{Index: 1, Name: "NVIDIA H100 PCIe"},
			{Index: 3, Name: "NVIDIA H100 PCIe"},
		}, nil
	}
	t.Cleanup(func() { apiListNvidiaGPUs = prevList })

	h := &handler{opts: HandlerOptions{App: &app.App{}}}
	req := httptest.NewRequest("POST", "/api/bee-bench/nvidia/perf/run", strings.NewReader(`{"profile":"standard","gpu_indices":[1,3],"run_nccl":false}`))
	rec := httptest.NewRecorder()

	h.handleAPIBenchmarkNvidiaRun(rec, req)

	if rec.Code != 200 {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	globalQueue.mu.Lock()
	defer globalQueue.mu.Unlock()
	if len(globalQueue.tasks) != 1 {
		t.Fatalf("tasks=%d want 1", len(globalQueue.tasks))
	}
	task := globalQueue.tasks[0]
	if task.Target != "nvidia-bench-perf" {
		t.Fatalf("target=%q want nvidia-bench-perf", task.Target)
	}
	if got := task.params.GPUIndices; len(got) != 2 || got[0] != 1 || got[1] != 3 {
		t.Fatalf("gpu indices=%v want [1 3]", got)
	}
	if task.params.RunNCCL {
		t.Fatal("RunNCCL should reflect explicit false from request")
	}
	if task.Priority != taskPriorityBenchmark {
		t.Fatalf("priority=%d want %d", task.Priority, taskPriorityBenchmark)
	}
}

func TestHandleAPIBenchmarkNvidiaRunSplitsMixedGPUModels(t *testing.T) {
	globalQueue.mu.Lock()
	originalTasks := globalQueue.tasks
	globalQueue.tasks = nil
	globalQueue.mu.Unlock()
	t.Cleanup(func() {
		globalQueue.mu.Lock()
		globalQueue.tasks = originalTasks
		globalQueue.mu.Unlock()
	})
	prevList := apiListNvidiaGPUs
	apiListNvidiaGPUs = func(_ *app.App) ([]platform.NvidiaGPU, error) {
		return []platform.NvidiaGPU{
			{Index: 0, Name: "NVIDIA H100 PCIe"},
			{Index: 1, Name: "NVIDIA H100 PCIe"},
			{Index: 2, Name: "NVIDIA H200 NVL"},
		}, nil
	}
	t.Cleanup(func() { apiListNvidiaGPUs = prevList })

	h := &handler{opts: HandlerOptions{App: &app.App{}}}
	req := httptest.NewRequest("POST", "/api/bee-bench/nvidia/perf/run", strings.NewReader(`{"profile":"standard","gpu_indices":[0,1,2],"run_nccl":false}`))
	rec := httptest.NewRecorder()

	h.handleAPIBenchmarkNvidiaRun(rec, req)

	if rec.Code != 200 {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	var resp taskRunResponse
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if len(resp.TaskIDs) != 2 {
		t.Fatalf("task_ids=%v want 2 items", resp.TaskIDs)
	}
	globalQueue.mu.Lock()
	defer globalQueue.mu.Unlock()
	if len(globalQueue.tasks) != 2 {
		t.Fatalf("tasks=%d want 2", len(globalQueue.tasks))
	}
	if got := globalQueue.tasks[0].params.GPUIndices; len(got) != 2 || got[0] != 0 || got[1] != 1 {
		t.Fatalf("task[0] gpu indices=%v want [0 1]", got)
	}
	if got := globalQueue.tasks[1].params.GPUIndices; len(got) != 1 || got[0] != 2 {
		t.Fatalf("task[1] gpu indices=%v want [2]", got)
	}
	if got := globalQueue.tasks[0].Priority; got != taskPriorityBenchmark {
		t.Fatalf("task[0] priority=%d want %d", got, taskPriorityBenchmark)
	}
	if got := globalQueue.tasks[1].Priority; got != taskPriorityBenchmark {
		t.Fatalf("task[1] priority=%d want %d", got, taskPriorityBenchmark)
	}
}

func TestHandleAPIBenchmarkPowerFitRampQueuesBenchmarkPowerFitTasks(t *testing.T) {
	globalQueue.mu.Lock()
	originalTasks := globalQueue.tasks
	globalQueue.tasks = nil
	globalQueue.mu.Unlock()
	t.Cleanup(func() {
		globalQueue.mu.Lock()
		globalQueue.tasks = originalTasks
		globalQueue.mu.Unlock()
	})
	prevList := apiListNvidiaGPUs
	apiListNvidiaGPUs = func(_ *app.App) ([]platform.NvidiaGPU, error) {
		return []platform.NvidiaGPU{
			{Index: 0, Name: "NVIDIA H100 PCIe"},
			{Index: 1, Name: "NVIDIA H100 PCIe"},
			{Index: 2, Name: "NVIDIA H100 PCIe"},
		}, nil
	}
	t.Cleanup(func() { apiListNvidiaGPUs = prevList })

	h := &handler{opts: HandlerOptions{App: &app.App{}}}
	req := httptest.NewRequest("POST", "/api/bee-bench/nvidia/power/run", strings.NewReader(`{"profile":"standard","gpu_indices":[0,1,2],"ramp_up":true}`))
	rec := httptest.NewRecorder()

	h.handleAPIBenchmarkNvidiaRunKind("nvidia-bench-power").ServeHTTP(rec, req)

	if rec.Code != 200 {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	globalQueue.mu.Lock()
	defer globalQueue.mu.Unlock()
	if len(globalQueue.tasks) != 3 {
		t.Fatalf("tasks=%d want 3", len(globalQueue.tasks))
	}
	for i, task := range globalQueue.tasks {
		if task.Target != "nvidia-bench-power" {
			t.Fatalf("task[%d] target=%q", i, task.Target)
		}
		if task.Priority != taskPriorityBenchmark {
			t.Fatalf("task[%d] priority=%d want %d", i, task.Priority, taskPriorityBenchmark)
		}
	}
}

func TestHandleAPISATRunSplitsMixedNvidiaTaskSet(t *testing.T) {
	globalQueue.mu.Lock()
	originalTasks := globalQueue.tasks
	globalQueue.tasks = nil
	globalQueue.mu.Unlock()
	t.Cleanup(func() {
		globalQueue.mu.Lock()
		globalQueue.tasks = originalTasks
		globalQueue.mu.Unlock()
	})
	prevList := apiListNvidiaGPUs
	apiListNvidiaGPUs = func(_ *app.App) ([]platform.NvidiaGPU, error) {
		return []platform.NvidiaGPU{
			{Index: 0, Name: "NVIDIA H100 PCIe"},
			{Index: 1, Name: "NVIDIA H100 PCIe"},
			{Index: 2, Name: "NVIDIA H200 NVL"},
		}, nil
	}
	t.Cleanup(func() { apiListNvidiaGPUs = prevList })

	h := &handler{opts: HandlerOptions{App: &app.App{}}}
	req := httptest.NewRequest("POST", "/api/sat/nvidia-targeted-power/run", strings.NewReader(`{"profile":"acceptance","gpu_indices":[0,1,2]}`))
	rec := httptest.NewRecorder()

	h.handleAPISATRun("nvidia-targeted-power").ServeHTTP(rec, req)

	if rec.Code != 200 {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	globalQueue.mu.Lock()
	defer globalQueue.mu.Unlock()
	if len(globalQueue.tasks) != 2 {
		t.Fatalf("tasks=%d want 2", len(globalQueue.tasks))
	}
	if got := globalQueue.tasks[0].params.GPUIndices; len(got) != 2 || got[0] != 0 || got[1] != 1 {
		t.Fatalf("task[0] gpu indices=%v want [0 1]", got)
	}
	if got := globalQueue.tasks[1].params.GPUIndices; len(got) != 1 || got[0] != 2 {
		t.Fatalf("task[1] gpu indices=%v want [2]", got)
	}
	if got := globalQueue.tasks[0].Priority; got != taskPriorityValidate {
		t.Fatalf("task[0] priority=%d want %d", got, taskPriorityValidate)
	}
	if got := globalQueue.tasks[1].Priority; got != taskPriorityValidate {
		t.Fatalf("task[1] priority=%d want %d", got, taskPriorityValidate)
	}
}

func TestDefaultTaskPriorityOrder(t *testing.T) {
	got := []int{
		defaultTaskPriority("install-to-ram", taskParams{}),
		defaultTaskPriority("audit", taskParams{}),
		defaultTaskPriority("cpu", taskParams{}),
		defaultTaskPriority("cpu", taskParams{StressMode: true}),
		defaultTaskPriority("nvidia-stress", taskParams{}),
		defaultTaskPriority("nvidia-bench-perf", taskParams{}),
		defaultTaskPriority("nvidia-bench-power", taskParams{}),
	}
	want := []int{
		taskPriorityInstallToRAM,
		taskPriorityAudit,
		taskPriorityValidate,
		taskPriorityValidateStress,
		taskPriorityBurn,
		taskPriorityBenchmark,
		taskPriorityBenchmark,
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("priority[%d]=%d want %d", i, got[i], want[i])
		}
	}
	if !(got[0] > got[1] && got[1] > got[2] && got[2] > got[3] && got[3] > got[4] && got[4] > got[5] && got[5] == got[6]) {
		t.Fatalf("priority order=%v", got)
	}
}

func TestPushFanRingsTracksByNameAndCarriesForwardMissingSamples(t *testing.T) {
	h := &handler{}
	h.pushFanRings([]platform.FanReading{
		{Name: "FAN_A", RPM: 4200},
		{Name: "FAN_B", RPM: 5100},
	})
	h.pushFanRings([]platform.FanReading{
		{Name: "FAN_B", RPM: 5200},
	})

	if len(h.fanNames) != 2 || h.fanNames[0] != "FAN_A" || h.fanNames[1] != "FAN_B" {
		t.Fatalf("fanNames=%v", h.fanNames)
	}
	aVals, _ := h.ringFans[0].snapshot()
	bVals, _ := h.ringFans[1].snapshot()
	if len(aVals) != 2 || len(bVals) != 2 {
		t.Fatalf("fan ring lengths: A=%d B=%d", len(aVals), len(bVals))
	}
	if aVals[1] != 4200 {
		t.Fatalf("FAN_A should carry forward last value, got %v", aVals)
	}
	if bVals[1] != 5200 {
		t.Fatalf("FAN_B should use latest sampled value, got %v", bVals)
	}
}
