package config

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestLoadRootConfig(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "rootConfig.yaml")

	// Define the rootConfig content with unsorted specs
	configContent := `
o11yConfig:
  customerConfigsV2:
    dedicated:
      specs:
        k8s:
          - name: "large"
            maxNodes: 100
          - name: "small"
            maxNodes: 10
          - name: "medium"
            maxNodes: 50
        tidbcluster:
          - name: "large"
            maxNodes: 100
          - name: "small"
            maxNodes: 10
          - name: "medium"
            maxNodes: 50
    nextgen:
      k8s:
        specs:
          - name: "large"
            maxNodes: 100
          - name: "small"
            maxNodes: 10
          - name: "medium"
            maxNodes: 50
      sharedpool:
        specs:
          - name: "large"
            maxNodes: 100
          - name: "small"
            maxNodes: 10
          - name: "medium"
            maxNodes: 50
        default:
          vector:
            pdAddress: serverless-cluster-pd.tidb-serverless.svc:2379
            clusterSecretName: vmagent-cluster-secret
            managerServerURL: https://manager-server.tidb-admin.svc:8000
            tidbNamespace: super-vip-tidb-pool,canary-super-vip-tidb-pool
            enableTiKVTopSQL: false
      tidbgroup:
        specs:
          - name: "large"
            maxNodes: 100
          - name: "small"
            maxNodes: 10
          - name: "medium"
            maxNodes: 50
`

	err := os.WriteFile(configPath, []byte(configContent), 0o644)
	assert.NoError(t, err)

	rootConfig, err := LoadRootConfig(configPath)
	assert.NoError(t, err)

	expected := CustomerAgentSpecs{
		{Name: "small", MaxNodes: 10},
		{Name: "medium", MaxNodes: 50},
		{Name: "large", MaxNodes: 100},
	}

	assert.Equal(t, expected, rootConfig.O11yConfig.CustomerConfigsV2.Dedicated.Specs.K8s)
	assert.Equal(t, expected, rootConfig.O11yConfig.CustomerConfigsV2.Dedicated.Specs.TiDBCluster)
	assert.Equal(t, expected, rootConfig.O11yConfig.CustomerConfigsV2.NextGen.K8s.Specs)
	assert.Equal(t, expected, rootConfig.O11yConfig.CustomerConfigsV2.NextGen.SharedPool.Specs)
	assert.Equal(t, expected, rootConfig.O11yConfig.CustomerConfigsV2.NextGen.TiDBGroup.Specs)
	vectorCfg := rootConfig.O11yConfig.CustomerConfigsV2.NextGen.SharedPool.Default.Vector
	assert.Equal(t, "serverless-cluster-pd.tidb-serverless.svc:2379", vectorCfg.PDAddress)
	assert.Equal(t, "vmagent-cluster-secret", vectorCfg.ClusterSecretName)
	assert.Equal(t, "https://manager-server.tidb-admin.svc:8000", vectorCfg.ManagerServerURL)
	assert.Equal(t, "super-vip-tidb-pool,canary-super-vip-tidb-pool", vectorCfg.TiDBNamespace)
}

func TestCustomerAgentSpecs(t *testing.T) {
	specs := CustomerAgentSpecs{
		{Name: "small", MaxNodes: 10},
		{Name: "medium", MaxNodes: 50},
		{Name: "large", MaxNodes: 100},
	}

	t.Run("Index", func(t *testing.T) {
		assert.Equal(t, 0, specs.Index("small"))
		assert.Equal(t, 1, specs.Index("medium"))
		assert.Equal(t, 2, specs.Index("large"))
		assert.Equal(t, -1, specs.Index("non-existent"))
	})

	t.Run("Select", func(t *testing.T) {
		assert.Equal(t, "small", specs.Select("small").Name)
		assert.Nil(t, specs.Select("non-existent"))
	})

	t.Run("SelectUp", func(t *testing.T) {
		// current="small", max="large" -> "medium"
		up := specs.SelectUp("small", "large")
		assert.Equal(t, "medium", up.Name)

		// current="medium", max="large" -> "large"
		up = specs.SelectUp("medium", "large")
		assert.Equal(t, "large", up.Name)

		// current="large", max="large" -> "large" (already at max)
		up = specs.SelectUp("large", "large")
		assert.Equal(t, "large", up.Name)

		// current="small", max="medium" -> "medium"
		up = specs.SelectUp("small", "medium")
		assert.Equal(t, "medium", up.Name)

		// current not found
		assert.Nil(t, specs.SelectUp("non-existent", "large"))

		// max not found
		assert.Nil(t, specs.SelectUp("small", "non-existent"))

		// current > max (index wise)
		// Assuming we want to upgrade "large" but max allowed is "small" -> nil
		assert.Nil(t, specs.SelectUp("large", "small"))
	})

	t.Run("SelectDown", func(t *testing.T) {
		// current="large", min="small" -> "medium"
		down := specs.SelectDown("large", "small")
		assert.Equal(t, "medium", down.Name)

		// current="medium", min="small" -> "small"
		down = specs.SelectDown("medium", "small")
		assert.Equal(t, "small", down.Name)

		// current="small", min="small" -> nil (already at min)
		down = specs.SelectDown("small", "small")
		assert.Equal(t, "small", down.Name)

		// current="large", min="medium" -> "medium"
		down = specs.SelectDown("large", "medium")
		assert.Equal(t, "medium", down.Name)

		// current < min (index wise)
		// Assuming we want to downgrade "small" but min allowed is "large" -> nil
		down = specs.SelectDown("small", "large")
		assert.Equal(t, "large", down.Name)

		// current not found
		assert.Nil(t, specs.SelectDown("non-existent", "small"))

		// min not found
		assert.Nil(t, specs.SelectDown("large", "non-existent"))
	})
}

func TestRootConfigIsServerlessMode(t *testing.T) {
	tests := []struct {
		name string
		mode string
		want bool
	}{
		{name: "serverless mode", mode: ModeServerless, want: true},
		{name: "empty mode", mode: "", want: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := RootConfig{Mode: tt.mode}
			assert.Equal(t, tt.want, cfg.IsServerlessMode())
		})
	}
}
