package tests

import (
	"fmt"
	"testing"
	"time"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/constant"
	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/eventbushandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/marketing"
	"github.com/pingcap/dbaas/billing2/model/marketing/unit_filter"
	"github.com/pingcap/dbaas/billing2/utils/panicIf"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

var rechargeModel = &marketing.RechargeCreditsModel{}

type RechargeCreditsTestSuite struct {
	tests.BillingTestSuite
	generalType            *forms.UnitFilter
	GeneralUnitFilterID    uint
	DefaultRechargePlanReq *forms.RechargeCreditsPlan
}

func TestRechargeCreditsSuite(t *testing.T) {
	suite.Run(t, new(RechargeCreditsTestSuite))
}

func (s *RechargeCreditsTestSuite) SetupTest() {
	s.BillingTestSuite.SetupTest()
	assert := s.Assert()
	datainit.CreateBillingConfig(1, entity.BillingConfigType_DirectCustomer)

	err := datainit.CreatePaymentMock(1)
	assert.NoError(err)

	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   datainit.TestUnit,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	generalUnitFilter, err := unitFilterModel.CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "General", Description: "General"},
		General:        true,
	})
	s.generalType = generalUnitFilter

	assert.NoError(err)
	config.Get().Set(config.CreditsPricePackage, datainit.NoUsePackageId)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}
	config.Get().Set(config.RechargeCreditsBeforeSeconds, 10800)

	s.DefaultRechargePlanReq = &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().Add(time.Hour * 24),
	}
}

func (s *RechargeCreditsTestSuite) getAllRechargeTxEvent() (ret []*eventbushandler.Event) {
	events := eventbushandler.Get().(*tests.LocalEventBusHandlerImpl).GetAllTxEvents()
	for _, e := range events {
		if e.EventType == marketing.RechargeEventType {
			ret = append(ret, e)
		}
	}
	return
}

func (s *RechargeCreditsTestSuite) createDefaultRechargePlan() *forms.RechargeCreditsPlan {
	var err error
	assert := s.Assert()
	info, err := rechargeModel.CreateRechargePlan(s.DefaultRechargePlanReq)
	assert.NoError(err)
	return info
}

func (s *RechargeCreditsTestSuite) TestCreateRechargePlan() {
	var err error
	assert := s.Assert()
	req := s.DefaultRechargePlanReq
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	assert.Equal(req.Rule, info.Rule)
	assert.Equal(req.Value, info.Value)
	assert.Equal(req.Cycle, info.Cycle)
	assert.False(info.Internal)
}

