package tests

import (
	"fmt"
	"strconv"
	"testing"
	"time"

	"github.com/google/uuid"
	"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/handler/infrahandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/billing/tiered_billing"
	"github.com/pingcap/dbaas/billing2/model/costexplorer"
	"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/pingcap/dbaas/billing2/workers"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

var rechargeModel = &marketing.RechargeCreditsModel{}
var unitFilterModel = &unit_filter.Model{}
var tieredBillingModel = &tiered_billing.TieredBillingModel{}

const oldServerlessBizData = "{\"free_credits\": \"5\", \"invoice_limit\": \"0\"}"
const zeroFreeCreditsServerlessBizData = "{\"free_credits\": \"0\", \"invoice_limit\": \"1\"}"

const (
	MockSKU1 = "mock-sku-1"
	MockSKU2 = "mock-sku-2"
)

type SpendLimitServerlessSuite struct {
	tests.BillingTestSuite
	generalType              *forms.UnitFilter
	defaultRechargePlanID    uint
	defaultServerlessBizData string
}

func (suite *SpendLimitServerlessSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	assert := suite.Assert()
	datainit.CreateBillingConfig(1, entity.BillingConfigType_DirectCustomer)
	_, err := (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   datainit.TestUnit,
		Price:     decimal.RequireFromString("1"),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	generalUnitFilter, err := unitFilterModel.CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "General", Description: "General"},
		General:        true,
	})
	assert.NoError(err)
	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   MockSKU1,
		Price:     decimal.RequireFromString("0.001"),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   MockSKU2,
		Price:     decimal.RequireFromString("0.001"),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	suite.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)
	rechargePlan, err := rechargeModel.CreateRechargePlan(&forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(5),
		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),
		Internal:    true,
		Source:      entity.RechargeCreditsPlanSource_Web,
	})
	assert.NoError(err)
	suite.defaultRechargePlanID = rechargePlan.PlanID
	suite.defaultServerlessBizData = fmt.Sprintf("{\"invoice_limit\": \"0\", \"recharge_plan_id\": %d}", rechargePlan.PlanID)
	infrahandler.Set(&MockInfraHandlerImpl{})
}

func TestSpendLimitServerlessSuite(t *testing.T) {
	suite.Run(t, new(SpendLimitServerlessSuite))
}

