package networkcapacity

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
)

func TestEvaluateAWSRegionalCapacity(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name      string
		prefix    int
		reserve   int64
		supported bool
		count     int64
		category  ResultCategory
	}{
		{name: "lower provider boundary", prefix: 16, reserve: 50, supported: true, count: 6127, category: ResultCategorySupported},
		{name: "three-zone /23", prefix: 23, reserve: 50, supported: true, count: 31, category: ResultCategorySupported},
		{name: "zero admin reserve", prefix: 23, reserve: 0, supported: true, count: 44, category: ResultCategorySupported},
		{name: "admin reserve consumes usable addresses", prefix: 23, reserve: 177, supported: false, count: 0, category: ResultCategoryZeroNodeCapacity},
		{name: "prefix below AWS range", prefix: 15, supported: false, category: ResultCategoryPrefixRange},
		{name: "prefix above AWS range", prefix: 24, supported: false, category: ResultCategoryPrefixRange},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			result, err := Evaluate(Input{
				Provider:            consts.ProviderTypeEKS,
				PrefixLength:        tt.prefix,
				HighAvailability:    HighAvailabilityRegional,
				RegionZones:         []string{"us-west-2c", "us-west-2a", "us-west-2b"},
				ReservedAdminPodIPs: tt.reserve,
			})
			require.NoError(t, err)
			require.Equal(t, tt.supported, result.Supported)
			require.Equal(t, tt.count, result.SupportedNodeCount)
			require.Equal(t, tt.category, result.Category)
			require.NoError(t, ValidateResult(result))
		})
	}
}

func TestEvaluateAWSZonalEndpointCapacity(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name      string
		prefix    int
		zones     []string
		supported bool
		category  ResultCategory
	}{
		{
			name:      "three endpoint zones fit /19",
			prefix:    19,
			zones:     []string{"us-west-2c", "us-west-2a", "us-west-2b"},
			supported: true,
			category:  ResultCategorySupported,
		},
		{
			name:      "three endpoint zones do not fit /20",
			prefix:    20,
			zones:     []string{"us-west-2a", "us-west-2b", "us-west-2c"},
			supported: false,
			category:  ResultCategoryEndpointCapacity,
		},
		{
			name:      "exact fit",
			prefix:    20,
			zones:     []string{"us-west-2a", "us-west-2b"},
			supported: true,
			category:  ResultCategorySupported,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			result, err := Evaluate(Input{
				Provider:                 consts.ProviderTypeEKS,
				PrefixLength:             tt.prefix,
				HighAvailability:         HighAvailabilityZonal,
				RegionZones:              []string{"us-west-2a", "us-west-2b", "us-west-2c"},
				ConfiguredNodeZones:      []string{"us-west-2a"},
				EndpointSubnetsEnabled:   true,
				PrivateLinkEndpointZones: tt.zones,
				ReservedAdminPodIPs:      50,
			})
			require.NoError(t, err)
			require.Equal(t, tt.supported, result.Supported)
			require.Equal(t, tt.category, result.Category)
		})
	}
}

func TestEvaluateRejectsInvalidInput(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name  string
		input Input
	}{
		{
			name: "negative prefix",
			input: Input{
				Provider:         consts.ProviderTypeEKS,
				PrefixLength:     -1,
				HighAvailability: HighAvailabilityRegional,
			},
		},
		{
			name: "prefix over IPv4 bound",
			input: Input{
				Provider:         consts.ProviderTypeEKS,
				PrefixLength:     33,
				HighAvailability: HighAvailabilityRegional,
			},
		},
		{
			name: "negative admin reserve",
			input: Input{
				Provider:            consts.ProviderTypeEKS,
				PrefixLength:        20,
				HighAvailability:    HighAvailabilityRegional,
				ReservedAdminPodIPs: -1,
			},
		},
		{
			name: "unknown HA",
			input: Input{
				Provider:         consts.ProviderTypeEKS,
				PrefixLength:     20,
				HighAvailability: 99,
			},
		},
		{
			name: "zonal without configured node zone",
			input: Input{
				Provider:         consts.ProviderTypeEKS,
				PrefixLength:     19,
				HighAvailability: HighAvailabilityZonal,
				RegionZones:      []string{"a", "b"},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			_, err := Evaluate(tt.input)
			require.Error(t, err)
		})
	}
}

func TestEvaluateRejectsUnsupportedProvider(t *testing.T) {
	t.Parallel()

	_, err := Evaluate(Input{
		Provider:         consts.ProviderTypeACK,
		PrefixLength:     20,
		HighAvailability: HighAvailabilityRegional,
	})
	require.ErrorContains(t, err, `unsupported for provider "ack"`)
}

func TestValidateResult(t *testing.T) {
	t.Parallel()

	require.Error(t, ValidateResult(Result{Supported: true}))
	require.Error(t, ValidateResult(Result{SupportedNodeCount: 1}))
	require.NoError(t, ValidateResult(Result{Supported: true, SupportedNodeCount: 1}))
	require.NoError(t, ValidateResult(Result{}))
}
