package innerring

import (
	"strings"
	"testing"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
	"github.com/nspcc-dev/neo-go/pkg/util"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/require"
)

func TestParseContractsSuccess(t *testing.T) {
	t.Parallel()
	file := strings.NewReader(`
contracts:
 frostfs: ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62
 processing: 597f5894867113a41e192801709c02497f611de8
 balance: d2aa48d14b17b11bc4c68205027884a96706dd16
 container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
 frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
 netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
 proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
 alphabet:
  amount: 2
  az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
  buky: e2ba789320899658b100f331bdebb74474757920
`)

	v := viper.New()
	v.SetConfigType("yaml")
	err := v.ReadConfig(file)
	require.NoError(t, err, "read config file failed")

	t.Run("all enabled", func(t *testing.T) {
		t.Parallel()
		c, err := parseContracts(v, nil, false, false)
		require.NoError(t, err, "failed to parse contracts")

		frostfsExp, _ := util.Uint160DecodeStringLE("ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62")
		require.Equal(t, frostfsExp, c.frostfs, "invalid frostfs")

		processingExp, _ := util.Uint160DecodeStringLE("597f5894867113a41e192801709c02497f611de8")
		require.Equal(t, processingExp, c.processing, "invalid processing")

		balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
		require.Equal(t, balanceExp, c.balance, "invalid balance")

		containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
		require.Equal(t, containerExp, c.container, "invalid container")

		frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
		require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")

		netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
		require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")

		proxyExp, _ := util.Uint160DecodeStringLE("abc8794bb40a21f2db5f21ae62741eb46c8cad1c")
		require.Equal(t, proxyExp, c.proxy, "invalid proxy")

		require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")

		azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
		require.Equal(t, azExp, c.alphabet[az], "invalid az")

		bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
		require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
	})

	t.Run("all disabled", func(t *testing.T) {
		t.Parallel()
		c, err := parseContracts(v, nil, true, true)
		require.NoError(t, err, "failed to parse contracts")

		require.Equal(t, util.Uint160{}, c.frostfs, "invalid frostfs")

		require.Equal(t, util.Uint160{}, c.processing, "invalid processing")

		balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
		require.Equal(t, balanceExp, c.balance, "invalid balance")

		containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
		require.Equal(t, containerExp, c.container, "invalid container")

		frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
		require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")

		netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
		require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")

		proxyExp, _ := util.Uint160DecodeStringLE("abc8794bb40a21f2db5f21ae62741eb46c8cad1c")
		require.Equal(t, proxyExp, c.proxy, "invalid proxy")

		require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")

		azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
		require.Equal(t, azExp, c.alphabet[az], "invalid az")

		bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
		require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
	})

	t.Run("main notary disabled", func(t *testing.T) {
		t.Parallel()
		c, err := parseContracts(v, nil, false, true)
		require.NoError(t, err, "failed to parse contracts")

		frostfsExp, _ := util.Uint160DecodeStringLE("ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62")
		require.Equal(t, frostfsExp, c.frostfs, "invalid frostfs")

		require.Equal(t, util.Uint160{}, c.processing, "invalid processing")

		balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
		require.Equal(t, balanceExp, c.balance, "invalid balance")

		containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
		require.Equal(t, containerExp, c.container, "invalid container")

		frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
		require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")

		netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
		require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")

		proxyExp, _ := util.Uint160DecodeStringLE("abc8794bb40a21f2db5f21ae62741eb46c8cad1c")
		require.Equal(t, proxyExp, c.proxy, "invalid proxy")

		require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")

		azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
		require.Equal(t, azExp, c.alphabet[az], "invalid az")

		bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
		require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
	})
}

func TestParseContractsInvalid(t *testing.T) {
	t.Parallel()
	t.Run("invalid frostfs contract", func(t *testing.T) {
		t.Parallel()
		file := strings.NewReader(`
contracts:
 frostfs: invalid_data
 processing: 597f5894867113a41e192801709c02497f611de8
 balance: d2aa48d14b17b11bc4c68205027884a96706dd16
 container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
 frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
 netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
 proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
 alphabet:
  amount: 2
  az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
  buky: e2ba789320899658b100f331bdebb74474757920
`)

		v := viper.New()
		v.SetConfigType("yaml")
		err := v.ReadConfig(file)
		require.NoError(t, err, "read config file failed")

		_, err = parseContracts(v, nil, false, false)
		require.Error(t, err, "unexpected success")
	})

	t.Run("invalid alphabet count", func(t *testing.T) {
		t.Parallel()
		file := strings.NewReader(`
contracts:
 frostfs: ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62
 processing: 597f5894867113a41e192801709c02497f611de8
 balance: d2aa48d14b17b11bc4c68205027884a96706dd16
 container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
 frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
 netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
 proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
 alphabet:
  amount: 3
  az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
  buky: e2ba789320899658b100f331bdebb74474757920
`)

		v := viper.New()
		v.SetConfigType("yaml")
		err := v.ReadConfig(file)
		require.NoError(t, err, "read config file failed")

		azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
		bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")

		morph := &testParserMorph{
			values: map[string]util.Uint160{
				"az":   azExp,
				"buky": bukyExp,
			},
		}

		_, err = parseContracts(v, morph, false, false)
		require.ErrorContains(t, err, "could not read all contracts: required 3, read 2", "unexpected success")
	})
}

type testParserMorph struct {
	values map[string]util.Uint160
}

func (m *testParserMorph) NNSContractAddress(name string) (sh util.Uint160, err error) {
	if value, found := m.values[name]; found {
		return value, nil
	}
	return util.Uint160{}, client.ErrNNSRecordNotFound
}