func (suite *SpendLimitServerlessSuite) TestOldServerlessSpendLimit() {
	assert := suite.Assert()
	tenantID := uint(1)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)

	q1 := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Tenant,
		RangeID:   tenantID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   oldServerlessBizData,
	}
	_, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q1.RangeType = entity.SpendLimitRangeType_Cluster
	q1.RangeID = 1
	q1.BizData = "{\"free_credits\": \"2\", \"invoice_limit\": \"-1\"}"
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q1.BizData = oldServerlessBizData
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.NoError(err)
	assert.Equal(q1.RangeID, s1.RangeID)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)

	assert.Equal(decimal.Zero, bizData.InvoiceLimit)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)

	plan, err := rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.Equal(bizData.FreeCredits, plan.Value)
	assert.True(plan.Internal)
	tenantPlan, err := rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Len(tenantPlan.UnitFilters, 1)
	t, err := unitFilterModel.GetUnitFilter(tenantPlan.UnitFilters[0].FilterID)
	assert.NoError(err)
	assert.Equal(map[string]string{constant.ClusterLabelKey: fmt.Sprint(q1.RangeID)}, t.Labels)
	assert.Equal(decimal.NewFromInt(5), s1.Limit)

	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q2 := q1
	q2.RangeID = 2
	q2.BizData = oldServerlessBizData
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q2)
	assert.NoError(err)

	invalidS1 := *s1
	invalidS1.RangeID = 2
	_, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, &invalidS1)
	assert.Error(err)
	s1.BizData = "{\"free_credits\": \"9999\", \"invoice_limit\": \"1111\"}"
	s1New, err := spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(1111), bizData.InvoiceLimit)
	s1.BizData = "{\"invoice_limit\": \"0\"}"
	s1New, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(0), bizData.InvoiceLimit)

	q3 := q2
	q3.BizData = oldServerlessBizData
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q3)
	assert.Error(err)

	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s2.SpendLimitSettingID)
	assert.NoError(err)
	events := eventbushandler.Get().(*tests.LocalEventBusHandlerImpl).GetAllTxEvents(constant.SpendLimitEventType)
	for _, e := range events {
		info, ok := e.EventData.(*forms.SpendLimitInfo)
		assert.True(ok)
		assert.Equal(entity.SpendLimitStatus_Deprecated, info.Status)
	}

	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q3)
	assert.NoError(err)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimit() {
	assert := suite.Assert()
	tenantID := uint(1)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)

	q1 := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Tenant,
		RangeID:   tenantID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	_, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q1.RangeType = entity.SpendLimitRangeType_Cluster
	q1.RangeID = 1
	q1.BizData = "{\"free_credits\": \"2\", \"invoice_limit\": \"-1\"}"
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q1.BizData = oldServerlessBizData
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.NoError(err)
	assert.Equal(q1.RangeID, s1.RangeID)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)

	assert.Equal(decimal.Zero, bizData.InvoiceLimit)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	rechargePlanID := bizData.RechargePlanID
	tenantRechargePlanID := bizData.TenantRechargePlanID

	plan, err := rechargeModel.GetRechargePlan(rechargePlanID)
	assert.NoError(err)
	assert.Equal(bizData.FreeCredits, plan.Value)
	assert.True(plan.Internal)
	tenantPlan, err := rechargeModel.GetTenantRechargePlan(tenantRechargePlanID)
	assert.NoError(err)
	assert.Len(tenantPlan.UnitFilters, 1)
	t, err := unitFilterModel.GetUnitFilter(tenantPlan.UnitFilters[0].FilterID)
	assert.NoError(err)
	assert.Equal(map[string]string{constant.ClusterLabelKey: fmt.Sprint(q1.RangeID)}, t.Labels)
	assert.Equal(decimal.NewFromInt(5), s1.Limit)

	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.Error(err)

	q2 := q1
	q2.RangeID = 2
	q2.BizData = oldServerlessBizData
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q2)
	assert.NoError(err)

	dupS2 := *s1
	dupS2.RangeID = 2
	_, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, &dupS2)
	assert.Error(err)

	s1.BizData = `{"invoice_limit": 100}`
	s1New, err := spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(100), bizData.InvoiceLimit)
	assert.Equal(rechargePlanID, bizData.RechargePlanID)
	assert.Equal(tenantRechargePlanID, bizData.TenantRechargePlanID)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)

	s1.BizData = `{"invoice_limit": 0}`
	s1New, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(0), bizData.InvoiceLimit)
	assert.Equal(rechargePlanID, bizData.RechargePlanID)
	assert.Equal(tenantRechargePlanID, bizData.TenantRechargePlanID)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)

	q3 := q2
	q3.BizData = oldServerlessBizData
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q3)
	assert.Error(err)

	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s2.SpendLimitSettingID)
	assert.NoError(err)
	events := eventbushandler.Get().(*tests.LocalEventBusHandlerImpl).GetAllTxEvents(constant.SpendLimitEventType)
	for _, e := range events {
		info, ok := e.EventData.(*forms.SpendLimitInfo)
		assert.True(ok)
		assert.Equal(entity.SpendLimitStatus_Deprecated, info.Status)
	}

	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q3)
	assert.NoError(err)
}

