package uevent

import (
	"context"
	"errors"
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"slices"
)

// synthAdd is prepared bytes written to uevent to cause a synthetic add event
// to be emitted during coldboot.
var synthAdd = []byte(KOBJ_ADD.String())

// Coldboot writes "add" to every uevent file that it finds in /sys/devices.
// This causes the kernel to regenerate the uevents for these paths.
//
// The specified pathname must present the sysfs root.
//
// Note that while [AOSP documentation] claims to also scan /sys/class and
// /sys/block, this is no longer the case, and the documentation was not updated
// when this changed.
//
// [AOSP documentation]: https://android.googlesource.com/platform/system/core/+/master/init/README.ueventd.md
func Coldboot(
	ctx context.Context,
	pathname string,
	uuid *UUID,
	visited chan<- string,
	handleWalkErr func(error) error,
) error {
	if handleWalkErr == nil {
		handleWalkErr = func(err error) error {
			if errors.Is(err, fs.ErrNotExist) {
				log.Println("coldboot", err)
				return nil
			}
			return err
		}
	}

	add := synthAdd
	if uuid != nil {
		add = slices.Concat(add, []byte{' '}, []byte(uuid.String()))
	}

	return filepath.WalkDir(filepath.Join(pathname, "devices"), func(
		path string,
		d fs.DirEntry,
		err error,
	) error {
		if err != nil {
			return handleWalkErr(err)
		}
		if err = ctx.Err(); err != nil {
			return err
		}

		if d.IsDir() || d.Name() != "uevent" {
			return nil
		}
		if err = os.WriteFile(path, add, 0); err != nil {
			return handleWalkErr(err)
		}

		select {
		case visited <- path:
			break

		case <-ctx.Done():
			return ctx.Err()
		}

		return nil
	})
}
