package parser

import (
	"fmt"
	"regexp"
	"strings"
	"time"

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

var manufacturedYearWeekPattern = regexp.MustCompile(`^\d{4}-W\d{2}$`)

// NormalizeManufacturedYearWeek converts common FRU manufacturing date formats
// into contract-compatible YYYY-Www values. Unknown or ambiguous inputs return "".
func NormalizeManufacturedYearWeek(raw string) string {
	value := strings.TrimSpace(raw)
	if value == "" {
		return ""
	}
	upper := strings.ToUpper(value)
	if manufacturedYearWeekPattern.MatchString(upper) {
		return upper
	}

	layouts := []string{
		time.RFC3339,
		"2006-01-02T15:04:05",
		"2006-01-02 15:04:05",
		"2006-01-02",
		"2006/01/02",
		"01/02/2006 15:04:05",
		"01/02/2006",
		"01-02-2006",
		"Mon Jan 2 15:04:05 2006",
		"Mon Jan _2 15:04:05 2006",
		"Jan 2 2006",
		"Jan _2 2006",
	}
	for _, layout := range layouts {
		if ts, err := time.Parse(layout, value); err == nil {
			year, week := ts.ISOWeek()
			return formatYearWeek(year, week)
		}
	}
	return ""
}

func formatYearWeek(year, week int) string {
	if year <= 0 || week <= 0 || week > 53 {
		return ""
	}
	return fmt.Sprintf("%04d-W%02d", year, week)
}

// ApplyManufacturedYearWeekFromFRU attaches normalized manufactured_year_week to
// component details by exact serial-number match. Board-level FRU entries are not
// expanded to components.
func ApplyManufacturedYearWeekFromFRU(frus []models.FRUInfo, hw *models.HardwareConfig) {
	if hw == nil || len(frus) == 0 {
		return
	}
	bySerial := make(map[string]string, len(frus))
	for _, fru := range frus {
		serial := normalizeFRUSerial(fru.SerialNumber)
		yearWeek := NormalizeManufacturedYearWeek(fru.MfgDate)
		if serial == "" || yearWeek == "" {
			continue
		}
		if _, exists := bySerial[serial]; exists {
			continue
		}
		bySerial[serial] = yearWeek
	}
	if len(bySerial) == 0 {
		return
	}

	for i := range hw.CPUs {
		attachYearWeek(&hw.CPUs[i].Details, bySerial[normalizeFRUSerial(hw.CPUs[i].SerialNumber)])
	}
	for i := range hw.Memory {
		attachYearWeek(&hw.Memory[i].Details, bySerial[normalizeFRUSerial(hw.Memory[i].SerialNumber)])
	}
	for i := range hw.Storage {
		attachYearWeek(&hw.Storage[i].Details, bySerial[normalizeFRUSerial(hw.Storage[i].SerialNumber)])
	}
	for i := range hw.PCIeDevices {
		attachYearWeek(&hw.PCIeDevices[i].Details, bySerial[normalizeFRUSerial(hw.PCIeDevices[i].SerialNumber)])
	}
	for i := range hw.GPUs {
		attachYearWeek(&hw.GPUs[i].Details, bySerial[normalizeFRUSerial(hw.GPUs[i].SerialNumber)])
	}
	for i := range hw.NetworkAdapters {
		attachYearWeek(&hw.NetworkAdapters[i].Details, bySerial[normalizeFRUSerial(hw.NetworkAdapters[i].SerialNumber)])
	}
	for i := range hw.PowerSupply {
		attachYearWeek(&hw.PowerSupply[i].Details, bySerial[normalizeFRUSerial(hw.PowerSupply[i].SerialNumber)])
	}
}

func attachYearWeek(details *map[string]any, yearWeek string) {
	if yearWeek == "" {
		return
	}
	if *details == nil {
		*details = map[string]any{}
	}
	if existing, ok := (*details)["manufactured_year_week"]; ok && strings.TrimSpace(toString(existing)) != "" {
		return
	}
	(*details)["manufactured_year_week"] = yearWeek
}

func normalizeFRUSerial(v string) string {
	s := strings.TrimSpace(v)
	if s == "" {
		return ""
	}
	switch strings.ToUpper(s) {
	case "N/A", "NA", "NULL", "UNKNOWN", "-", "0":
		return ""
	default:
		return strings.ToUpper(s)
	}
}

func toString(v any) string {
	switch x := v.(type) {
	case string:
		return x
	default:
		return strings.TrimSpace(fmt.Sprint(v))
	}
}