func (suite *SpendLimitServerlessSuite) TestDeleteServerlessSpendLimit() {
	assert := suite.Assert()
	tenantID := uint(1)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)

	q1 := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   1,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.NoError(err)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)

	rechargePlanID := bizData.RechargePlanID
	assert.Equal(suite.defaultRechargePlanID, rechargePlanID)

	tenantRechargePlanID := bizData.TenantRechargePlanID
	tenantRechargePlan, err := rechargeModel.GetTenantRechargePlan(tenantRechargePlanID)
	assert.NoError(err)
	assert.Equal(suite.defaultRechargePlanID, tenantRechargePlan.MainPlanID)

	q2 := q1
	q2.RangeID = 2
	q2.BizData = oldServerlessBizData
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q2)
	assert.NoError(err)

	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s2.BizData)
	assert.NoError(err)
	assert.NotEqual(suite.defaultRechargePlanID, bizData.RechargePlanID)

	sysRechargePlan, err := rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.Equal(entity.CreditsChargeType_SYSTEM, sysRechargePlan.Source)

	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	tenantRechargePlan, err = rechargeModel.GetTenantRechargePlan(tenantRechargePlanID)
	assert.NoError(err)
	assert.NotNil(tenantRechargePlan.ClosedAt)
	rechargePlan, err := rechargeModel.GetRechargePlan(rechargePlanID)
	assert.NoError(err)
	assert.Nil(rechargePlan.ClosedAt)

	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s2.SpendLimitSettingID)
	assert.NoError(err)
	tenantRechargePlan, err = rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.NotNil(tenantRechargePlan.ClosedAt)
	rechargePlan, err = rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.NotNil(rechargePlan.ClosedAt)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitNoFreeCredits() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   zeroFreeCreditsServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	assert.Equal(q.RangeID, s1.RangeID)
	assert.Equal(decimal.NewFromInt(1), s1.Limit)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)

	assert.Equal(decimal.Zero, bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(1), bizData.InvoiceLimit)
	assert.Zero(bizData.TenantRechargePlanID)
	assert.Zero(bizData.RechargePlanID)

	s1.BizData = "{\"invoice_limit\": \"2\"}"
	s1New, err := spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)

	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(2), bizData.InvoiceLimit)

	list, err := spendLimitModel.ListSpendLimitSettings(tenantID, nil, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s1New.Limit, list[0].Limit)
	assert.Equal(s1New.BizData, list[0].BizData)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitUpdateToNoFreeCredits() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	assert.Equal(q.RangeID, s1.RangeID)
	s1BizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	assert.NotZero(s1BizData.TenantRechargePlanID)
	assert.NotZero(s1BizData.RechargePlanID)

	err = rechargeModel.DisableRechargePlan(s1BizData.RechargePlanID)
	assert.NoError(err)
	err = rechargeModel.DisableTenantRechargePlan(s1BizData.RechargePlanID)
	assert.NoError(err)
	s1New, err := spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)

	s1NewBizData, err := spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.Zero, s1NewBizData.FreeCredits)

	plan, err := rechargeModel.GetRechargePlan(s1NewBizData.RechargePlanID)
	assert.NoError(err)
	assert.NotNil(plan.ClosedAt)

	tenantPlan, err := rechargeModel.GetTenantRechargePlan(s1BizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.NotNil(tenantPlan.ClosedAt)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitUpdateFreeCredits() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	assert.Equal(q.RangeID, s1.RangeID)
	s1BizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), s1BizData.FreeCredits)

	_, err = rechargeModel.UpdateRechargePlanValue(s1BizData.RechargePlanID, decimal.NewFromInt(4))
	s1New, err := spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, s1)
	assert.NoError(err)

	s1NewBizData, err := spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(4), s1NewBizData.FreeCredits)
	plan, err := rechargeModel.GetRechargePlan(s1BizData.RechargePlanID)
	assert.Equal(decimal.NewFromInt(4), plan.Value)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitAllIsZero() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   "{\"free_credits\": \"0\", \"invoice_limit\": \"0\"}",
	}
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.Error(err)

	q.BizData = "{\"free_credits\": \"0.0000001\", \"invoice_limit\": \"0\"}"
	_, err = spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.Error(err)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitCreditsInfo() {
	var err error
	tenantID := uint(1)
	billingConfigID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)

	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	assert.Equal(decimal.Zero, bizData.InvoiceLimit)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(5), bizData.RemainingCredits)

	list, err := spendLimitModel.ListSpendLimitSettings(tenantID, &forms.SpendLimitFilter{RangeID: s1.RangeID, RangeType: s1.RangeType}, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s1.Limit, list[0].Limit)
	assert.Equal(s1.BizData, list[0].BizData)

	plan, err := rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.Equal(bizData.FreeCredits, plan.Value)
	tenantPlan, err := rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Len(tenantPlan.UnitFilters, 1)
	t, err := unitFilterModel.GetUnitFilter(tenantPlan.UnitFilters[0].FilterID)
	assert.NoError(err)
	assert.Equal(map[string]string{constant.ClusterLabelKey: fmt.Sprint(q.RangeID)}, t.Labels)
	assert.Equal(decimal.NewFromInt(5), s1.Limit)

	timeutils.SetMockNow(timeutils.GetNow().AddDate(0, 0, 2))
	creditsPackageModel := &marketing.CreditsPackageModel{}
	noContainedCredits, err := creditsPackageModel.CreateCreditsPackage(tenantID, &forms.CreditsPackage{
		TotalCredits:          decimal.NewFromInt(1),
		BindTypes:             []*forms.UnitFilterSlim{&suite.generalType.UnitFilterSlim},
		Usage:                 entity.CreditsPackageUsage_Marketing,
		StartDate:             timeutils.GetNow(),
		ExpiredDate:           timeutils.GetNow().AddDate(0, 1, 0),
		BindedBillingConfigID: &billingConfigID,
	})
	assert.Equal(entity.CreditsPackageStatus_Active, noContainedCredits.Status)
	assert.NoError(err)
	s1New, err := spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(5), bizData.RemainingCredits)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)
	err = creditsPackageModel.CloseCreditsPackage(noContainedCredits.CreditsPackageID, "")
	assert.NoError(err)
	_, err = (&billing.MeasureModel{}).CostUnits(tenantID, &forms.UnitCosts{
		CostID:        "1",
		Cost:          1,
		Unit:          datainit.TestUnit,
		Labels:        map[string]string{constant.ClusterLabelKey: fmt.Sprint(q.RangeID)},
		CostTimeStamp: timeutils.GetNow().Unix() + 10,
	})
	assert.NoError(err)
	_, err = creditsPackageModel.CreateCreditsPackage(tenantID, &forms.CreditsPackage{
		TotalCredits:          decimal.NewFromInt(1),
		BindTypes:             []*forms.UnitFilterSlim{&suite.generalType.UnitFilterSlim},
		Usage:                 entity.CreditsPackageUsage_Marketing,
		StartDate:             timeutils.GetNow(),
		ExpiredDate:           timeutils.GetNow().AddDate(0, 1, 0),
		Internal:              true,
		BindedBillingConfigID: &billingConfigID,
	})
	assert.NoError(err)
	s1New, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(4), bizData.RemainingCredits)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)

	_, err = rechargeModel.UpdateRechargePlanValue(bizData.RechargePlanID, decimal.NewFromInt(8))
	assert.NoError(err)
	s2, err := spendLimitModel.GetSpendLimitSetting(tenantID, s1New.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s2.BizData)
	assert.NoError(err)
	assert.Equal(decimal.Zero, bizData.InvoiceLimit)
	assert.Equal(decimal.NewFromInt(8), bizData.FreeCredits)

	list, err = spendLimitModel.ListSpendLimitSettings(tenantID, &forms.SpendLimitFilter{RangeID: s2.RangeID, RangeType: s2.RangeType}, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s2.Limit, list[0].Limit)
	assert.Equal(s2.BizData, list[0].BizData)

	s1New, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(8), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(4), bizData.RemainingCredits)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)

	list, err = spendLimitModel.ListSpendLimitSettings(tenantID, &forms.SpendLimitFilter{RangeID: s1.RangeID, RangeType: s1.RangeType}, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s1New.Limit, list[0].Limit)
	assert.Equal(s1New.BizData, list[0].BizData)

	timeutils.SetMockNow(timeutils.GetNow().AddDate(0, 1, 0))

	recharged, err := rechargeModel.TryRecharge(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.True(recharged)

	s1New, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(8), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(8), bizData.RemainingCredits)
	assert.Equal(decimal.NewFromInt(8), bizData.ActiveTotalCredits)

	list, err = spendLimitModel.ListSpendLimitSettings(tenantID, &forms.SpendLimitFilter{RangeID: s1.RangeID, RangeType: s1.RangeType}, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s1New.Limit, list[0].Limit)
	assert.Equal(s1New.BizData, list[0].BizData)

	workers.CloseExpiredCreditsPackage()

	s1New, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1New.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(8), bizData.FreeCredits)
	assert.Equal(decimal.NewFromInt(8), bizData.RemainingCredits)
	assert.Equal(decimal.NewFromInt(8), bizData.ActiveTotalCredits)

	list, err = spendLimitModel.ListSpendLimitSettings(tenantID, &forms.SpendLimitFilter{RangeID: s1.RangeID, RangeType: s1.RangeType}, nil)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s1New.Limit, list[0].Limit)
	assert.Equal(s1New.BizData, list[0].BizData)

}

