//go:build windows

// install_windows.go — thin wrappers around the Service Control
// Manager via golang.org/x/sys/windows/svc/mgr. Used by the agent's
// `install` / `uninstall` / `start` / `stop` subcommands.
//
// UNTESTED in CI. Mirrors the canonical example shape; if you need
// to extend this, prefer copying from x/sys/windows/svc/example
// over inventing new patterns.
package service

import (
	"fmt"
	"os"
	"path/filepath"

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

// Install registers the service with the SCM, pointing it at the
// currently-running binary. The service starts on every boot and
// runs as LocalSystem (default).
func Install() error {
	exe, err := os.Executable()
	if err != nil {
		return fmt.Errorf("install: locate executable: %w", err)
	}
	exe, err = filepath.Abs(exe)
	if err != nil {
		return fmt.Errorf("install: absolutise path: %w", err)
	}
	m, err := mgr.Connect()
	if err != nil {
		return fmt.Errorf("install: connect SCM: %w", err)
	}
	defer m.Disconnect()
	if existing, err := m.OpenService(ServiceName); err == nil {
		_ = existing.Close()
		return fmt.Errorf("service %q already installed; uninstall first", ServiceName)
	}
	s, err := m.CreateService(ServiceName, exe, mgr.Config{
		StartType:   mgr.StartAutomatic,
		DisplayName: "Restic-manager agent",
		Description: "Backs up this host on the schedule the central restic-manager dictates.",
	}, "run")
	if err != nil {
		return fmt.Errorf("install: create service: %w", err)
	}
	defer s.Close()
	return nil
}

// Uninstall removes the service from the SCM. Caller is expected to
// stop the service first; this returns the SCM's error if it's
// still running.
func Uninstall() error {
	m, err := mgr.Connect()
	if err != nil {
		return fmt.Errorf("uninstall: connect SCM: %w", err)
	}
	defer m.Disconnect()
	s, err := m.OpenService(ServiceName)
	if err != nil {
		return fmt.Errorf("uninstall: open service: %w", err)
	}
	defer s.Close()
	if err := s.Delete(); err != nil {
		return fmt.Errorf("uninstall: delete service: %w", err)
	}
	return nil
}

// Start asks the SCM to start the installed service. No-op if it's
// already running (the SCM returns an error which we surface).
func Start() error {
	m, err := mgr.Connect()
	if err != nil {
		return err
	}
	defer m.Disconnect()
	s, err := m.OpenService(ServiceName)
	if err != nil {
		return err
	}
	defer s.Close()
	return s.Start()
}

// Stop sends a stop control to the service.
func Stop() error {
	m, err := mgr.Connect()
	if err != nil {
		return err
	}
	defer m.Disconnect()
	s, err := m.OpenService(ServiceName)
	if err != nil {
		return err
	}
	defer s.Close()
	_, err = s.Control(0x00000001) // SERVICE_CONTROL_STOP
	return err
}
