package store

import (
	"errors"

	"github.com/google/uuid"

	query "code.fbi.h-da.de/danet/gosdn/controller/store"
)

type storableConstraint interface {
	GetID() uuid.UUID
}

// GenericStore provides a in-memory implementation for multiple stores.
type GenericStore[T storableConstraint] struct {
	store map[uuid.UUID]T
}

// NewGenericStore returns a specific in-memory store for a type T.
func NewGenericStore[T storableConstraint]() *GenericStore[T] {
	return &GenericStore[T]{
		store: make(map[uuid.UUID]T),
	}
}

func (t *GenericStore[T]) Add(item T) error {
	_, ok := t.store[item.GetID()]
	if ok {
		return errors.New("item already exists")
	}

	t.store[item.GetID()] = item

	return nil
}

func (t *GenericStore[T]) Update(item T) error {
	_, ok := t.store[item.GetID()]
	if !ok {
		return errors.New("item not found")
	}

	t.store[item.GetID()] = item

	return nil
}

func (t *GenericStore[T]) Delete(item T) error {
	_, ok := t.store[item.GetID()]
	if !ok {
		return errors.New("item not found")
	}

	delete(t.store, item.GetID())

	return nil
}

func (t *GenericStore[T]) Get(query query.Query) (T, error) {
	// First search for direct hit on UUID.
	item, ok := t.store[query.ID]
	if !ok {
		return *new(T), errors.New("item not found")
	}

	return item, nil
}

func (t *GenericStore[T]) GetAll() ([]T, error) {
	var allItems []T

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

	return allItems, nil
}