func (suite *SpendLimitServerlessSuite) TestDeleteServerlessSpendLimitInvalidateRechargePlan() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s.BizData)
	assert.NoError(err)
	rechargePlan, err := rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.Nil(rechargePlan.ClosedAt)
	tenantRechargePlan, err := rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Nil(tenantRechargePlan.ClosedAt)
	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s.SpendLimitSettingID)
	assert.NoError(err)
	rechargePlan, err = rechargeModel.GetRechargePlan(bizData.RechargePlanID)
	assert.NoError(err)
	assert.Nil(rechargePlan.ClosedAt)
	tenantRechargePlan, err = rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.NotNil(tenantRechargePlan.ClosedAt)
}

func (suite *SpendLimitServerlessSuite) TestServerlessTieredBilling() {
	var err error
	tenantID := uint(1)
	clusterID := uint(1)
	assert := suite.Assert()
	q := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   zeroFreeCreditsServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	tiered, err := tieredBillingModel.GetTieredBilling(bizData.TieredBillingID)
	assert.NoError(err)
	assert.Equal(bizData.InvoiceLimit, tiered.TargetValue)

	q.BizData = "{\"invoice_limit\": \"32\"}"
	s1, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, s1.SpendLimitSettingID, &q)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	tiered, err = tieredBillingModel.GetTieredBilling(bizData.TieredBillingID)
	assert.NoError(err)
	assert.Equal(bizData.InvoiceLimit, tiered.TargetValue)

	err = spendLimitModel.DeleteSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	_, err = tieredBillingModel.GetTieredBilling(bizData.TieredBillingID)
	assert.Error(err)

	clusterID++
	q = forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s2.BizData)
	assert.NoError(err)
	tiered, err = tieredBillingModel.GetTieredBilling(bizData.TieredBillingID)
	assert.NoError(err)
	assert.Equal(bizData.InvoiceLimit, tiered.TargetValue)
}

