// SPDX-License-Identifier: GPL-3.0-or-later

//go:build windows

package service

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// windowsManager implements Manager using Windows Task Scheduler for user-level
// and the Windows Service Control Manager (sc.exe) for system-level services.
//
// We use Task Scheduler for user-level because it doesn't require admin rights,
// and sc.exe for system-level because Windows Services are the proper mechanism
// for persistent background processes.
type windowsManager struct{}

const (
	// taskName is the Task Scheduler task name.
	taskName = "PrivateBackupBackend"

	// windowsServiceName is the Windows Service name (system-level).
	windowsServiceName = "PrivateBackupBackend"
)

func (m *windowsManager) InitSystem() InitSystem {
	return InitWindows
}

func (m *windowsManager) IsInstalled() bool {
	// Check Task Scheduler.
	out, err := exec.Command("schtasks", "/Query", "/TN", taskName, "/FO", "LIST").Output()
	if err == nil && len(out) > 0 {
		return true
	}

	// Check Windows Service.
	out, err = exec.Command("sc", "query", windowsServiceName).Output()
	if err == nil && strings.Contains(string(out), windowsServiceName) {
		return true
	}
	return false
}

func (m *windowsManager) installedLevel() ServiceLevel {
	// Check Task Scheduler first (user-level).
	out, err := exec.Command("schtasks", "/Query", "/TN", taskName, "/FO", "LIST").Output()
	if err == nil && len(out) > 0 {
		return LevelUser
	}
	return LevelSystem
}

func (m *windowsManager) Install(cfg Config) error {
	if m.IsInstalled() {
		return ErrServiceAlreadyInstalled
	}

	if cfg.Level == LevelSystem {
		return m.installWindowsService(cfg)
	}
	return m.installScheduledTask(cfg)
}

// installScheduledTask creates a Windows Task Scheduler task that runs at logon
// and restarts on failure. This doesn't require admin privileges.
func (m *windowsManager) installScheduledTask(cfg Config) error {
	// Build the command line.
	args := cfg.BinaryPath
	if len(cfg.Args) > 0 {
		args += " " + strings.Join(cfg.Args, " ")
	}

	// Create a scheduled task that:
	// - Runs at user logon
	// - Restarts on failure (up to 3 times with 10-second delay)
	// - Runs indefinitely
	// /SC ONLOGON: trigger at user logon
	// /RL LIMITED: run with limited privileges (no admin)
	createArgs := []string{
		"/Create",
		"/TN", taskName,
		"/TR", args,
		"/SC", "ONLOGON",
		"/RL", "LIMITED",
		"/F",
	}

	cmd := exec.Command("schtasks", createArgs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to create scheduled task: %w", err)
	}

	// Also create an XML task definition for restart-on-failure behavior
	// since schtasks /Create doesn't support all options. We'll modify
	// the task XML inline.
	xmlArgs := []string{
		"/Change",
		"/TN", taskName,
		"/RI", "1", // Restart interval: 1 minute
		"/K", // Kill task at end of duration (disabled—runs indefinitely)
	}
	_ = exec.Command("schtasks", xmlArgs...).Run() // Best-effort.

	return nil
}

// installWindowsService creates a Windows Service via sc.exe.
// Requires administrator privileges.
func (m *windowsManager) installWindowsService(cfg Config) error {
	// Check for admin privileges.
	if !isWindowsAdmin() {
		return ErrInsufficientPrivileges
	}

	binPath := cfg.BinaryPath
	if len(cfg.Args) > 0 {
		binPath += " " + strings.Join(cfg.Args, " ")
	}

	cmd := exec.Command("sc", "create", windowsServiceName,
		"binpath=", binPath,
		"DisplayName=", cfg.DisplayName,
		"start=", "auto",
		"obj=", "LocalSystem",
	)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to create Windows service: %w", err)
	}

	// Set description.
	_ = exec.Command("sc", "description", windowsServiceName, cfg.Description).Run()

	// Configure auto-restart on failure: reset after 60s, restart after 10s up to 3 times.
	_ = exec.Command("sc", "failure", windowsServiceName,
		"reset=", "60",
		"actions=", "restart/10000/restart/10000/restart/10000",
	).Run()

	return nil
}

func (m *windowsManager) Uninstall() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}

	level := m.installedLevel()

	if level == LevelUser {
		// Remove scheduled task.
		_ = m.Stop()
		cmd := exec.Command("schtasks", "/Delete", "/TN", taskName, "/F")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	}

	// Remove Windows Service.
	_ = m.Stop()
	cmd := exec.Command("sc", "delete", windowsServiceName)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *windowsManager) Start() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}

	level := m.installedLevel()
	if level == LevelUser {
		cmd := exec.Command("schtasks", "/Run", "/TN", taskName)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	}
	cmd := exec.Command("sc", "start", windowsServiceName)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *windowsManager) Stop() error {
	if !m.IsInstalled() {
		return ErrServiceNotInstalled
	}

	level := m.installedLevel()
	if level == LevelUser {
		cmd := exec.Command("schtasks", "/End", "/TN", taskName)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	}
	cmd := exec.Command("sc", "stop", windowsServiceName)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (m *windowsManager) Restart() error {
	if err := m.Stop(); err != nil {
		// Ignore stop errors—may not be running.
	}
	return m.Start()
}

func (m *windowsManager) Status() (Status, error) {
	st := Status{
		InitSystem: InitWindows,
	}

	if !m.IsInstalled() {
		return st, nil
	}
	st.Installed = true

	level := m.installedLevel()
	if level == LevelSystem {
		st.Level = "system"
		st.UnitName = windowsServiceName

		out, err := exec.Command("sc", "query", windowsServiceName).Output()
		if err == nil {
			outStr := string(out)
			if strings.Contains(outStr, "RUNNING") {
				st.Running = true
			}
			// Check if auto-start is configured.
			cfgOut, err := exec.Command("sc", "qc", windowsServiceName).Output()
			if err == nil && strings.Contains(string(cfgOut), "AUTO_START") {
				st.Enabled = true
			}
		}
	} else {
		st.Level = "user"
		st.UnitName = taskName

		out, err := exec.Command("schtasks", "/Query", "/TN", taskName, "/FO", "LIST", "/V").Output()
		if err == nil {
			outStr := string(out)
			if strings.Contains(outStr, "Running") {
				st.Running = true
			}
			if strings.Contains(outStr, "Ready") || strings.Contains(outStr, "Running") {
				st.Enabled = true
			}
		}
	}

	return st, nil
}

// isWindowsAdmin checks if the current process has administrator privileges.
func isWindowsAdmin() bool {
	// Try to access a path that requires admin privileges.
	testPath := filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "config", "system")
	_, err := os.Open(testPath)
	return err == nil
}
