//go:build upgrader_integration
// +build upgrader_integration

package k8sversion_test

import (
	"context"
	"path/filepath"
	"testing"
	"time"

	autoscalingv1beta1 "github.com/crossplane-contrib/provider-aws/apis/autoscaling/v1beta1"
	ec2v1beta1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1beta1"
	"github.com/crossplane-contrib/provider-aws/apis/eks/manualv1alpha1"
	cpeksv1beta1 "github.com/crossplane-contrib/provider-aws/apis/eks/v1beta1"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
	operatorv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"go.uber.org/mock/gomock"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/record"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/envtest"
	logf "sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/controllers/k8sversion"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/account"
	"github.com/tidbcloud/infra-provider/pkg/cloud/aws"
	"github.com/tidbcloud/infra-provider/pkg/cloud/gcp"
	"github.com/tidbcloud/infra-provider/pkg/config"
	utiltime "github.com/tidbcloud/infra-provider/pkg/util/time"
)

var (
	k8sClient  client.Client
	k8sManager ctrl.Manager
	testEnv    *envtest.Environment

	mockAWSCli *aws.MockClient
	mockGCPCli *gcp.MockClient

	mockClock *utiltime.MockClock

	ctx    context.Context
	cancel context.CancelFunc

	fakeRecorder record.EventRecorder
)

func TestK8sversion(t *testing.T) {
	ctl := gomock.NewController(t)
	defer ctl.Finish()

	mockAWSCli = aws.NewMockClient(ctl)
	mockGCPCli = gcp.NewMockClient(ctl)

	fakeRecorder = record.NewFakeRecorder(3)
	mockClock = utiltime.NewMockClock(ctl)

	RegisterFailHandler(Fail)
	RunSpecs(t, "k8s version controller Suite")
}

var _ = SynchronizedBeforeSuite(func() []byte {
	logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(false)))
	By("bootstrapping test environment")

	ctx, cancel = context.WithCancel(context.TODO())

	// Install CRDs
	testEnv = &envtest.Environment{
		CRDDirectoryPaths: []string{
			filepath.Join("..", "..", "..", "..", "assets", "provider-aws", "package", "crds"),
			filepath.Join("..", "..", "..", "..", "infra-api", "config", "crd", "bases"),
		},
		ErrorIfCRDPathMissing: true,
	}

	cfg, err := testEnv.Start()
	Expect(err).ToNot(HaveOccurred())
	Expect(cfg).ToNot(BeNil())

	scheme := runtime.NewScheme()
	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
	utilruntime.Must(cloudv1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(v1alpha1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(operatorv1alpha1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(cpeksv1beta1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(ec2v1beta1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(manualv1alpha1.SchemeBuilder.AddToScheme(scheme))
	utilruntime.Must(autoscalingv1beta1.SchemeBuilder.AddToScheme(scheme))
	k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
		Scheme:                 scheme,
		HealthProbeBindAddress: ":8082",
	})
	Expect(err).ToNot(HaveOccurred())

	rootCfg := &config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Network: config.NetworkConfig{
				Region: "us-west-2",
			},
			FullManage: config.FullManage{
				Info: account.Info{
					NetworkInfo: account.NetworkInfo{
						AssumeRoleExternalID: "test",
					},
				},
			},
		},
		Predicate: v1.LabelSelector{
			MatchLabels: map[string]string{
				cloudv1.ProviderTypeLabel: "eks",
			},
		},
	}
	mockAWSCli.EXPECT().SSM(gomock.Any(), gomock.Any()).AnyTimes()
	err = k8sversion.NewReconciler(k8sManager.GetClient(), k8sManager.GetCache(), mockAWSCli, mockGCPCli, rootCfg, mockClock, fakeRecorder).SetupWithManager(k8sManager)
	Expect(err).ToNot(HaveOccurred())

	go func() {
		defer GinkgoRecover()

		err = k8sManager.Start(ctrl.SetupSignalHandler())
		Expect(err).ToNot(HaveOccurred(), "failed to run manager")
		gexec.KillAndWait(4 * time.Second)
		Expect(testEnv.Stop()).NotTo(HaveOccurred())
	}()

	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
	Expect(err).NotTo(HaveOccurred())
	Expect(k8sClient).NotTo(BeNil())
	return nil
}, func(_ []byte) {})

var _ = SynchronizedAfterSuite(func() {}, func() {
	cancel()
	By("tearing down the test environment")
})
