package task

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/namer"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestContext_ProviderConfigName(t *testing.T) {
	t.Run("account nil", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{},
		}
		assert.Equal(t, "", c.ProviderConfigName())
	})
	t.Run("account not nil", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						ProviderConfigName: "test",
					},
				},
			},
		}
		assert.Equal(t, "test", c.ProviderConfigName())
	})
}

func TestContext_InternetName(t *testing.T) {
	t.Run("annotation empty", func(t *testing.T) {
		c := &Context{
			Internet: &v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "internet-test-1af1a690",
				},
			},
		}
		assert.Contains(t, c.InternetName(), "internet-test-1af1a690")
	})
}

func TestContext_VPCName(t *testing.T) {
	t.Run("get from vpc status", func(t *testing.T) {
		c := &Context{
			Internet: &v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "internet-test-1af1a690",
				},
			},
			VPC: &v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "vpc-test-1af1a690",
				},
				Status: v1alpha1.VPCStatus{
					VPC: v1alpha1.VPCRes{
						Name: "vpc-test-generate",
					},
				},
			},
		}
		assert.Equal(t, "vpc-test-generate", c.VPCName())
	})
	t.Run("generate unique identifier", func(t *testing.T) {
		c := &Context{
			Internet: &v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "internet-test-1af1a690",
				},
			},
			VPC: &v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "vpc-test-1af1a690",
				},
			},
			Config: config.RootConfig{
				ResourceNamePrefix: "tidbcloud",
			},
		}
		assert.Contains(t, c.VPCName(), controllers.UniqueIdentifier(c.VPC.ObjectMeta, namer.WithPrefix(c.Config.ResourceNamePrefix)))
	})
}

func TestContext_GetZones(t *testing.T) {
	t.Run("ignore node zones and return vpc zones", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					Zones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
				},
			},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones:     []string{"us-west-2a", "us-west-2b", "us-west-2c"},
						NodeZones: []string{"us-west-2a", "us-west-2b"},
					},
				},
			},
		}

		assert.Equal(t, []string{"us-west-2a", "us-west-2b", "us-west-2c"}, c.GetZones())
	})

	t.Run("fallback to vpc zones when node zones empty", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					Zones: []string{"us-west-2a", "us-west-2b"},
				},
			},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
					},
				},
			},
		}

		assert.Equal(t, []string{"us-west-2a", "us-west-2b"}, c.GetZones())
	})

	t.Run("fallback to region zones when vpc zones empty", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
					},
				},
			},
		}

		assert.Empty(t, c.GetZones())
	})
}

func TestContext_GetNodeZones(t *testing.T) {
	t.Run("use vpc node zones when configured", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					NodeZones: []string{"us-west-2a", "us-west-2b"},
					Zones:     []string{"us-west-2a", "us-west-2b", "us-west-2c"},
				},
			},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones:     []string{"us-west-2a", "us-west-2b", "us-west-2c"},
						NodeZones: []string{"us-west-2a", "us-west-2c"},
					},
				},
			},
		}

		assert.Equal(t, []string{"us-west-2a", "us-west-2b"}, c.GetNodeZones())
	})

	t.Run("fallback to vpc zones when vpc node zones empty", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					Zones: []string{"us-west-2a", "us-west-2b"},
				},
			},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones:     []string{"us-west-2a", "us-west-2b", "us-west-2c"},
						NodeZones: []string{"us-west-2a", "us-west-2c"},
					},
				},
			},
		}

		assert.Equal(t, []string{"us-west-2a", "us-west-2b"}, c.GetNodeZones())
	})

	t.Run("return empty when vpc zones empty", func(t *testing.T) {
		c := &Context{
			VPC: &v1alpha1.VPC{},
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					RegionConfig: config.RegionConfig{
						Zones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
					},
				},
			},
		}

		assert.Empty(t, c.GetNodeZones())
	})
}
