package nucleus

import (
	"testing"

	rpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-registry"
	eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin"
	"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"
	"google.golang.org/grpc"
)

func getMockNetworkElement(mneID uuid.UUID, plugin plugin.Plugin) (networkelement.NetworkElement, error) {
	return &CommonNetworkElement{
		UUID:      mneID,
		Plugin:    plugin,
		transport: &mocks.Transport{},
		pndID:     uuid.New(),
	}, nil
}

func getNetworkElementTestStores(t *testing.T, mneID uuid.UUID) (networkelement.Service, plugin.Service, networkelement.NetworkElement, plugin.Plugin) {
	mockPluginID, err := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	if err != nil {
		t.Error(err)
	}
	mockPlugin := &mocks.Plugin{}
	mockPlugin.On("ID").Return(mockPluginID)
	mockPlugin.On("Model", mock.Anything).Return([]byte("hello"), nil)
	mockPlugin.On("SetNode", mock.Anything, mock.Anything).Return(nil)
	eventService := eventservice.NewMockEventService()
	pluginStore := NewMemoryPluginStore()
	networkElementStore := NewMemoryNetworkElementStore()
	pluginService := NewPluginService(pluginStore, eventService, func(lp plugin.LoadedPlugin) (plugin.Plugin, error) {
		return mockPlugin, nil
	}, rpb.NewPluginRegistryServiceClient(&grpc.ClientConn{}))
	networkElementService := NewNetworkElementService(
		networkElementStore,
		pluginService,
		eventService,
	)

	err = pluginService.Add(mockPlugin)
	if err != nil {
		t.Error("could not add plugin: ", err)
	}

	mockNetworkElement, err := getMockNetworkElement(mneID, mockPlugin)
	if err != nil {
		t.Log(err)
	}
	err = networkElementService.Add(mockNetworkElement)
	if err != nil {
		t.Error("could not add network element: ", err)
	}

	return networkElementService, pluginService, mockNetworkElement, mockPlugin
}

func TestNetworkElementService_Get(t *testing.T) {
	mneID := uuid.New()

	networkElementService, _, mockNetworkElement, _ := getNetworkElementTestStores(t, mneID)

	mne, err := networkElementService.Get(store.Query{
		ID:   mockNetworkElement.ID(),
		Name: mockNetworkElement.Name(),
	})
	if err != nil {
		t.Error("could not get network element: ", err)
	}

	if mockNetworkElement.ID() != mne.ID() {
		t.Errorf("Expected ID=%s, got %s", mockNetworkElement.ID(), mne.ID())
	}
}

func TestNetworkElementService_Delete(t *testing.T) {
	mneID := uuid.New()

	networkElementService, _, mockNetworkElement, _ := getNetworkElementTestStores(t, mneID)

	mne, err := networkElementService.Get(store.Query{
		ID:   mockNetworkElement.ID(),
		Name: mockNetworkElement.Name(),
	})
	if err != nil {
		t.Error("could not get network element: ", err)
	}

	err = networkElementService.Delete(mne)
	if err != nil {
		t.Error("could not delete network element: ", err)
	}
}

func TestNetworkElementService_GetAll(t *testing.T) {
	mneID := uuid.New()
	mneID2 := uuid.New()

	networkElementService, _, _, plugin := getNetworkElementTestStores(t, mneID)
	mockNetworkElement2, err := getMockNetworkElement(mneID2, plugin)
	if err != nil {
		t.Log(err)
	}

	err = networkElementService.Add(mockNetworkElement2)
	if err != nil {
		t.Error("could not add network element: ", err)
	}

	mnes, err := networkElementService.GetAll()
	if err != nil {
		t.Error("could not get all network elements: ", err)
	}

	if len(mnes) != 2 {
		t.Errorf("Expected len(network elements)=2, got %d", len(mnes))
	}
}
