package vm

import (
	"testing"

	"git.marketally.com/tutus-one/tutus-chain/pkg/util"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations"
	"git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode"
	"github.com/stretchr/testify/require"
)

func TestInvocationTree(t *testing.T) {
	script := []byte{
		byte(opcode.PUSH3), byte(opcode.DEC),
		byte(opcode.DUP), byte(opcode.PUSH0), byte(opcode.JMPEQ), (2 + 2 + 2 + 6 + 1),
		byte(opcode.CALL), (2 + 2), // CALL shouldn't affect invocation tree.
		byte(opcode.JMP), 0xf9, // DEC
		byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.DROP),
		byte(opcode.RET),
		byte(opcode.RET),
		byte(opcode.PUSHINT8), 0xff,
	}

	cnt := 0
	v := newTestVM()
	v.SyscallHandler = func(v *VM, _ uint32) error {
		if len(v.Istack()) > 4 { // top -> call -> syscall -> call -> syscall -> ...
			v.Estack().PushVal(1)
			return nil
		}
		cnt++
		v.LoadScriptWithHash(script, util.Uint160{byte(cnt)}, 0)
		return nil
	}
	v.EnableInvocationTree()
	v.LoadScript(script)
	topHash := v.Context().ScriptHash()
	require.NoError(t, v.Run())

	res := &invocations.Tree{
		Calls: []*invocations.Tree{{
			Current: topHash,
			Calls: []*invocations.Tree{
				{
					Current: util.Uint160{1},
					Calls: []*invocations.Tree{
						{
							Current: util.Uint160{2},
						},
						{
							Current: util.Uint160{3},
						},
					},
				},
				{
					Current: util.Uint160{4},
					Calls: []*invocations.Tree{
						{
							Current: util.Uint160{5},
						},
						{
							Current: util.Uint160{6},
						},
					},
				},
			},
		}},
	}
	require.Equal(t, res, v.GetInvocationTree())
}
