//go:build windows

package main

import (
	"flag"
	"log"
	"os"
	"path/filepath"

	"golang.org/x/sys/windows/svc"
	"tailscale.com/cmd/mirage/internal/launch"
	"tailscale.com/envknob"
	"tailscale.com/util/winutil"
)

type cliArgs struct {
	install        bool
	uninstall      bool
	prepareUpgrade bool
	firewall       bool
	subprocess     bool
}

func main() {
	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "/subproc":
			log.Printf("mirage entrypoint: /subproc")
			defer attachMirageBackgroundLog()()
			prepareMirageBackgroundRuntime()
			if err := runMirageSubprocess(); err != nil {
				log.Fatalf("mirage subprocess failed: %v", err)
			}
			return
		case "/firewall":
			log.Printf("mirage entrypoint: /firewall")
			defer attachMirageBackgroundLog()()
			prepareMirageBackgroundRuntime()
			if err := runMirageFirewallHelper(); err != nil {
				log.Fatalf("mirage firewall helper failed: %v", err)
			}
			return
		case "/prepare-upgrade":
			log.Printf("mirage entrypoint: /prepare-upgrade")
			defer attachMirageBackgroundLog()()
			prepareMirageBackgroundRuntime()
			if err := runMiragePrepareUpgrade(); err != nil {
				log.Fatalf("mirage upgrade preparation failed: %v", err)
			}
			return
		}
	}

	var args cliArgs
	flag.BoolVar(&args.install, "install", false, "install the Mirage background service")
	flag.BoolVar(&args.uninstall, "uninstall", false, "uninstall the Mirage background service")
	flag.BoolVar(&args.prepareUpgrade, "prepare-upgrade", false, "prepare Mirage for an upgrade install")
	flag.BoolVar(&args.firewall, "firewall", false, "run Mirage firewall helper mode")
	flag.BoolVar(&args.subprocess, "subproc", false, "run Mirage daemon subprocess mode")
	flag.Parse()

	isService, err := svc.IsWindowsService()
	if err != nil {
		log.Fatalf("svc.IsWindowsService failed: %v", err)
	}

	mode := launch.ResolveMode(launch.Args{
		Install:        args.install,
		Uninstall:      args.uninstall,
		PrepareUpgrade: args.prepareUpgrade,
		Firewall:       args.firewall,
		Subprocess:     args.subprocess,
	}, isService)
	log.Printf("mirage launch mode: %s (windows service=%t)", mode, isService)

	if mode != launch.ModeGUI {
		defer attachMirageBackgroundLog()()
		prepareMirageBackgroundRuntime()
	}

	switch mode {
	case launch.ModeGUI:
		if _, err := winutil.CreateAppMutex("Mirage"); err != nil {
			return
		}
		if err := runGUI(); err != nil {
			log.Fatalf("mirage gui failed: %v", err)
		}
	case launch.ModeInstallService:
		if err := installMirageService(); err != nil {
			log.Fatalf("mirage service install failed: %v", err)
		}
	case launch.ModeUninstallService:
		if err := uninstallMirageService(); err != nil {
			log.Fatalf("mirage service uninstall failed: %v", err)
		}
	case launch.ModePrepareUpgrade:
		if err := runMiragePrepareUpgrade(); err != nil {
			log.Fatalf("mirage upgrade preparation failed: %v", err)
		}
	case launch.ModeFirewall:
		if err := runMirageFirewallHelper(); err != nil {
			log.Fatalf("mirage firewall helper failed: %v", err)
		}
	case launch.ModeSubprocess:
		if err := runMirageSubprocess(); err != nil {
			log.Fatalf("mirage subprocess failed: %v", err)
		}
	case launch.ModeService:
		if err := runMirageWindowsService(); err != nil {
			log.Fatalf("mirage service host failed: %v", err)
		}
	default:
		log.Fatalf("unknown launch mode %q", mode)
	}
}

func prepareMirageBackgroundRuntime() {
	envknob.ApplyDiskConfig()
}

func attachMirageBackgroundLog() func() {
	programData := os.Getenv("ProgramData")
	if programData == "" {
		return func() {}
	}
	logDir := filepath.Join(programData, "Mirage")
	if err := os.MkdirAll(logDir, 0o700); err != nil {
		log.Printf("creating log directory failed: %v", err)
		return func() {}
	}
	path := filepath.Join(logDir, "mirage-service.log")
	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
	if err != nil {
		log.Printf("opening background log %q failed: %v", path, err)
		return func() {}
	}
	log.SetFlags(log.LstdFlags | log.Lmicroseconds)
	log.SetOutput(f)
	return func() {
		_ = f.Close()
	}
}
