package sensors

import (
	"math"
	"testing"
)

func TestParse_IntelCPU(t *testing.T) {
	// Typical Intel CPU output from sensors -j
	intelJSON := `{
		"coretemp-isa-0000": {
			"Adapter": "ISA adapter",
			"Package id 0": {
				"temp1_input": 45.000,
				"temp1_max": 100.000,
				"temp1_crit": 100.000,
				"temp1_crit_alarm": 0.000
			},
			"Core 0": {
				"temp2_input": 43.000,
				"temp2_max": 100.000,
				"temp2_crit": 100.000,
				"temp2_crit_alarm": 0.000
			},
			"Core 1": {
				"temp3_input": 44.000,
				"temp3_max": 100.000,
				"temp3_crit": 100.000,
				"temp3_crit_alarm": 0.000
			},
			"Core 2": {
				"temp4_input": 42.000,
				"temp4_max": 100.000,
				"temp4_crit": 100.000,
				"temp4_crit_alarm": 0.000
			},
			"Core 3": {
				"temp5_input": 45.000,
				"temp5_max": 100.000,
				"temp5_crit": 100.000,
				"temp5_crit_alarm": 0.000
			}
		}
	}`

	data, err := Parse(intelJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if data.CPUPackage != 45.0 {
		t.Errorf("CPUPackage = %v, want 45.0", data.CPUPackage)
	}

	if data.CPUMax != 45.0 {
		t.Errorf("CPUMax = %v, want 45.0", data.CPUMax)
	}

	if len(data.Cores) != 4 {
		t.Errorf("len(Cores) = %d, want 4", len(data.Cores))
	}

	if data.Cores["Core 0"] != 43.0 {
		t.Errorf("Core 0 = %v, want 43.0", data.Cores["Core 0"])
	}
}

func TestParse_AMDCPU(t *testing.T) {
	// Typical AMD Ryzen output from sensors -j
	amdJSON := `{
		"k10temp-pci-00c3": {
			"Adapter": "PCI adapter",
			"Tctl": {
				"temp1_input": 52.000
			},
			"Tdie": {
				"temp2_input": 52.000
			},
			"Tccd1": {
				"temp3_input": 48.000
			},
			"Tccd2": {
				"temp4_input": 50.000
			}
		}
	}`

	data, err := Parse(amdJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	// Should use Tdie as package temp
	if data.CPUPackage != 52.0 {
		t.Errorf("CPUPackage = %v, want 52.0", data.CPUPackage)
	}

	if data.CPUMax != 52.0 {
		t.Errorf("CPUMax = %v, want 52.0", data.CPUMax)
	}
}

func TestParse_NVMe(t *testing.T) {
	nvmeJSON := `{
		"nvme-pci-0100": {
			"Adapter": "PCI adapter",
			"Composite": {
				"temp1_input": 38.850,
				"temp1_max": 81.850,
				"temp1_min": -273.150,
				"temp1_crit": 84.850,
				"temp1_alarm": 0.000
			},
			"Sensor 1": {
				"temp2_input": 38.850,
				"temp2_max": 65261.850,
				"temp2_min": -273.150
			}
		},
		"nvme-pci-0200": {
			"Adapter": "PCI adapter",
			"Composite": {
				"temp1_input": 42.000
			}
		}
	}`

	data, err := Parse(nvmeJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if len(data.NVMe) != 2 {
		t.Errorf("len(NVMe) = %d, want 2", len(data.NVMe))
	}

	if data.NVMe["nvme0"] != 38.85 {
		t.Errorf("nvme0 = %v, want 38.85", data.NVMe["nvme0"])
	}

	if data.NVMe["nvme1"] != 42.0 {
		t.Errorf("nvme1 = %v, want 42.0", data.NVMe["nvme1"])
	}
}

func TestParse_GPU(t *testing.T) {
	gpuJSON := `{
		"amdgpu-pci-0300": {
			"Adapter": "PCI adapter",
			"edge": {
				"temp1_input": 55.000
			},
			"junction": {
				"temp2_input": 58.000
			},
			"mem": {
				"temp3_input": 52.000
			}
		}
	}`

	data, err := Parse(gpuJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if len(data.GPU) != 3 {
		t.Errorf("len(GPU) = %d, want 3", len(data.GPU))
	}

	// Check edge temp
	edgeKey := "amdgpu-pci-0300_edge"
	if data.GPU[edgeKey] != 55.0 {
		t.Errorf("GPU[%s] = %v, want 55.0", edgeKey, data.GPU[edgeKey])
	}
}

func TestParse_Combined(t *testing.T) {
	// Combined CPU + NVMe + GPU output
	combinedJSON := `{
		"coretemp-isa-0000": {
			"Package id 0": {"temp1_input": 50.000},
			"Core 0": {"temp2_input": 48.000}
		},
		"nvme-pci-0100": {
			"Composite": {"temp1_input": 40.000}
		},
		"amdgpu-pci-0300": {
			"edge": {"temp1_input": 60.000}
		}
	}`

	data, err := Parse(combinedJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if data.CPUPackage != 50.0 {
		t.Errorf("CPUPackage = %v, want 50.0", data.CPUPackage)
	}

	if len(data.NVMe) != 1 {
		t.Errorf("len(NVMe) = %d, want 1", len(data.NVMe))
	}

	if data.NVMe["nvme0"] != 40.0 {
		t.Errorf("nvme0 = %v, want 40.0", data.NVMe["nvme0"])
	}

	if len(data.GPU) != 1 {
		t.Errorf("len(GPU) = %d, want 1", len(data.GPU))
	}
}

func TestParse_Empty(t *testing.T) {
	_, err := Parse("")
	if err == nil {
		t.Error("Parse() should fail on empty input")
	}

	_, err = Parse("   ")
	if err == nil {
		t.Error("Parse() should fail on whitespace-only input")
	}
}

func TestParse_InvalidJSON(t *testing.T) {
	_, err := Parse("not valid json")
	if err == nil {
		t.Error("Parse() should fail on invalid JSON")
	}

	_, err = Parse("{incomplete")
	if err == nil {
		t.Error("Parse() should fail on incomplete JSON")
	}
}

func TestParse_EmptyObject(t *testing.T) {
	data, err := Parse("{}")
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if data.Available {
		t.Error("Expected Available to be false for empty object")
	}
}

func TestParse_NoRecognizedSensors(t *testing.T) {
	// JSON with unknown sensor types
	unknownJSON := `{
		"unknown-chip-0000": {
			"Adapter": "Unknown adapter",
			"some_sensor": {"temp1_input": 50.000}
		}
	}`

	data, err := Parse(unknownJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if data.Available {
		t.Error("Expected Available to be false for unknown sensors")
	}
}

func TestParse_SuperIOChip(t *testing.T) {
	// NCT6775 SuperIO chip output
	superioJSON := `{
		"nct6775-isa-0290": {
			"Adapter": "ISA adapter",
			"CPUTIN": {
				"temp1_input": 45.000,
				"temp1_max": 80.000,
				"temp1_max_hyst": 75.000
			},
			"SYSTIN": {
				"temp2_input": 35.000
			}
		}
	}`

	data, err := Parse(superioJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if data.CPUPackage != 45.0 {
		t.Errorf("CPUPackage = %v, want 45.0", data.CPUPackage)
	}
}

func TestParse_RaspberryPi(t *testing.T) {
	// Raspberry Pi thermal zone output
	rpiJSON := `{
		"cpu_thermal-virtual-0": {
			"Adapter": "Virtual device",
			"temp1": {
				"temp1_input": 52.000
			}
		}
	}`

	data, err := Parse(rpiJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}
}

func TestIsCPUChip(t *testing.T) {
	tests := []struct {
		chip     string
		expected bool
	}{
		{"coretemp-isa-0000", true},
		{"k10temp-pci-00c3", true},
		{"zenpower-pci-00c3", true},
		{"nct6775-isa-0290", true},
		{"cpu_thermal-virtual-0", true},
		{"acpitz-acpi-0", true},
		{"rp1_adc-isa-0000", true}, // Raspberry Pi RP1 ADC
		{"nvme-pci-0100", false},
		{"amdgpu-pci-0300", false},
		{"unknown-chip", false},
	}

	for _, tc := range tests {
		t.Run(tc.chip, func(t *testing.T) {
			result := isCPUChip(tc.chip)
			if result != tc.expected {
				t.Errorf("isCPUChip(%q) = %v, want %v", tc.chip, result, tc.expected)
			}
		})
	}
}

func TestExtractTempInput(t *testing.T) {
	tests := []struct {
		name     string
		input    map[string]interface{}
		expected float64
		isNaN    bool
	}{
		{
			name:     "float value",
			input:    map[string]interface{}{"temp1_input": 45.5},
			expected: 45.5,
		},
		{
			name:     "int value",
			input:    map[string]interface{}{"temp1_input": 45},
			expected: 45.0,
		},
		{
			name:  "no input field",
			input: map[string]interface{}{"temp1_max": 100.0},
			isNaN: true,
		},
		{
			name:  "empty map",
			input: map[string]interface{}{},
			isNaN: true,
		},
		{
			name:  "wrong suffix",
			input: map[string]interface{}{"temp1_max": 45.0},
			isNaN: true,
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := extractTempInput(tc.input)
			if tc.isNaN {
				if !math.IsNaN(result) {
					t.Errorf("extractTempInput() = %v, want NaN", result)
				}
			} else {
				if result != tc.expected {
					t.Errorf("extractTempInput() = %v, want %v", result, tc.expected)
				}
			}
		})
	}
}

func TestParse_MaxTempCalculation(t *testing.T) {
	// Test that CPUMax is correctly calculated from cores when no package temp
	coresOnlyJSON := `{
		"coretemp-isa-0000": {
			"Core 0": {"temp2_input": 40.000},
			"Core 1": {"temp3_input": 45.000},
			"Core 2": {"temp4_input": 42.000},
			"Core 3": {"temp5_input": 48.000}
		}
	}`

	data, err := Parse(coresOnlyJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	// Should use max core temp as package temp
	if data.CPUPackage != 48.0 {
		t.Errorf("CPUPackage = %v, want 48.0 (max core temp)", data.CPUPackage)
	}

	if data.CPUMax != 48.0 {
		t.Errorf("CPUMax = %v, want 48.0", data.CPUMax)
	}
}

func TestParse_NouveauGPU(t *testing.T) {
	// Nouveau (open-source NVIDIA) driver output
	nouveauJSON := `{
		"nouveau-pci-0100": {
			"Adapter": "PCI adapter",
			"temp1": {
				"temp1_input": 45.000
			}
		}
	}`

	data, err := Parse(nouveauJSON)
	if err != nil {
		t.Fatalf("Parse() error: %v", err)
	}

	if !data.Available {
		t.Error("Expected Available to be true")
	}

	if len(data.GPU) != 1 {
		t.Errorf("len(GPU) = %d, want 1", len(data.GPU))
	}
}
