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"
	reconcileerr "github.com/tidbcloud/infra-api/controllers/common/error"
	"github.com/tidbcloud/infra-api/pkg/consts"
)

func TestCopyLabelsForWorkloads(t *testing.T) {
	cases := []struct {
		input  map[string]string
		expect map[string]string
	}{
		{
			input: map[string]string{
				consts.TenantTagKey: "123456",
			},
			expect: map[string]string{
				consts.TenantTagKey: "123456",
			},
		},
		{
			input: map[string]string{
				consts.TenantTagKey:  "123456",
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "345678",
			},
			expect: map[string]string{
				consts.TenantTagKey:  "123456",
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "345678",
			},
		},
		{
			input: map[string]string{
				consts.TenantTagKey:  "123456",
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "345678",
				"abc":                "def",
			},
			expect: map[string]string{
				consts.TenantTagKey:  "123456",
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "345678",
			},
		},
		{
			input: map[string]string{
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "345678",
				"abc":                "def",
			},
			expect: map[string]string{
				consts.ProjectTagKey: "234567",
				consts.ClusterTagKey: "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)
			}
		})
	}
}
