//go:build windows

package deploy

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"sync"
	"syscall"
)

// TunnelKind identifies a tracked tunnel process.
type TunnelKind string

const (
	TunnelCloudflared TunnelKind = "cloudflared"
	TunnelSSHForward  TunnelKind = "ssh_forward"
)

// SSHForwardMeta describes one local port forward on the agent.
type SSHForwardMeta struct {
	LocalPort  int    `json:"local_port"`
	RemoteHost string `json:"remote_host"`
	RemotePort int    `json:"remote_port"`
	SSHUser    string `json:"ssh_user,omitempty"`
	JumpHost   string `json:"jump_host,omitempty"`
}

type tunnelEntry struct {
	kind TunnelKind
	pid  int
	meta string // JSON metadata for ssh forwards; URL for cloudflared
}

var (
	tunnelMu        sync.Mutex
	trackedTunnels  []tunnelEntry
)

// RegisterTunnelPID records a background tunnel process for later stop/status.
func RegisterTunnelPID(kind TunnelKind, pid int, meta string) {
	if pid <= 0 {
		return
	}
	tunnelMu.Lock()
	defer tunnelMu.Unlock()
	if kind == TunnelCloudflared {
		next := make([]tunnelEntry, 0, len(trackedTunnels)+1)
		for _, t := range trackedTunnels {
			if t.kind != TunnelCloudflared {
				next = append(next, t)
			}
		}
		trackedTunnels = append(next, tunnelEntry{kind: kind, pid: pid, meta: meta})
		return
	}
	trackedTunnels = append(trackedTunnels, tunnelEntry{kind: kind, pid: pid, meta: meta})
}

const processQueryLimitedInformation = 0x1000

var processAliveFn = processAliveImpl

func processAlive(pid int) bool {
	return processAliveFn(pid)
}

func processAliveImpl(pid int) bool {
	if pid <= 0 {
		return false
	}
	h, err := syscall.OpenProcess(processQueryLimitedInformation, false, uint32(pid))
	if err != nil {
		return false
	}
	defer syscall.CloseHandle(h)
	var code uint32
	err = syscall.GetExitCodeProcess(h, &code)
	if err != nil {
		return false
	}
	return code == 259 // STILL_ACTIVE
}

func killPID(pid int) error {
	if pid <= 0 {
		return fmt.Errorf("invalid pid")
	}
	return HiddenRun("taskkill", "/F", "/PID", fmt.Sprintf("%d", pid))
}

func pruneDead() {
	alive := trackedTunnels[:0]
	for _, t := range trackedTunnels {
		if processAlive(t.pid) {
			alive = append(alive, t)
		}
	}
	trackedTunnels = alive
}

// TunnelStatus returns JSON describing tracked tunnel processes.
func TunnelStatus() string {
	tunnelMu.Lock()
	pruneDead()
	snap := append([]tunnelEntry(nil), trackedTunnels...)
	tunnelMu.Unlock()

	st := TunnelStatusJSON{SSHForwards: []SSHForwardLive{}}
	for _, t := range snap {
		switch t.kind {
		case TunnelCloudflared:
			st.CloudflaredRunning = true
			st.CloudflaredURL = t.meta
			st.CloudflaredPID = t.pid
		case TunnelSSHForward:
			var meta SSHForwardMeta
			_ = json.Unmarshal([]byte(t.meta), &meta)
			st.SSHForwards = append(st.SSHForwards, SSHForwardLive{
				LocalPort:  meta.LocalPort,
				RemoteHost: meta.RemoteHost,
				RemotePort: meta.RemotePort,
				SSHUser:    meta.SSHUser,
				JumpHost:   meta.JumpHost,
				PID:        t.pid,
				Running:    true,
			})
		}
	}
	b, _ := json.Marshal(st)
	return string(b)
}

// StopTunnels stops tracked tunnel kinds. Empty kinds stops all.
func StopTunnels(kinds ...TunnelKind) (stopped int, msgs []string) {
	tunnelMu.Lock()
	defer tunnelMu.Unlock()
	pruneDead()

	wantAll := len(kinds) == 0
	want := map[TunnelKind]bool{}
	for _, k := range kinds {
		want[k] = true
	}

	remaining := trackedTunnels[:0]
	for _, t := range trackedTunnels {
		if wantAll || want[t.kind] {
			if err := killPID(t.pid); err != nil {
				msgs = append(msgs, fmt.Sprintf("%s pid %d: %v", t.kind, t.pid, err))
			} else {
				stopped++
				msgs = append(msgs, fmt.Sprintf("stopped %s pid %d", t.kind, t.pid))
			}
			continue
		}
		remaining = append(remaining, t)
	}
	trackedTunnels = remaining
	return stopped, msgs
}

// ResetTrackedTunnels clears registry without killing (tests only).
func ResetTrackedTunnels() {
	tunnelMu.Lock()
	trackedTunnels = nil
	tunnelMu.Unlock()
}

// CloudflaredTargetFromEnv returns trimmed URL or empty.
func CloudflaredTargetFromEnv() string {
	return strings.TrimSpace(os.Getenv("AETHERFORGE_TUNNEL_URL"))
}
