package store

import (
	"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/change"
	"github.com/google/uuid"
	log "github.com/sirupsen/logrus"

	mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
)

// ChangeStore is used to store Changes.
type ChangeStore struct {
	*genericStore
}

// NewChangeStore returns a ChangeStore.
func NewChangeStore() *ChangeStore {
	return &ChangeStore{genericStore: newGenericStore()}
}

// GetChange takes a Change's UUID and returns the Change. If the requested
// Change does not exist an error is returned.
func (s *ChangeStore) GetChange(id uuid.UUID) (change.Change, error) {
	item, err := s.genericStore.Get(id)
	if err != nil {
		return nil, err
	}
	c, ok := item.(change.Change)
	if !ok {
		return nil, &customerrs.InvalidTypeAssertionError{
			Value: c,
			Type:  (*change.Change)(nil),
		}
	}
	log.WithFields(log.Fields{
		"uuid": id,
	}).Debug("change was accessed")
	return c, nil
}

// Pending returns the UUIDs of all pending changes.
func (s *ChangeStore) Pending() []uuid.UUID {
	return filterChanges(s, mnepb.ChangeState_CHANGE_STATE_PENDING)
}

// Committed returns the UUIDs of all pending changes.
func (s *ChangeStore) Committed() []uuid.UUID {
	return filterChanges(s, mnepb.ChangeState_CHANGE_STATE_COMMITTED)
}

// Confirmed returns the UUIDs of all pending changes.
func (s *ChangeStore) Confirmed() []uuid.UUID {
	return filterChanges(s, mnepb.ChangeState_CHANGE_STATE_CONFIRMED)
}

func filterChanges(store *ChangeStore, state mnepb.ChangeState) []uuid.UUID {
	changes := make([]uuid.UUID, 0)
	for _, ch := range store.Store {
		switch c := ch.(type) {
		case change.Change:
			if c.State() == state {
				changes = append(changes, c.ID())
			}
		}
	}
	return changes
}
