package nucleus

import (
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
	"code.fbi.h-da.de/danet/gosdn/controller/store"
	"github.com/google/uuid"
)

// NetworkElementServiceMock provides a in-memory implementation for multiple stores.
type NetworkElementServiceMock struct {
	Store           map[uuid.UUID]networkelement.NetworkElement
	nameLookupTable map[string]uuid.UUID
}

// NewNetworkElementServiceMock returns a specific in-memory store for network element service.
func NewNetworkElementServiceMock() networkelement.Service {
	return &NetworkElementServiceMock{
		Store:           make(map[uuid.UUID]networkelement.NetworkElement),
		nameLookupTable: make(map[string]uuid.UUID),
	}
}

// Add adds a item network element.
func (t *NetworkElementServiceMock) Add(item networkelement.NetworkElement) error {
	_, ok := t.Store[item.ID()]
	if ok {
		return nil
	}

	t.Store[item.ID()] = item
	t.nameLookupTable[item.Name()] = item.ID()

	return nil
}

// Update updates a item network element.
func (t *NetworkElementServiceMock) Update(item networkelement.NetworkElement) error {
	_, ok := t.Store[item.ID()]
	if ok {
		return nil
	}

	t.Store[item.ID()] = item
	t.nameLookupTable[item.Name()] = item.ID()

	return nil
}

// UpdateModel updates a item network element.
func (t *NetworkElementServiceMock) UpdateModel(item uuid.UUID, model string) error {
	return nil
}

// Delete deletes a item network element.
func (t *NetworkElementServiceMock) Delete(item networkelement.NetworkElement) error {
	delete(t.Store, item.ID())

	return nil
}

// Get gets a item network element.
func (t *NetworkElementServiceMock) Get(query store.Query) (networkelement.NetworkElement, error) {
	// First search for direct hit on UUID.
	item, ok := t.Store[query.ID]
	if !ok {
		// Second search for name
		id, ok := t.nameLookupTable[query.Name]
		if !ok {
			return nil, nil
		}

		item, ok := t.Store[id]
		if !ok {
			return nil, nil
		}

		return item, nil
	}

	return item, nil
}

// GetAll gets all items.
func (t *NetworkElementServiceMock) GetAll() ([]networkelement.NetworkElement, error) {
	var allItems []networkelement.NetworkElement

	for _, item := range t.Store {
		allItems = append(allItems, item)
	}

	return allItems, nil
}

// GetAllAsLoaded gets all items as `networkelement.LoadedNetworkElement`.
func (t *NetworkElementServiceMock) GetAllAsLoaded() ([]networkelement.LoadedNetworkElement, error) {
	var allItems []networkelement.LoadedNetworkElement

	for _, item := range t.Store {
		allItems = append(allItems, networkelement.LoadedNetworkElement{
			ID:   item.ID().String(),
			Name: item.Name(),
		})
	}

	return allItems, nil
}
