//go:build linux && !android

package device

import (
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"

	"github.com/netbirdio/netbird/client/iface/wgaddr"
)

type wgLink struct {
	attrs *netlink.LinkAttrs
}

func newWGLink(name string) *wgLink {
	attrs := netlink.NewLinkAttrs()
	attrs.Name = name

	return &wgLink{
		attrs: &attrs,
	}
}

// Attrs returns the Wireguard's default attributes
func (l *wgLink) Attrs() *netlink.LinkAttrs {
	return l.attrs
}

// Type returns the interface type
func (l *wgLink) Type() string {
	return "wireguard"
}

// Close deletes the link interface
func (l *wgLink) Close() error {
	return netlink.LinkDel(l)
}

func (l *wgLink) recreate() error {
	name := l.attrs.Name

	// check if interface exists
	link, err := netlink.LinkByName(name)
	if err != nil {
		switch err.(type) {
		case netlink.LinkNotFoundError:
			break
		default:
			return fmt.Errorf("link by name: %w", err)
		}
	}

	// remove if interface exists
	if link != nil {
		err = netlink.LinkDel(l)
		if err != nil {
			return err
		}
	}

	log.Debugf("adding device: %s", name)
	err = netlink.LinkAdd(l)
	if os.IsExist(err) {
		log.Infof("interface %s already exists. Will reuse.", name)
	} else if err != nil {
		return fmt.Errorf("link add: %w", err)
	}

	return nil
}

func (l *wgLink) setMTU(mtu int) error {
	if err := netlink.LinkSetMTU(l, mtu); err != nil {
		log.Errorf("error setting MTU on interface: %s", l.attrs.Name)

		return fmt.Errorf("link set mtu: %w", err)
	}

	return nil
}

func (l *wgLink) up() error {
	if err := netlink.LinkSetUp(l); err != nil {
		log.Errorf("error bringing up interface: %s", l.attrs.Name)
		return fmt.Errorf("link setup: %w", err)
	}

	return nil
}

func (l *wgLink) assignAddr(address wgaddr.Address) error {
	//delete existing addresses
	list, err := netlink.AddrList(l, 0)
	if err != nil {
		return fmt.Errorf("list addr: %w", err)
	}

	if len(list) > 0 {
		for _, a := range list {
			addr := a
			err = netlink.AddrDel(l, &addr)
			if err != nil {
				return fmt.Errorf("del addr: %w", err)
			}
		}
	}

	name := l.attrs.Name
	addrStr := address.String()

	log.Debugf("adding address %s to interface: %s", addrStr, name)

	addr, err := netlink.ParseAddr(addrStr)
	if err != nil {
		return fmt.Errorf("parse addr: %w", err)
	}

	err = netlink.AddrAdd(l, addr)
	if os.IsExist(err) {
		log.Infof("interface %s already has the address: %s", name, addrStr)
	} else if err != nil {
		return fmt.Errorf("add addr: %w", err)
	}

	// On linux, the link must be brought up
	if err := netlink.LinkSetUp(l); err != nil {
		return fmt.Errorf("link setup: %w", err)
	}

	return nil
}
