package txhash

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
	"github.com/go-redis/redis/v8"
	"github.com/wormhole-foundation/wormhole-explorer/common/domain"
	"go.uber.org/zap"
)

const txHashByVaaIDKey = "tx-hash-by-vaa-id"

type redisTxHash struct {
	client     *redis.Client
	prefix     string
	expiration time.Duration
	logger     *zap.Logger
}

func NewRedisTxHash(client *redis.Client,
	prefix string,
	expiration time.Duration,
	logger *zap.Logger) *redisTxHash {
	return &redisTxHash{
		client:     client,
		prefix:     prefix,
		expiration: expiration,
		logger:     logger,
	}
}

func (t *redisTxHash) Set(ctx context.Context, uniqueVaaID string, txHash TxHash) error {
	body, err := json.Marshal(txHash)
	if err != nil {
		return err
	}

	key := t.createKey(uniqueVaaID)
	if res := t.client.Set(ctx, key, string(body), t.expiration); res.Err() != nil {
		t.logger.Warn("Error setting tx hash in redis", zap.Error(res.Err()), zap.String("vaaId", uniqueVaaID))
		return res.Err()
	}

	return nil
}

func (r *redisTxHash) SetObservation(ctx context.Context, o *gossipv1.SignedObservation) error {
	txHash, err := CreateTxHash(r.logger, o)
	if err != nil {
		r.logger.Error("Error creating txHash", zap.Error(err))
		return err
	}
	uniqueVaaID := domain.CreateUniqueVaaIDByObservation(o)
	return r.Set(ctx, uniqueVaaID, *txHash)
}

func (r *redisTxHash) Get(ctx context.Context, uniqueVaaID string) (*TxHash, error) {
	key := r.createKey(uniqueVaaID)
	res := r.client.Get(ctx, key)
	if res.Err() == nil {
		var txHash TxHash
		err := json.Unmarshal([]byte(res.Val()), &txHash)
		if err != nil {
			return nil, err
		}
		return &txHash, nil
	}
	if res.Err() == redis.Nil {
		return nil, ErrTxHashNotFound
	}
	return nil, res.Err()
}

func (r *redisTxHash) createKey(uniqueVaaID string) string {
	return fmt.Sprintf("%s:%s:%s", r.prefix, txHashByVaaIDKey, uniqueVaaID)
}

func (r *redisTxHash) GetName() string {
	return "redis"
}
