package vpcpeering

import (
	"context"
	"testing"

	// "github.com/aws/aws-sdk-go-v2/service/ec2"
	// ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
	// "go.uber.org/mock/gomock"
	// "k8s.io/utils/pointer"
	"github.com/go-logr/logr/testr"
	"github.com/stretchr/testify/require"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/yaml"

	xpec2v1alpha1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"

	// "github.com/tidbcloud/infra-provider/regional-server/test/service/mockec2client"
	"github.com/tidbcloud/infra-provider/regional-server/test/testdata"
)

func TestVPCPeeringReconcilerPausedAnno(t *testing.T) {
	s := runtime.NewScheme()
	// ctl := gomock.NewController(t)
	// just mock the api to make test case work
	// not really check the calling logic correct.
	// ec2client := mockec2client.NewMockClient(ctl)
	// ec2client.EXPECT().DescribeRouteTables(
	// 	gomock.Any(),
	// 	gomock.Any(),
	// 	gomock.Any()).
	// 	DoAndReturn(func(_ context.Context, _ *ec2.DescribeRouteTablesInput, _ ...func(*ec2.Options)) (*ec2.DescribeRouteTablesOutput, error) {
	// 		out := new(ec2.DescribeRouteTablesOutput)
	// 		out.RouteTables = make([]ec2types.RouteTable, 0)
	// 		return out, nil
	// 	}).AnyTimes()
	// ec2client.EXPECT().CreateRoute(
	// 	gomock.Any(),
	// 	gomock.Any(),
	// 	gomock.Any()).
	// 	DoAndReturn(func(ctx context.Context, params *ec2.CreateRouteInput, optFns ...func(*ec2.Options)) (*ec2.CreateRouteOutput, error) {
	// 		out := new(ec2.CreateRouteOutput)
	// 		out.Return = pointer.Bool(true)
	// 		return out, nil
	// 	}).AnyTimes()
	// ec2client.EXPECT().DeleteRoute(
	// 	gomock.Any(),
	// 	gomock.Any(),
	// 	gomock.Any()).
	// 	DoAndReturn(func(ctx context.Context, params *ec2.DeleteRouteInput, optFns ...func(*ec2.Options)) (*ec2.DeleteRouteOutput, error) {
	// 		out := new(ec2.DeleteRouteOutput)
	// 		return out, nil
	// 	}).AnyTimes()
	require.NoError(t, v1alpha1.AddToScheme(s))
	require.NoError(t, xpec2v1alpha1.SchemeBuilder.AddToScheme(s))
	logger := testr.New(t)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	defaultVPC := new(v1alpha1.VPC)
	{
		bs, err := testdata.LoadDefaultVPCYAML()
		require.NoError(t, err)
		require.NoError(t, yaml.Unmarshal(bs, defaultVPC))
	}
	{
		bs, err := testdata.LoadDefaultVPCPeeringYAML()
		require.NoError(t, err)
		require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	}
	{
		// if there is an skip annotation, reconcile should do nothing
		vpcpeering := defaultVPCPeering.DeepCopy()
		vpcpeering.Status.Phase = v1alpha1.VPCPeeringPhaseCreating
		vpcpeering.Status.ExternalName = ""
		vpcpeering.Status.CrossplaneResourceName = ""
		delete(vpcpeering.Annotations, v1alpha1.LabelKeyExternalName)
		delete(vpcpeering.Annotations, v1alpha1.AnnotationKeySourceVPCID)
		delete(vpcpeering.Annotations, v1alpha1.AnnotationKeySourceVPCCIDR)
		vpcpeering.Annotations[v1alpha1.AnnotationKeyReconciliationPaused] = "true"
		vpc := defaultVPC.DeepCopy()
		kubeClient := fake.NewClientBuilder().WithScheme(s).WithObjects(vpcpeering, vpc).WithStatusSubresource(vpcpeering, vpc).Build()
		rr := NewVPCPeeringReconciler(
			kubeClient,
			kubeClient,
			logger,
			s,
			record.NewFakeRecorder(1024))
		restult, err := rr.Reconcile(context.TODO(), reconcile.Request{
			NamespacedName: types.NamespacedName{
				Namespace: "foo",
				Name:      "foo",
			},
		})
		require.NoError(t, err)
		// not reconcile on skipped cr.
		require.True(t, restult.IsZero())
		// no vpcpeeringconntion is created.
		list := new(xpec2v1alpha1.VPCPeeringConnectionList)
		require.NoError(t, kubeClient.List(context.TODO(), list))
		require.Equal(t, 0, len(list.Items))
		// vpcpeering cr is not modified.
		newvpcpeering := vpcpeering.DeepCopy()
		require.NoError(t, kubeClient.Get(context.TODO(), client.ObjectKeyFromObject(newvpcpeering), newvpcpeering))
		require.Equal(t, vpcpeering, newvpcpeering)
	}
}

func Test_isNotReadyVPCPeering(t *testing.T) {
	cases := []struct {
		name     string
		peering  *v1alpha1.VPCPeering
		expected bool
	}{
		{
			name: "not ready",
			peering: &v1alpha1.VPCPeering{
				Status: v1alpha1.VPCPeeringStatus{
					Phase: v1alpha1.VPCPeeringPhaseCreating,
				},
			},
			expected: true,
		},
		{
			name: "ready",
			peering: &v1alpha1.VPCPeering{
				Status: v1alpha1.VPCPeeringStatus{
					Phase: v1alpha1.VPCPeeringPhaseActive,
				},
			},
			expected: false,
		},
		{
			name: "deleting",
			peering: &v1alpha1.VPCPeering{
				Status: v1alpha1.VPCPeeringStatus{
					Phase: v1alpha1.VPCPeeringPhaseDeleting,
				},
			},
			expected: false,
		},
		{
			name: "empty",
			peering: &v1alpha1.VPCPeering{
				Status: v1alpha1.VPCPeeringStatus{
					Phase: "",
				},
			},
			expected: true,
		},
		{
			name: "pending",
			peering: &v1alpha1.VPCPeering{
				Status: v1alpha1.VPCPeeringStatus{
					Phase: v1alpha1.VPCPeeringPhasePending,
				},
			},
			expected: true,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			require.Equal(t, tc.expected, isNotReadyVPCPeering(tc.peering))
		})
	}
}
