package auth

import (
	"sync"

	"code.secondbit.org/uuid"
)

type memstore struct {
	tokens             map[string]Token
	refreshTokenLookup map[string]string
	profileTokenLookup map[string][]string
	tokenLock          sync.RWMutex

	grants    map[string]Grant
	grantLock sync.RWMutex

	clients             map[string]Client
	profileClientLookup map[string][]uuid.ID
	clientLock          sync.RWMutex

	endpoints    map[string][]Endpoint
	endpointLock sync.RWMutex

	profiles    map[string]Profile
	profileLock sync.RWMutex

	logins             map[string]Login
	profileLoginLookup map[string][]string
	loginLock          sync.RWMutex
}

// NewMemstore returns an in-memory version of our datastores,
// which is handy for tests. Though the implementation is concurrency-safe,
// if makes no attempt to persist the data, and therefore it is inadvisable
// to use it in a production setting.
func NewMemstore() *memstore {
	return &memstore{
		tokens:              map[string]Token{},
		refreshTokenLookup:  map[string]string{},
		profileTokenLookup:  map[string][]string{},
		grants:              map[string]Grant{},
		clients:             map[string]Client{},
		profileClientLookup: map[string][]uuid.ID{},
		endpoints:           map[string][]Endpoint{},
		profiles:            map[string]Profile{},
		logins:              map[string]Login{},
		profileLoginLookup:  map[string][]string{},
	}
}

func (m *memstore) lookupTokenByRefresh(token string) (string, error) {
	m.tokenLock.RLock()
	defer m.tokenLock.RUnlock()
	t, ok := m.refreshTokenLookup[token]
	if !ok {
		return "", ErrTokenNotFound
	}
	return t, nil
}

func (m *memstore) lookupTokensByProfileID(id string) ([]string, error) {
	m.tokenLock.RLock()
	defer m.tokenLock.RUnlock()
	return m.profileTokenLookup[id], nil
}

func (m *memstore) lookupClientsByProfileID(id string) []uuid.ID {
	m.clientLock.RLock()
	defer m.clientLock.RUnlock()
	c, ok := m.profileClientLookup[id]
	if !ok {
		return []uuid.ID{}
	}
	return c
}
