package nucleus

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

type storableConstraint interface {
	ID() uuid.UUID
	Name() string
}

// GenericService provides a in-memory implementation for multiple stores.
type GenericService[T storableConstraint] struct {
	Store           map[uuid.UUID]T
	nameLookupTable map[string]uuid.UUID
}

// NewGenericService returns a specific in-memory store for a type T.
func NewGenericService[T storableConstraint]() GenericService[T] {
	return GenericService[T]{
		Store:           make(map[uuid.UUID]T),
		nameLookupTable: make(map[string]uuid.UUID),
	}
}

// Add adds a item T.
func (t *GenericService[T]) Add(item T) 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 T.
func (t *GenericService[T]) Update(item T) error {
	_, ok := t.Store[item.ID()]
	if ok {
		return nil
	}

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

	return nil
}

// Delete deletes a item T.
func (t *GenericService[T]) Delete(item T) error {
	delete(t.Store, item.ID())

	return nil
}

// Get gets a item T.
func (t *GenericService[T]) Get(query store.Query) (T, 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 *new(T), nil
		}

		item, ok := t.Store[id]
		if !ok {
			return *new(T), nil
		}

		return item, nil
	}

	return item, nil
}

// GetAll gets all items.
func (t *GenericService[T]) GetAll() ([]T, error) {
	var allItems []T

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

	return allItems, nil
}
