//go:build windows

// service_windows.go — Service Control Manager integration for the
// agent on Windows (P2-16). Implements the svc.Handler interface so
// `restic-manager-agent run` works under both interactive and SCM
// contexts. install/uninstall live in install_windows.go.
//
// UNTESTED on Windows in this repo's CI (the runners are Linux).
// The shape mirrors the canonical example in
// golang.org/x/sys/windows/svc/example. Treat any deviation from
// that example as suspicious.
package service

import (
	"context"
	"errors"
	"log/slog"

	"golang.org/x/sys/windows/svc"
)

// ServiceName is the SCM identifier for the agent service.
const ServiceName = "restic-manager-agent"

// AgentRun is the function the service handler calls to start the
// agent's main loop. Pass cmd/agent's run-loop entry point at the
// call site so this package stays free of cross-cmd imports.
type AgentRun func(ctx context.Context) error

// Run delegates to the SCM dispatcher when running under Windows
// service control, otherwise runs the agent loop in the foreground
// (for `restic-manager-agent run` from a console, e.g. while
// debugging on a developer's box).
func Run(agentRun AgentRun) error {
	isService, err := svc.IsWindowsService()
	if err != nil {
		return err
	}
	if !isService {
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		return agentRun(ctx)
	}
	return svc.Run(ServiceName, &handler{run: agentRun})
}

// handler implements svc.Handler. Execute is called once when the
// service is started. We spawn the agent loop in a goroutine and
// listen for SCM Stop / Shutdown notifications, cancelling the
// context to wind down cleanly.
type handler struct {
	run AgentRun
}

func (h *handler) Execute(_ []string, req <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
	const accepted = svc.AcceptStop | svc.AcceptShutdown
	status <- svc.Status{State: svc.StartPending}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	doneCh := make(chan error, 1)
	go func() {
		doneCh <- h.run(ctx)
	}()
	status <- svc.Status{State: svc.Running, Accepts: accepted}

	for {
		select {
		case c := <-req:
			switch c.Cmd {
			case svc.Interrogate:
				status <- c.CurrentStatus
			case svc.Stop, svc.Shutdown:
				slog.Info("svc: stop requested")
				cancel()
				status <- svc.Status{State: svc.StopPending}
				if err := <-doneCh; err != nil && !errors.Is(err, context.Canceled) {
					slog.Warn("svc: agent loop exited with error", "err", err)
					return false, 1
				}
				return false, 0
			}
		case err := <-doneCh:
			// Agent loop exited on its own — uncommon (only via signal
			// or fatal error). Surface as an SCM stop.
			if err != nil && !errors.Is(err, context.Canceled) {
				slog.Warn("svc: agent loop exited unexpectedly", "err", err)
				return false, 1
			}
			return false, 0
		}
	}
}
