package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"hakurei.app/internal/kobject"
	"hakurei.app/internal/report"
	"hakurei.app/internal/uevent"
)

// ModprobeError describes an unsuccessful modprobe invocation.
type ModprobeError struct {
	ModAlias string `json:"modalias"`
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
	ExitCode int    `json:"exit_code"`
}

var _ report.RepresentableError = ModprobeError{}

func (ModprobeError) Representable() {}
func (e ModprobeError) Error() string {
	return fmt.Sprintf(
		"%s (exit status %d)",
		strings.TrimPrefix(strings.TrimSpace(e.Stderr), "modprobe: "),
		e.ExitCode,
	)
}

// dispatchModprobe invokes modprobe for [uevent.KOBJ_ADD] events raising new
// MODALIAS strings.
func dispatchModprobe(
	ctx context.Context,
	s *kobject.State,
) {
	aliases := make(chan string, 1<<8)
	go func() {
		defer close(aliases)
		s.Range(ctx, func(o *kobject.Object, act uevent.KobjectAction) bool {
			if act == uevent.KOBJ_ADD && o.Driver == "" && o.ModAlias != "" {
				aliases <- o.ModAlias
			}
			return true
		})
	}()

	for alias := range aliases {
		stdout, err := exec.Command("/system/sbin/modprobe", alias).Output()
		if err == nil {
			if len(stdout) > 0 {
				log.Println(string(stdout))
			}
			continue
		}

		exitError, ok := errors.AsType[*exec.ExitError](err)
		if !ok || exitError == nil {
			r.Dispatch(report.Degraded, "invoke modprobe", err)
			continue
		}

		r.Dispatch(report.Trivial, "load device driver", ModprobeError{
			ModAlias: alias,
			Stdout:   string(stdout),
			Stderr:   string(exitError.Stderr),
			ExitCode: exitError.ExitCode(),
		})
	}
}
