package meta

import (
	"context"
	"os"
	"os/exec"
	"syscall"

	"nadir/internal/config"
	"nadir/internal/oscmd"

	"github.com/danielgtaylor/huma/v2"
)

// RegisterUpdate wires POST /api/update. It runs the equivalent of
// `sudo nadir update` in a detached session and returns 202 immediately; the
// systemctl restart that ends the updater drops in-flight connections, so the
// caller should poll /api/health to confirm the new version is up.
//
// configPath is re-read by the handler so a missing release_repo (or any other
// config error) surfaces as 4xx/5xx to the caller, not as stderr only.
//
// Authorization: requires (meta, root). Only roles with a wildcard grant
// (the default admin role) match, since "meta" isn't a real module with a
// declared permission vocabulary.
func RegisterUpdate(api huma.API, configPath string) {
	huma.Register(api, huma.Operation{
		OperationID:   "meta-update",
		Method:        "POST",
		Path:          "/api/update",
		Summary:       "Update nadir to the latest release",
		Description:   "Equivalent to running `sudo nadir update` on the host: queries server.release_repo for the latest release, downloads the binary matching the host's architecture, atomically replaces the running binary, and restarts the systemd unit. Returns 202 immediately; the service restart drops in-flight connections, so poll /api/health to confirm the new version is up. Requires the wildcard admin role.",
		Tags:          []string{"Meta"},
		Metadata:      map[string]any{"module": "meta", "permission": "root"},
		Errors:        []int{400, 401, 403, 500},
		DefaultStatus: 202,
	}, func(ctx context.Context, _ *struct{}) (*oscmd.StatusOutput, error) {
		if configPath != "" {
			cfg, err := config.Load(configPath)
			if err != nil {
				return nil, huma.Error500InternalServerError("config load failed", err)
			}
			if cfg.Server.ReleaseRepo == "" {
				return nil, huma.Error400BadRequest("server.release_repo not set in " + configPath)
			}
		}
		exe, err := os.Executable()
		if err != nil {
			return nil, huma.Error500InternalServerError("could not resolve own binary path", err)
		}
		cmd := exec.Command(exe, "update")
		// Detach from the server's process group so `systemctl restart nadir`
		// (the final step of `nadir update`) doesn't kill its own updater.
		cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Start(); err != nil {
			return nil, huma.Error500InternalServerError("could not start updater", err)
		}
		return oscmd.OK(), nil
	})
}
