package snapshot

import (
	"errors"
	"fmt"

	"github.com/nspcc-dev/neo-go/pkg/util"
	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
	"github.com/nspcc-dev/neofs-api-go/pkg/netmap"
	"github.com/nspcc-dev/neofs-node/pkg/morph/client"
)

const getNetmapSnapshotMethod = "netmap"

// Fetch returns current netmap node infos.
// Consider using pkg/morph/client/netmap for this.
func Fetch(cli *client.Client, contract util.Uint160) (*netmap.Netmap, error) {
	rawNetmapStack, err := cli.TestInvoke(contract, getNetmapSnapshotMethod)
	if err != nil {
		return nil, err
	}

	if ln := len(rawNetmapStack); ln != 1 {
		return nil, errors.New("invalid RPC response")
	}

	rawNodeInfos, err := client.ArrayFromStackItem(rawNetmapStack[0])
	if err != nil {
		return nil, err
	}

	result := make([]netmap.NodeInfo, 0, len(rawNodeInfos))

	for i := range rawNodeInfos {
		nodeInfo, err := peerInfoFromStackItem(rawNodeInfos[i])
		if err != nil {
			return nil, fmt.Errorf("invalid RPC response: %w", err)
		}

		result = append(result, *nodeInfo)
	}

	return netmap.NewNetmap(netmap.NodesFromInfo(result))
}

func peerInfoFromStackItem(prm stackitem.Item) (*netmap.NodeInfo, error) {
	node := netmap.NewNodeInfo()

	subItems, err := client.ArrayFromStackItem(prm)
	if err != nil {
		return nil, err
	} else if ln := len(subItems); ln != 1 {
		return nil, errors.New("invalid RPC response")
	} else if rawNodeInfo, err := client.BytesFromStackItem(subItems[0]); err != nil {
		return nil, err
	} else if err = node.Unmarshal(rawNodeInfo); err != nil {
		return nil, err
	}

	return node, nil
}