package enum

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
)

func TestToString(t *testing.T) {
	type testCase[E enumStringer] struct {
		name  string
		input E
		want  string
	}

	providerTests := []testCase[globalcommonv1.CloudProvider_Name]{
		{
			name:  "CloudProvider_AWS to aws",
			input: globalcommonv1.CloudProvider_AWS,
			want:  "aws",
		},
		{
			name:  "CloudProvider_GCP to gcp",
			input: globalcommonv1.CloudProvider_GCP,
			want:  "gcp",
		},
		{
			name:  "CloudProvider_NAME_UNSPECIFIED to NAME_UNSPECIFIED",
			input: globalcommonv1.CloudProvider_NAME_UNSPECIFIED,
			want:  "NAME_UNSPECIFIED",
		},
	}
	for _, tt := range providerTests {
		t.Run(tt.name, func(t *testing.T) {
			if got := ToString(tt.input); got != tt.want {
				t.Errorf("ToString() = %v, want %v", got, tt.want)
			}
		})
	}

	regionTests := []testCase[globalcommonv1.Region_Name]{
		{
			name:  "Region_AWS_US_EAST_2 to us-east-2",
			input: globalcommonv1.Region_AWS_US_EAST_2,
			want:  "us-east-2",
		},
		{
			name:  "CloudProvider_NAME_UNSPECIFIED to NAME_UNSPECIFIED",
			input: globalcommonv1.Region_NAME_UNSPECIFIED,
			want:  "NAME_UNSPECIFIED",
		},
	}
	for _, tt := range regionTests {
		t.Run(tt.name, func(t *testing.T) {
			if got := ToString(tt.input); got != tt.want {
				t.Errorf("ToString() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestFromString(t *testing.T) {
	type testCase struct {
		name    string
		input   string
		want    int32
		wantErr bool
	}

	providerTests := []testCase{
		{
			name:    "aws to CloudProvider_AWS",
			input:   "aws",
			want:    int32(globalcommonv1.CloudProvider_AWS),
			wantErr: false,
		},
		{
			name:    "gcp to CloudProvider_GCP",
			input:   "gcp",
			want:    int32(globalcommonv1.CloudProvider_GCP),
			wantErr: false,
		},
		{
			name:    "NAME_UNSPECIFIED to CloudProvider_NAME_UNSPECIFIED",
			input:   "NAME_UNSPECIFIED",
			want:    int32(globalcommonv1.CloudProvider_NAME_UNSPECIFIED),
			wantErr: false,
		},
		{
			name:    "unknown string literal",
			input:   "foobar",
			want:    0,
			wantErr: true,
		},
	}
	for _, tt := range providerTests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := FromString[globalcommonv1.CloudProvider_Name](tt.input)
			if (err != nil) != tt.wantErr {
				t.Errorf("FromString() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("FromString() got = %v, want %v", got, tt.want)
			}
		})
	}

	regionTests := []testCase{
		{
			name:    "us-east-1 to Region_AWS_US_EAST_1",
			input:   "us-east-1",
			want:    int32(globalcommonv1.Region_AWS_US_EAST_1),
			wantErr: false,
		},
		{
			name:    "us-west-2 to Region_AWS_US_WEST_2",
			input:   "us-west-2",
			want:    int32(globalcommonv1.Region_AWS_US_WEST_2),
			wantErr: false,
		},
		{
			name:    "unknown string literal",
			input:   "foobar",
			want:    0,
			wantErr: true,
		},
	}
	for _, tt := range regionTests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := FromString[globalcommonv1.Region_Name](tt.input)
			if (err != nil) != tt.wantErr {
				t.Errorf("FromString() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("FromString() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestRegionFromString(t *testing.T) {
	type args struct {
		provider string
		region   string
	}
	tests := []struct {
		name string
		args args
		want globalcommonv1.Region_Name
		err  error
	}{
		{
			name: "AWS us-east-1",
			args: args{
				provider: "aws",
				region:   "us-east-1",
			},
			want: globalcommonv1.Region_AWS_US_EAST_1,
			err:  nil,
		},
		{
			name: "AWS ap-southeast-1",
			args: args{
				provider: "aws",
				region:   "ap-southeast-1",
			},
			want: globalcommonv1.Region_AWS_AP_SOUTHEAST_1,
			err:  nil,
		},
		{
			name: "GCP us-central1",
			args: args{
				provider: "gcp",
				region:   "us-central1",
			},
			want: globalcommonv1.Region_GCP_US_CENTRAL1,
			err:  nil,
		},
		{
			name: "Azure eastus",
			args: args{
				provider: "azure",
				region:   "eastus",
			},
			want: globalcommonv1.Region_AZURE_EASTUS,
			err:  nil,
		},
		{
			name: "AliCloud ap-southeast-1",
			args: args{
				provider: "alicloud",
				region:   "ap-southeast-1",
			},
			want: globalcommonv1.Region_ALICLOUD_AP_SOUTHEAST_1,
			err:  nil,
		},
		{
			name: "Unknown provider",
			args: args{
				provider: "aaa",
				region:   "eastus",
			},
			want: globalcommonv1.Region_NAME_UNSPECIFIED,
			err:  fmt.Errorf("invalid region enum value: %s", "AAA_EASTUS"),
		},
		{
			name: "empty provider",
			args: args{
				provider: "",
				region:   "ap-southeast-1",
			},
			want: globalcommonv1.Region_NAME_UNSPECIFIED,
			err:  nil,
		},
		{
			name: "Unknown region",
			args: args{
				provider: "aws",
				region:   "bbb",
			},
			want: globalcommonv1.Region_NAME_UNSPECIFIED,
			err:  fmt.Errorf("invalid region enum value: %s", "AWS_BBB"),
		},
		{
			name: "empty region",
			args: args{
				provider: "aaa",
				region:   "",
			},
			want: globalcommonv1.Region_NAME_UNSPECIFIED,
			err:  nil,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got, err := RegionFromString(tt.args.provider, tt.args.region); got != tt.want {
				if tt.err != nil {
					require.EqualError(t, err, tt.err.Error())
				} else {
					require.NoError(t, err)
				}
				t.Errorf("RegionFromString() = %v, want %v", got, tt.want)
			}
		})
	}
}
