package inspur

import (
	"strings"
	"testing"

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

func TestParseNetworkAdapterInfo_ResolvesModelFromPCIIDsForRawHexModel(t *testing.T) {
	text := `RESTful Network Adapter info:
{
  "sys_adapters": [
    {
      "id": 1,
      "name": "NIC1",
      "Location": "#CPU0_PCIE4",
      "present": 1,
      "slot": 4,
      "vendor_id": 32902,
      "device_id": 5409,
      "vendor": "",
      "model": "0x1521",
      "fw_ver": "",
      "status": "OK",
      "sn": "",
      "pn": "",
      "port_num": 4,
      "port_type": "Base-T",
      "ports": []
    }
  ]
}
RESTful fan`

	hw := &models.HardwareConfig{}
	parseNetworkAdapterInfo(text, hw)

	if len(hw.NetworkAdapters) != 1 {
		t.Fatalf("expected 1 network adapter, got %d", len(hw.NetworkAdapters))
	}
	got := hw.NetworkAdapters[0]
	if got.Model == "" {
		t.Fatalf("expected NIC model resolved from pci.ids, got empty")
	}
	if !strings.Contains(strings.ToUpper(got.Model), "I350") {
		t.Fatalf("expected I350 in model, got %q", got.Model)
	}
	if got.Vendor == "" {
		t.Fatalf("expected NIC vendor resolved from pci.ids")
	}
}

func TestParseNetworkAdapterInfo_MergesIntoExistingInventory(t *testing.T) {
	text := `RESTful Network Adapter info:
{
  "sys_adapters": [
    {
      "id": 1,
      "name": "NIC1",
      "Location": "#CPU0_PCIE4",
      "present": 1,
      "slot": 4,
      "vendor_id": 32902,
      "device_id": 5409,
      "vendor": "Mellanox",
      "model": "ConnectX-6",
      "fw_ver": "22.1.0",
      "status": "OK",
      "sn": "",
      "pn": "",
      "port_num": 2,
      "port_type": "QSFP",
      "ports": [
        { "id": 1, "mac_addr": "00:11:22:33:44:55" }
      ]
    }
  ]
}
RESTful fan`

	hw := &models.HardwareConfig{
		NetworkAdapters: []models.NetworkAdapter{
			{
				Slot:         "Slot 4",
				BDF:          "0000:17:00.0",
				SerialNumber: "NIC-SN-1",
				Present:      true,
			},
		},
	}
	parseNetworkAdapterInfo(text, hw)

	if len(hw.NetworkAdapters) != 1 {
		t.Fatalf("expected merged single adapter, got %d", len(hw.NetworkAdapters))
	}
	got := hw.NetworkAdapters[0]
	if got.BDF != "0000:17:00.0" {
		t.Fatalf("expected existing BDF to survive merge, got %q", got.BDF)
	}
	if got.Model != "ConnectX-6" {
		t.Fatalf("expected model from component log, got %q", got.Model)
	}
	if got.SerialNumber != "NIC-SN-1" {
		t.Fatalf("expected serial from existing inventory to survive merge, got %q", got.SerialNumber)
	}
	if len(got.MACAddresses) != 1 || got.MACAddresses[0] != "00:11:22:33:44:55" {
		t.Fatalf("expected MAC addresses from component log, got %#v", got.MACAddresses)
	}
}

func TestParseComponentLogSensors_ExtractsFanBackplaneAndPSUSummary(t *testing.T) {
	text := `RESTful PSU info:
{
  "power_supplies": [
    { "id": 0, "present": 1, "status": "OK", "ps_in_power": 123, "ps_out_power": 110, "psu_max_temperature": 41 }
  ],
  "present_power_reading": 999
}
RESTful Network Adapter info:
{ "sys_adapters": [] }
RESTful fan info:
{
  "fans": [
    { "id": 1, "fan_name": "FAN0_F_Speed", "present": "OK", "status": "OK", "status_str": "OK", "speed_rpm": 9200, "speed_percent": 35, "max_speed_rpm": 20000, "fan_model": "6056" }
  ],
  "fans_power": 33
}
RESTful diskbackplane info:
[
  { "port_count": 8, "driver_count": 4, "front": 1, "backplane_index": 0, "present": 1, "cpld_version": "3.1", "temperature": 18 }
]
BMC`

	sensors := ParseComponentLogSensors([]byte(text))
	if len(sensors) == 0 {
		t.Fatalf("expected sensors from component.log, got none")
	}

	has := func(name string) bool {
		for _, s := range sensors {
			if s.Name == name {
				return true
			}
		}
		return false
	}

	if !has("FAN0_F_Speed") {
		t.Fatalf("expected FAN0_F_Speed sensor in parsed output")
	}
	if !has("Backplane0_Temp") {
		t.Fatalf("expected Backplane0_Temp sensor in parsed output")
	}
	if !has("PSU_Present_Power_Reading") {
		t.Fatalf("expected PSU_Present_Power_Reading sensor in parsed output")
	}
}

func TestParseComponentLogEvents_FanCriticalStatus(t *testing.T) {
	text := `RESTful fan info:
{
  "fans": [
    { "id": 7, "fan_name": "FAN3_R_Speed", "present": "OK", "status": "Critical", "status_str": "Critical", "speed_rpm": 0, "speed_percent": 0, "max_speed_rpm": 20000, "fan_model": "6056" }
  ],
  "fans_power": 0
}
RESTful diskbackplane info:
[]
BMC`

	events := ParseComponentLogEvents([]byte(text))
	if len(events) != 1 {
		t.Fatalf("expected 1 fan event, got %d", len(events))
	}
	if events[0].EventType != "Fan Status" {
		t.Fatalf("expected Fan Status event type, got %q", events[0].EventType)
	}
	if events[0].Severity != models.SeverityCritical {
		t.Fatalf("expected critical severity, got %q", events[0].Severity)
	}
}

func TestParseHDDInfo_MergesIntoExistingStorage(t *testing.T) {
	text := `RESTful HDD info:
[
  {
    "id": 1,
    "present": 1,
    "enable": 1,
    "SN": "SER123",
    "model": "Sample SSD",
    "capacity": 1024,
    "manufacture": "ACME",
    "firmware": "1.0.0",
    "locationstring": "OB01",
    "capablespeed": 6
  }
]
RESTful PSU`

	hw := &models.HardwareConfig{
		Storage: []models.Storage{
			{
				Slot: "OB01",
				Type: "SSD",
			},
		},
	}

	parseHDDInfo(text, hw)
	if len(hw.Storage) != 1 {
		t.Fatalf("expected 1 storage item, got %d", len(hw.Storage))
	}
	if hw.Storage[0].SerialNumber != "SER123" {
		t.Fatalf("expected serial from HDD section, got %q", hw.Storage[0].SerialNumber)
	}
	if hw.Storage[0].Model != "Sample SSD" {
		t.Fatalf("expected model from HDD section, got %q", hw.Storage[0].Model)
	}
	if hw.Storage[0].Firmware != "1.0.0" {
		t.Fatalf("expected firmware from HDD section, got %q", hw.Storage[0].Firmware)
	}
}
