//go:build !windows

package deploy

import (
	"bufio"
	"net"
	"os"
	"os/exec"
	"strings"
)

// arpHosts returns IPv4 hosts in the ARP cache on the local subnets.
// On Linux it reads /proc/net/arp; on macOS/BSD it falls back to running
// `arp -n`. Returns nil if nothing useful is found (caller does full scan).
func arpHosts() []string {
	hosts := arpFromProc()
	if len(hosts) == 0 {
		hosts = arpFromCmd()
	}
	return hosts
}

// arpFromProc parses Linux's /proc/net/arp.
// Format: IP address  HW type  Flags  HW address  Mask  Device
func arpFromProc() []string {
	f, err := os.Open("/proc/net/arp")
	if err != nil {
		return nil
	}
	defer f.Close()

	local := getLocalIPs()
	subnets := make(map[string]bool)
	for _, ip := range local {
		subnets[getSubnet(ip)] = true
	}

	var hosts []string
	seen := make(map[string]bool)
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) < 4 {
			continue // skip header
		}
		ip := net.ParseIP(fields[0])
		if ip == nil || ip.To4() == nil {
			continue
		}
		// Flags = 0x0 means incomplete — skip
		if fields[2] == "0x0" {
			continue
		}
		ipStr := ip.To4().String()
		sub := getSubnet(ipStr)
		if !subnets[sub] || seen[ipStr] {
			continue
		}
		seen[ipStr] = true
		hosts = append(hosts, ipStr)
	}
	_ = scanner.Err()
	return hosts
}

// arpFromCmd runs `arp -n` for macOS/BSD where /proc/net/arp doesn't exist.
func arpFromCmd() []string {
	raw, err := exec.Command("arp", "-n").Output()
	if err != nil {
		return nil
	}
	out := string(raw)

	local := getLocalIPs()
	subnets := make(map[string]bool)
	for _, ip := range local {
		subnets[getSubnet(ip)] = true
	}

	var hosts []string
	seen := make(map[string]bool)
	for _, line := range strings.Split(out, "\n") {
		fields := strings.Fields(line)
		if len(fields) < 2 {
			continue
		}
		ip := net.ParseIP(fields[0])
		if ip == nil || ip.To4() == nil || ip.IsLoopback() {
			continue
		}
		// arp -n marks incomplete entries as "(incomplete)"
		if strings.Contains(line, "incomplete") {
			continue
		}
		ipStr := ip.To4().String()
		sub := getSubnet(ipStr)
		if !subnets[sub] || seen[ipStr] {
			continue
		}
		seen[ipStr] = true
		hosts = append(hosts, ipStr)
	}
	return hosts
}
