package supermicro

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

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

// CrashDumpData represents the structure of CDump.txt
type CrashDumpData struct {
	CrashData struct {
		METADATA   Metadata       `json:"METADATA"`
		PROCESSORS ProcessorsData `json:"PROCESSORS"`
	} `json:"crash_data"`
}

// ProcessorsData contains processor crash data
type ProcessorsData struct {
	Version string     `json:"_version"`
	CPU0    Processors `json:"cpu0"`
	CPU1    Processors `json:"cpu1"`
}

// Metadata contains crashdump metadata
type Metadata struct {
	CPU0          CPUMetadata `json:"cpu0"`
	CPU1          CPUMetadata `json:"cpu1"`
	BMCFWVer      string      `json:"bmc_fw_ver"`
	BIOSId        string      `json:"bios_id"`
	MEFWVer       string      `json:"me_fw_ver"`
	Timestamp     string      `json:"timestamp"`
	TriggerType   string      `json:"trigger_type"`
	PlatformName  string      `json:"platform_name"`
	CrashdumpVer  string      `json:"crashdump_ver"`
	ResetDetected string      `json:"_reset_detected"`
}

// CPUMetadata contains CPU metadata
type CPUMetadata struct {
	CPUID         string `json:"cpuid"`
	CoreMask      string `json:"core_mask"`
	CHACount      string `json:"cha_count"`
	CoreCount     string `json:"core_count"`
	PPIN          string `json:"ppin"`
	UcodePatchVer string `json:"ucode_patch_ver"`
}

// Processors contains processor crash data
type Processors struct {
	MCA MCAData `json:"MCA"`
}

// MCAData contains Machine Check Architecture data
type MCAData struct {
	Uncore map[string]interface{} `json:"uncore"`
}

// ParseCrashDump parses CDump.txt file
func ParseCrashDump(content []byte, result *models.AnalysisResult) error {
	var data CrashDumpData
	if err := json.Unmarshal(content, &data); err != nil {
		return fmt.Errorf("failed to parse CDump.txt: %w", err)
	}

	// Initialize Hardware.Firmware slice if nil
	if result.Hardware.Firmware == nil {
		result.Hardware.Firmware = make([]models.FirmwareInfo, 0)
	}

	// Parse metadata
	parseMetadata(&data.CrashData.METADATA, result)

	// Parse CPU information
	parseCPUInfo(&data.CrashData.METADATA, result)

	// Parse MCA errors
	parseMCAErrors(&data.CrashData, result)

	return nil
}

// parseMetadata extracts metadata information
func parseMetadata(metadata *Metadata, result *models.AnalysisResult) {
	// Store firmware versions in HardwareConfig.Firmware
	if metadata.BMCFWVer != "" {
		result.Hardware.Firmware = append(result.Hardware.Firmware, models.FirmwareInfo{
			DeviceName: "BMC",
			Version:    metadata.BMCFWVer,
		})
	}

	if metadata.BIOSId != "" {
		result.Hardware.Firmware = append(result.Hardware.Firmware, models.FirmwareInfo{
			DeviceName: "BIOS",
			Version:    metadata.BIOSId,
		})
	}

	if metadata.MEFWVer != "" {
		result.Hardware.Firmware = append(result.Hardware.Firmware, models.FirmwareInfo{
			DeviceName: "ME",
			Version:    metadata.MEFWVer,
		})
	}

	// Create event for crashdump trigger
	timestamp := time.Now()
	if metadata.Timestamp != "" {
		if t, err := time.Parse(time.RFC3339, metadata.Timestamp); err == nil {
			timestamp = t
		}
	}

	triggerType := metadata.TriggerType
	if triggerType == "" {
		triggerType = "Unknown"
	}

	severity := models.SeverityInfo
	if metadata.ResetDetected != "" && metadata.ResetDetected != "NONE" {
		severity = models.SeverityWarning
	}

	result.Events = append(result.Events, models.Event{
		Timestamp:   timestamp,
		Source:      "Crashdump",
		EventType:   "System Crashdump",
		Description: fmt.Sprintf("Crashdump collected (%s)", triggerType),
		Severity:    severity,
		RawData:     fmt.Sprintf("Version: %s, Reset: %s", metadata.CrashdumpVer, metadata.ResetDetected),
	})
}

