package middleware

import (
	"time"

	"github.com/cloudwan/gohan/metrics"
	"github.com/cloudwan/gohan/schema"
	"github.com/patrickmn/go-cache"
)

type CachedIdentityService struct {
	IdentityService
	cache *cache.Cache
}

func (c *CachedIdentityService) GetTenantName(tenantID string) (string, error) {
	i, ok := c.cache.Get(tenantID)
	if ok {
		c.updateCounter(1, "name.hit")
		return i.(string), nil
	}
	c.updateCounter(1, "name.miss")

	name, err := c.IdentityService.GetTenantName(tenantID)
	if err != nil {
		return "", err
	}
	c.cache.Set(tenantID, name, cache.DefaultExpiration)
	return name, nil
}

func (c *CachedIdentityService) VerifyToken(token string) (schema.Authorization, error) {
	i, ok := c.cache.Get(token)
	if ok {
		c.updateCounter(1, "token.hit")
		return i.(schema.Authorization), nil
	}
	c.updateCounter(1, "token.miss")

	a, err := c.IdentityService.VerifyToken(token)
	if err != nil {
		return nil, err
	}
	c.cache.Set(token, a, cache.DefaultExpiration)
	return a, nil
}

func (c *CachedIdentityService) GetServiceAuthorization() (schema.Authorization, error) {
	defer c.measureTime(time.Now(), "get_service_authorization")
	return c.VerifyToken(c.GetServiceTokenID())
}

func (c *CachedIdentityService) updateCounter(delta int64, action string) {
	metrics.UpdateCounter(delta, "auth.cache.%s", action)
}

func (c *CachedIdentityService) measureTime(timeStarted time.Time, action string) {
	metrics.UpdateTimer(timeStarted, "auth.cache.%s", action)
}

func NewCachedIdentityService(inner IdentityService, ttl time.Duration) IdentityService {
	cleanupInterval := 4 * ttl
	return &CachedIdentityService{
		IdentityService: inner,
		cache:           cache.New(ttl, cleanupInterval),
	}
}