func (suite *SpendLimitServerlessSuite) TestCheckSpendLimitServerlessWaterMark() {
	panicIf.Error(datainit.CreatePaymentMock(uint(1)))
	_, err := (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey: datainit.TestUnit,
	})
	panicIf.Error(err)
	panicIf.Error(datainit.CreatePricePackageAndBind(uint(1)))
	assert := suite.Assert()
	tenantID := uint(1)
	clusterID := uint(13333)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)
	for _, val := range []string{"0.9", "1", "0.75"} {
		err = (&entity.SpendLimitAlertThreshold{
			BizType: entity.SpendLimitBizType_ServerlessCluster, Proportion: decimal.RequireFromString(val),
		}).Create(db.GetDB())
		assert.NoError(err)
	}

	setting, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("1"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	})
	assert.NoError(err)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)

	unitCosts := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          datainit.TestUnit,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          5,
	}
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)

	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err := spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Excessive, info.Status)
	assert.Equal(decimal.NewFromInt(0), info.WaterMark)

	eb := eventbushandler.Get().(*tests.LocalEventBusHandlerImpl)
	var events []*eventbushandler.Event
	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 0)

	setting.BizData = "{\"free_credits\": \"5\", \"invoice_limit\": \"100\"}"
	_, err = spendLimitModel.UpdateSpendLimitSettings(setting.TenantID, setting.SpendLimitSettingID, setting)
	assert.NoError(err)
	unitCosts.CostID = uuid.NewString()
	unitCosts.Cost = 74
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)

	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, info.Status)
	assert.Equal(decimal.NewFromInt(79), info.Costs)
	assert.Equal(decimal.NewFromInt(74), info.WaterMark)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 0)

	unitCosts.CostID = uuid.NewString()
	unitCosts.Cost = 1
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(80), info.Costs)
	assert.Equal(decimal.NewFromInt(75), info.WaterMark)

	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 1)
	assert.Equal(decimal.RequireFromString("0.75"), events[0].EventData.(*forms.SpendLimitThresholdAlert).Threshold)

	unitCosts.CostID = uuid.NewString()
	unitCosts.Cost = 14
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(94), info.Costs)
	assert.Equal(decimal.NewFromInt(89), info.WaterMark)

	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 1)

	unitCosts.CostID = uuid.NewString()
	unitCosts.Cost = 1
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(95), info.Costs)
	assert.Equal(decimal.NewFromInt(90), info.WaterMark)

	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 2)
	assert.Equal(decimal.RequireFromString("0.9"), events[1].EventData.(*forms.SpendLimitThresholdAlert).Threshold)

	unitCosts.CostID = uuid.NewString()
	unitCosts.Cost = 18
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(105), info.Costs)
	assert.Equal(decimal.NewFromInt(100), info.WaterMark)

	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 3)
	assert.Equal(decimal.RequireFromString("1"), events[2].EventData.(*forms.SpendLimitThresholdAlert).Threshold)

	// next month
	now := billingDate.AddDate(0, 1, 0)
	timeutils.SetMockNow(now)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(info.BizData)
	assert.NoError(err)
	recharged, err := rechargeModel.TryRecharge(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.True(recharged)

	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 3)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, info.Status)
	assert.Equal(decimal.NewFromInt(0), info.WaterMark)

	setting.BizData = "{\"free_credits\": \"5\", \"invoice_limit\": \"95\"}"
	_, err = spendLimitModel.UpdateSpendLimitSettings(setting.TenantID, setting.SpendLimitSettingID, setting)
	assert.NoError(err)

	unitCosts.CostID = uuid.NewString()
	unitCosts.CostTimeStamp = now.Unix()
	unitCosts.Cost = 5
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	unitCosts.CostID = uuid.NewString()
	unitCosts.CostTimeStamp = now.Unix()
	unitCosts.Cost = 85
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 4)
	assert.Equal(decimal.RequireFromString("0.75"), events[3].EventData.(*forms.SpendLimitThresholdAlert).Threshold)
	assert.Equal(decimal.NewFromInt(85), events[3].EventData.(*forms.SpendLimitThresholdAlert).WaterMark)

	unitCosts.CostID = uuid.NewString()
	unitCosts.CostTimeStamp = now.Unix()
	unitCosts.Cost = 5
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	err = spendLimitModel.CheckSpendLimit(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	events = eb.GetAllTxEvents(constant.SpendLimitThresholdEventType)
	assert.Len(events, 5)
	assert.Equal(decimal.RequireFromString("0.9"), events[4].EventData.(*forms.SpendLimitThresholdAlert).Threshold)
	assert.Equal(decimal.NewFromInt(90), events[4].EventData.(*forms.SpendLimitThresholdAlert).WaterMark)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitInfo() {
	var err error
	assert := suite.Assert()
	billingDate := datainit.BillingDate
	panicIf.Error(err)
	tenantID := uint(1)
	clusterID := uint(3)
	setting, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("1"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	})
	assert.NoError(err)

	unitCosts := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU1,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          1782,
	}
	cost, err := measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	assert.True(cost.Internal)

	info, err := spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("1.78"), info.Costs)

	unitCosts2 := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU2,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          218,
	}
	cost, err = measureModel.CostUnits(tenantID, &unitCosts2)
	assert.NoError(err)
	assert.True(cost.Internal)

	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("2"), info.Costs)

	explore, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 0, &forms.CostExplorerQuery{StartDate: billingDate, EndDate: billingDate.AddDate(1, 0, 0), Internal: true})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("1.99"), explore.TotalCosts.CostPrice)
}

