package node

import (
	"context"
	"encoding/hex"
	"time"

	"github.com/benbjohnson/clock"
	gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
	"github.com/wormhole-foundation/wormhole/sdk/vaa"
	"go.uber.org/zap"
)

// Multiplex observation requests to the appropriate chain
func handleReobservationRequests(
	ctx context.Context,
	clock clock.Clock,
	logger *zap.Logger,
	obsvReqC <-chan *gossipv1.ObservationRequest,
	chainObsvReqC map[vaa.ChainID]chan *gossipv1.ObservationRequest,
) {
	// Due to the automatic re-observation requests sent out by the processor we may end
	// up getting multiple requests to re-observe the same tx. Keep a cache of the
	// requests received in the last 11 minutes so that we don't end up repeatedly
	// re-observing the same transactions.
	type cachedRequest struct {
		chainId vaa.ChainID
		txHash  string
	}

	cache := make(map[cachedRequest]time.Time)
	ticker := clock.Ticker(7 * time.Minute)
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			now := clock.Now()
			for r, t := range cache {
				if now.Sub(t) > 11*time.Minute {
					delete(cache, r)
				}
			}
		case req := <-obsvReqC:
			r := cachedRequest{
				chainId: vaa.ChainID(req.ChainId),
				txHash:  hex.EncodeToString(req.TxHash),
			}

			if _, ok := cache[r]; ok {
				// We've recently seen a re-observation request for this tx
				// so skip this one.
				logger.Info("skipping duplicate re-observation request",
					zap.Stringer("chain", r.chainId),
					zap.String("tx_hash", r.txHash),
				)
				continue
			}

			if channel, ok := chainObsvReqC[r.chainId]; ok {
				select {
				case channel <- req:
					cache[r] = clock.Now()

				default:
					logger.Warn("failed to send reobservation request to watcher",
						zap.Stringer("chain_id", r.chainId),
						zap.String("tx_hash", r.txHash))
				}
			} else {
				logger.Error("unknown chain ID for reobservation request",
					zap.Uint16("chain_id", uint16(r.chainId)),
					zap.String("tx_hash", r.txHash))
			}
		}
	}
}