func (s *RechargeCreditsTestSuite) TestTryRecharge() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	info := s.createDefaultRechargePlan()
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	credits, err := creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(s.DefaultRechargePlanReq.Value, credits[0].TotalCredits)
	cnt, err := entity.NewRechargeCreditsPlanRecordQuerySet(db.GetDB()).MainPlanIDEq(tenantPlan.MainPlanID).Count()
	assert.NoError(err)
	assert.Equal(int64(1), cnt)
	var chargeRecord entity.RechargeCreditsPlanRecord
	err = entity.NewRechargeCreditsPlanRecordQuerySet(db.GetDB()).MainPlanIDEq(tenantPlan.MainPlanID).One(&chargeRecord)
	assert.NoError(err)
	assert.Equal(credits[0].CreditsPackageID, chargeRecord.CreditsPackageID)
	recharged, err = rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.False(recharged)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlan() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	info := s.createDefaultRechargePlan()
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	_, ok := events[0].EventData.(*forms.TenantRechargeCreditsPlan)
	assert.True(ok)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	events = s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	var ent entity.TenantRechargeCreditsPlan
	err = entity.NewTenantRechargeCreditsPlanQuerySet(db.GetDB()).IDEq(tenantPlan.PlanID).One(&ent)
	assert.NoError(err)
	assert.Equal(uint(1), ent.RechargeTimes)
	assert.Equal(info.PlanID, ent.MainPlanID)
	assert.Equal(timeutils.GetNow().AddDate(0, 1, 0), ent.NextCycleStartTime)
	credits, err := creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(s.DefaultRechargePlanReq.Value, credits[0].TotalCredits)
	assert.False(credits[0].Internal)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlanNextStartOfNaturalCycle() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_NextStartOfNaturalCycle,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 2, 0),
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 0)
	var ent entity.TenantRechargeCreditsPlan
	err = entity.NewTenantRechargeCreditsPlanQuerySet(db.GetDB()).IDEq(tenantPlan.PlanID).One(&ent)
	assert.NoError(err)
	assert.Equal(timeutils.GetNow().AddDate(0, 1, 0), ent.StartDate)
	assert.Equal(timeutils.GetNow().AddDate(0, 1, 0), ent.NextCycleStartTime)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlanTwice() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.Error(err)
	unitFilterReq := &forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{
			Name:     "test",
			Internal: true,
			ParentID: config.Get().GetUint(config.GeneralCreditTypeID),
		},
		General:        false,
		Region:         forms.UnitFilterALL,
		Provider:       forms.UnitFilterALL,
		Category:       forms.UnitFilterALL,
		UnitKeyPattern: ".*",
		Labels:         map[string]string{constant.ClusterLabelKey: fmt.Sprint(1)},
	}
	uf, err := (&unit_filter.Model{}).CreateUnitFilter(unitFilterReq)
	assert.NoError(err)
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID, &uf.UnitFilterSlim)
	assert.NoError(err)
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID, &uf.UnitFilterSlim)
	assert.Error(err)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlanRechargeTwice() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	timeutils.SetMockNow(datainit.BillingDate.AddDate(0, 1, 0).Add(-1 * time.Second))
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	_, ok := events[0].EventData.(*forms.TenantRechargeCreditsPlan)
	assert.True(ok)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	events = s.getAllRechargeTxEvent()
	assert.Len(events, 2)
	recharged, err = rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	events = s.getAllRechargeTxEvent()
	assert.Len(events, 2)
	var ent entity.TenantRechargeCreditsPlan
	err = entity.NewTenantRechargeCreditsPlanQuerySet(db.GetDB()).IDEq(tenantPlan.PlanID).One(&ent)
	assert.NoError(err)
	assert.Equal(uint(2), ent.RechargeTimes)
	assert.Equal(info.PlanID, ent.MainPlanID)
	assert.Equal(datainit.BillingDate.AddDate(0, 2, 0), ent.NextCycleStartTime)
	credits, err := creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 2)
	assert.Equal(req.Value, credits[1].TotalCredits)
	assert.False(credits[1].Internal)
	assert.Equal(datainit.BillingDate.AddDate(0, 1, 0), credits[1].ExpiredDate)
	assert.Equal(req.Value, credits[0].TotalCredits)
	assert.False(credits[0].Internal)
	assert.Equal(datainit.BillingDate.AddDate(0, 1, 0), credits[0].StartDate)
	assert.Equal(datainit.BillingDate.AddDate(0, 2, 0), credits[0].ExpiredDate)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlanRechargeConcurrently() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	timeutils.SetMockNow(datainit.BillingDate)
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	_, ok := events[0].EventData.(*forms.TenantRechargeCreditsPlan)
	assert.True(ok)
	concurrency := 10
	chList := make([]chan bool, concurrency)
	for i := 0; i != concurrency; i++ {
		chList[i] = make(chan bool)
		go func(ch chan bool) {
			timeutils.SetMockNow(datainit.BillingDate)
			recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
			assert.NoError(err)
			ch <- recharged
		}(chList[i])
	}
	successRecharged := 0
	for i := 0; i != concurrency; i++ {
		select {
		case recharged := <-chList[i]:
			if recharged {
				successRecharged++
			}
			close(chList[i])
		case <-time.After(time.Second):
			panic("timeout")
		}
	}
	assert.Equal(1, successRecharged)
	tenantPlan, err = rechargeModel.GetTenantRechargePlan(tenantPlan.PlanID)
	assert.NoError(err)
	assert.Equal(uint(1), tenantPlan.RechargeTimes)
	credits, err := creditsPackageModel.ListTenantCreditsPackages(tenantID, 0)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(req.Value, credits[0].TotalCredits)
}

func (s *RechargeCreditsTestSuite) TestEnableTenantRechargePlanMaxUnitFilters() {
	assert := s.Assert()
	var err error
	ufs := make([]*forms.UnitFilterSlim, config.Get().GetInt(config.MaxTenantPlanUnitFilters))
	for i := 0; i != config.Get().GetInt(config.MaxTenantPlanUnitFilters); i++ {
		unitFilterReq := &forms.UnitFilter{
			UnitFilterSlim: forms.UnitFilterSlim{
				Name:     fmt.Sprintf("test-%d", i),
				Internal: true,
				ParentID: config.Get().GetUint(config.GeneralCreditTypeID),
			},
			General:        false,
			Region:         forms.UnitFilterALL,
			Provider:       forms.UnitFilterALL,
			Category:       forms.UnitFilterALL,
			UnitKeyPattern: ".*",
			Labels:         map[string]string{constant.ClusterLabelKey: fmt.Sprint(i + 1)},
		}
		uf, err := (&unit_filter.Model{}).CreateUnitFilter(unitFilterReq)
		assert.NoError(err)
		ufs[i] = &uf.UnitFilterSlim
	}
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantID := uint(1)
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID, ufs...)
	assert.NoError(err)
	ufs = append(ufs, &forms.UnitFilterSlim{FilterID: config.Get().GetUint(config.GeneralCreditTypeID)})
	_, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID, ufs...)
	assert.Error(err)
}