func (suite *SpendLimitServerlessSuite) TestServerlessFreeCreditsInvisible() {
	var err error
	assert := suite.Assert()
	billingDate := datainit.BillingDate
	panicIf.Error(err)
	tenantID := uint(1)
	clusterID := uint(3)
	setting, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("1"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   "{\"free_credits\": \"5\", \"invoice_limit\": \"1\"}",
	})
	assert.NoError(err)

	unitCosts := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU1,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          4899,
	}
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)

	info, err := spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("4.89"), info.Costs)

	unitCosts2 := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU2,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          120,
	}
	_, err = measureModel.CostUnits(tenantID, &unitCosts2)
	assert.NoError(err)
	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("5.00"), info.Costs)

	explore, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 0, &forms.CostExplorerQuery{StartDate: billingDate, EndDate: billingDate.AddDate(1, 0, 0)})
	assert.NoError(err)

	assert.Equal(decimal.RequireFromString("0"), explore.TotalCosts.CostPrice)

	err = datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	billingConfigID := uint(1)
	bindedInfo, err := (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		Internal:              false,
		InvoicePayable:        true,
		BindedBillingConfigID: &billingConfigID,
	})

	unitCosts3 := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU1,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          666,
	}
	r, err := measureModel.CostUnits(tenantID, &unitCosts3)
	assert.NoError(err)
	assert.Equal(r.ConsumedSource, entity.ConsumedSource_Invoice)
	assert.False(r.Internal)
	assert.Equal(r.BindedPackageID, bindedInfo.BindedPackageID)

	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("5.66"), info.Costs)

	explore, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 0, &forms.CostExplorerQuery{StartDate: billingDate, EndDate: billingDate.AddDate(1, 0, 0)})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("0.66"), explore.TotalCosts.CostPrice)

	unitCosts4 := forms.UnitCosts{
		CostID:        uuid.NewString(),
		CostTimeStamp: billingDate.Unix(),
		Unit:          MockSKU2,
		Labels:        map[string]string{constant.ClusterLabelKey: strconv.FormatUint(uint64(clusterID), 10)},
		Cost:          666,
	}
	r2, err := measureModel.CostUnits(tenantID, &unitCosts4)
	assert.NoError(err)
	assert.Equal(entity.ConsumedSource_Invoice, r2.ConsumedSource)
	assert.False(r2.Internal)

	info, err = spendLimitModel.GetSpendLimitInfo(tenantID, setting.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("6"), info.Costs)

	explore, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 0, &forms.CostExplorerQuery{StartDate: billingDate, EndDate: billingDate.AddDate(1, 0, 0)})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("0.99"), explore.TotalCosts.CostPrice)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitValue() {
	var err error
	assert := suite.Assert()
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)
	panicIf.Error(err)
	tenantID := uint(1)
	clusterID := uint(3)
	req := &forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("1"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   "{\"free_credits\": \"5\", \"invoice_limit\": \"1\"}",
	}
	setting, err := spendLimitModel.CreateSpendLimitSetting(tenantID, req)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(6), setting.Limit)
	setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, setting.SpendLimitSettingID)
	assert.Equal(decimal.NewFromInt(6), setting.Limit)
	setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, setting.SpendLimitSettingID)
	assert.Equal(decimal.NewFromInt(6), setting.Limit)

	bizData, err := spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)
	_, err = rechargeModel.UpdateRechargePlanValue(bizData.RechargePlanID, decimal.NewFromInt(8))
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(6), setting.Limit)
	setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, setting.SpendLimitSettingID)
	assert.Equal(decimal.NewFromInt(6), setting.Limit)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)

	req.BizData = "{\"invoice_limit\": \"2\"}"
	setting, err = spendLimitModel.UpdateSpendLimitSettings(tenantID, setting.SpendLimitSettingID, req)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(7), setting.Limit)
	setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, setting.SpendLimitSettingID)
	assert.Equal(decimal.NewFromInt(7), setting.Limit)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)

	timeutils.SetMockNow(billingDate.AddDate(0, 1, 0))
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)
	tenantPlan, err := rechargeModel.GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	recharged, err := rechargeModel.TryRecharge(tenantPlan.PlanID)
	assert.NoError(err)
	assert.True(recharged)
	setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, setting.SpendLimitSettingID)
	assert.Equal(decimal.NewFromInt(10), setting.Limit)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(8), bizData.ActiveTotalCredits)
}

