package callee

import (
	"time"

	"code.justin.tv/sse/malachai/pkg/internal/closer"
	"code.justin.tv/sse/malachai/pkg/internal/discovery"
	"code.justin.tv/sse/malachai/pkg/internal/keystore"
	"code.justin.tv/sse/malachai/pkg/jwtvalidation"
	"code.justin.tv/sse/malachai/pkg/log"
)

type pubkeyManager struct {
	messages      chan pubkeyEventer
	store         keystore.Storer
	log           log.S2SLogger
	purgeInterval time.Duration

	closer closer.API
}

func newPubkeyManager(pubkeyEvents chan pubkeyEventer, store keystore.Storer, log log.S2SLogger, purgeInterval time.Duration, closer closer.API) *pubkeyManager {
	return &pubkeyManager{
		closer:        closer,
		messages:      pubkeyEvents,
		store:         store,
		log:           log,
		purgeInterval: purgeInterval,
	}
}

func (pm *pubkeyManager) start() {
	purgeTimer := time.NewTimer(pm.purgeInterval)
	for {
		select {
		case <-pm.closer.Done():
			pm.log.Debug("pubkey invalidation manager context cancelled")
			return
		case <-purgeTimer.C:
			err := pm.store.PurgeExpiredKeys()
			if err != nil {
				pm.log.Error("cannot purge expired keys: " + err.Error())
			}
			purgeTimer.Reset(pm.purgeInterval)
		case event := <-pm.messages:
			err := event.Handle(pm.store)
			if err != nil {
				pm.log.Error("cannot handle pubkey event: " + err.Error())
			}
		}
	}
}

type pubkeyEventer interface {
	Handle(store keystore.Storer) error
}

type invalidatePubkeyEvent struct {
	key *jwtvalidation.SigningEntity
}

// Handle invalidates this pubkey from the keystore
func (iph *invalidatePubkeyEvent) Handle(store keystore.Storer) error {
	return store.InvalidateRSAPublicKey(iph.key)
}

type generatePubkeyEvent struct {
	key discovery.DynamoPublicKey
}

// Handle inserts this pubkey into the keystore
func (iph *generatePubkeyEvent) Handle(store keystore.Storer) error {
	pubkey := &discovery.PublicKey{
		Pubkey:      iph.key.Value,
		Fingerprint: iph.key.Fingerprint,
		ExpireAt:    iph.key.ExpireAt,
		CreatedAt:   iph.key.CreatedAt,
	}
	caller := &jwtvalidation.SigningEntity{
		Caller:      iph.key.Caller,
		Fingerprint: iph.key.Fingerprint,
	}
	return store.CacheRSAPublicKey(caller, pubkey)
}
