// stm: #unit
package store_test

import (
	"bytes"
	"context"
	"testing"

	"github.com/ipfs/go-datastore"
	syncds "github.com/ipfs/go-datastore/sync"
	"github.com/stretchr/testify/assert"

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

	"github.com/filecoin-project/lotus/blockstore"
	"github.com/filecoin-project/lotus/chain/consensus/filcns"
	"github.com/filecoin-project/lotus/chain/gen"
	"github.com/filecoin-project/lotus/chain/store"
	"github.com/filecoin-project/lotus/chain/types/mock"
)

func TestIndexSeeks(t *testing.T) {
	//stm: @CHAIN_STORE_IMPORT_001
	//stm: @CHAIN_STORE_GET_TIPSET_BY_HEIGHT_001, @CHAIN_STORE_PUT_TIPSET_001, @CHAIN_STORE_SET_GENESIS_BLOCK_001
	//stm: @CHAIN_STORE_CLOSE_001
	cg, err := gen.NewGenerator()
	if err != nil {
		t.Fatal(err)
	}

	gencar, err := cg.GenesisCar()
	if err != nil {
		t.Fatal(err)
	}

	gen := cg.Genesis()

	ctx := context.TODO()

	nbs := blockstore.NewMemorySync()
	cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), filcns.Weight, nil)
	defer cs.Close() //nolint:errcheck

	_, err = cs.Import(ctx, bytes.NewReader(gencar))
	if err != nil {
		t.Fatal(err)
	}

	cur := mock.TipSet(gen)
	if err := cs.PutTipSet(ctx, mock.TipSet(gen)); err != nil {
		t.Fatal(err)
	}
	assert.NoError(t, cs.SetGenesis(ctx, gen))

	// Put 113 blocks from genesis
	for i := 0; i < 113; i++ {
		nextts := mock.TipSet(mock.MkBlock(cur, 1, 1))

		if err := cs.PutTipSet(ctx, nextts); err != nil {
			t.Fatal(err)
		}
		cur = nextts
	}

	// Put 50 null epochs + 1 block
	skip := mock.MkBlock(cur, 1, 1)
	skip.Height += 50

	skipts := mock.TipSet(skip)

	if err := cs.PutTipSet(ctx, skipts); err != nil {
		t.Fatal(err)
	}

	ts, err := cs.GetTipsetByHeight(ctx, skip.Height-10, skipts, false)
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, abi.ChainEpoch(164), ts.Height())

	for i := 0; i <= 113; i++ {
		ts3, err := cs.GetTipsetByHeight(ctx, abi.ChainEpoch(i), skipts, false)
		if err != nil {
			t.Fatal(err)
		}
		assert.Equal(t, abi.ChainEpoch(i), ts3.Height())
	}
}
