package kobject

import (
	"errors"
	"maps"
	"strconv"
	"strings"
	"unsafe"

	"hakurei.app/internal/uevent"
)

// Event is a [uevent.Message] with known environment variables processed.
type Event struct {
	// alloc_uevent_skb: action_string
	Action uevent.KobjectAction `json:"action"`
	// alloc_uevent_skb: devpath
	DevPath string `json:"devpath"`

	// Uninterpreted environment variable pairs. An entry missing a separator
	// gains the value "\x00".
	Env map[string]string `json:"env"`

	// SEQNUM value set by the kernel.
	Sequence uint64 `json:"seqnum"`
	// SYNTH_UUID value set on trigger, nil denotes a non-synthetic event.
	Synth *uevent.UUID `json:"synth_uuid,omitempty"`
	// SUBSYSTEM value set by the kernel.
	Subsystem string `json:"subsystem"`
}

// Clone returns a copy of e.
func (e *Event) Clone() Event {
	v := *e
	v.Env = maps.Clone(e.Env)
	return v
}

// makeColdboot allocates a new [Object] from e in [StateColdboot].
func (e *Event) makeColdboot() *Object {
	return &Object{
		State:     StateColdboot,
		DevPath:   e.DevPath,
		Subsystem: e.Subsystem,
	}
}

// Populate populates e with the contents of a [uevent.Message].
//
// The ACTION and DEVPATH environment variables are ignored and assumed to be
// consistent with the header.
func (e *Event) Populate(reportErr func(error), m *uevent.Message) {
	if reportErr == nil {
		reportErr = func(error) {}
	}

	*e = Event{
		Action:  m.Action,
		DevPath: m.DevPath,
		Env:     make(map[string]string),
	}

	for _, s := range m.Env {
		k, v, ok := strings.Cut(s, "=")
		if !ok {
			if _, ok = e.Env[s]; !ok {
				e.Env[s] = "\x00"
			}
			continue
		}

		switch k {
		case "ACTION", "DEVPATH":
			continue

		case "SEQNUM":
			seq, err := strconv.ParseUint(v, 10, 64)
			if err != nil {
				if _e := errors.Unwrap(err); _e != nil {
					err = _e
				}
				reportErr(err)

				e.Env[k] = v
				continue
			}
			e.Sequence = seq

		case "SYNTH_UUID":
			var uuid uevent.UUID
			err := uuid.UnmarshalText(unsafe.Slice(unsafe.StringData(v), len(v)))
			if err != nil {
				reportErr(err)

				e.Env[k] = v
				continue
			}
			e.Synth = &uuid

		case "SUBSYSTEM":
			e.Subsystem = v

		default:
			e.Env[k] = v
		}
	}
}
