package network

import (
	"fmt"
	"net"
	"time"

	"github.com/jetkvm/kvm/internal/hardware"
	"github.com/pion/mdns/v2"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netlink/nl"
)

var networkState struct {
	Up   bool
	IPv4 string
	IPv6 string
	MAC  string
}

type LocalIpInfo struct {
	IPv4 string
	IPv6 string
	MAC  string
}

func checkNetworkState() {
	iface, err := netlink.LinkByName("eth0")
	if err != nil {
		fmt.Printf("failed to get eth0 interface: %v\n", err)
		return
	}

	newState := struct {
		Up   bool
		IPv4 string
		IPv6 string
		MAC  string
	}{
		Up:  iface.Attrs().OperState == netlink.OperUp,
		MAC: iface.Attrs().HardwareAddr.String(),
	}

	addrs, err := netlink.AddrList(iface, nl.FAMILY_ALL)
	if err != nil {
		fmt.Printf("failed to get addresses for eth0: %v\n", err)
	}

	for _, addr := range addrs {
		if addr.IP.To4() != nil {
			newState.IPv4 = addr.IP.String()
		} else if addr.IP.To16() != nil && newState.IPv6 == "" {
			newState.IPv6 = addr.IP.String()
		}
	}

	if newState != networkState {
		networkState = newState
		fmt.Println("network state changed")
		hardware.RequestDisplayUpdate()
	}
}

func startMDNS() error {
	addr4, err := net.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4)
	if err != nil {
		return err
	}

	addr6, err := net.ResolveUDPAddr("udp6", mdns.DefaultAddressIPv6)
	if err != nil {
		return err
	}

	l4, err := net.ListenUDP("udp4", addr4)
	if err != nil {
		return err
	}

	l6, err := net.ListenUDP("udp6", addr6)
	if err != nil {
		return err
	}

	_, err = mdns.Server(ipv4.NewPacketConn(l4), ipv6.NewPacketConn(l6), &mdns.Config{
		LocalNames: []string{"jetkvm.local"}, //TODO: make it configurable
	})
	if err != nil {
		return err
	}
	//defer server.Close()
	return nil
}

func init() {
	updates := make(chan netlink.LinkUpdate)
	done := make(chan struct{})

	if err := netlink.LinkSubscribe(updates, done); err != nil {
		fmt.Println("failed to subscribe to link updates: %v", err)
		return
	}

	go func() {
		waitCtrlClientConnected()
		checkNetworkState()
		ticker := time.NewTicker(1 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case update := <-updates:
				if update.Link.Attrs().Name == "eth0" {
					fmt.Printf("link update: %+v\n", update)
					checkNetworkState()
				}
			case <-ticker.C:
				checkNetworkState()
			case <-done:
				return
			}
		}
	}()
	fmt.Println("Starting mDNS server")
	err := startMDNS()
	if err != nil {
		fmt.Println("failed to run mDNS: %v", err)
	}
}