package api

import (
	"time"

	"code.secondbit.org/uuid.hg"
)

type Memstore struct {
	collections map[string]Collection
	domains     map[string]Domain
	items       map[string]Item
}

func NewMemstore() Memstore {
	return Memstore{
		collections: map[string]Collection{},
		domains:     map[string]Domain{},
		items:       map[string]Item{},
	}
}

func (m Memstore) CreateCollection(c Collection) error {
	m.collections[c.ID.String()] = c
	return nil
}

func (m Memstore) UpdateCollection(id uuid.ID, change CollectionChange) error {
	c, ok := m.collections[id.String()]
	if !ok {
		return ErrCollectionNotFound
	}
	c.ApplyChange(change)
	m.collections[id.String()] = c
	return nil
}

func (m Memstore) GetCollectionByDomain(domain string) (Collection, error) {
	d, ok := m.domains[domain]
	if !ok {
		return Collection{}, ErrCollectionNotFound
	}
	return m.GetCollectionByID(d.CollectionID)
}

func (m Memstore) GetCollectionByID(id uuid.ID) (Collection, error) {
	if c, ok := m.collections[id.String()]; ok {
		return c, nil
	}
	return Collection{}, ErrCollectionNotFound
}

func (m Memstore) GetCollectionsByUser(id uuid.ID) ([]Collection, error) {
	collections := []Collection{}
	for _, c := range m.collections {
		if c.Owner.Equal(id) {
			collections = append(collections, c)
		}
	}
	return collections, nil
}

func (m Memstore) AddDomainToCollection(id uuid.ID, domain string) error {
	if _, ok := m.domains[domain]; ok {
		return ErrDomainAlreadyExists
	}
	m.domains[domain] = Domain{
		Domain:       domain,
		CollectionID: id,
		Created:      time.Now(),
	}
	return nil
}

func (m Memstore) RemoveDomainFromCollection(id uuid.ID, domain string) error {
	if _, ok := m.domains[domain]; !ok {
		return ErrDomainNotFound
	}
	delete(m.domains, domain)
	return nil
}

func (m Memstore) GetDomainsByCollection(id uuid.ID) ([]Domain, error) {
	domains := []Domain{}
	for _, d := range m.domains {
		if d.CollectionID.Equal(id) {
			domains = append(domains, d)
		}
	}
	return domains, nil
}

func (m Memstore) DeleteCollection(c Collection) error {
	if _, ok := m.collections[c.ID.String()]; !ok {
		return ErrCollectionNotFound
	}
	delete(m.collections, c.ID.String())
	return nil
}

func (m Memstore) GetItemsByCollectionDomain(domain string, num, offset int) ([]Item, error) {
	collection, err := m.GetCollectionByDomain(domain)
	if err != nil {
		return []Item{}, err
	}
	return m.GetItemsByCollectionID(collection.ID, num, offset)
}

func (m Memstore) GetItemsByCollectionID(id uuid.ID, num, offset int) ([]Item, error) {
	if _, ok := m.collections[id.String()]; !ok {
		return []Item{}, ErrCollectionNotFound
	}
	items := []Item{}
	for _, item := range m.items {
		if item.CollectionID.Equal(id) {
			items = append(items, item)
		}
	}
	if len(items) < offset {
		return []Item{}, nil
	}
	end := offset + num
	if len(items) < end {
		end = len(items)
	}
	return items[offset:end], nil
}

func (m Memstore) AddItemToCollection(id uuid.ID, item Item) error {
	if _, ok := m.collections[id.String()]; !ok {
		return ErrCollectionNotFound
	}
	if _, ok := m.items[id.String()+"/"+item.Name]; ok {
		return ErrItemAlreadyExists
	}
	item.CollectionID = id
	m.items[id.String()+"/"+item.Name] = item
	return nil
}

func (m Memstore) GetItemByName(collectionID uuid.ID, name string) (Item, error) {
	if _, ok := m.collections[collectionID.String()]; !ok {
		return Item{}, ErrCollectionNotFound
	}
	if item, ok := m.items[collectionID.String()+"/"+name]; ok {
		return item, nil
	}
	return Item{}, ErrItemNotFound
}

func (m Memstore) DeleteItem(item Item) error {
	if _, ok := m.items[item.CollectionID.String()+"/"+item.Name]; !ok {
		return ErrItemNotFound
	}
	delete(m.items, item.CollectionID.String()+"/"+item.Name)
	return nil
}