func (s *RechargeCreditsTestSuite) TestFirstCreditsConvertorProportional() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Proportional,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
		Duration:    time.Hour * 31 * 24,
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	_, ok := events[0].EventData.(*forms.TenantRechargeCreditsPlan)
	assert.True(ok)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	events = s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	credits, err := creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(req.Value, credits[0].TotalCredits)

	tenantID++
	datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)

	timeutils.SetMockNow(datainit.BillingDate.AddDate(0, 0, 3)) // 2021.11.04
	tenantPlan, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	recharged, err = rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	credits, err = creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	precision := config.Get().GetInt32(config.BillingCostsPrecisionDigits)
	assert.Equal(req.Value.Mul(decimal.NewFromInt(30-4+1).Div(decimal.NewFromInt(30))).Truncate(precision), credits[0].TotalCredits)

	tenantID++
	datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)
	timeutils.SetMockNow(datainit.BillingDate.AddDate(0, 0, 29)) // 2021.11.30
	tenantPlan, err = rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	recharged, err = rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	credits, err = creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(req.Value.Mul(decimal.NewFromInt(1).Div(decimal.NewFromInt(30))).Truncate(precision), credits[0].TotalCredits)
}

func (s *RechargeCreditsTestSuite) TestRechargeHandler() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	eb, err := tests.NewLocalEventBusHandler()
	panicIf.Error(err)
	panicIf.Error(eventbushandler.Get().Close())
	eventbushandler.Set(eb)
	err = rechargeModel.RegisterEventHandler()
	assert.NoError(err)
	panicIf.Error(eventbushandler.Get().Run())
	info := s.createDefaultRechargePlan()
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)

	var ent entity.TenantRechargeCreditsPlan
	var credits []forms.CreditsPackage
	require.Eventually(s.T(), func() bool {
		err = entity.NewTenantRechargeCreditsPlanQuerySet(db.GetDB()).IDEq(tenantPlan.PlanID).One(&ent)
		if err != nil || ent.RechargeTimes != 1 {
			return false
		}
		credits, err = creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
		return err == nil && len(credits) == 1
	}, 5*time.Second, 10*time.Millisecond, "recharge credits package was not created within 5s")

	assert.NoError(err)
	assert.Equal(uint(1), ent.RechargeTimes)
	assert.Equal(info.PlanID, ent.MainPlanID)
	assert.Equal(timeutils.GetNow().AddDate(0, 1, 0), ent.NextCycleStartTime)
	assert.Len(credits, 1)
	assert.Equal(s.DefaultRechargePlanReq.Value, credits[0].TotalCredits)
}

func (s *RechargeCreditsTestSuite) TestRechargeInternalCredits() {
	var err error
	tenantID := uint(1)
	assert := s.Assert()
	timeutils.SetMockNow(datainit.BillingDate)
	req := &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().AddDate(0, 1, 0),
		Internal:    true,
	}
	info, err := rechargeModel.CreateRechargePlan(req)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, info.PlanID)
	assert.NoError(err)
	events := s.getAllRechargeTxEvent()
	assert.Len(events, 1)
	tenantPlan, ok := events[0].EventData.(*forms.TenantRechargeCreditsPlan)
	assert.True(ok)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)

	var ent entity.TenantRechargeCreditsPlan
	err = entity.NewTenantRechargeCreditsPlanQuerySet(db.GetDB()).IDEq(tenantPlan.PlanID).One(&ent)
	assert.NoError(err)
	assert.Equal(uint(1), ent.RechargeTimes)
	assert.Equal(datainit.BillingDate.AddDate(0, 1, 0), ent.NextCycleStartTime)

	credits, err := creditsPackageModel.ListTenantAllCreditsPackages(tenantID)
	assert.NoError(err)
	assert.Len(credits, 1)
	assert.Equal(s.DefaultRechargePlanReq.Value, credits[0].TotalCredits)
	assert.True(credits[0].Internal)
}

func (s *RechargeCreditsTestSuite) TestNextRechargeTime() {
	assert := s.Assert()
	var err error
	next, err := rechargeModel.NextCycleStartTime(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 2, 28, 0, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 3, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 3, 5, 10, 8, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 4, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 4, 30, 0, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 5, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 5, 31, 7, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 6, 17, 4, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2023, 7, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), entity.RechargePlanCycle_Month, forms.FirstCycleFinishedAt_EndOfNaturalCycle)
	assert.NoError(err)
	assert.Equal(time.Date(2024, 3, 1, 0, 0, 0, 0, time.UTC), next)
	next, err = rechargeModel.NextCycleStartTime(time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), "1", "1")
	assert.Error(err)
}
