package common

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
)

func TestBuildPeeringRouteLabels(t *testing.T) {
	tests := []struct {
		name           string
		vpcPeeringName string
		wantKey        string
	}{
		{
			name:           "Normal peering name",
			vpcPeeringName: "test-peering",
			wantKey:        v1alpha1.VPCPeeringNameKey,
		},
		{
			name:           "Complex peering name",
			vpcPeeringName: "prod-vpc-peering-us-west-2",
			wantKey:        v1alpha1.VPCPeeringNameKey,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := BuildPeeringRouteLabels(tt.vpcPeeringName)

			assert.NotNil(t, got, "Labels map should not be nil")
			assert.Contains(t, got, tt.wantKey, "Labels should contain VPCPeeringNameKey")
			assert.Equal(t, tt.vpcPeeringName, got[tt.wantKey],
				"Label value should match vpc peering name")
			assert.Len(t, got, 1, "Should only have one label")
		})
	}
}

func TestGetNodeRouteTableNames(t *testing.T) {
	tests := []struct {
		name        string
		internet    *v1alpha1.Internet
		filter      string
		want        []string
		wantErr     bool
		errContains string
	}{
		{
			name: "AWS filter - finds node route tables",
			internet: &v1alpha1.Internet{
				Status: v1alpha1.InternetStatus{
					RouteTables: []v1alpha1.RouteTable{
						{Name: "vpc-route-table-nodes-us-west-2a"},
						{Name: "vpc-route-table-nodes-us-west-2b"},
						{Name: "vpc-route-table-pods-us-west-2a"},
					},
				},
			},
			filter:  "-nodes-",
			want:    []string{"vpc-route-table-nodes-us-west-2a", "vpc-route-table-nodes-us-west-2b"},
			wantErr: false,
		},
		{
			name: "AliCloud filter - finds node route tables",
			internet: &v1alpha1.Internet{
				Status: v1alpha1.InternetStatus{
					RouteTables: []v1alpha1.RouteTable{
						{Name: "vpc-route-table-nodes"},
						{Name: "vpc-route-table-pods"},
					},
				},
			},
			filter:  "-nodes",
			want:    []string{"vpc-route-table-nodes"},
			wantErr: false,
		},
		{
			name:        "Nil internet",
			internet:    nil,
			filter:      "-nodes-",
			want:        nil,
			wantErr:     true,
			errContains: "nil/empty",
		},
		{
			name: "Empty route tables",
			internet: &v1alpha1.Internet{
				Status: v1alpha1.InternetStatus{
					RouteTables: []v1alpha1.RouteTable{},
				},
			},
			filter:      "-nodes-",
			want:        nil,
			wantErr:     true,
			errContains: "nil/empty",
		},
		{
			name: "No matching route tables",
			internet: &v1alpha1.Internet{
				Status: v1alpha1.InternetStatus{
					RouteTables: []v1alpha1.RouteTable{
						{Name: "vpc-route-table-pods-us-west-2a"},
						{Name: "vpc-route-table-services-us-west-2b"},
					},
				},
			},
			filter:      "-nodes-",
			want:        nil,
			wantErr:     true,
			errContains: "no node route tables found",
		},
		{
			name: "Multiple route tables with mixed names",
			internet: &v1alpha1.Internet{
				Status: v1alpha1.InternetStatus{
					RouteTables: []v1alpha1.RouteTable{
						{Name: "vpc-nodes-az1"},
						{Name: "vpc-pods-az1"},
						{Name: "vpc-nodes-az2"},
						{Name: "vpc-services-az1"},
						{Name: "vpc-nodes-az3"},
					},
				},
			},
			filter:  "-nodes-",
			want:    []string{"vpc-nodes-az1", "vpc-nodes-az2", "vpc-nodes-az3"},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := GetNodeRouteTableNames(tt.internet, tt.filter)

			if tt.wantErr {
				assert.Error(t, err, "Expected an error")
				if tt.errContains != "" {
					assert.Contains(t, err.Error(), tt.errContains,
						"Error message should contain expected text")
				}
				assert.Nil(t, got, "Result should be nil when error occurs")
			} else {
				assert.NoError(t, err, "Should not return an error")
				assert.ElementsMatch(t, tt.want, got,
					"Should return expected route table names")
			}
		})
	}
}

// Test edge cases and invariants
func TestGenVPCPeeringRouteName_Deterministic(t *testing.T) {
	// Same inputs should always produce same output
	name1 := GenVPCPeeringRouteName("peering-1", "table-1")
	name2 := GenVPCPeeringRouteName("peering-1", "table-1")
	assert.Equal(t, name1, name2, "Function should be deterministic")
}

func TestGenVPCPeeringRouteName_Unique(t *testing.T) {
	// Different inputs should produce different outputs
	name1 := GenVPCPeeringRouteName("peering-1", "table-1")
	name2 := GenVPCPeeringRouteName("peering-2", "table-1")
	name3 := GenVPCPeeringRouteName("peering-1", "table-2")

	assert.NotEqual(t, name1, name2, "Different peering names should produce different routes")
	assert.NotEqual(t, name1, name3, "Different table names should produce different routes")
	assert.NotEqual(t, name2, name3, "All combinations should be unique")
}

func TestBuildPeeringRouteLabels_Immutability(t *testing.T) {
	// Returned map should be a new instance each time
	labels1 := BuildPeeringRouteLabels("peering-1")
	labels2 := BuildPeeringRouteLabels("peering-1")

	// Modify one
	labels1["test-key"] = "test-value"

	// Other should not be affected
	assert.NotContains(t, labels2, "test-key",
		"Modifying one labels map should not affect another")
}

func TestGetNodeRouteTableNames_PreservesOrder(t *testing.T) {
	// Route tables should be returned in the order they appear in the status
	internet := &v1alpha1.Internet{
		Status: v1alpha1.InternetStatus{
			RouteTables: []v1alpha1.RouteTable{
				{Name: "vpc-nodes-c"},
				{Name: "vpc-nodes-a"},
				{Name: "vpc-nodes-b"},
			},
		},
	}

	got, err := GetNodeRouteTableNames(internet, "-nodes-")
	assert.NoError(t, err)
	assert.Equal(t, []string{"vpc-nodes-c", "vpc-nodes-a", "vpc-nodes-b"}, got,
		"Should preserve original order")
}
