/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package controllers

import (
	"database/sql"
	"regexp"

	"github.com/apache/shardingsphere-on-cloud/shardingsphere-operator/api/v1alpha1"
	mockChaos "github.com/apache/shardingsphere-on-cloud/shardingsphere-operator/pkg/kubernetes/chaosmesh/mocks"

	"bou.ke/monkey"
	"github.com/DATA-DOG/go-sqlmock"
	"github.com/golang/mock/gomock"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"k8s.io/apimachinery/pkg/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func mockchaosStub(chaos *mockChaos.MockChaos) {
	chaos.EXPECT().NewNetworkChaos(gomock.Any(), gomock.Any()).Return(gomock.Any()).AnyTimes()
	chaos.EXPECT().NewPodChaos(gomock.Any(), gomock.Any()).Return(gomock.Any()).AnyTimes()

	chaos.EXPECT().CreatePodChaos(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	chaos.EXPECT().CreateNetworkChaos(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()

	chaos.EXPECT().DeleteNetworkChaos(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	chaos.EXPECT().DeletePodChaos(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()

	chaos.EXPECT().UpdatePodChaos(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	chaos.EXPECT().UpdateNetworkChaos(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()

	chaos.EXPECT().GetNetworkChaosByNamespacedName(gomock.Any(), gomock.Any()).Return(gomock.Any(), nil).AnyTimes()
	chaos.EXPECT().GetPodChaosByNamespacedName(gomock.Any(), gomock.Any()).Return(gomock.Any(), nil).AnyTimes()
}

func mockDBStub(mock sqlmock.Sqlmock) {
	mock.ExpectExec(regexp.QuoteMeta("REGISTER STORAGE UNIT")).WillReturnResult(sqlmock.NewResult(1, 1))
}

var _ = Describe("shardingsphere mock test", func() {
	var (
	/*
		testNamespacedName = types.NamespacedName{
			Namespace: "test-ssChaos-namespace",
			Name:      "test-ssChaos-name",
		}
		duration = "30s"
	*/
	)
	var (
		/*
				ctx        = context.TODO()
				reconciler *ChaosReconciler
			fakeClient client.Client
		*/
		mockCtrl  *gomock.Controller
		mockchaos *mockChaos.MockChaos
		db        *sql.DB
	)

	BeforeEach(func() {
		scheme := runtime.NewScheme()
		Expect(clientgoscheme.AddToScheme(scheme)).To(Succeed())
		Expect(v1alpha1.AddToScheme(scheme)).To(Succeed())
		fakeClient = fake.NewClientBuilder().WithScheme(scheme).Build()
		mockCtrl = gomock.NewController(GinkgoT())

		mockchaos = mockChaos.NewMockChaos(mockCtrl)
		mockchaosStub(mockchaos)
		/*
			reconciler = &ChaosReconciler{
				Client:    fakeClient,
				Scheme:    scheme,
				Log:       logf.Log,
				Events:    record.NewFakeRecorder(100),
				Chaos:     mockchaos,
				ExecCtrls: make([]*ExecCtrl, 0),
				ConfigMap: configmap.NewConfigMapClient(fakeClient),
			}
		*/

		var (
			dbmock sqlmock.Sqlmock
			err    error
		)

		db, dbmock, err = sqlmock.New()
		Expect(err).To(BeNil())
		Expect(db).NotTo(BeNil())
		Expect(dbmock).NotTo(BeNil())

		monkey.Patch(sql.Open, func(driverName, dataSourceName string) (*sql.DB, error) {
			return db, nil
		})

		mockDBStub(dbmock)
	})

	AfterEach(func() {
		monkey.UnpatchAll()
		db.Close()
	})

	/*
			Context("create shardingsphere chaos", func() {
				It("should create successfully", func() {
					ssChaos := &v1alpha1.Chaos{
						ObjectMeta: metav1.ObjectMeta{
							Name:      testNamespacedName.Name,
							Namespace: testNamespacedName.Namespace,
						},
						Spec: v1alpha1.ChaosSpec{
							EmbedChaos: v1alpha1.EmbedChaos{
								PodChaos: &v1alpha1.PodChaosSpec{
									PodSelector: v1alpha1.PodSelector{
										LabelSelectors: map[string]string{
											"app.kubernetes.io/component": "zookeeper",
										},
									},
									Action: v1alpha1.PodFailure,
									Params: v1alpha1.PodChaosParams{
										PodFailure: &v1alpha1.PodFailureParams{
											Duration: &duration,
										},
									},
								},
							},
							PressureCfg: v1alpha1.PressureCfg{
								SsHost:   "127.0.0.1:3306/ds_1",
								Duration: metav1.Duration{Duration: 30 * time.Second},
								ReqTime:  metav1.Duration{Duration: 30 * time.Second},
								DistSQLs: []v1alpha1.DistSQL{
									{
										SQL:  "REGISTER STORAGE UNIT ?()",
										Args: []string{"ds_1"},
									},
								},
								ConcurrentNum: 2,
								ReqNum:        5,
							},
						},
						Status: v1alpha1.ShardingSphereChaosStatus{},
					}

					Expect(fakeClient.Create(ctx, ssChaos)).Should(Succeed())
					chaos := &v1alpha1.Chaos{}
					_, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: testNamespacedName})
					Expect(err).To(BeNil())
					Expect(fakeClient.Get(ctx, testNamespacedName, chaos)).Should(Succeed())
					Expect(fakeClient.Delete(ctx, chaos)).Should(Succeed())
				})
			})

			Context("reconcile ssChaos in BeforeSteady phase", func() {
				It("chaos should be nil,execRecorder should be steady", func() {
					ssChaos := &v1alpha1.Chaos{
						ObjectMeta: metav1.ObjectMeta{
							Name:      testNamespacedName.Name,
							Namespace: testNamespacedName.Namespace,
						},
						Spec: v1alpha1.ChaosSpec{
							EmbedChaos: v1alpha1.EmbedChaos{
								PodChaos: &v1alpha1.PodChaosSpec{
									PodSelector: v1alpha1.PodSelector{
										LabelSelectors: map[string]string{
											"app.kubernetes.io/component": "zookeeper",
										},
									},
									Action: v1alpha1.PodFailure,
									Params: v1alpha1.PodChaosParams{
										PodFailure: &v1alpha1.PodFailureParams{
											Duration: &duration,
										},
									},
								},
							},
							PressureCfg: v1alpha1.PressureCfg{
								SsHost:   "127.0.0.1:3306/ds_1",
								Duration: metav1.Duration{Duration: 30 * time.Second},
								ReqTime:  metav1.Duration{Duration: 30 * time.Second},
								DistSQLs: []v1alpha1.DistSQL{
									{
										SQL:  "REGISTER STORAGE UNIT ?()",
										Args: []string{"ds_1"},
									},
								},
								ConcurrentNum: 2,
								ReqNum:        5,
							},
						},
						Status: v1alpha1.ChaosStatus{},
					}

					Expect(fakeClient.Create(ctx, ssChaos)).Should(Succeed())
					for i := 0; i < 5; i++ {
						_, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: testNamespacedName})
						Expect(err).To(BeNil())
					}

					var inSteadyChaos v1alpha1.Chaos
					Expect(fakeClient.Get(ctx, testNamespacedName, &inSteadyChaos)).Should(Succeed())
					Expect(inSteadyChaos.Status.Phase).To(Equal(v1alpha1.BeforeSteady))

					Expect(len(reconciler.ExecCtrls)).To(Equal(1))
					Expect(fakeClient.Delete(ctx, &inSteadyChaos)).Should(Succeed())
				})
			})

		Context("reconcile ssChaos in BeforeChaos", func() {
			It("phase should in beforeChaos,execRecorder should gt 2", func() {
				ssChaos := &v1alpha1.Chaos{
					ObjectMeta: metav1.ObjectMeta{
						Name:      testNamespacedName.Name,
						Namespace: testNamespacedName.Namespace,
					},
					Spec: v1alpha1.ChaosSpec{
						EmbedChaos: v1alpha1.EmbedChaos{
							PodChaos: &v1alpha1.PodChaosSpec{
								PodSelector: v1alpha1.PodSelector{
									LabelSelectors: map[string]string{
										"app.kubernetes.io/component": "zookeeper",
									},
								},
								Action: v1alpha1.PodFailure,
								Params: v1alpha1.PodChaosParams{
									PodFailure: &v1alpha1.PodFailureParams{
										Duration: &duration,
									},
								},
							},
						},
						PressureCfg: v1alpha1.PressureCfg{
							SsHost:   "127.0.0.1:3306/ds_1",
							Duration: metav1.Duration{Duration: 30 * time.Second},
							ReqTime:  metav1.Duration{Duration: 30 * time.Second},
							DistSQLs: []v1alpha1.DistSQL{
								{
									SQL:  "REGISTER STORAGE UNIT ?()",
									Args: []string{"ds_1"},
								},
							},
							ConcurrentNum: 2,
							ReqNum:        5,
						},
					},
					Status: v1alpha1.ChaosStatus{
						Phase: v1alpha1.BeforeChaos,
					},
				}

				Expect(fakeClient.Create(ctx, ssChaos)).Should(Succeed())
				var chao v1alpha1.Chaos
				Expect(fakeClient.Get(ctx, testNamespacedName, &chao)).Should(Succeed())
				steadyExec := pressure.NewPressure(reconcile.MakeJobName(ssChaos.Name, reconcile.InSteady), ssChaos.Spec.PressureCfg.DistSQLs)
				steadyExec.Active = false
				execCtx, cancel := context.WithCancel(ctx)
				execCtrl := ExecCtrl{
					cancel:   cancel,
					pressure: steadyExec,
					ctx:      execCtx,
				}
				reconciler.ExecCtrls = append(reconciler.ExecCtrls, &execCtrl)

				for i := 0; i < 10; i++ {
					_, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: testNamespacedName})
					Expect(err).To(BeNil())
				}
				Expect(len(reconciler.ExecCtrls)).To(Equal(2))

				var inChaosChaos v1alpha1.Chaos
				Expect(fakeClient.Get(ctx, testNamespacedName, &inChaosChaos)).Should(Succeed())
				Expect(inChaosChaos.Status.Phase).To(Equal(v1alpha1.BeforeChaos))
			})
		})
	*/
})
