package nvidia

import (
	"testing"
	"time"

	"git.mchus.pro/mchus/logpile/internal/models"
	"git.mchus.pro/mchus/logpile/internal/parser"
)

func TestParseVerboseRunTestStartTimes(t *testing.T) {
	content := []byte(`
2026-01-22 09:11:32,458 - Testing nvswitch
2026-01-22 09:45:36,016 - Testing gpu_fieldiag
`)
	got := parseVerboseRunTestStartTimes(content)

	nvs := got["nvswitch"]
	if nvs.IsZero() {
		t.Fatalf("expected nvswitch timestamp")
	}
	gpu := got["gpu_fieldiag"]
	if gpu.IsZero() {
		t.Fatalf("expected gpu_fieldiag timestamp")
	}
	if nvs.UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
		t.Fatalf("unexpected nvswitch timestamp: %s", nvs.Format(time.RFC3339))
	}
	if gpu.UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
		t.Fatalf("unexpected gpu_fieldiag timestamp: %s", gpu.Format(time.RFC3339))
	}
}

func TestParseRunLogTestStartTimes(t *testing.T) {
	content := []byte(`
Start time               Thu, 22 Jan 2026 07:42:26
Testing gpumem FAILED [ 26:12s ]
Testing gpustress OK [ 7:10s ]
Testing nvswitch OK [ 9:25s ]
`)

	got := parseRunLogTestStartTimes(content)
	if got["gpumem"].UTC().Format(time.RFC3339) != "2026-01-22T04:42:26Z" {
		t.Fatalf("unexpected gpumem start: %s", got["gpumem"].Format(time.RFC3339))
	}
	if got["gpustress"].UTC().Format(time.RFC3339) != "2026-01-22T05:08:38Z" {
		t.Fatalf("unexpected gpustress start: %s", got["gpustress"].Format(time.RFC3339))
	}
	if got["nvswitch"].UTC().Format(time.RFC3339) != "2026-01-22T05:15:48Z" {
		t.Fatalf("unexpected nvswitch start: %s", got["nvswitch"].Format(time.RFC3339))
	}
}

func TestApplyGPUAndNVSwitchCheckTimes(t *testing.T) {
	gpuTs := time.Date(2026, 1, 22, 9, 45, 36, 0, time.UTC)
	nvsTs := time.Date(2026, 1, 22, 9, 11, 32, 0, time.UTC)

	result := &models.AnalysisResult{
		Hardware: &models.HardwareConfig{
			GPUs: []models.GPU{
				{Slot: "GPUSXM5", Status: "FAIL"},
			},
			PCIeDevices: []models.PCIeDevice{
				{Slot: "NVSWITCH0", DeviceClass: "NVSwitch", Status: "PASS"},
				{Slot: "NIC0", DeviceClass: "NetworkController", Status: "PASS"},
			},
		},
	}

	ApplyGPUAndNVSwitchCheckTimes(result, componentCheckTimes{
		GPUBySlot:      map[string]time.Time{"GPUSXM5": gpuTs},
		NVSwitchBySlot: map[string]time.Time{"NVSWITCH0": nvsTs},
	})

	if got := result.Hardware.GPUs[0].StatusCheckedAt; got == nil || !got.Equal(gpuTs) {
		t.Fatalf("expected gpu status_checked_at %s, got %v", gpuTs.Format(time.RFC3339), got)
	}
	if result.Hardware.GPUs[0].StatusAtCollect == nil || !result.Hardware.GPUs[0].StatusAtCollect.At.Equal(gpuTs) {
		t.Fatalf("expected gpu status_at_collection.at %s", gpuTs.Format(time.RFC3339))
	}
	if got := result.Hardware.PCIeDevices[0].StatusCheckedAt; got == nil || !got.Equal(nvsTs) {
		t.Fatalf("expected nvswitch status_checked_at %s, got %v", nvsTs.Format(time.RFC3339), got)
	}
	if result.Hardware.PCIeDevices[0].StatusAtCollect == nil || !result.Hardware.PCIeDevices[0].StatusAtCollect.At.Equal(nvsTs) {
		t.Fatalf("expected nvswitch status_at_collection.at %s", nvsTs.Format(time.RFC3339))
	}
	if result.Hardware.PCIeDevices[1].StatusCheckedAt != nil {
		t.Fatalf("expected non-nvswitch device status_checked_at to stay nil")
	}
}

func TestCollectGPUAndNVSwitchCheckTimes_FromVerboseRun(t *testing.T) {
	files := []parser.ExtractedFile{
		{
			Path: "verbose_run.log",
			Content: []byte(`
2026-01-22 09:11:32,458 - Testing nvswitch
2026-01-22 09:45:36,016 - Testing gpu_fieldiag
`),
		},
	}

	got := CollectGPUAndNVSwitchCheckTimes(files)
	if got.GPUDefault.UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
		t.Fatalf("unexpected GPU check time: %s", got.GPUDefault.Format(time.RFC3339))
	}
	if got.NVSwitchDefault.UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
		t.Fatalf("unexpected NVSwitch check time: %s", got.NVSwitchDefault.Format(time.RFC3339))
	}
}

func TestCollectGPUAndNVSwitchCheckTimes_FromComponentOutputLogs(t *testing.T) {
	files := []parser.ExtractedFile{
		{
			Path: "gpu_fieldiag/SXM5_SN_1653925025497/output.log",
			Content: []byte(`
$ some command
MODS start: Thu Jan 22 09:45:36 2026 
`),
		},
		{
			Path: "nvswitch/output.log",
			Content: []byte(`
$ cmd devname=0000:08:00.0,NVSWITCH3 devname=0000:07:00.0,NVSWITCH2 devname=0000:06:00.0,NVSWITCH1 devname=0000:05:00.0,NVSWITCH0
MODS start: Thu Jan 22 09:11:32 2026 
`),
		},
	}

	got := CollectGPUAndNVSwitchCheckTimes(files)
	if got.GPUBySerial["1653925025497"].UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
		t.Fatalf("unexpected GPU serial check time: %s", got.GPUBySerial["1653925025497"].Format(time.RFC3339))
	}
	if got.GPUBySlot["GPUSXM5"].UTC().Format(time.RFC3339) != "2026-01-22T06:45:36Z" {
		t.Fatalf("unexpected GPU slot check time: %s", got.GPUBySlot["GPUSXM5"].Format(time.RFC3339))
	}
	if got.NVSwitchBySlot["NVSWITCH0"].UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
		t.Fatalf("unexpected NVSwitch0 check time: %s", got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339))
	}
	if got.NVSwitchBySlot["NVSWITCH3"].UTC().Format(time.RFC3339) != "2026-01-22T06:11:32Z" {
		t.Fatalf("unexpected NVSwitch3 check time: %s", got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339))
	}
}
