package common

import (
	"context"
	"fmt"

	"golang.org/x/time/rate"
)

const (
	ListNamespaces      Event = "ListNamespaces"
	ListServices        Event = "ListServices"
	GetOperation        Event = "GetOperation"
	DiscoverInstances   Event = "DiscoverInstances"
	CreateHttpNamespace Event = "CreateHttpNamespace"
	CreateService       Event = "CreateService"
	RegisterInstance    Event = "RegisterInstance"
	DeregisterInstance  Event = "DeregisterInstance"
)

type Event string

type RateLimiter struct {
	rateLimiters map[Event]*rate.Limiter
}

// NewDefaultRateLimiter returns the rate limiters with the default limits for the AWS CloudMap's API calls
func NewDefaultRateLimiter() RateLimiter {
	return RateLimiter{rateLimiters: map[Event]*rate.Limiter{
		// Below are the default limits for the AWS CloudMap's APIs
		// TODO: make it customizable in the future
		ListNamespaces:      rate.NewLimiter(rate.Limit(0.5), 5),    // 1 ListNamespaces API calls per second
		ListServices:        rate.NewLimiter(rate.Limit(2), 10),     // 2 ListServices API calls per second
		GetOperation:        rate.NewLimiter(rate.Limit(100), 200),  // 100 GetOperation API calls per second
		DiscoverInstances:   rate.NewLimiter(rate.Limit(500), 1000), // 500 DiscoverInstances API calls per second
		CreateHttpNamespace: rate.NewLimiter(rate.Limit(0.5), 5),    // 1 CreateHttpNamespace API calls per second
		CreateService:       rate.NewLimiter(rate.Limit(5), 50),     // 5 CreateService API calls per second
		RegisterInstance:    rate.NewLimiter(rate.Limit(50), 100),   // 50 RegisterInstance API calls per second
		DeregisterInstance:  rate.NewLimiter(rate.Limit(50), 100),   // 50 DeregisterInstance API calls per second
	}}
}

// Wait blocks until limit permits an event to happen. It returns an error if the Context is canceled, or the expected wait time exceeds the Context's Deadline.
func (r RateLimiter) Wait(ctx context.Context, event Event) error {
	if limiter, ok := r.rateLimiters[event]; ok {
		return limiter.Wait(ctx)
	}
	return fmt.Errorf("event %s not found in the list of limiters", event)
}
