package replication

import (
	"context"
	"encoding/json"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	mock_cdcdao "github.com/pingcap/dbaas/central/db/dbutil/cdc/mock"
	mock_clusterdao "github.com/pingcap/dbaas/central/db/dbutil/cluster/mock"
	mock_replicationdao "github.com/pingcap/dbaas/central/db/dbutil/replication/mock"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/dbaas/central/db/tenant"
	"github.com/pingcap/dbaas/central/infra-api/mock_infraapi"
	"github.com/pingcap/log"
	. "github.com/smartystreets/goconvey/convey"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/pkgv2/clusterproxy"
	"github.com/tidbcloud/pkgv2/clusterproxy/mock_clusterproxy"
	mock_encrypt "github.com/tidbcloud/pkgv2/secret/encrypt/mock_encryptor"
)

func TestSetGCLifeTime(t *testing.T) {
	Convey("TestSetGCLifeTime", t, func() {
		var (
			err error
		)
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()
		logger := log.L()
		ctx := context.Background()
		mockInfraCli := mock_infraapi.NewMockInfraFacade(ctrl)
		mockClusterDaoCli := mock_clusterdao.NewMockClusterDaoFacade(ctrl)
		mockCdcDaoCli := mock_cdcdao.NewMockCDCDaoFacade(ctrl)
		mockReplicationDaoCli := mock_replicationdao.NewMockReplicationDaoFacade(ctrl)
		mockEncryptorCli := mock_encrypt.NewMockEncryptor(ctrl)
		mockClusterproxy := mock_clusterproxy.NewMockClusterProxy(ctrl)
		mockGetProxy := func(clusterID uint64, status cloudv1.ClusterStatus, timeout int) (clusterproxy.ClusterProxy, error) {
			return mockClusterproxy, nil
		}
		model := Model{
			log:        logger,
			api:        mockInfraCli,
			encryptor:  mockEncryptorCli,
			clusterdao: mockClusterDaoCli,
			cdcdao:     mockCdcDaoCli,
			dao:        mockReplicationDaoCli,
		}
		bgContext := BgContext{
			Model: model,
			log:   logger,
			tenantAndUser: &tenant.User{
				TenantID: entity.ID(111),
			},
		}
		r := &entity.Replication{
			Updatable: entity.Updatable{
				Base: entity.Base{
					ID:        entity.ID(1),
					CreatedAt: time.Now().Add(-24 * time.Hour),
				},
			},
			PrimaryClusterID: entity.ID(1),
			PrimaryCluster: &entity.Cluster{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID:        entity.ID(1),
						CreatedAt: time.Now().Add(-24 * time.Hour),
					},
				},
			},
		}

		Convey("set success err should be nil", func() {
			mockInfraCli.EXPECT().GetCluster(ctx, gomock.Any(), gomock.Any()).Return(
				&cloudv1.Cluster{Status: cloudv1.ClusterStatus{DB: &cloudv1.DBStatus{Version: "v5.3"}}},
				nil,
			)
			mockClusterproxy.EXPECT().ExecuteSQL(ctx, gomock.Any(), gomock.Any()).Return(nil)
			err = bgContext.SetGCLifeTime(
				ctx,
				r,
				"72h",
				mockGetProxy)
			So(err, ShouldBeNil)
		})
	})
}

func TestGetGCLifeTime(t *testing.T) {
	Convey("TestGetGCLifeTime", t, func() {
		var (
			err      error
			duration string
			data     []byte
		)
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()
		logger := log.L()
		ctx := context.Background()
		mockInfraCli := mock_infraapi.NewMockInfraFacade(ctrl)
		mockClusterDaoCli := mock_clusterdao.NewMockClusterDaoFacade(ctrl)
		mockCdcDaoCli := mock_cdcdao.NewMockCDCDaoFacade(ctrl)
		mockReplicationDaoCli := mock_replicationdao.NewMockReplicationDaoFacade(ctrl)
		mockEncryptorCli := mock_encrypt.NewMockEncryptor(ctrl)
		mockClusterproxy := mock_clusterproxy.NewMockClusterProxy(ctrl)
		mockGetProxy := func(clusterID uint64, status cloudv1.ClusterStatus, timeout int) (clusterproxy.ClusterProxy, error) {
			return mockClusterproxy, nil
		}
		model := Model{
			log:        logger,
			api:        mockInfraCli,
			encryptor:  mockEncryptorCli,
			clusterdao: mockClusterDaoCli,
			cdcdao:     mockCdcDaoCli,
			dao:        mockReplicationDaoCli,
		}
		bgContext := BgContext{
			Model: model,
			log:   logger,
			tenantAndUser: &tenant.User{
				TenantID: entity.ID(111),
			},
		}
		r := &entity.Replication{
			Updatable: entity.Updatable{
				Base: entity.Base{
					ID:        entity.ID(1),
					CreatedAt: time.Now().Add(-24 * time.Hour),
				},
			},
			PrimaryClusterID: entity.ID(1),
			PrimaryCluster: &entity.Cluster{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID:        entity.ID(1),
						CreatedAt: time.Now().Add(-24 * time.Hour),
					},
				},
			},
		}
		data, err = json.Marshal(VariableModel{Variable: []string{"NzJo"}})
		So(err, ShouldBeNil)

		Convey("get success err should be nil", func() {
			mockInfraCli.EXPECT().GetCluster(ctx, gomock.Any(), gomock.Any()).Return(
				&cloudv1.Cluster{Status: cloudv1.ClusterStatus{DB: &cloudv1.DBStatus{Version: "v5.3"}}},
				nil,
			)
			mockClusterproxy.EXPECT().QuerySQL(ctx, gomock.Any(), gomock.Any()).Return(data, nil)
			duration, err = bgContext.GetGCLifeTime(
				ctx,
				r,
				mockGetProxy)
			So(duration, ShouldEqual, "72h")
			So(err, ShouldBeNil)
		})
	})
}
