package inspur

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// auditSNChangedNVMeRegex matches:
// "Front Back Plane N NVMe DiskM  SN changed from X   to Y"
// Captures: disk_num, new_serial
var auditSNChangedNVMeRegex = regexp.MustCompile(`NVMe Disk(\d+)\s+SN changed from \S+\s+to\s+(\S+)`)

// auditSNChangedRAIDRegex matches:
// "Raid(Pcie Slot:N) HDD(enclosure id:E slot:S)  SN changed from X   to Y"
// Captures: pcie_slot, enclosure_id, slot_num, new_serial
var auditSNChangedRAIDRegex = regexp.MustCompile(`Raid\(Pcie Slot:(\d+)\) HDD\(enclosure id:(\d+) slot:(\d+)\)\s+SN changed from \S+\s+to\s+(\S+)`)

// ParseAuditLogNVMeSerials parses audit.log and returns the final (latest) serial number
// per NVMe disk number. The disk number matches the numeric suffix in PCIe location
// strings like "#NVME0", "#NVME2", etc. from devicefrusdr.log.
// Entries where the serial changed to "NULL" are excluded.
func ParseAuditLogNVMeSerials(content []byte) map[int]string {
	serials := make(map[int]string)

	for _, line := range strings.Split(string(content), "\n") {
		m := auditSNChangedNVMeRegex.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		diskNum, err := strconv.Atoi(m[1])
		if err != nil {
			continue
		}
		serial := strings.TrimSpace(m[2])
		if strings.EqualFold(serial, "NULL") || serial == "" {
			delete(serials, diskNum)
		} else {
			serials[diskNum] = serial
		}
	}
	if len(serials) == 0 {
		return nil
	}
	return serials
}

// ParseAuditLogRAIDSerials parses audit.log and returns the final (latest) serial number
// per RAID backplane disk. Key format is "BP{enclosure_id-1}:{slot_num}" (e.g. "BP0:0").
//
// Each disk slot is claimed by a specific RAID controller (Pcie Slot:N). NULL events from
// an old controller do not clear serials assigned by a newer controller, preventing stale
// deletions when disks are migrated between RAID arrays.
func ParseAuditLogRAIDSerials(content []byte) map[string]string {
	// owner tracks which PCIe RAID controller slot last assigned a serial to a disk key.
	serials := make(map[string]string)
	owner := make(map[string]int)

	for _, line := range strings.Split(string(content), "\n") {
		m := auditSNChangedRAIDRegex.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		pcieSlot, err := strconv.Atoi(m[1])
		if err != nil {
			continue
		}
		enclosureID, err := strconv.Atoi(m[2])
		if err != nil {
			continue
		}
		slotNum, err := strconv.Atoi(m[3])
		if err != nil {
			continue
		}
		serial := strings.TrimSpace(m[4])
		key := fmt.Sprintf("BP%d:%d", enclosureID-1, slotNum)
		if strings.EqualFold(serial, "NULL") || serial == "" {
			// Only clear if this controller was the last to set the serial.
			if owner[key] == pcieSlot {
				delete(serials, key)
				delete(owner, key)
			}
		} else {
			serials[key] = serial
			owner[key] = pcieSlot
		}
	}
	if len(serials) == 0 {
		return nil
	}
	return serials
}
