package collector

import (
	"os"
	"path/filepath"
	"testing"
)

func TestParseCPUs_dual_socket(t *testing.T) {
	out := mustReadFile(t, "testdata/dmidecode_type4.txt")
	cpus := parseCPUs(out)

	if len(cpus) != 2 {
		t.Fatalf("expected 2 CPUs, got %d", len(cpus))
	}

	cpu0 := cpus[0]
	if cpu0.Socket == nil || *cpu0.Socket != 0 {
		t.Errorf("cpu0 socket: got %v, want 0", cpu0.Socket)
	}
	if cpu0.Model == nil || *cpu0.Model != "Intel(R) Xeon(R) Gold 6530" {
		t.Errorf("cpu0 model: got %v", cpu0.Model)
	}
	if cpu0.Manufacturer == nil || *cpu0.Manufacturer != "Intel" {
		t.Errorf("cpu0 manufacturer: got %v, want Intel", cpu0.Manufacturer)
	}
	if cpu0.Cores == nil || *cpu0.Cores != 32 {
		t.Errorf("cpu0 cores: got %v, want 32", cpu0.Cores)
	}
	if cpu0.Threads == nil || *cpu0.Threads != 64 {
		t.Errorf("cpu0 threads: got %v, want 64", cpu0.Threads)
	}
	if cpu0.MaxFrequencyMHz == nil || *cpu0.MaxFrequencyMHz != 4000 {
		t.Errorf("cpu0 max_frequency_mhz: got %v, want 4000", cpu0.MaxFrequencyMHz)
	}
	if cpu0.FrequencyMHz == nil || *cpu0.FrequencyMHz != 2100 {
		t.Errorf("cpu0 frequency_mhz: got %v, want 2100", cpu0.FrequencyMHz)
	}
	if cpu0.Status == nil || *cpu0.Status != "OK" {
		t.Errorf("cpu0 status: got %v, want OK", cpu0.Status)
	}
	if cpu0.SerialNumber != nil {
		t.Errorf("cpu0 serial should stay nil without source data, got %v", cpu0.SerialNumber)
	}

	cpu1 := cpus[1]
	if cpu1.Socket == nil || *cpu1.Socket != 1 {
		t.Errorf("cpu1 socket: got %v, want 1", cpu1.Socket)
	}
	if cpu1.SerialNumber != nil {
		t.Errorf("cpu1 serial should stay nil without source data, got %v", cpu1.SerialNumber)
	}
}

func TestParseCPUs_unpopulated_skipped(t *testing.T) {
	out := mustReadFile(t, "testdata/dmidecode_type4_disabled.txt")
	cpus := parseCPUs(out)

	if len(cpus) != 1 {
		t.Fatalf("expected 1 CPU (unpopulated skipped), got %d", len(cpus))
	}
	if cpus[0].Socket == nil || *cpus[0].Socket != 0 {
		t.Errorf("expected socket 0, got %v", cpus[0].Socket)
	}
}

func TestCollectCPUsSetsFirmwareFromMicrocode(t *testing.T) {
	tmp := t.TempDir()
	origBase := cpuSysBaseDir
	cpuSysBaseDir = tmp
	t.Cleanup(func() { cpuSysBaseDir = origBase })

	if err := os.MkdirAll(filepath.Join(tmp, "cpu0", "microcode"), 0755); err != nil {
		t.Fatalf("mkdir microcode dir: %v", err)
	}
	if err := os.WriteFile(filepath.Join(tmp, "cpu0", "microcode", "version"), []byte("0x2b000643\n"), 0644); err != nil {
		t.Fatalf("write microcode version: %v", err)
	}

	origRun := execDmidecode
	execDmidecode = func(typeNum string) (string, error) {
		if typeNum != "4" {
			t.Fatalf("unexpected dmidecode type: %s", typeNum)
		}
		return mustReadFile(t, "testdata/dmidecode_type4.txt"), nil
	}
	t.Cleanup(func() { execDmidecode = origRun })

	cpus := collectCPUs()
	if len(cpus) != 2 {
		t.Fatalf("expected 2 CPUs, got %d", len(cpus))
	}
	for i, cpu := range cpus {
		if cpu.Firmware == nil || *cpu.Firmware != "0x2b000643" {
			t.Fatalf("cpu[%d] firmware=%v want microcode", i, cpu.Firmware)
		}
	}
}

func TestParseCPUStatus(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"Populated, Enabled", "OK"},
		{"Populated, Disabled By User", statusWarning},
		{"Populated, Disabled By BIOS", statusWarning},
		{"Unpopulated", statusEmpty},
		{"Not Populated", statusEmpty},
		{"Unknown", statusUnknown},
		{"", statusUnknown},
	}
	for _, tt := range tests {
		got := parseCPUStatus(tt.input)
		if got != tt.want {
			t.Errorf("parseCPUStatus(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestParseSocketIndex(t *testing.T) {
	tests := []struct {
		input string
		want  int
		ok    bool
	}{
		{"CPU0", 0, true},
		{"CPU1", 1, true},
		{"Processor 1", 1, true},
		{"Socket 2", 2, true},
		{"", 0, false},
		{"No digits here", 0, false},
	}
	for _, tt := range tests {
		got, ok := parseSocketIndex(tt.input)
		if ok != tt.ok || got != tt.want {
			t.Errorf("parseSocketIndex(%q) = (%d, %v), want (%d, %v)", tt.input, got, ok, tt.want, tt.ok)
		}
	}
}

func TestCleanManufacturer(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"Intel(R) Corporation", "Intel"},
		{"AMD", "AMD"},
		{"To Be Filled By O.E.M.", ""},
		{"  Intel(R) Corporation  ", "Intel"},
	}
	for _, tt := range tests {
		got := cleanManufacturer(tt.input)
		if got != tt.want {
			t.Errorf("cleanManufacturer(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestParseMHz(t *testing.T) {
	tests := []struct {
		input string
		want  int
	}{
		{"4000 MHz", 4000},
		{"2100 MHz", 2100},
		{"Unknown", 0},
		{"", 0},
		{"N/A", 0},
	}
	for _, tt := range tests {
		got := parseMHz(tt.input)
		if got != tt.want {
			t.Errorf("parseMHz(%q) = %d, want %d", tt.input, got, tt.want)
		}
	}
}
