package nucleus

import (
	"testing"

	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
	"code.fbi.h-da.de/danet/gosdn/controller/conflict"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
	"code.fbi.h-da.de/danet/gosdn/controller/mocks"
	"code.fbi.h-da.de/danet/gosdn/controller/store"
	"github.com/google/uuid"
	"github.com/stretchr/testify/mock"
)

func returnBasicTransportOption() tpb.TransportOption {
	return tpb.TransportOption{
		Address:  "test:///",
		Username: "test",
		Password: "test",
		TransportOption: &tpb.TransportOption_GnmiTransportOption{
			GnmiTransportOption: &tpb.GnmiTransportOption{
				Compression:     "",
				GrpcDialOptions: nil,
				Token:           "",
				Encoding:        0,
			},
		},
	}
}

func TestAddNetworkElement(t *testing.T) {
	defer ensureStoreFileForTestsIsRemoved(store.NetworkElementFilenameSuffix)

	mneID, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	pluginID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	trop := returnBasicTransportOption()

	plugin1 := &mocks.Plugin{}
	plugin1.On("ID").Return(pluginID1)
	plugin1.On("Model", mock.Anything).Return([]byte{}, nil)

	networkElementStore := NewNetworkElementStore()
	mne, _ := NewNetworkElement("testNetworkElement", mneID, &trop, defaultPndID, plugin1, conflict.Metadata{})

	err := networkElementStore.Add(mne)
	if err != nil {
		t.Error(err)
	}
}

func TestGetAllNetworkElements(t *testing.T) {
	defer ensureStoreFileForTestsIsRemoved(store.NetworkElementFilenameSuffix)

	networkElementStore := NewNetworkElementStore()

	pluginID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	plugin := &mocks.Plugin{}
	plugin.On("ID").Return(pluginID)
	plugin.On("Model", mock.Anything).Return([]byte{}, nil)

	mneID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	mneID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	transportOptions := returnBasicTransportOption()

	mne1, err := NewNetworkElement("testname", mneID1, &transportOptions, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	mne2, err := NewNetworkElement("testname2", mneID2, &transportOptions, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	inputNetworkElements := [2]networkelement.NetworkElement{mne1, mne2}

	for _, mne := range inputNetworkElements {
		err := networkElementStore.Add(mne)
		if err != nil {
			t.Error(err)
		}
	}

	returnedNetworkElements, err := networkElementStore.GetAll()
	if err != nil {
		t.Error(err)
	}

	length := len(returnedNetworkElements)
	if length != 2 {
		t.Errorf("GetAll() length of array = %v, want %v", length, 2)
	}

	for i, mne := range returnedNetworkElements {
		if mne.ID != inputNetworkElements[i].ID().String() {
			t.Errorf("GetAll() = %v, want %v", mne.ID, inputNetworkElements[i].ID().String())
		}
		if mne.Name != inputNetworkElements[i].Name() {
			t.Errorf("GetAll() = %v, want %v", mne.Name, inputNetworkElements[i].Name())
		}
	}
}

func TestGetNetworkElement(t *testing.T) {
	defer ensureStoreFileForTestsIsRemoved(store.NetworkElementFilenameSuffix)

	networkElementStore := NewNetworkElementStore()

	pluginID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	plugin := &mocks.Plugin{}
	plugin.On("ID").Return(pluginID)
	plugin.On("Model", mock.Anything).Return([]byte{}, nil)

	mneID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	mneID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	trop := returnBasicTransportOption()

	mne1, err := NewNetworkElement("testname", mneID1, &trop, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	mne2, err := NewNetworkElement("testname2", mneID2, &trop, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	inputNetworkElements := [2]networkelement.NetworkElement{mne1, mne2}

	for _, mne := range inputNetworkElements {
		err := networkElementStore.Add(mne)
		if err != nil {
			t.Error(err)
		}
	}

	returnNetworkElement, err := networkElementStore.Get(store.Query{ID: mneID2, Name: "testname2"})
	if err != nil {
		t.Error(err)
	}

	if returnNetworkElement.ID != inputNetworkElements[1].ID().String() {
		t.Errorf("Get() = %v, want %v", returnNetworkElement.ID, inputNetworkElements[1].ID().String())
	}
	if returnNetworkElement.Name != inputNetworkElements[1].Name() {
		t.Errorf("Get() = %v, want %v", returnNetworkElement.Name, inputNetworkElements[1].Name())
	}
}

func TestUpdateNetworkElement(t *testing.T) {
	defer ensureStoreFileForTestsIsRemoved(store.NetworkElementFilenameSuffix)

	mneID, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	pluginID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")

	trop := returnBasicTransportOption()

	updatedNetworkElementName := "testNetworkElement2"

	plugin1 := &mocks.Plugin{}
	plugin1.On("ID").Return(pluginID1)
	plugin1.On("Model", mock.Anything).Return([]byte{}, nil)

	networkElementStore := NewNetworkElementStore()
	mne, _ := NewNetworkElement("testNetworkElement", mneID, &trop, defaultPndID, plugin1, conflict.Metadata{})

	err := networkElementStore.Add(mne)
	if err != nil {
		t.Error(err)
	}

	mne, _ = NewNetworkElement(updatedNetworkElementName, mneID, &trop, defaultPndID, plugin1, conflict.Metadata{})

	err = networkElementStore.Update(mne)
	if err != nil {
		t.Error(err)
	}

	returnNetworkElement, err := networkElementStore.Get(store.Query{ID: mneID, Name: updatedNetworkElementName})
	if err != nil {
		t.Error(err)
	}

	if returnNetworkElement.ID != mneID.String() {
		t.Errorf("Get() = %v, want %v", returnNetworkElement.ID, mneID.String())
	}
	if returnNetworkElement.Name != updatedNetworkElementName {
		t.Errorf("Get() = %v, want %v", returnNetworkElement.Name, updatedNetworkElementName)
	}
}

func TestDeleteNetworkElement(t *testing.T) {
	defer ensureStoreFileForTestsIsRemoved(store.NetworkElementFilenameSuffix)

	networkElementStore := NewNetworkElementStore()

	pluginID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	plugin := &mocks.Plugin{}
	plugin.On("ID").Return(pluginID)
	plugin.On("Model", mock.Anything).Return([]byte{}, nil)

	mneID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	mneID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	trop := returnBasicTransportOption()

	mne1, err := NewNetworkElement("testname", mneID1, &trop, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	mne2, err := NewNetworkElement("testname2", mneID2, &trop, defaultPndID, plugin, conflict.Metadata{})
	if err != nil {
		t.Error(err)
	}

	inputNetworkElements := [2]networkelement.NetworkElement{mne1, mne2}

	for _, mne := range inputNetworkElements {
		err := networkElementStore.Add(mne)
		if err != nil {
			t.Error(err)
		}
	}

	err = networkElementStore.Delete(mne1)
	if err != nil {
		t.Error(err)
	}

	returnNetworkElements, err := networkElementStore.GetAll()
	if err != nil {
		t.Error(err)
	}

	length := len(returnNetworkElements)
	if length != 1 {
		t.Errorf("GetAll() length of array = %v, want %v", length, 2)
	}

	for _, mne := range returnNetworkElements {
		if mne.ID != inputNetworkElements[1].ID().String() {
			t.Errorf("GetAll() = %v, want %v", mne.ID, inputNetworkElements[1].ID().String())
		}
		if mne.Name != inputNetworkElements[1].Name() {
			t.Errorf("GetAll() = %v, want %v", mne.Name, inputNetworkElements[1].Name())
		}
	}
}