func (suite *SpendLimitServerlessSuite) TestServerlessSpendLimitRechargeTwice() {
	assert := suite.Assert()
	tenantID := uint(1)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)

	clusterID := uint(1)
	q1 := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   suite.defaultServerlessBizData,
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.NoError(err)
	bizData, err := spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)
	tenantPlan, err := (&marketing.RechargeCreditsModel{}).GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Equal(uint(1), tenantPlan.RechargeTimes)

	nextMoth := datainit.BillingDate.AddDate(0, 1, 0)
	_, err = rechargeModel.UpdateRechargePlanValue(bizData.RechargePlanID, decimal.NewFromInt(8))
	assert.NoError(err)
	s1, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s1.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(5), bizData.ActiveTotalCredits)
	tenantPlan, err = (&marketing.RechargeCreditsModel{}).GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Equal(uint(1), tenantPlan.RechargeTimes)
	timeutils.SetMockNow(time.Date(nextMoth.Year(), nextMoth.Month(), 1, 0, 0, 0, 0, time.UTC).Add(-time.Second))
	charged, err := rechargeModel.TryRecharge(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.True(charged)
	tenantPlan, err = (&marketing.RechargeCreditsModel{}).GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Equal(uint(2), tenantPlan.RechargeTimes)

	q2 := q1
	q2.BizData = "{\"free_credits\": \"8\", \"invoice_limit\": \"1\"}"
	q2.RangeID = uint(2)
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q2)
	assert.NoError(err)
	bizData, err = spendLimitModel.UnmarshallServerlessBizData(s2.BizData)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(8), bizData.ActiveTotalCredits)
	tenantPlan, err = (&marketing.RechargeCreditsModel{}).GetTenantRechargePlan(bizData.TenantRechargePlanID)
	assert.NoError(err)
	assert.Equal(uint(2), tenantPlan.RechargeTimes)

	timeutils.SetMockNow(nextMoth)
	for _, setting := range []*forms.SpendLimitSetting{s1, s2} {
		setting, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
		assert.NoError(err)
		bizData, err = spendLimitModel.UnmarshallServerlessBizData(setting.BizData)
		assert.NoError(err)
		assert.Equal(decimal.NewFromInt(8), bizData.ActiveTotalCredits)
	}
}

