package nucleus

import (
	"log"
	"os"
	"path/filepath"
	"testing"

	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
	"code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem"
	"code.fbi.h-da.de/danet/gosdn/controller/store"
	"github.com/google/uuid"
)

func ensureDeviceFilesForTestAreRemoved() {
	store.EnsureFilesystemStorePathExists(filesystem.DeviceFilenameSuffix)
	wildcartFilename := "*-" + filesystem.DeviceFilenameSuffix
	path := store.GetCompletePathToFileStore(wildcartFilename)

	files, err := filepath.Glob(path)

	if err != nil {
		log.Println(err)
	}
	for _, f := range files {
		if err := os.Remove(f); err != nil {
			log.Println(err)
		}
	}
}

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 TestAddDevice(t *testing.T) {
	ensureDeviceFilesForTestAreRemoved()
	defer ensureDeviceFilesForTestAreRemoved()

	pndID, _ := uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
	deviceID, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	sbiID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	trop := returnBasicTransportOption()

	sbi1, _ := NewSBI(spb.Type_TYPE_OPENCONFIG, sbiID1)

	deviceStore := NewDeviceStore(pndID)
	device, _ := NewDevice("testdevice", deviceID, &trop, sbi1)

	err := deviceStore.Add(device)
	if err != nil {
		t.Error(err)
	}
}

func TestGetAllDevices(t *testing.T) {
	ensureDeviceFilesForTestAreRemoved()
	defer ensureDeviceFilesForTestAreRemoved()

	pndID, _ := uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
	deviceStore := NewDeviceStore(pndID)

	sbiID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	sbi, _ := NewSBI(spb.Type_TYPE_OPENCONFIG, sbiID)

	deviceID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	deviceID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	transportOptions := returnBasicTransportOption()

	device1, err := NewDevice("testname", deviceID1, &transportOptions, sbi)
	if err != nil {
		t.Error(err)
	}

	device2, err := NewDevice("testname2", deviceID2, &transportOptions, sbi)
	if err != nil {
		t.Error(err)
	}

	inputDevices := [2]device.Device{device1, device2}

	for _, device := range inputDevices {
		err := deviceStore.Add(device)
		if err != nil {
			t.Error(err)
		}
	}

	returnedDevices, err := deviceStore.GetAll()
	if err != nil {
		t.Error(err)
	}

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

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

func TestGetDevice(t *testing.T) {
	ensureDeviceFilesForTestAreRemoved()
	defer ensureDeviceFilesForTestAreRemoved()

	pndID, _ := uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
	deviceStore := NewDeviceStore(pndID)

	sbiID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	sbi, _ := NewSBI(spb.Type_TYPE_OPENCONFIG, sbiID)

	deviceID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	deviceID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	trop := returnBasicTransportOption()

	device1, err := NewDevice("testname", deviceID1, &trop, sbi)
	if err != nil {
		t.Error(err)
	}

	device2, err := NewDevice("testname2", deviceID2, &trop, sbi)
	if err != nil {
		t.Error(err)
	}

	inputDevices := [2]device.Device{device1, device2}

	for _, device := range inputDevices {
		err := deviceStore.Add(device)
		if err != nil {
			t.Error(err)
		}
	}

	returnDevice, err := deviceStore.Get(store.Query{ID: deviceID2, Name: "testname2"})
	if err != nil {
		t.Error(err)
	}

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

func TestUpdateDevice(t *testing.T) {
	ensureDeviceFilesForTestAreRemoved()
	defer ensureDeviceFilesForTestAreRemoved()

	pndID, _ := uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
	deviceID, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	sbiID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")

	trop := returnBasicTransportOption()

	updatedDeviceName := "testdevice2"

	sbi1, _ := NewSBI(spb.Type_TYPE_OPENCONFIG, sbiID1)

	deviceStore := NewDeviceStore(pndID)
	device, _ := NewDevice("testdevice", deviceID, &trop, sbi1)

	err := deviceStore.Add(device)
	if err != nil {
		t.Error(err)
	}

	device, _ = NewDevice(updatedDeviceName, deviceID, &trop, sbi1)

	err = deviceStore.Update(device)
	if err != nil {
		t.Error(err)
	}

	returnDevice, err := deviceStore.Get(store.Query{ID: deviceID, Name: updatedDeviceName})
	if err != nil {
		t.Error(err)
	}

	if returnDevice.ID != deviceID.String() {
		t.Errorf("Get() = %v, want %v", returnDevice.ID, deviceID.String())
	}
	if returnDevice.Name != updatedDeviceName {
		t.Errorf("Get() = %v, want %v", returnDevice.Name, updatedDeviceName)
	}
}

func TestDeleteDevice(t *testing.T) {
	ensureDeviceFilesForTestAreRemoved()
	defer ensureDeviceFilesForTestAreRemoved()

	pndID, _ := uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad")
	deviceStore := NewDeviceStore(pndID)

	sbiID, _ := uuid.Parse("ssssssss-ssss-ssss-ssss-ssssssssssss")
	sbi, _ := NewSBI(spb.Type_TYPE_OPENCONFIG, sbiID)

	deviceID1, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
	deviceID2, _ := uuid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab")

	trop := returnBasicTransportOption()

	device1, err := NewDevice("testname", deviceID1, &trop, sbi)
	if err != nil {
		t.Error(err)
	}

	device2, err := NewDevice("testname2", deviceID2, &trop, sbi)
	if err != nil {
		t.Error(err)
	}

	inputDevices := [2]device.Device{device1, device2}

	for _, device := range inputDevices {
		err := deviceStore.Add(device)
		if err != nil {
			t.Error(err)
		}
	}

	err = deviceStore.Delete(device1)
	if err != nil {
		t.Error(err)
	}

	returnDevices, err := deviceStore.GetAll()
	if err != nil {
		t.Error(err)
	}

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

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