package output_blackhole

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/cluster-service-ng/pkg/autoscale/framework"
)

func TestOutputBlackholeSetNewClusterTopology(t *testing.T) {
	t.Run("logs topology without error", func(t *testing.T) {
		rec := framework.NewEventsCounter(EventOutputBlackholeSetNewClusterTopology)

		outputIface, err := NewOutputBlackhole(rec, &OutputBlackholeConfig{})
		require.NoError(t, err)

		output := outputIface.(framework.OutputInterfaceV1)

		current := framework.ClusterTopologyV1{
			"tidb": {
				Name:     "tidb",
				Type:     "8C32G",
				Replicas: 2,
			},
		}

		expected := framework.ClusterTopologyV1{
			"tidb": {
				Name:     "tidb",
				Type:     "8C32G",
				Replicas: 4,
			},
		}

		err = output.SetNewClusterTopology("cluster-1", current, expected)
		require.NoError(t, err)
		require.Equal(t, 1, rec.GetCount(EventOutputBlackholeSetNewClusterTopology))
	})

	t.Run("handles empty topology", func(t *testing.T) {
		rec := framework.NewEventsCounter(EventOutputBlackholeSetNewClusterTopology)

		outputIface, err := NewOutputBlackhole(rec, &OutputBlackholeConfig{})
		require.NoError(t, err)

		output := outputIface.(framework.OutputInterfaceV1)

		err = output.SetNewClusterTopology("cluster-1", framework.ClusterTopologyV1{}, framework.ClusterTopologyV1{})
		require.NoError(t, err)
		require.Equal(t, 1, rec.GetCount(EventOutputBlackholeSetNewClusterTopology))
	})
}

func TestOutputBlackholeConfig(t *testing.T) {
	t.Run("FillBlankValuesFrom returns self", func(t *testing.T) {
		cfg := &OutputBlackholeConfig{}
		result := cfg.FillBlankValuesFrom(nil)
		require.Same(t, cfg, result)
	})

	t.Run("UniqStr returns blackhole", func(t *testing.T) {
		cfg := &OutputBlackholeConfig{}
		require.Equal(t, "output-blackhole", cfg.UniqStr())
	})
}

func TestTopologyToString(t *testing.T) {
	tests := []struct {
		name     string
		topology framework.ClusterTopologyV1
		expected string
	}{
		{
			name:     "empty topology",
			topology: framework.ClusterTopologyV1{},
			expected: "{}",
		},
		{
			name: "single component",
			topology: framework.ClusterTopologyV1{
				"tidb": {
					Name:     "tidb",
					Type:     "8C32G",
					Replicas: 3,
				},
			},
			expected: "{tidb:tidb:8C32G:3}",
		},
		{
			name: "multiple components sorted",
			topology: framework.ClusterTopologyV1{
				"tikv": {
					Name:     "tikv",
					Type:     "8C64G",
					Replicas: 3,
				},
				"tidb": {
					Name:     "tidb",
					Type:     "8C32G",
					Replicas: 2,
				},
				"pd": {
					Name:     "pd",
					Type:     "4C16G",
					Replicas: 1,
				},
			},
			expected: "{pd:pd:4C16G:1, tidb:tidb:8C32G:2, tikv:tikv:8C64G:3}",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := TopologyToString(tt.topology)
			require.Equal(t, tt.expected, result)
		})
	}
}

func TestTopologyToStringOrderDeterminism(t *testing.T) {
	topology := framework.ClusterTopologyV1{
		"tikv":    {Name: "tikv", Type: "8C64G", Replicas: 3},
		"tidb":    {Name: "tidb", Type: "8C32G", Replicas: 2},
		"pd":      {Name: "pd", Type: "4C16G", Replicas: 1},
		"tiflash": {Name: "tiflash", Type: "8C64G", Replicas: 2},
	}

	expected := "{pd:pd:4C16G:1, tidb:tidb:8C32G:2, tiflash:tiflash:8C64G:2, tikv:tikv:8C64G:3}"

	for i := 0; i < 100; i++ {
		result := TopologyToString(topology)
		require.Equal(t, expected, result, "iteration %d: order should be deterministic", i)
	}
}
