package wgfunc

import (
	"golang.zx2c4.com/wireguard/wgctrl"
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
	"testing"
)
import "github.com/vishvananda/netlink"
import "github.com/stretchr/testify/assert"



func TestRemoveDeviceIfIPRangeInvalide(t *testing.T) {
	serverConfig := WireGuardConfig{
		Name: "wgUnit",
		IPRanges: []string{"10.0.0.1/24", "abc/24"},
	}
	server, err := NewWireGuard(serverConfig)
	assert.Nil(t, err, "shouldn't return error while creating server")

	err = server.Up()
	if err == nil || err.Error() != "invalid CIDR address: abc/24" {
		t.Errorf("Should return invalide CIDR error")
	}

	foundDevice := checkForNetworkDevice("wgUnit", t)

	assert.Falsef(t, foundDevice, "should not fount device with name wgUnit")
}


func TestRemoveDeviceIfPrivateKeyIsInvalide(t *testing.T) {
	serverConfig := WireGuardConfig{
		Name: "wgUnit",
		IPRanges: []string{"10.0.0.1/24"},
	}
	server, err := NewWireGuard(serverConfig)
	assert.Nil(t, err, "shouldn't return error while creating server")

	err = server.Up()
	if err == nil || err.Error() != "wgtypes: incorrect key size: 0" {
		t.Errorf("Should return invalide key size error")
	}

	foundDevice := checkForNetworkDevice("wgUnit", t)

	assert.Falsef(t, foundDevice, "should not fount device with name wgUnit")
}

func TestCantRunSetupWireguardTwice(t *testing.T) {
	server := WireGuard{}
	server.name = "wgUnit"
	key, err := wgtypes.GeneratePrivateKey()
	assert.Nil(t, err, "cant create private key")
	server.wgConfig.PrivateKey = &key

	err = server.Up()
	if err == nil || err.Error() != "Wireguard already setedup" {
		t.Errorf("Should fail setup wireguard if privatkey is already set")
	}

	foundDevice := checkForNetworkDevice("wgUnit", t)

	assert.Falsef(t, foundDevice, "should not fount device with name wgUnit")
}

func TestNoWireguardDevicesExists(t *testing.T) {
	client, err := wgctrl.New()
	assert.Nil(t, err, "cant create wgctrl client")

	devices, err := client.Devices()
	assert.Nil(t, err, "cant get devices from wgctrl client")

	assert.Equalf(t, 0, len(devices), "there are wireguard devices")
}

func TestCreateWireguarderver(t *testing.T) {
	client, err := wgctrl.New()
	assert.Nil(t, err, "cant create wgctrl client")

	key, err := wgtypes.GeneratePrivateKey()
	assert.Nil(t, err, "cant create private key")

	serverConfig := WireGuardConfig{
		Name:       "wgUnit",
		IPRanges:   []string{"10.0.0.1/24"},
		Port:       54123,
		PrivateKey: key.String(),
	}
	server, err := NewWireGuard(serverConfig)
	assert.Nil(t, err, "cant create server")

	err = server.Up()

	foundDevice := checkForNetworkDevice("wgUnit", t)

	assert.True(t, foundDevice, "should fount device with name wgUnit")

	devices, err := client.Devices()
	assert.Nil(t, err, "cant get devices from wgctrl client")

	assert.Equalf(t, 1, len(devices), "there are wireguard devices")

	helpTearDown(server, t)
}

func TestCantAddPeerIfServerNotUp(t *testing.T) {
	serverConfig := WireGuardConfig{
		Name:       "wgUnit",
	}
	server, err := NewWireGuard(serverConfig)
	assert.Nil(t, err, "cant create server")

	peer := PeerConfig{}
	err = server.AddPeer(peer)
	if err == nil || err.Error() != "server not up" {
		t.Errorf("should not be able to add peer if server is offline")
	}

}

func TestCantAddPeerIfPublicKeyIsInvalide(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)

	config := PeerConfig{
		PublicKey:    "abc",
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "",
	}
	err := server.AddPeer(config)
	if err == nil || err.Error() != "wgtypes: failed to parse base64-encoded key: illegal base64 data at input byte 0" {
		t.Errorf("should not be able to add peer with wrong pub key")
	}
}

