package bucket

import (
	"context"
	"testing"

	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/go-logr/logr/testr"
	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/dataplane"
	s3v1beta1 "github.com/upbound/provider-aws/apis/s3/v1beta1"
	s3v1beta2 "github.com/upbound/provider-aws/apis/s3/v1beta2"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func TestCreateBucket(t *testing.T) {
	s := runtime.NewScheme()
	require.NoError(t, v1alpha1.AddToScheme(s))
	require.NoError(t, s3v1beta1.AddToScheme(s))
	require.NoError(t, s3v1beta2.AddToScheme(s))
	logger := testr.New(t)
	rootConfig := buildConfig()
	tmplConfig := config.TemplatesConfig{}

	dataplaneCR := buildDataPlane()
	bucket := buildBucket()
	cli := fake.NewClientBuilder().WithScheme(s).Build()
	bs := NewReconciler(
		cli,
		logger,
		record.NewFakeRecorder(1024),
		rootConfig,
		nil,
	)
	dr := dataplane.NewDataPlaneReconciler(
		cli,
		logger,
		s,
		record.NewFakeRecorder(1024),
		rootConfig,
		tmplConfig,
	)

	ctx := context.TODO()

	dataplaneReq := reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: "dev-us-west-2",
			Name:      "dataplane-test",
		},
	}

	require.NoError(t, cli.Create(ctx, dataplaneCR))
	// Add finalizer to the DataPlane CR
	_, err := dr.Reconcile(ctx, dataplaneReq)
	require.NoError(t, err)

	// Reconcile the DataPlane to create sub-resources
	_, err = dr.Reconcile(ctx, dataplaneReq)
	require.NoError(t, err)

	// Check if the DataPlane is created
	require.NoError(t, cli.Get(ctx, types.NamespacedName{
		Namespace: "dev-us-west-2",
		Name:      "dataplane-test",
	}, dataplaneCR))
	logger.Info("dataplane created", "appbox", dataplaneCR.Status.AppBox)

	require.NoError(t, cli.Create(ctx, bucket))
	bucketReq := reconcile.Request{
		NamespacedName: types.NamespacedName{
			Name: "bucket-test",
		},
	}

	// Should add a finalizer to the Bucket CR
	_, _ = bs.Reconcile(ctx, bucketReq)
	foundBucket := &v1alpha1.Bucket{}
	err = cli.Get(ctx, types.NamespacedName{
		Name: "bucket-test",
	}, foundBucket)
	require.NoError(t, err)
	require.Contains(t, foundBucket.GetFinalizers(), v1alpha1.Finalizer)
	require.Equal(t, v1alpha1.BucketPhaseCreating, foundBucket.Status.Phase)

	_, _ = bs.Reconcile(ctx, bucketReq)
	s3Bucket := &s3v1beta2.Bucket{}
	err = cli.Get(ctx, types.NamespacedName{
		Name: foundBucket.Status.Bucket.Name,
	}, s3Bucket)
	require.NoError(t, err)
	logger.Info("bucket created", "bucket", foundBucket.Status.Bucket.Name)

	s3Bucket.Status.Conditions = []xpv1.Condition{
		{
			Type:   xpv1.TypeReady,
			Status: corev1.ConditionTrue,
			Reason: xpv1.ReasonAvailable,
		},
	}
	require.NoError(t, cli.Update(ctx, s3Bucket))

	// Reconcile the Bucket to create sub-resources
	_, _ = bs.Reconcile(ctx, bucketReq)
	s3BucketPolicy := &s3v1beta1.BucketPolicy{}
	err = cli.Get(ctx, types.NamespacedName{
		Name: foundBucket.Status.Bucket.Name,
	}, s3BucketPolicy)
	logger.Info("s3 bucket policy created", "bucketPolicy", s3BucketPolicy.Name)
	require.NoError(t, err)
}

func buildConfig() config.RootConfig {
	return config.RootConfig{
		Namespace: "dev-us-west-2",
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "dev",
			RegionConfig: config.RegionConfig{
				LogicalRegionName: "us-west-2-wl",
				Name:              "us-west-2",
				Zones:             []string{"us-west-2a", "us-west-2b", "us-west-2c"},
			},
			APPBoxConfig: config.APPBoxConfig{
				K8sVersion: "v1.30.0",
			},
			DataPlaneRoleName:      "dataplane-manager",
			AWSDataPlaneExternalID: "123",
		},
	}
}

func buildDataPlane() *v1alpha1.DataPlane {
	return &v1alpha1.DataPlane{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: "dev-us-west-2",
			Name:      "dataplane-test",
		},
		Spec: v1alpha1.DataPlaneSpec{
			CloudTags: map[string]string{
				"region": "us-west-2",
			},
			VpcCidr:           "192.168.0.0/24",
			AvailabilityZones: []string{"us-west-2a", "us-west-2b", "us-west-2c"},
			AccountRef: v1alpha1.AccountReference{
				ProviderConfigName: "1234",
				AccountID:          "1234",
				Role:               "dataplane-manager",
			},
		},
	}
}

func buildBucket() *v1alpha1.Bucket {
	return &v1alpha1.Bucket{
		ObjectMeta: metav1.ObjectMeta{
			Name: "bucket-test",
			Labels: map[string]string{
				consts.DataplaneLabelKey:       "dataplane-test",
				"tags.tidbcloud.com/bucket-id": "bucket-test",
			},
			Finalizers: []string{
				v1alpha1.Finalizer,
			},
		},
		Spec: v1alpha1.BucketSpec{
			DataplaneId: "dataplane-test",
			Id:          "bucket-test",
			Region:      "us-west-2",
			Tags: map[string]string{
				"env": "test",
			},
			AccountRef: &v1alpha1.AccountReference{
				ProviderConfigName: "1234",
			},
			NodeRoleARN: "arn:aws:iam::1234:role/test-role",
		},
	}
}
