package configs

import (
	"testing"

	"k8s.io/apimachinery/pkg/api/resource"

	"github.com/tidbcloud/observability/pkg/common/errors"

	k8sv1 "k8s.io/api/core/v1"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/common/structs"
)

func TestGetServiceInfo(t *testing.T) {
	t.Run("normal", func(t *testing.T) {
		original := serviceInfo
		defer func() {
			serviceInfo = original
		}()
		serviceInfo = &structs.ServiceInfo{
			ServerName: "aaa",
		}
		assert.Equal(t, "aaa", GetServiceInfo().ServerName)
	})

	t.Run("panic", func(t *testing.T) {
		original := serviceInfo
		defer func() {
			serviceInfo = original
		}()
		serviceInfo = nil
		assert.Panics(t, func() {
			GetServiceInfo()
		})
	})
}

func Test_parseSpecQuantities(t *testing.T) {
	t.Run("normal", func(t *testing.T) {
		quantities := parseSpecQuantities("2", "4M", "20G")
		assert.Equal(t, 3, len(quantities))
		quantity, ok := quantities[k8sv1.ResourceCPU]
		assert.True(t, ok)
		assert.Equal(t, int64(2), quantity.Value())
		quantity, ok = quantities[k8sv1.ResourceMemory]
		assert.True(t, ok)
		assert.Equal(t, int64(4000000), quantity.Value())
		quantity, ok = quantities[k8sv1.ResourceStorage]
		assert.True(t, ok)
		assert.Equal(t, int64(20000000000), quantity.Value())
	})
}

func TestGetSpecContainer(t *testing.T) {
	container := GetSpecContainer()
	assert.NotEmpty(t, container)
	container = GetSpecContainer()
	assert.NotEmpty(t, container)
	for _, j := range container {
		assert.NotEqual(t, j.Limits.Cpu().AsApproximateFloat64(), j.Requests.Cpu().AsApproximateFloat64())
		assert.NotEqual(t, j.Limits.Memory().AsApproximateFloat64(), j.Requests.Memory().AsApproximateFloat64())
	}
}

func TestGetRoleArnByBizType(t *testing.T) {
	original := serviceInfo
	defer func() {
		serviceInfo = original
	}()
	serviceInfo = &structs.ServiceInfo{
		BizTypeConfigs: map[string]structs.BizTypeConfig{
			"biz1": {
				AgentManagerARN: "arn1",
			},
			"biz2": {
				AgentManagerARN: "arn2",
			},
		},
	}

	t.Run("normal", func(t *testing.T) {
		arn, err := GetAgentManagerARNByBizType("biz1")
		assert.NoError(t, err)
		assert.Equal(t, "arn1", arn)
		arn, err = GetAgentManagerARNByBizType("biz2")
		assert.NoError(t, err)
		assert.Equal(t, "arn2", arn)
	})

	t.Run("not supported", func(t *testing.T) {
		arn, err := GetAgentManagerARNByBizType("biz3")
		assert.Error(t, err)
		assert.Equal(t, errors.UnsupportedBizType, err.(errors.Error).Code())
		assert.Equal(t, "", arn)
	})
}

func TestGetMonitorReplicasByBizType(t *testing.T) {
	original := serviceInfo
	defer func() {
		serviceInfo = original
	}()
	serviceInfo = &structs.ServiceInfo{
		BizTypeConfigs: map[string]structs.BizTypeConfig{
			"biz1": {
				MonitorReplicas: 1,
			},
			"biz2": {
				MonitorReplicas: 0,
			},
		},
	}

	t.Run("normal", func(t *testing.T) {
		replicas, err := GetMonitorReplicasByBizType("biz1")
		assert.NoError(t, err)
		assert.Equal(t, int32(1), replicas)
	})

	t.Run("not supported", func(t *testing.T) {
		replicas, err := GetMonitorReplicasByBizType("biz3")
		assert.Error(t, err)
		assert.Equal(t, errors.UnsupportedBizType, err.(errors.Error).Code())
		assert.Equal(t, int32(-1), replicas)
	})

	t.Run("monitor replicas config invalid", func(t *testing.T) {
		replicas, err := GetMonitorReplicasByBizType("biz2")
		assert.Error(t, err)
		assert.Equal(t, errors.ConfigInvalid, err.(errors.Error).Code())
		assert.Equal(t, int32(-1), replicas)
	})
}

func TestGetAlertManagerInfo(t *testing.T) {
	original := serviceInfo
	defer func() {
		serviceInfo = original
	}()
	serviceInfo = &structs.ServiceInfo{
		AlertManagerInfo: structs.AlertManagerConfig{
			NameSpace: "observability",
			Name:      "alertNanager",
			Port:      "web",
		},
	}

	t.Run("normal", func(t *testing.T) {
		port := GetAlertManagerPort()
		assert.Equal(t, "web", port)

		name := GetAlertManagerName()
		assert.Equal(t, "alertNanager", name)

		namespace := GetAlertManagerNameSpace()
		assert.Equal(t, "observability", namespace)
	})
}

func Test_buildResourceLimits(t *testing.T) {
	spec := structs.MonitorSpec{
		Cpu:            "1",
		Memory:         "1Gi",
		InitialStorage: "5Gi",
		OverSell:       0.25,
	}
	limits := buildResourceLimits(spec)
	assert.NotEmpty(t, limits)
	cpu, _ := resource.ParseQuantity(spec.Cpu)
	mem, _ := resource.ParseQuantity(spec.Memory)
	quantityCpu := limits[k8sv1.ResourceCPU]
	quantityMem := limits[k8sv1.ResourceMemory]
	assert.Equal(t, cpu.AsApproximateFloat64()*(1+spec.OverSell), quantityCpu.AsApproximateFloat64())
	assert.Equal(t, mem.AsApproximateFloat64()*(1+spec.OverSell), quantityMem.AsApproximateFloat64())
}

func Test_account_info(t *testing.T) {
	original := serviceInfo
	defer func() {
		serviceInfo = original
	}()
	serviceInfo = &structs.ServiceInfo{
		AccountInfo: structs.AccountInfoConfig{
			Account:                  "test_account",
			Oidc:                     "test_oidc",
			OidcARN:                  "test_oidcarn",
			ResourceGroup:            "test_resource_group",
			BlobStorageResourceGroup: "test_storage_resource_group",
			StorageAccount:           "test_azure_sotrage_account",
		},
	}
	assert.Equal(t, "test_account", GetAccountID())
	assert.Equal(t, "test_oidc", GetOidc())
	assert.Equal(t, "test_oidcarn", GetOidcArn())
	assert.Equal(t, "test_account", GetAccountID())
	assert.Equal(t, "test_resource_group", GetResourceGroup())
	assert.Equal(t, "test_storage_resource_group", GetStorageResourceGroup())
	assert.Equal(t, "test_azure_sotrage_account", GetAzureStorageAccount())
}

func TestGetAzureStorageAccount(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetAzureStorageAccount(), "GetAzureStorageAccount()")
		})
	}
}

func TestGetOidc(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetOidc(), "GetOidc()")
		})
	}
}

func TestGetOidcArn(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetOidcArn(), "GetOidcArn()")
		})
	}
}

func TestGetResourceGroup(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetResourceGroup(), "GetResourceGroup()")
		})
	}
}

func TestGetStorageResourceGroup(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetStorageResourceGroup(), "GetStorageResourceGroup()")
		})
	}
}

func TestGetSusbcriptionID(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetSusbcriptionID(), "GetSusbcriptionID()")
		})
	}
}
