package cluster

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/pingcap/dbaas/central/model/util"
	"github.com/tidbcloud/pkgv2/mail/mock_mail"

	"github.com/pingcap/dbaas/central/db/mock_db"
	"github.com/pingcap/dbaas/central/model/util/mock_util"
	"gorm.io/gorm"

	"github.com/pingcap/dbaas/central/repository/mock_repo"

	"github.com/pingcap/dbaas/central/db/entity"

	"github.com/pingcap/dbaas/central/config"

	v1 "github.com/tidbcloud/infra-api/api/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/pingcap/dbaas/central/event"

	"github.com/golang/mock/gomock"
	"github.com/pingcap/dbaas/central/infra-api/mock_infraapi"
	. "github.com/smartystreets/goconvey/convey"
)

func TestMakeClusterTrafficUsagePeriod(t *testing.T) {
	Convey("TestMakeClusterTrafficUsagePeriod", t, func() {
		// 2021-01-04 01:21:23 Asia/Shanghai
		ts := int64(1609694483)

		loc, _ := time.LoadLocation("Asia/Shanghai")
		t := time.Unix(ts, 0).In(loc)
		year, week := t.ISOWeek()
		So(year, ShouldEqual, 2021)
		So(week, ShouldEqual, 1)
		So(MakeClusterTrafficUsagePeriod(t), ShouldEqual, "2020:53")

		year, week = t.UTC().ISOWeek()
		So(year, ShouldEqual, 2020)
		So(week, ShouldEqual, 53)
		So(MakeClusterTrafficUsagePeriod(t.UTC()), ShouldEqual, "2020:53")
	})
}

func Test_devTierNetworkThresholdSwitchProcessor(t *testing.T) {
	Convey("Test_devTierNetworkThresholdSwitchProcessor", t, func() {
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		mockInfraCli := mock_infraapi.NewMockInfraFacade(ctrl)
		p := NewDevTierNetworkThresholdSwitchProcessor(mockInfraCli)
		ctx := context.Background()
		data := &event.ClusterThresholdStateChanged{
			TenantID:         1,
			ProjectID:        2,
			ClusterID:        3,
			TriggerTime:      time.Now(),
			IsDevTier:        true,
			IngressThrottled: true,
			EgressThrottled:  true,
		}
		infraClusterObj := &v1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Annotations: map[string]string{
					ingressTrafficAnnKey: config.ThrottledDevTierBandwidth,
					egressTrafficAnnKey:  config.ThrottledDevTierBandwidth,
				},
			},
		}
		Convey("should be skip when ev.IsDevTier is false", func() {
			data.IsDevTier = false

			err := p.OnEvent(ctx, data)
			So(err, ShouldBeNil)
		})
		Convey("should clear annotations when throttled is false", func() {
			data.IngressThrottled = false
			data.EgressThrottled = false
			mockInfraCli.EXPECT().GetCluster(ctx, data.ProjectID, data.ClusterID).Return(
				infraClusterObj, nil,
			)
			newInfraClusterObj := *infraClusterObj
			newInfraClusterObj.Annotations = map[string]string{}
			mockInfraCli.EXPECT().UpdateCluster(ctx, &newInfraClusterObj)

			err := p.OnEvent(ctx, data)
			So(err, ShouldBeNil)
		})
		Convey("should set annotations when throttled is true", func() {
			infraClusterObj.ObjectMeta.Annotations = map[string]string{}
			mockInfraCli.EXPECT().GetCluster(ctx, data.ProjectID, data.ClusterID).Return(
				infraClusterObj, nil,
			)
			newInfraClusterObj := *infraClusterObj
			newInfraClusterObj.Annotations = map[string]string{
				ingressTrafficAnnKey: config.ThrottledDevTierBandwidth,
				egressTrafficAnnKey:  config.ThrottledDevTierBandwidth,
			}
			mockInfraCli.EXPECT().UpdateCluster(ctx, &newInfraClusterObj)

			err := p.OnEvent(ctx, data)
			So(err, ShouldBeNil)
		})
	})
}

func Test_devTierNetworkThrottledNotifyProcessor(t *testing.T) {
	Convey("Test_devTierNetworkThrottledNotifyProcessor", t, func() {
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		mockTenantRepo := mock_repo.NewMockTenantRepository(ctrl)
		mockUserRepo := mock_repo.NewMockUserRepository(ctrl)
		mockMail := mock_mail.NewMockFacade(ctrl)
		mockNotificationMgr := mock_util.NewMockNotificationManager(ctrl)
		db := &gorm.DB{}
		mockDBProvider := mock_db.NewFakeTransProvider(db)
		p := NewDevTierNetworkThrottledNotifyProcessor(mockTenantRepo, mockUserRepo, mockDBProvider, mockNotificationMgr, mockMail)
		ctx := context.Background()
		data := &event.ClusterThresholdStateChanged{
			TenantID:         1,
			ProjectID:        2,
			ClusterID:        3,
			TriggerTime:      time.Now(),
			IsDevTier:        true,
			IngressThrottled: true,
			EgressThrottled:  true,
		}
		tenant := entity.Tenant{
			Updatable: entity.Updatable{Base: entity.Base{ID: entity.ID(data.TenantID)}},
			Email:     "tenant_email@test.com",
		}
		tenantUser := entity.User{
			Updatable: entity.Updatable{Base: entity.Base{ID: entity.ID(111)}},
			Email:     tenant.Email,
			FirstName: "first name of tenantUser",
		}

		Convey("should be success when all is well", func() {
			mockTenantRepo.EXPECT().Load(ctx, entity.ID(data.TenantID)).Return(tenant, nil)
			mockUserRepo.EXPECT().LoadByEmail(ctx, tenant.Email).Return(tenantUser, nil)
			mockNotificationMgr.EXPECT().SendNotificationOnce(ctx, gomock.Any(), tenant.ID, entity.NotificationSceneClusterNetworkThreshold,
				p.makeClusterThresholdExceedIdentifier(ctx, data.TenantID, data.TriggerTime), gomock.Any()).DoAndReturn(util.FakeSendNotificationOnce)
			vars := map[string]string{
				"userName": tenantUser.FirstName,
			}
			mockMail.EXPECT().SendMailWithTemplate(ctx, []string{tenant.Email}, config.EmailSubjectDevTierClusterNetworkThrottled, config.TNDevTierClusterNetworkThreshold, vars, nil)

			err := p.OnEvent(ctx, data)
			So(err, ShouldBeNil)
		})
		Convey("makeClusterThresholdExceedIdentifier", func() {
			// 2006-01-02, year: 2006, week:1
			now := time.Date(2006, 1, 2, 1, 2, 3, 0, time.UTC)
			r1 := p.makeClusterThresholdExceedIdentifier(ctx, data.TenantID, now)
			So(r1, ShouldEqual, fmt.Sprintf("%d:%d:%d", data.TenantID, 2006, 1))
		})
	})
}
