//go:build windows

package updater

import (
	"context"
	"fmt"
	"log/slog"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"
)

// helperScript is rendered with fmt.Sprintf, args order:
//
//	%[1]s — running binary path (source for the .old copy)
//	%[2]s — .old path
//	%[3]s — staged .new path
//	%[4]s — running binary path (rename target)
const helperScript = `@echo off
timeout /t 3 /nobreak >nul
copy /Y "%[1]s" "%[2]s"
sc stop restic-manager-agent
:wait
sc query restic-manager-agent | find "STOPPED" >nul
if errorlevel 1 (timeout /t 1 /nobreak >nul & goto wait)
move /Y "%[3]s" "%[4]s"
sc start restic-manager-agent
del "%%~f0"
`

// Update on Windows can't overwrite the running .exe in-process
// (exclusive file lock), so we stage the new binary, write a small
// detached helper script that waits, stops the service, swaps the
// binary, and starts the service, then exit cleanly. SCM treats
// clean exits after sc stop as intentional and does not auto-restart;
// the helper's final sc start handles that.
func Update(ctx context.Context, serverURL string) error {
	binPath, err := resolveOwnBinary()
	if err != nil {
		return err
	}
	stage, err := fetch(ctx, serverURL, binPath)
	if err != nil {
		return err
	}
	helperPath := filepath.Join(filepath.Dir(binPath), "agent-update.cmd")
	body := fmt.Sprintf(helperScript, binPath, binPath+".old", stage, binPath)
	if err := os.WriteFile(helperPath, []byte(body), 0o755); err != nil {
		return err
	}
	cmd := exec.Command("cmd.exe", "/c", helperPath)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow:    true,
		CreationFlags: 0x00000008 | 0x08000000, // DETACHED_PROCESS | CREATE_NO_WINDOW
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	slog.Info("agent self-update: helper spawned, exiting cleanly",
		"binary", binPath, "helper", helperPath)
	time.Sleep(200 * time.Millisecond)
	os.Exit(0)
	return nil // unreachable
}

// swap is unused on Windows — the helper script does the swap.
// Defined to satisfy the build (UpdateForTest references it).
func swap(_, _ string) error {
	return fmt.Errorf("updater.swap not implemented on Windows; use the helper script via Update")
}