// parseCPUInfo extracts CPU information
func parseCPUInfo(metadata *Metadata, result *models.AnalysisResult) {
	cpus := []struct {
		socket int
		data   CPUMetadata
	}{
		{0, metadata.CPU0},
		{1, metadata.CPU1},
	}

	for _, cpu := range cpus {
		if cpu.data.CPUID == "" {
			continue
		}

		// Parse core count
		coreCount := 0
		if cpu.data.CoreCount != "" {
			if count, err := strconv.ParseInt(strings.TrimPrefix(cpu.data.CoreCount, "0x"), 16, 64); err == nil {
				coreCount = int(count)
			}
		}

		cpuModel := models.CPU{
			Socket: cpu.socket,
			Model:  fmt.Sprintf("Intel CPU (CPUID: %s)", cpu.data.CPUID),
			Cores:  coreCount,
		}

		// Add PPIN
		if cpu.data.PPIN != "" && cpu.data.PPIN != "0x0" {
			cpuModel.PPIN = cpu.data.PPIN
		}

		result.Hardware.CPUs = append(result.Hardware.CPUs, cpuModel)

		// Add microcode version to firmware list
		if cpu.data.UcodePatchVer != "" {
			result.Hardware.Firmware = append(result.Hardware.Firmware, models.FirmwareInfo{
				DeviceName: fmt.Sprintf("CPU%d Microcode", cpu.socket),
				Version:    cpu.data.UcodePatchVer,
			})
		}
	}
}

// parseMCAErrors extracts Machine Check Architecture errors
func parseMCAErrors(crashData *struct {
	METADATA   Metadata       `json:"METADATA"`
	PROCESSORS ProcessorsData `json:"PROCESSORS"`
}, result *models.AnalysisResult) {
	timestamp := time.Now()
	if crashData.METADATA.Timestamp != "" {
		if t, err := time.Parse(time.RFC3339, crashData.METADATA.Timestamp); err == nil {
			timestamp = t
		}
	}

	// Parse each CPU's MCA data
	cpuProcs := []struct {
		name string
		data Processors
	}{
		{"cpu0", crashData.PROCESSORS.CPU0},
		{"cpu1", crashData.PROCESSORS.CPU1},
	}

	for _, cpu := range cpuProcs {
		if cpu.data.MCA.Uncore == nil {
			continue
		}

		// Check each MCA bank for errors
		for bankName, bankDataRaw := range cpu.data.MCA.Uncore {
			bankData, ok := bankDataRaw.(map[string]interface{})
			if !ok {
				continue
			}

			// Look for status register
			statusKey := strings.ToLower(bankName) + "_status"
			statusRaw, ok := bankData[statusKey]
			if !ok {
				continue
			}

			statusStr, ok := statusRaw.(string)
			if !ok {
				continue
			}

			// Parse status value
			status, err := strconv.ParseUint(strings.TrimPrefix(statusStr, "0x"), 16, 64)
			if err != nil {
				continue
			}

			// Check if MCA error is valid (bit 63 = Valid)
			if status&(1<<63) != 0 {
				// MCA error detected
				severity := models.SeverityWarning
				if status&(1<<61) != 0 { // UC bit = uncorrected error
					severity = models.SeverityCritical
				}

				description := fmt.Sprintf("MCA Error in %s bank %s", cpu.name, bankName)
				if status&(1<<61) != 0 {
					description += " (Uncorrected)"
				} else {
					description += " (Corrected)"
				}

				result.Events = append(result.Events, models.Event{
					Timestamp:   timestamp,
					Source:      "MCA",
					EventType:   "Machine Check",
					Description: description,
					Severity:    severity,
					RawData:     fmt.Sprintf("Status: %s, CPU: %s, Bank: %s", statusStr, cpu.name, bankName),
				})
			}
		}
	}
}
