package divert

import (
	"fmt"
	"net"
	"strings"
)

// FilterParams collects the inputs needed to build a WinDivert filter
// expression for Drover's outbound capture.
type FilterParams struct {
	// TargetPIDs is the set of PIDs whose outbound traffic should be
	// captured (e.g. Discord variants). When empty, the resulting
	// filter is "false" — captures nothing — which is the right
	// behaviour while procscan reports zero Discord processes.
	TargetPIDs []uint32

	// OwnPID is drover.exe's own PID. Excluded from capture so our
	// SOCKS5 traffic to the upstream proxy doesn't get re-captured.
	OwnPID uint32

	// UpstreamIP is the resolved IPv4 of the upstream SOCKS5 proxy.
	// Excluded from capture as a second line of defence against
	// self-loops. If unparseable, "0.0.0.0" is substituted (caller
	// should validate before calling).
	UpstreamIP string
}

// BuildFilter returns a WinDivert filter expression string suitable
// for WinDivertOpen. The expression captures only outbound IPv4 TCP/UDP
// from the listed PIDs, excluding our own process and the upstream
// proxy's IP.
func BuildFilter(p FilterParams) string {
	if len(p.TargetPIDs) == 0 {
		return "false"
	}

	upstream := p.UpstreamIP
	if net.ParseIP(upstream).To4() == nil {
		upstream = "0.0.0.0"
	}

	pidClauses := make([]string, len(p.TargetPIDs))
	for i, pid := range p.TargetPIDs {
		pidClauses[i] = fmt.Sprintf("processId == %d", pid)
	}
	pidClause := "(" + strings.Join(pidClauses, " or ") + ")"

	parts := []string{
		"outbound",
		"(tcp or udp)",
		"ip",
		pidClause,
		fmt.Sprintf("processId != %d", p.OwnPID),
		fmt.Sprintf("ip.DstAddr != %s", upstream),
		"not (ip.DstAddr >= 224.0.0.0 and ip.DstAddr <= 239.255.255.255)",
		"not (ip.DstAddr >= 127.0.0.0 and ip.DstAddr <= 127.255.255.255)",
		"not (ip.DstAddr >= 169.254.0.0 and ip.DstAddr <= 169.254.255.255)",
	}
	return strings.Join(parts, " and ")
}
