package conformance

import (
	"context"

	"github.com/filecoin-project/go-state-types/abi"
	"github.com/filecoin-project/test-vectors/schema"

	"github.com/filecoin-project/lotus/chain/rand"
)

type ReplayingRand struct {
	reporter Reporter
	recorded schema.Randomness
	fallback rand.Rand
}

var _ rand.Rand = (*ReplayingRand)(nil)

// NewReplayingRand replays recorded randomness when requested, falling back to
// fixed randomness if the value cannot be found; hence this is a safe
// backwards-compatible replacement for fixedRand.
func NewReplayingRand(reporter Reporter, recorded schema.Randomness) *ReplayingRand {
	return &ReplayingRand{
		reporter: reporter,
		recorded: recorded,
		fallback: NewFixedRand(),
	}
}

func (r *ReplayingRand) match(requested schema.RandomnessRule) ([32]byte, bool) {
	for _, other := range r.recorded {
		if other.On.Kind == requested.Kind &&
			other.On.Epoch == requested.Epoch {
			return *(*[32]byte)(other.Return), true
		}
	}
	return [32]byte{}, false
}

func (r *ReplayingRand) GetChainRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) {
	rule := schema.RandomnessRule{
		Kind:  schema.RandomnessChain,
		Epoch: int64(round),
	}

	if ret, ok := r.match(rule); ok {
		r.reporter.Logf("returning saved chain randomness: epoch=%d, result=%x", round, ret)
		return ret, nil
	}

	r.reporter.Logf("returning fallback chain randomness: epoch=%d", round)

	return r.fallback.GetChainRandomness(ctx, round)
}

func (r *ReplayingRand) GetBeaconRandomness(ctx context.Context, round abi.ChainEpoch) ([32]byte, error) {
	rule := schema.RandomnessRule{
		Kind:  schema.RandomnessBeacon,
		Epoch: int64(round),
	}

	if ret, ok := r.match(rule); ok {
		r.reporter.Logf("returning saved beacon randomness:  epoch=%d, result=%x", round, ret)
		return ret, nil
	}

	r.reporter.Logf("returning fallback beacon randomness: epoch=%d, ", round)

	return r.fallback.GetBeaconRandomness(ctx, round)
}