func TestCantAddPeerIfPublicKeyIsEmpty(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)

	config := PeerConfig{
		PublicKey:    "",
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "",
	}
	err := server.AddPeer(config)
	if err == nil || err.Error() != "wgtypes: incorrect key size: 0" {
		t.Errorf("should not be able to add peer with wrong pub key")
	}
}

func TestAddPeerInvalideEndpointIP(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)
	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "abc:123",
	}
	err := server.AddPeer(config)

	assert.Equalf(t, "Invalide endpoint ip", err.Error(), "shouldnt be able to add peer with invalide endpoint ip")
}

func TestAddPeerInvalideEndpointPort(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)
	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "127.0.0.1:abv",
	}
	err := server.AddPeer(config)

	assert.Equalf(t, "Cant convert port to int: strconv.Atoi: parsing \"abv\": invalid syntax", err.Error(), "shouldnt be able to add peer with invalide endpoint port")
}

func TestAddPeerWithEndpoitnDomain(t *testing.T) {
	t.Skipf("Not implemented now")
	server := helperCreateServer(t)
	defer helpTearDown(server, t)
	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "home.kekskurse.de:123",
	}
	err := server.AddPeer(config)

	assert.Nil(t, err, "should be able to add peer with domain as endpoint")
}

func TestAddPeerWithInvalideAllowedIP(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)
	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.g.3/32"},
		Endpoint:     "home.kekskurse.de:123",
	}
	err := server.AddPeer(config)

	assert.Equalf(t, "invalid CIDR address: 10.0.g.3/32", err.Error(), "should not allow peer with invalide allowed ip")

	config = PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/54"},
		Endpoint:     "home.kekskurse.de:123",
	}
	err = server.AddPeer(config)
	assert.Equalf(t, "invalid CIDR address: 10.0.0.3/54", err.Error(), "should not allow peer with invalide allowed ip")

}

func TestAddPeer(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)

	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "",
	}
	err := server.AddPeer(config)
	assert.Nil(t, err, "should be able to add peer")

	assert.Equalf(t, key.PublicKey().String(), server.wgConfig.Peers[0].PublicKey.String(), "public key should be in the wgConfig Object")
	assert.Equal(t, "10.0.0.3/32", server.wgConfig.Peers[0].AllowedIPs[0].String(), "allowd ip should be set currect")
}

func TestRemovePeer(t *testing.T) {
	server := helperCreateServer(t)
	defer helpTearDown(server, t)

	key, _ := wgtypes.GeneratePrivateKey()

	config := PeerConfig{
		PublicKey:    key.PublicKey().String(),
		PreSharedKey: "",
		AllowedIPs:   []string{"10.0.0.3/32"},
		Endpoint:     "",
	}
	err := server.AddPeer(config)
	assert.Nil(t, err, "should be able to add peer")

	err = server.RemovePeer(config.PublicKey)
	assert.Nil(t, err, "should be able to remove peer")

	assert.Equalf(t, 0, len(server.wgConfig.Peers), "peer list should be empty again")

}

func helperCreateServer(t *testing.T) WireGuard {
	key, err := wgtypes.GeneratePrivateKey()
	assert.Nil(t, err, "cant create private key")

	serverConfig := WireGuardConfig{
		Name:       "wgUnit",
		IPRanges:   []string{"10.0.0.1/24"},
		Port:       54123,
		PrivateKey: key.String(),
	}
	server, err := NewWireGuard(serverConfig)
	assert.Nil(t, err, "cant create server")

	err = server.Up()

	foundDevice := checkForNetworkDevice("wgUnit", t)

	assert.True(t, foundDevice, "should fount device with name wgUnit")

	return server
}

func helpTearDown(server WireGuard, t *testing.T) {
	err := server.Down()
	assert.Nil(t, err, "should not return error while shutting down server")

	foundDevice := checkForNetworkDevice(server.name, t)
	assert.Falsef(t, foundDevice, "device should not exist anymore")
}

func checkForNetworkDevice(name string, t *testing.T) bool {
	res, err := netlink.LinkList()

	assert.Nil(t, err, "shouldn't return an error return devices")

	foundDevice := false
	for _, device := range res {
		if device.Attrs().Name == name {
			foundDevice = true
		}
	}

	return foundDevice
}