package auth

import (
	"errors"
	"time"

	"code.secondbit.org/uuid"
)

var (
	// ErrNoGrantStore is returned when a Context tries to act on a grantStore without setting one first.
	ErrNoGrantStore = errors.New("no grantStore was specified for the Context")
	// ErrGrantNotFound is returned when a Grant is requested but not found in the grantStore.
	ErrGrantNotFound = errors.New("grant not found in grantStore")
	// ErrGrantAlreadyExists is returned when a Grant is added to a grantStore, but another Grant with the
	// same Code already exists in the grantStore.
	ErrGrantAlreadyExists = errors.New("grant already exists in grantStore")
)

// Grant represents an authorization grant made by a user to a Client, to
// access user data within a defined Scope for a limited amount of time.
type Grant struct {
	Code        string
	Created     time.Time
	ExpiresIn   int32
	ClientID    uuid.ID
	Scope       string
	RedirectURI string
	State       string
	ProfileID   uuid.ID
}

type grantStore interface {
	getGrant(code string) (Grant, error)
	saveGrant(grant Grant) error
	deleteGrant(code string) error
}

func (m *memstore) getGrant(code string) (Grant, error) {
	m.grantLock.RLock()
	defer m.grantLock.RUnlock()
	grant, ok := m.grants[code]
	if !ok {
		return Grant{}, ErrGrantNotFound
	}
	return grant, nil
}

func (m *memstore) saveGrant(grant Grant) error {
	m.grantLock.Lock()
	defer m.grantLock.Unlock()
	_, ok := m.grants[grant.Code]
	if ok {
		return ErrGrantAlreadyExists
	}
	m.grants[grant.Code] = grant
	return nil
}

func (m *memstore) deleteGrant(code string) error {
	m.grantLock.Lock()
	defer m.grantLock.Unlock()
	_, ok := m.grants[code]
	if !ok {
		return ErrGrantNotFound
	}
	delete(m.grants, code)
	return nil
}
