package utils

import (
	"errors"
	"fmt"
	"os"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	reconcileerr "github.com/tidbcloud/infra-provider/pkg/controllers/common/error"
)

func TestCopyLabelsForWorkloads(t *testing.T) {
	cases := []struct {
		input  map[string]string
		expect map[string]string
	}{
		{
			input: map[string]string{
				consts.TenantLabelKey: "123456",
			},
			expect: map[string]string{
				consts.TenantLabelKey: "123456",
			},
		},
		{
			input: map[string]string{
				consts.TenantLabelKey:  "123456",
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
			},
			expect: map[string]string{
				consts.TenantLabelKey:  "123456",
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
			},
		},
		{
			input: map[string]string{
				consts.TenantLabelKey:  "123456",
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
				"abc":                  "def",
			},
			expect: map[string]string{
				consts.TenantLabelKey:  "123456",
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
			},
		},
		{
			input: map[string]string{
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
				"abc":                  "def",
			},
			expect: map[string]string{
				consts.ProjectLabelKey: "234567",
				consts.ClusterLabelKey: "345678",
			},
		},
	}

	for _, c := range cases {
		g := NewGomegaWithT(t)
		g.Expect(CopyLabelsForWorkloads(c.input)).Should(Equal(c.expect))
	}
}

func TestIsRealError(t *testing.T) {
	tests := []struct {
		name string
		err  error
		want bool
	}{
		{
			name: "A format error",
			err:  fmt.Errorf("an error for %s", "testing"),
			want: true,
		},
		{
			name: "A custom error",
			err:  errors.New("test"),
			want: true,
		},
		{
			name: "A built-in error",
			err:  &os.PathError{},
			want: true,
		},
		{
			name: "err is nil",
			err:  nil,
			want: false,
		},
		{
			name: "No requeued error",
			err:  &reconcileerr.RequeueAfterError{Caused: nil},
			want: false,
		},
		{
			name: "Has requeued error",
			err:  &reconcileerr.RequeueAfterError{Caused: fmt.Errorf("test")},
			want: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := IsRealError(tt.err); got != tt.want {
				t.Errorf("IsRealError() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGetMember(t *testing.T) {
	tests := []struct {
		name string
		comp cloudv1.ComponentType
		want v1alpha1.MemberType
	}{
		{
			name: "TiDB",
			comp: cloudv1.ComponentTiDB,
			want: v1alpha1.TiDBMemberType,
		},
		{
			name: "Admin TiDB",
			comp: cloudv1.ComponentAdminTiDB,
			want: v1alpha1.TiDBMemberType,
		},
		{
			name: "TiKV",
			comp: cloudv1.ComponentTiKV,
			want: v1alpha1.TiKVMemberType,
		},
		{
			name: "PD",
			comp: cloudv1.ComponentPD,
			want: v1alpha1.PDMemberType,
		},
		{
			name: "TiFlash",
			comp: cloudv1.ComponentTiFlash,
			want: v1alpha1.TiFlashMemberType,
		},
		{
			name: "Backup",
			comp: cloudv1.ComponentBackup,
			want: v1alpha1.UnknownMemberType,
		},
		{
			name: "TiCDC",
			comp: cloudv1.ComponentTiCDC,
			want: v1alpha1.TiCDCMemberType,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := GetMemberType(tt.comp); got != tt.want {
				t.Errorf("GetMemberType() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestTokenBasedAuthEnabled(t *testing.T) {
	tests := []struct {
		name    string
		labels  map[string]string
		version string
		want    bool
	}{
		{
			name: "normal case - eks",
			labels: map[string]string{
				cloudv1.ClusterLabelWantTokenBasedAuth: "true",
				cloudv1.ProviderTypeLabel:              consts.ProviderTypeEKS,
			},
			version: "v6.4.0",
			want:    true,
		},
		{
			name: "normal case - gke",
			labels: map[string]string{
				cloudv1.ClusterLabelWantTokenBasedAuth: "true",
				cloudv1.ProviderTypeLabel:              consts.ProviderTypeEKS,
			},
			version: "v6.4.0",
			want:    true,
		},
		{
			name: "normal case - devtier",
			labels: map[string]string{
				cloudv1.ClusterLabelWantTokenBasedAuth: "true",
				cloudv1.ProviderTypeLabel:              consts.ProviderTypeAWSFreeTier,
			},
			version: "v6.4.0",
			want:    true,
		},
		{
			name: "NOT supported providerType: aws",
			labels: map[string]string{
				cloudv1.ClusterLabelWantTokenBasedAuth: "true",
				cloudv1.ProviderTypeLabel:              consts.ProviderTypeAWS,
			},
			version: "v6.4.0",
			want:    false,
		},
		{
			name:    "less than min version",
			labels:  map[string]string{cloudv1.ClusterLabelWantTokenBasedAuth: "true"},
			version: "v6.3.0",
			want:    false,
		},
		{
			name:    "empty version",
			labels:  map[string]string{cloudv1.ClusterLabelWantTokenBasedAuth: "true"},
			version: "",
			want:    false,
		},
		{
			name:    "invalid 'want-token-based-auth' label",
			labels:  map[string]string{cloudv1.ClusterLabelWantTokenBasedAuth: ""},
			version: "v6.4.0",
			want:    false,
		},
		{
			name:    "miss 'want-token-based-auth' label",
			labels:  map[string]string{},
			version: "v6.4.0",
			want:    false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cluster := &cloudv1.Cluster{}
			cluster.Labels = tt.labels
			if got := TokenBasedAuthEnabled(cluster, tt.version); got != tt.want {
				t.Errorf("TokenBasedAuthEnabled() = %v, want %v", got, tt.want)
			}
		})
	}
}
