package config

import (
	"strings"
	"testing"
)

func TestGetBizTypeByServicePlan_BizTypeConfigs(t *testing.T) {
	tests := []struct {
		name           string
		bizType        string // legacy field
		bizTypeConfigs map[string]string
		servicePlan    string
		expected       string
	}{
		{
			name: "dev essential",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
				"essential": "tidb-x",
				"premium":   "tidb-x-premium",
			},
			servicePlan: "Essential",
			expected:    "tidb-x",
		},
		{
			name: "dev premium",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
				"essential": "tidb-x",
				"premium":   "tidb-x-premium",
			},
			servicePlan: "Premium",
			expected:    "tidb-x-premium",
		},
		{
			name: "essential v2 uses explicit nextgen mapping",
			bizTypeConfigs: map[string]string{
				"dedicated":    "dev-tidbcloud",
				"essential_v2": "dev-tidbcloud-nextgen",
			},
			servicePlan: " Essential_V2 ",
			expected:    "dev-tidbcloud-nextgen",
		},
		{
			name: "essential v2 missing mapping never falls back to dedicated",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
			},
			servicePlan: "Essential_V2",
			expected:    "",
		},
		{
			name:        "essential v2 legacy config has no safe fallback",
			bizType:     "dev-tidbcloud",
			servicePlan: "Essential_V2",
			expected:    "",
		},
		{
			name: "dedicated keep base",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
				"essential": "tidb-x",
				"premium":   "tidb-x-premium",
			},
			servicePlan: "Dedicated",
			expected:    "dev-tidbcloud",
		},
		{
			name: "case insensitive lookup",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
				"premium":   "tidb-x-premium",
			},
			servicePlan: "PREMIUM",
			expected:    "tidb-x-premium",
		},
		{
			name: "unknown plan falls back to dedicated entry",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
			},
			servicePlan: "UnknownPlan",
			expected:    "dev-tidbcloud",
		},
		{
			name: "empty servicePlan falls back to dedicated entry",
			bizTypeConfigs: map[string]string{
				"dedicated": "dev-tidbcloud",
				"premium":   "dev-tidbcloud-nextgen",
			},
			servicePlan: "",
			expected:    "dev-tidbcloud",
		},
		{
			name:           "nil map falls back to legacy BizType",
			bizType:        "dev-tidbcloud",
			bizTypeConfigs: nil,
			servicePlan:    "Essential",
			expected:       "dev-tidbcloud",
		},
		{
			name:           "nil map empty servicePlan falls back to legacy BizType",
			bizType:        "dev-tidbcloud",
			bizTypeConfigs: nil,
			servicePlan:    "",
			expected:       "dev-tidbcloud",
		},
		{
			name:           "nil map nil BizType returns empty",
			bizTypeConfigs: nil,
			servicePlan:    "Essential",
			expected:       "",
		},
		{
			name:           "empty map falls back to legacy BizType",
			bizType:        "staging-tidbcloud",
			bizTypeConfigs: map[string]string{},
			servicePlan:    "Dedicated",
			expected:       "staging-tidbcloud",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := &O11yGlobalServerConfig{
				BizType:        tt.bizType,
				BizTypeConfigs: tt.bizTypeConfigs,
			}
			if got := cfg.GetBizTypeByServicePlan(tt.servicePlan); got != tt.expected {
				t.Fatalf("GetBizTypeByServicePlan() = %q, want %q", got, tt.expected)
			}
		})
	}
}

func TestNormalizeTargetClusterServicePlans(t *testing.T) {
	cfg := &CentralConfig{
		TargetClusters: TargetClusters{
			{Id: 1, ServicePlan: ""},
			{Id: 2, ServicePlan: "premium"},
			{Id: 3, ServicePlan: " essential_v2 "},
		},
	}

	if err := normalizeTargetClusterServicePlans(cfg.TargetClusters); err != nil {
		t.Fatalf("normalizeTargetClusterServicePlans() error = %v", err)
	}

	if got := cfg.TargetClusters[0].ServicePlan; got != "Dedicated" {
		t.Fatalf("TargetClusters[0].ServicePlan = %q, want Dedicated", got)
	}
	if got := cfg.TargetClusters[1].ServicePlan; got != "Premium" {
		t.Fatalf("TargetClusters[1].ServicePlan = %q, want Premium", got)
	}
	if got := cfg.TargetClusters[2].ServicePlan; got != "Essential_V2" {
		t.Fatalf("TargetClusters[2].ServicePlan = %q, want Essential_V2", got)
	}
}

func TestNormalizeTargetClusterServicePlans_RejectUnknown(t *testing.T) {
	cfg := TargetClusters{
		{Id: 9, ServicePlan: "BYOC"},
	}

	err := normalizeTargetClusterServicePlans(cfg)
	if err == nil {
		t.Fatal("expected unsupported service plan error")
	}
	if !strings.Contains(err.Error(), "unsupported service plan") {
		t.Fatalf("error = %v, want unsupported service plan", err)
	}
	if !strings.Contains(err.Error(), "targetClusters[0]") {
		t.Fatalf("error = %v, want target cluster index context", err)
	}
	if !strings.Contains(err.Error(), "id=9") {
		t.Fatalf("error = %v, want target cluster id context", err)
	}
}

