// Package xfusion provides parser for xFusion iBMC diagnostic dump archives.
// Tested with: xFusion G5500 V7 iBMC dump (tar.gz format, exported via iBMC UI)
//
// Archive structure: dump_info/AppDump/... and dump_info/LogDump/...
//
// IMPORTANT: Increment parserVersion when modifying parser logic!
package xfusion

import (
	"strings"

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

const parserVersion = "1.1"

func init() {
	parser.Register(&Parser{})
}

// Parser implements VendorParser for xFusion iBMC dump archives.
type Parser struct{}

func (p *Parser) Name() string    { return "xFusion iBMC Dump Parser" }
func (p *Parser) Vendor() string  { return "xfusion" }
func (p *Parser) Version() string { return parserVersion }

// Detect checks if files match the xFusion iBMC dump format.
// Returns confidence score 0-100.
func (p *Parser) Detect(files []parser.ExtractedFile) int {
	confidence := 0
	for _, f := range files {
		path := strings.ToLower(f.Path)
		switch {
		case strings.Contains(path, "appdump/frudata/fruinfo.txt"):
			confidence += 50
		case strings.Contains(path, "rtosdump/versioninfo/app_revision.txt"):
			confidence += 30
		case strings.Contains(path, "appdump/sensor_alarm/sensor_info.txt"):
			confidence += 10
		case strings.Contains(path, "appdump/card_manage/card_info"):
			confidence += 20
		case strings.Contains(path, "logdump/netcard/netcard_info.txt"):
			confidence += 20
		}
		if confidence >= 100 {
			return 100
		}
	}
	return confidence
}

// Parse parses xFusion iBMC dump and returns an analysis result.
func (p *Parser) Parse(files []parser.ExtractedFile) (*models.AnalysisResult, error) {
	result := &models.AnalysisResult{
		Events:  make([]models.Event, 0),
		FRU:     make([]models.FRUInfo, 0),
		Sensors: make([]models.SensorReading, 0),
		Hardware: &models.HardwareConfig{
			Firmware:        make([]models.FirmwareInfo, 0),
			Devices:         make([]models.HardwareDevice, 0),
			CPUs:            make([]models.CPU, 0),
			Memory:          make([]models.MemoryDIMM, 0),
			Storage:         make([]models.Storage, 0),
			Volumes:         make([]models.StorageVolume, 0),
			PCIeDevices:     make([]models.PCIeDevice, 0),
			GPUs:            make([]models.GPU, 0),
			NetworkCards:    make([]models.NIC, 0),
			NetworkAdapters: make([]models.NetworkAdapter, 0),
			PowerSupply:     make([]models.PSU, 0),
		},
	}

	if f := findByAnyPath(files, "appdump/frudata/fruinfo.txt", "rtosdump/versioninfo/fruinfo.txt"); f != nil {
		parseFRUInfo(f.Content, result)
	}
	if f := findByPath(files, "appdump/sensor_alarm/sensor_info.txt"); f != nil {
		result.Sensors = parseSensorInfo(f.Content)
	}
	if f := findByPath(files, "appdump/cpumem/cpu_info"); f != nil {
		result.Hardware.CPUs = parseCPUInfo(f.Content)
	}
	if f := findByPath(files, "appdump/cpumem/mem_info"); f != nil {
		result.Hardware.Memory = parseMemInfo(f.Content)
	}
	var nicCards []xfusionNICCard
	if f := findByPath(files, "appdump/card_manage/card_info"); f != nil {
		gpus, cards := parseCardInfo(f.Content)
		result.Hardware.GPUs = gpus
		nicCards = cards
	}
	if f := findByPath(files, "logdump/netcard/netcard_info.txt"); f != nil || len(nicCards) > 0 {
		var content []byte
		if f != nil {
			content = f.Content
		}
		adapters, legacyNICs := mergeNetworkAdapters(nicCards, parseNetcardInfo(content))
		result.Hardware.NetworkAdapters = adapters
		result.Hardware.NetworkCards = legacyNICs
	}
	if f := findByPath(files, "appdump/bmc/psu_info.txt"); f != nil {
		result.Hardware.PowerSupply = parsePSUInfo(f.Content)
	}
	if f := findByPath(files, "appdump/storagemgnt/raid_controller_info.txt"); f != nil {
		parseStorageControllerInfo(f.Content, result)
	}
	if f := findByPath(files, "rtosdump/versioninfo/app_revision.txt"); f != nil {
		parseAppRevision(f.Content, result)
	}
	for _, f := range findDiskInfoFiles(files) {
		disk := parseDiskInfo(f.Content)
		if disk != nil {
			result.Hardware.Storage = append(result.Hardware.Storage, *disk)
		}
	}
	if f := findByPath(files, "logdump/maintenance_log"); f != nil {
		result.Events = parseMaintenanceLog(f.Content)
	}

	result.Protocol = "ipmi"
	result.SourceType = models.SourceTypeArchive
	parser.ApplyManufacturedYearWeekFromFRU(result.FRU, result.Hardware)

	return result, nil
}

// findByPath returns the first file whose lowercased path contains the given substring.
func findByPath(files []parser.ExtractedFile, substring string) *parser.ExtractedFile {
	for i := range files {
		if strings.Contains(strings.ToLower(files[i].Path), substring) {
			return &files[i]
		}
	}
	return nil
}

func findByAnyPath(files []parser.ExtractedFile, substrings ...string) *parser.ExtractedFile {
	for _, substring := range substrings {
		if f := findByPath(files, substring); f != nil {
			return f
		}
	}
	return nil
}

// findDiskInfoFiles returns all PhysicalDrivesInfo disk_info files.
func findDiskInfoFiles(files []parser.ExtractedFile) []parser.ExtractedFile {
	var out []parser.ExtractedFile
	for _, f := range files {
		path := strings.ToLower(f.Path)
		if strings.Contains(path, "physicaldrivesinfo/") && strings.HasSuffix(path, "/disk_info") {
			out = append(out, f)
		}
	}
	return out
}
