package inspur

import (
	"strings"
	"testing"

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

const solSmartdSample = `
[   17.219818] smartd[3321]: Device: /dev/sda [SAT], Micron_5400_MTFDDAK480TGA, S/N:2310400DC7E3, WWN:5-00a075-1400dc7e3, FW:D4CM003, 480 GB
[   17.553024] smartd[3321]: Device: /dev/sdc [SAT], MTFDDAK3T8TGA-1BC1ZABDA, S/N:25134F172DB3, WWN:5-00a075-14f172db3, FW:D4DK403, 3.84 TB
[   17.553331] smartd[3321]: Device: /dev/sde [SAT], Micron_5400_MTFDDAK480TGA, S/N:2310400DC80F, WWN:5-00a075-1400dc80f, FW:D4CM003, 480 GB
[   17.553709] smartd[3321]: Device: /dev/sdh [SAT], MTFDDAK3T8TGA-1BC1ZABDA, S/N:25134F57DAB8, WWN:5-00a075-14f57dab8, FW:D4DK403, 3.84 TB
[   17.886180] smartd[3321]: Device: /dev/sda [SAT], state written to /var/lib/smartmontools/smartd.Micron-2310400DC7E3.ata.state
`

func TestParseSOLSmartdDevices_Dedup(t *testing.T) {
	devices := parseSOLSmartdDevices([]byte(solSmartdSample))
	if len(devices) != 4 {
		t.Fatalf("expected 4 unique devices, got %d: %v", len(devices), devices)
	}
	// order matches first-seen
	if devices[0].Serial != "2310400DC7E3" {
		t.Errorf("first device serial: got %q, want 2310400DC7E3", devices[0].Serial)
	}
	if devices[0].SizeGB != 480 {
		t.Errorf("first device size: got %d, want 480", devices[0].SizeGB)
	}
	if devices[1].SizeGB != 3840 {
		t.Errorf("TB device size: got %d, want 3840", devices[1].SizeGB)
	}
	if devices[1].Firmware != "D4DK403" {
		t.Errorf("firmware: got %q, want D4DK403", devices[1].Firmware)
	}
}

func TestParseSOLSmartdDevices_SkipsNonInfoLines(t *testing.T) {
	content := `
[   17.886177] smartd[3321]: Device: /dev/sda [SAT], state written to /var/lib/smartmontools/smartd.foo.ata.state
[   17.040843] smartd[3321]: Device: /dev/sda [SAT], not found in smartd database 7.3/5319.
[   17.040865] smartd[3321]: Device: /dev/sda [SAT], is SMART capable. Adding to "monitor" list.
`
	devices := parseSOLSmartdDevices([]byte(content))
	if len(devices) != 0 {
		t.Errorf("expected 0 devices, got %d", len(devices))
	}
}

func TestParseSolSizeGB(t *testing.T) {
	cases := []struct {
		value, unit string
		want        int
	}{
		{"480", "GB", 480},
		{"1.92", "TB", 1920},
		{"3.84", "TB", 3840},
		{"1", "TB", 1000},
		{"0", "GB", 0},
	}
	for _, c := range cases {
		got := parseSolSizeGB(c.value, c.unit)
		if got != c.want {
			t.Errorf("parseSolSizeGB(%q, %q) = %d, want %d", c.value, c.unit, got, c.want)
		}
	}
}

func TestSolStorageType(t *testing.T) {
	cases := []struct {
		model string
		want  string
	}{
		{"MTFDDAK3T8TGA-1BC1ZABDA", "SSD"},
		{"Micron_5400_MTFDDAK480TGA", "SSD"},
		{"INTEL SSDSC2KB019TZ", "SSD"},
		{"SEAGATE ST4000NM0115", "HDD"},
	}
	for _, c := range cases {
		got := solStorageType(c.model)
		if got != c.want {
			t.Errorf("solStorageType(%q) = %q, want %q", c.model, got, c.want)
		}
	}
}

func TestEnrichStorageFromSOLSmartd_ModelMatch(t *testing.T) {
	files := []parser.ExtractedFile{
		{
			Path:    "onekeylog/log/sollog/SOLHostCapture.log",
			Content: []byte(solSmartdSample),
		},
	}
	hw := &models.HardwareConfig{
		Storage: []models.Storage{
			{Slot: "BP0:0", Model: "MTFDDAK3T8TGA-1BC1ZABDA", SizeGB: 3576, Present: true},
			{Slot: "BP0:1", Model: "MTFDDAK3T8TGA-1BC1ZABDA", SizeGB: 3576, Present: true},
		},
	}

	enrichStorageFromSOLSmartd(files, hw)

	// The two existing slots must have received serials via model match.
	for _, s := range hw.Storage[:2] {
		if s.SerialNumber == "" {
			t.Errorf("slot %q: expected serial to be assigned via model match", s.Slot)
		}
		if s.SizeGB != 3576 {
			t.Errorf("slot %q: size should be preserved, got %d", s.Slot, s.SizeGB)
		}
	}
	// The two unmatched Micron entries should be added as new storage entries.
	if len(hw.Storage) != 4 {
		t.Errorf("expected 4 total storage entries (2 existing + 2 new Micron), got %d", len(hw.Storage))
	}
}

func TestEnrichStorageFromSOLSmartd_PlaceholderSlots(t *testing.T) {
	files := []parser.ExtractedFile{
		{
			Path:    "onekeylog/log/sollog/SOLHostCapture.log",
			Content: []byte(solSmartdSample),
		},
	}
	hw := &models.HardwareConfig{
		Storage: []models.Storage{
			{Slot: "BP0:0", Present: true},
			{Slot: "BP0:1", Present: true},
		},
	}

	enrichStorageFromSOLSmartd(files, hw)

	for _, s := range hw.Storage {
		if s.SerialNumber == "" {
			t.Errorf("slot %q: expected serial to be assigned", s.Slot)
		}
		if s.Model == "" {
			t.Errorf("slot %q: expected model to be assigned", s.Slot)
		}
	}
}

func TestEnrichStorageFromSOLSmartd_SkipsExistingSerial(t *testing.T) {
	files := []parser.ExtractedFile{
		{
			Path:    "onekeylog/log/sollog/SOLHostCapture.log",
			Content: []byte(solSmartdSample),
		},
	}
	hw := &models.HardwareConfig{
		Storage: []models.Storage{
			{Slot: "BP0:0", SerialNumber: "2310400DC7E3", Present: true},
		},
	}
	before := len(hw.Storage)

	enrichStorageFromSOLSmartd(files, hw)

	// BP0:0 should still have original serial unchanged
	if hw.Storage[0].SerialNumber != "2310400DC7E3" {
		t.Errorf("existing serial was changed: got %q", hw.Storage[0].SerialNumber)
	}
	// Remaining 3 devices should be added as new entries
	if len(hw.Storage) <= before {
		t.Errorf("expected new entries to be added, got %d (same as before)", len(hw.Storage))
	}
}

func TestEnrichStorageFromSOLSmartd_MergesTwoFiles(t *testing.T) {
	// Two SOL files with partial overlap; combined unique serials = 3
	file1 := `[   17.0] smartd[1]: Device: /dev/sda [SAT], ModelA, S/N:SN001, WWN:w, FW:fw1, 480 GB`
	file2 := strings.Join([]string{
		`[   17.0] smartd[2]: Device: /dev/sda [SAT], ModelA, S/N:SN001, WWN:w, FW:fw1, 480 GB`,
		`[   17.1] smartd[2]: Device: /dev/sdb [SAT], ModelB, S/N:SN002, WWN:w, FW:fw2, 480 GB`,
		`[   17.2] smartd[2]: Device: /dev/sdc [SAT], ModelC, S/N:SN003, WWN:w, FW:fw3, 480 GB`,
	}, "\n")

	files := []parser.ExtractedFile{
		{Path: "log/sollog/SOLHostCapture.log", Content: []byte(file1)},
		{Path: "runningdata/var/sollog/SOLHostCapture.log", Content: []byte(file2)},
	}
	hw := &models.HardwareConfig{}

	enrichStorageFromSOLSmartd(files, hw)

	if len(hw.Storage) != 3 {
		t.Fatalf("expected 3 unique storage entries, got %d", len(hw.Storage))
	}
}