func TestNormalizeTargetClusterComponents(t *testing.T) {
	cfg := TargetClusters{
		{
			Id: 1,
			Components: []ClusterComponent{
				{ComponentType: "component_type_tidb", CpuNum: 2},
				{ComponentType: "COMPONENT_TYPE_TIPROXY", CpuNum: 1},
				{ComponentType: "COMPONENT_TYPE_UNSPECIFIED", CpuNum: 9},
				{ComponentType: "COMPONENT_TYPE_FUTURE_THING", CpuNum: 99},
			},
		},
	}

	normalizeTargetClusterComponents(cfg)

	if len(cfg[0].Components) != 1 {
		t.Fatalf("len(Components) = %d, want 1", len(cfg[0].Components))
	}
	if cfg[0].Components[0].ComponentType != "COMPONENT_TYPE_TIDB" {
		t.Fatalf("Components[0].ComponentType = %q, want COMPONENT_TYPE_TIDB", cfg[0].Components[0].ComponentType)
	}
}

func TestNormalizeTargetClusterComponentsDropsTiCDC(t *testing.T) {
	cfg := TargetClusters{
		{
			Id: 1,
			Components: []ClusterComponent{
				{ComponentType: "COMPONENT_TYPE_TICDC", CpuNum: 4},
				{ComponentType: "COMPONENT_TYPE_TIDB", CpuNum: 2},
			},
		},
	}

	normalizeTargetClusterComponents(cfg)

	if len(cfg[0].Components) != 1 {
		t.Fatalf("len(Components) = %d, want 1", len(cfg[0].Components))
	}
	if cfg[0].Components[0].ComponentType != "COMPONENT_TYPE_TIDB" {
		t.Fatalf("Components[0].ComponentType = %q, want COMPONENT_TYPE_TIDB", cfg[0].Components[0].ComponentType)
	}
}

func TestNormalizeTargetCluster(t *testing.T) {
	cluster := &TargetCluster{
		Id:          7,
		ServicePlan: "premium",
		Components: []ClusterComponent{
			{ComponentType: "component_type_tidb", CpuNum: 2},
			{ComponentType: "COMPONENT_TYPE_TICDC", CpuNum: 4},
			{ComponentType: "COMPONENT_TYPE_TIPROXY", CpuNum: 6},
			{ComponentType: "COMPONENT_TYPE_FUTURE_THING", CpuNum: 8},
		},
	}

	if err := NormalizeTargetCluster(cluster); err != nil {
		t.Fatalf("NormalizeTargetCluster() error = %v", err)
	}
	if cluster.ServicePlan != "Premium" {
		t.Fatalf("ServicePlan = %q, want Premium", cluster.ServicePlan)
	}
	if len(cluster.Components) != 1 {
		t.Fatalf("len(Components) = %d, want 1", len(cluster.Components))
	}
	if cluster.Components[0].ComponentType != "COMPONENT_TYPE_TIDB" {
		t.Fatalf("Components[0].ComponentType = %q, want COMPONENT_TYPE_TIDB", cluster.Components[0].ComponentType)
	}
}

func TestTargetClusterNodeCPUCapacityComponentMap(t *testing.T) {
	cluster := TargetCluster{
		Components: []ClusterComponent{
			{ComponentType: "COMPONENT_TYPE_TIDB", CpuNum: 2},
			{ComponentType: "COMPONENT_TYPE_TIKV", CpuNum: 3},
			{ComponentType: "COMPONENT_TYPE_PD", CpuNum: 1},
			{ComponentType: "COMPONENT_TYPE_TIFLASH", CpuNum: 5},
			{ComponentType: "COMPONENT_TYPE_TIPROXY", CpuNum: 4},
			{ComponentType: "COMPONENT_TYPE_FUTURE_THING", CpuNum: 8},
		},
	}

	got := cluster.NodeCPUCapacityComponentMap()

	if len(got) != 3 {
		t.Fatalf("len(NodeCPUCapacityComponentMap) = %d, want 3", len(got))
	}
	if got["tidb"] != 2 {
		t.Fatalf("NodeCPUCapacityComponentMap[tidb] = %v, want 2", got["tidb"])
	}
	if got["tikv"] != 3 {
		t.Fatalf("NodeCPUCapacityComponentMap[tikv] = %v, want 3", got["tikv"])
	}
	if got["tiflash"] != 5 {
		t.Fatalf("NodeCPUCapacityComponentMap[tiflash] = %v, want 5", got["tiflash"])
	}
	if _, ok := got["pd"]; ok {
		t.Fatal("NodeCPUCapacityComponentMap should skip pd")
	}
	if _, ok := got["tiproxy"]; ok {
		t.Fatal("NodeCPUCapacityComponentMap should skip tiproxy")
	}
}