func (suite *SpendLimitServerlessSuite) TestClosePaidServerlessClustersSpendingLimits() {
	assert := suite.Assert()
	tenantID := uint(1)
	billingDate := datainit.BillingDate
	timeutils.SetMockNow(billingDate)

	clusterID := uint(1)
	q1 := forms.SpendLimitSetting{
		Interval:  entity.SpendLimitInterval_Monthly,
		CostType:  entity.SpendLimitCostType_Total,
		Limit:     decimal.RequireFromString("0.5"),
		RangeType: entity.SpendLimitRangeType_Cluster,
		RangeID:   clusterID,
		BizType:   entity.SpendLimitBizType_ServerlessCluster,
		BizData:   "{\"free_credits\": \"0\", \"invoice_limit\": \"1\"}",
	}
	s1, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q1)
	assert.NoError(err)
	q2 := q1
	q2.RangeID++
	q2.BizData = "{\"free_credits\": \"1\", \"invoice_limit\": \"0\"}"
	s2, err := spendLimitModel.CreateSpendLimitSetting(tenantID, &q2)
	assert.NoError(err)
	err = spendLimitModel.ClosePaidServerlessClustersSpendingLimits(tenantID)
	assert.NoError(err)
	s1, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Closing, s1.Status)
	s2, err = spendLimitModel.GetSpendLimitSetting(tenantID, s2.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, s2.Status)
	events := eventbushandler.Get().(*tests.LocalEventBusHandlerImpl).GetAllTxEvents(constant.SpendLimitEventType)
	assert.Len(events, 1)
	assert.Equal(entity.SpendLimitStatus_Closing, events[0].EventData.(*forms.SpendLimitInfo).Status)
	err = spendLimitModel.RecoverClosingSpendingLimits(tenantID)
	assert.NoError(err)
	events = eventbushandler.Get().(*tests.LocalEventBusHandlerImpl).GetAllTxEvents(constant.SpendLimitEventType)
	assert.Len(events, 3)
	assert.Equal(entity.SpendLimitStatus_Recovered, events[1].EventData.(*forms.SpendLimitInfo).Status)
	assert.Equal(entity.SpendLimitStatus_Fit, events[2].EventData.(*forms.SpendLimitInfo).Status)
	s1, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, s1.Status)
	s2, err = spendLimitModel.GetSpendLimitSetting(tenantID, s2.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, s2.Status)
	err = entity.NewSpendLimitSettingQuerySet(db.GetDB()).IDEq(s1.SpendLimitSettingID).GetUpdater().SetStatus(entity.SpendLimitStatus_Recovered).Update()
	assert.NoError(err)
	s1, err = spendLimitModel.GetSpendLimitSetting(tenantID, s1.SpendLimitSettingID)
	assert.NoError(err)
	assert.Equal(entity.SpendLimitStatus_Fit, s1.Status)
	err = entity.NewSpendLimitSettingQuerySet(db.GetDB()).IDEq(s1.SpendLimitSettingID).GetUpdater().SetStatus(entity.SpendLimitStatus_Recovered).Update()
	assert.NoError(err)
	s, err := spendLimitModel.ListTenantSpendLimitSettings(tenantID, true)
	assert.NoError(err)
	assert.Len(s, 2)
	assert.Equal(entity.SpendLimitStatus_Fit, s[0].Status)
	assert.Equal(entity.SpendLimitStatus_Fit, s[1].Status)
}
