package netmap_test

import (
	"strings"
	"testing"

	. "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
	netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
	"github.com/stretchr/testify/require"
)

func TestPlacementPolicyEncoding(t *testing.T) {
	v := netmaptest.PlacementPolicy()

	t.Run("binary", func(t *testing.T) {
		var v2 PlacementPolicy
		require.NoError(t, v2.Unmarshal(v.Marshal()))

		require.Equal(t, v, v2)
	})

	t.Run("json", func(t *testing.T) {
		data, err := v.MarshalJSON()
		require.NoError(t, err)

		var v2 PlacementPolicy
		require.NoError(t, v2.UnmarshalJSON(data))

		require.Equal(t, v, v2)
	})
}

func TestPlacementPolicyWriteString(t *testing.T) {
	var testCases = []struct {
		name   string
		input  string
		output string // If the output is empty, make it equal to input.
	}{
		{
			name: "no compound operators",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER Color EQ Red AS Color`,
		},
		{
			name: "no brackets in single level same-operator chain",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER Color EQ Red OR Color EQ Blue OR Color EQ Green AS Color`,
		},
		{
			name: "no brackets aroung higher precedence op",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER Color EQ Red OR Color EQ Blue AND Color NE Green AS Color`,
		},
		{
			name: "no brackets aroung higher precedence op, even if present in the input",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER Color EQ Red OR (Color EQ Blue AND Color NE Green) AS Color`,
			output: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER Color EQ Red OR Color EQ Blue AND Color NE Green AS Color`,
		},
		{
			name: "brackets aroung lower precedence op",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER (Color EQ Red OR Color EQ Blue) AND Color NE Green AS Color`,
		},
		{
			name: "no extra brackets for bracketed same-operator chain",
			input: `REP 1
CBF 1
SELECT 1 FROM Color
FILTER (Color EQ Red OR Color EQ Blue OR Color EQ Yellow) AND Color NE Green AS Color`,
		},
	}

	for _, tc := range testCases {
		var p PlacementPolicy
		require.NoError(t, p.DecodeString(tc.input))

		var sb strings.Builder
		require.NoError(t, p.WriteStringTo(&sb))

		if tc.output == "" {
			require.Equal(t, tc.input, sb.String())
		} else {
			require.Equal(t, tc.output, sb.String())

			var p1 PlacementPolicy
			require.NoError(t, p1.DecodeString(tc.output))
			require.Equal(t, p, p1)
		}
	}
}

func TestDecodeSelectFilterExpr(t *testing.T) {
	for _, s := range []string{
		"SELECT 1 FROM *",
		"FILTER Color EQ 'Red' AS RedNode",
		`
		  FILTER Color EQ 'Red' AS RedNode
		  FILTER @RedNode AND Shape EQ 'Cirle' AS RedCircleNode
		`,
		`
		  SELECT 1 FROM RedCircleNode
		  FILTER Color EQ 'Red' AS RedNode
		  FILTER @RedNode AND Shape EQ 'Cirle' AS RedCircleNode
		`,
		`
		  CBF 1
		  SELECT 1 FROM RedCircleNode
		  FILTER Color EQ 'Red' AS RedNode
		  FILTER @RedNode AND Shape EQ 'Cirle' AS RedCircleNode
		`,
	} {
		_, err := DecodeSelectFilterString(s)
		require.NoError(t, err)
	}
}