package api

import (
	"time"

	"secondbit.org/uuid"
)

type Memstore struct {
	collections map[string]Collection
	domains     map[string]Domain
	items       map[string]Item
	users       map[string]User
	logins      map[string]Login
}

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

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

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

func (m Memstore) GetCollectionByDomain(domain string) (Collection, error) {
	d, ok := m.domains[domain]
	if !ok {
		return Collection{}, CollectionNotFoundError
	}
	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{}, CollectionNotFoundError
}

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 DomainAlreadyExistsError
	}
	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 DomainNotFoundError
	}
	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 CollectionNotFoundError
	}
	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{}, CollectionNotFoundError
	}
	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 CollectionNotFoundError
	}
	if _, ok := m.items[id.String()+"/"+item.Name]; ok {
		return ItemAlreadyExistsError
	}
	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{}, CollectionNotFoundError
	}
	if item, ok := m.items[collectionID.String()+"/"+name]; ok {
		return item, nil
	}
	return Item{}, ItemNotFoundError
}

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

func (m Memstore) GetUserByID(id uuid.ID) (User, error) {
	if u, ok := m.users[id.String()]; ok {
		return u, nil
	}
	return User{}, UserNotFoundError
}

func (m Memstore) GetUserByLogin(loginType, value, passphrase string) (User, error) {
	login, ok := m.logins[loginType+":"+value]
	if !ok {
		return User{}, UserNotFoundError
	}
	user, err := m.GetUserByID(login.UserID)
	if err != nil {
		return user, err
	}
	if user.Passphrase != passphrase {
		return User{}, UserNotFoundError
	}
	return user, nil
}

func (m Memstore) CreateUser(u User) error {
	m.users[u.ID.String()] = u
	return nil
}

func (m Memstore) AddLoginToUser(login Login, user uuid.ID) error {
	if _, ok := m.logins[login.Type+":"+login.Value]; ok {
		return LoginAlreadyExistsError
	}
	m.logins[login.Type+":"+login.Value] = login
	return nil
}

func (m Memstore) RemoveLoginFromUser(loginType, value string, user uuid.ID) error {
	if _, ok := m.logins[loginType+":"+value]; !ok {
		return LoginNotFoundError
	}
	delete(m.logins, loginType+":"+value)
	return nil
}

func (m Memstore) GetLoginsByUser(user uuid.ID) ([]Login, error) {
	logins := []Login{}
	for _, login := range m.logins {
		if login.UserID.Equal(user) {
			logins = append(logins, login)
		}
	}
	return logins, nil
}

func (m Memstore) UpdateUser(u User) error {
	if _, ok := m.users[u.ID.String()]; !ok {
		return UserNotFoundError
	}
	m.users[u.ID.String()] = u
	return nil
}

func (m Memstore) DeleteUser(u User) error {
	if _, ok := m.users[u.ID.String()]; !ok {
		return UserNotFoundError
	}
	delete(m.users, u.ID.String())
	return nil
}
