package collector

import (
	"os/exec"
	"strconv"
	"strings"
	"time"
)

type WireGuardTunnel struct {
	Interface  string           `json:"interface"`
	PublicKey  string           `json:"public_key"`
	ListenPort int             `json:"listen_port"`
	Peers      []WireGuardPeer `json:"peers"`
}

type WireGuardPeer struct {
	PublicKey       string   `json:"public_key"`
	Endpoint        string   `json:"endpoint"`
	AllowedIPs      []string `json:"allowed_ips"`
	LatestHandshake int64    `json:"latest_handshake_epoch"`
	HandshakeAge    string   `json:"handshake_age"`
	TransferRx      int64    `json:"transfer_rx_bytes"`
	TransferTx      int64    `json:"transfer_tx_bytes"`
	Keepalive       int      `json:"persistent_keepalive"`
	Status          string   `json:"status"` // "active", "inactive", "unknown"
}

func CollectWireGuard() []WireGuardTunnel {
	// wg show all dump: maschinenlesbares Format
	// Voller Pfad, da daemon(8) evtl. reduzierten PATH hat
	wgPath := "/usr/bin/wg"
	if _, err := exec.LookPath("wg"); err == nil {
		wgPath = "wg"
	}
	out, err := exec.Command(wgPath, "show", "all", "dump").Output()
	if err != nil {
		return nil
	}

	tunnels := make(map[string]*WireGuardTunnel)
	var order []string

	for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
		if line == "" {
			continue
		}
		fields := strings.Split(line, "\t")
		if len(fields) < 5 {
			continue
		}

		iface := fields[0]

		// Interface-Zeile: iface privkey pubkey listenport fwmark
		if _, exists := tunnels[iface]; !exists {
			port := 0
			if len(fields) >= 4 {
				port, _ = strconv.Atoi(fields[3])
			}
			pubkey := ""
			if len(fields) >= 3 {
				pubkey = fields[2]
			}
			tunnels[iface] = &WireGuardTunnel{
				Interface:  iface,
				PublicKey:  pubkey,
				ListenPort: port,
			}
			order = append(order, iface)
			continue
		}

		// Peer-Zeile: iface pubkey presharedkey endpoint allowedips latest-handshake transfer-rx transfer-tx keepalive
		if len(fields) < 9 {
			continue
		}

		peer := WireGuardPeer{
			PublicKey: fields[1],
			Endpoint:  fields[3],
		}

		// Allowed IPs
		if fields[4] != "(none)" {
			peer.AllowedIPs = strings.Split(fields[4], ",")
		}

		// Latest handshake (epoch)
		if ts, err := strconv.ParseInt(fields[5], 10, 64); err == nil && ts > 0 {
			peer.LatestHandshake = ts
			age := time.Since(time.Unix(ts, 0))
			if age < 3*time.Minute {
				peer.Status = "active"
			} else if age < 10*time.Minute {
				peer.Status = "inactive"
			} else {
				peer.Status = "inactive"
			}
			// Menschenlesbare Altersangabe
			if age.Hours() >= 24 {
				peer.HandshakeAge = strconv.Itoa(int(age.Hours()/24)) + "d " + strconv.Itoa(int(age.Hours())%24) + "h ago"
			} else if age.Hours() >= 1 {
				peer.HandshakeAge = strconv.Itoa(int(age.Hours())) + "h " + strconv.Itoa(int(age.Minutes())%60) + "m ago"
			} else if age.Minutes() >= 1 {
				peer.HandshakeAge = strconv.Itoa(int(age.Minutes())) + "m " + strconv.Itoa(int(age.Seconds())%60) + "s ago"
			} else {
				peer.HandshakeAge = strconv.Itoa(int(age.Seconds())) + "s ago"
			}
		} else {
			peer.Status = "unknown"
			peer.HandshakeAge = "never"
		}

		// Transfer RX/TX
		peer.TransferRx, _ = strconv.ParseInt(fields[6], 10, 64)
		peer.TransferTx, _ = strconv.ParseInt(fields[7], 10, 64)

		// Keepalive
		if fields[8] != "off" {
			peer.Keepalive, _ = strconv.Atoi(fields[8])
		}

		tunnels[iface].Peers = append(tunnels[iface].Peers, peer)
	}

	// Geordnet zurueckgeben
	var result []WireGuardTunnel
	for _, name := range order {
		result = append(result, *tunnels[name])
	}
	return result
}
