package cacheauthorization

import (
	"context"

	"code.justin.tv/amzn/TwitchS2S2/c7s"
	"code.justin.tv/amzn/TwitchS2S2/internal/authorization"
	"code.justin.tv/amzn/TwitchS2S2/internal/authorization/authorizationiface"
)

// New returns a new Authorizations struct
func New(
	authorizations authorizationiface.AuthorizationsAPI,
	config *c7s.Config,
) *CachedAuthorizations {
	return &CachedAuthorizations{
		authorizations: authorizations,
		cache:          newLru(config.AccessTokenCacheSize),
	}
}

// Cache is the cache interface used for our cached authorization results.
type Cache interface {
	Fetch(authorizationType, authorizationToken string) (*authorization.Authorization, bool, error)
	Put(authorizationType, authorizationToken string, authz *authorization.Authorization) error
}

// CachedAuthorizations is a cached implementation of Authorization
type CachedAuthorizations struct {
	authorizations authorizationiface.AuthorizationsAPI
	cache          Cache
}

// Validate an authorization with a cache
func (a *CachedAuthorizations) Validate(ctx context.Context, authorizationType, authorizationToken string) (*authorization.Authorization, error) {
	if authz, ok, err := a.cache.Fetch(authorizationType, authorizationToken); ok {
		if err != nil {
			return nil, err
		}
		if err := authz.ValidateTimeClaims(); err != nil {
			return nil, err
		}
		return authz, nil
	}

	authz, err := a.authorizations.Validate(ctx, authorizationType, authorizationToken)
	if err != nil {
		return nil, err
	}

	if err := a.cache.Put(authorizationType, authorizationToken, authz); err != nil {
		return nil, err
	}

	return authz, nil
}
