package routeupdater

import (
	"net"
	"testing"

	"github.com/filtr/infrastructure/go/openvpn-route-updater/internal/utility"
	"github.com/stretchr/testify/assert"

	"go.uber.org/zap/zaptest"
)

func TestCli(t *testing.T) {
	logger := zaptest.NewLogger(t).Sugar()
	testCli := &OpenVPNCli{log: logger, CliPath: "clipath"}
	assert.Equal(t, testCli, NewCli(logger, "clipath"))
}

func TestGroups(t *testing.T) {
	logger := zaptest.NewLogger(t).Sugar()
	cli := &testClient{log: logger, CliPath: "clipath"}
	//NewGroup
	OVPNGroup, err := NewGroup(logger, "Test group 2", cli)
	if err != nil {
		assert.FailNowf(t, "Can't initialize new group", err.Error())
	}
	OVPNGroupManager := &OpenVPNGroupManager{Group: OVPNGroup, log: logger}
	assert.Equal(t, "access_to.1", OVPNGroup.PublicRoutes[0].RouteID, "Test refresh")
	// Testing that route sort works as well
	assert.Equal(t, "access_to.0", OVPNGroup.PrivateRoutes[0].RouteID, "Test refresh")

	//Reindex
	assert.True(t, OVPNGroupManager.ReindexRoutes(), "Test routes reindex")
	assert.Equal(t, "access_to.2", OVPNGroup.PublicRoutes[0].RouteID, "Test private route reindex")

	//Refresh
	if err = OVPNGroupManager.Refresh(cli); err != nil {
		assert.FailNowf(t, "Can't refresh group settings", err.Error())
	}
	assert.Equal(t, "access_to.1", OVPNGroup.PublicRoutes[0].RouteID, "Test refresh")

	//DeletePublicRoutesCli and
	assert.True(t, OVPNGroupManager.ReindexRoutes(), "Test routes reindex")
	err = OVPNGroupManager.DeletePublicRoutesCli(cli)
	if err != nil {
		assert.FailNowf(t, "Can't delete public routes", err.Error())
	}
	assert.Nil(t, OVPNGroup.PublicRoutes)
	err = OVPNGroupManager.DeletePrivateRoutesCli(cli)
	if err != nil {
		assert.FailNowf(t, "Can't delete private routes", err.Error())
	}
	assert.Nil(t, OVPNGroup.PrivateRoutes)

	//GetPublicRouteValues GetPrivateRouteValues
	err = OVPNGroupManager.Refresh(cli)
	if err != nil {
		assert.FailNowf(t, "Can't refresh group settings", err.Error())
	}
	_ = OVPNGroupManager.ReindexRoutes()

	routes := OVPNGroupManager.GetPublicRouteValues()
	assert.Nil(t, utility.TwoSidedDiff(routes, []string{"+SUBNET:8.8.8.8/32"}), "GetPublicRouteValues")
	routes = OVPNGroupManager.GetPrivateRouteValues()
	assert.Nil(t, utility.TwoSidedDiff(routes, []string{"+SUBNET:10.10.0.0/16", "+SUBNET:10.50.0.0/16"}), "GetPrivateRouteValues")

	//SetPrivateRoutes SetPublicRoutes
	testPrivateRoutes := []string{"+SUBNET:10.50.0.0/16", "+SUBNET:10.51.0.0/16", "+SUBNET:10.52.0.0/16", "+SUBNET:10.53.0.0/16"}
	err = OVPNGroupManager.SetPrivateRoutes(testPrivateRoutes)
	if err != nil {
		assert.FailNowf(t, "Can't set private routes", err.Error())
	}
	assert.Nil(t, utility.TwoSidedDiff(OVPNGroupManager.GetPrivateRouteValues(), testPrivateRoutes), "SetPrivateRoutes")

	testPublicRoutes := []string{"+SUBNET:10.50.0.0/16", "+SUBNET:11.51.0.0/16", "+SUBNET:11.52.0.0/16", "+SUBNET:11.53.0.0/16"}
	expectedRoutes := []string{"+SUBNET:11.51.0.0/16", "+SUBNET:11.52.0.0/16", "+SUBNET:11.53.0.0/16"}
	err = OVPNGroupManager.SetPublicRoutes(testPublicRoutes)
	if err != nil {
		assert.FailNowf(t, "Can't set public routes", err.Error())
	}
	assert.Nil(t, utility.TwoSidedDiff(OVPNGroupManager.GetPublicRouteValues(), expectedRoutes), "SetPrivateRoutes")

	// Apply
	assert.Nil(t, OVPNGroupManager.ApplyPublicRoutes(cli), "ApplyPublicRoutes")
	assert.Nil(t, OVPNGroupManager.ApplyPrivateRoutes(cli), "ApplyPrivateRoutes")
	//_ = OVPNGroupManager.ReindexRoutes()
}

func TestUtility(t *testing.T) {
	testRouteNet := "+SUBNET:10.50.0.0/16"
	testRouteIP := "+SUBNET:8.8.8.8/32"
	_, testNet, err := net.ParseCIDR("10.50.0.0/16")
	if err != nil {
		assert.FailNowf(t, "Failed to parse test cidr", err.Error())
	}
	testIP := net.ParseIP("8.8.8.8")

	//IsRoutePrivate
	isPrivate, err := IsRoutePrivate(testRouteNet)
	if err != nil {
		assert.FailNowf(t, "IsRoutePrivate", err.Error())
	}
	assert.True(t, isPrivate, "IsRoutePrivate")
	isPrivate, err = IsRoutePrivate(testRouteIP)
	if err != nil {
		assert.FailNowf(t, "IsRoutePrivate", err.Error())
	}
	assert.False(t, isPrivate, "IsRoutePrivate")

	// RouteValueToIPNet
	ipNet, err := RouteValueToIPNet(testRouteNet)
	if err != nil {
		assert.FailNowf(t, "RouteValueToIPNet", err.Error())
	}
	assert.Equal(t, testNet, ipNet, "RouteValueToIPNet")

	// IPNetToRouteValue convert IPNet to route string
	assert.Equal(t, testRouteNet, IPNetToRouteValue(testNet), "IPNetToRouteValue")

	// SingleIPToRouteValue convert single IP address to route string format
	assert.Equal(t, testRouteIP, SingleIPToRouteValue(testIP), "SingleIPToRouteValue")

	testIPList := []net.IP{net.ParseIP("8.8.8.8").To4(), net.ParseIP("8.8.8.10").To4(), net.ParseIP("118.18.11.11").To4()}
	expectedRoutesIPList := []string{"+SUBNET:8.8.8.8/32", "+SUBNET:8.8.8.10/32", "+SUBNET:118.18.11.11/32"}
	assert.Equal(t, expectedRoutesIPList, IPListToRouteValues(testIPList), "IPListToRouteValues")

	expectedIPList := []net.IP{net.ParseIP("8.8.8.8").To4(), net.ParseIP("8.8.8.10").To4(), net.ParseIP("118.18.11.11").To4()}
	testRoutesIPList := []string{"+SUBNET:8.8.8.8/32", "+SUBNET:8.8.8.10/32", "+SUBNET:118.18.11.11/32"}
	testIPList, err = RoutesToIPList(testRoutesIPList)
	if err != nil {
		assert.FailNowf(t, "RoutesToIPList", err.Error())
	}
	assert.Equal(t, expectedIPList, testIPList)
}
