package tests

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

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"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/centralhandler"
	"github.com/pingcap/dbaas/billing2/handler/eventbushandler"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler/impl"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"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/model/payment/paymentsub"
	"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"
)

type SupportPlanTestSuite struct {
	tests.BillingTestSuite
}

func TestSupportPlanSuite(t *testing.T) {
	suite.Run(t, new(SupportPlanTestSuite))
}

func timeAddr(input time.Time) *time.Time {
	return &input
}

func (s *SupportPlanTestSuite) SetupTest() {
	assert := s.Assert()
	s.BillingTestSuite.SetupTest()
	model := &marketing.SupportPlanModel{}
	datainit.CreateBillingConfig(1, entity.BillingConfigType_DirectCustomer)
	err := model.CreateSupportPlanUnits()
	assert.NoError(err)
}

func (s *SupportPlanTestSuite) TestGetMeasureDay() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	supportPlanStartDate := datainit.BillingDate
	var supportPlanEndDate *time.Time = nil
	measureStartDate := datainit.BillingDate
	measureEndDate := datainit.BillingDate.Add(time.Hour * 24)
	_, _, day := model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, timeutils.GetTimeAddr(datainit.BillingDate), supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 24))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 12))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 0)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, timeutils.GetTimeAddr(datainit.BillingDate), supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 0)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 24))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 12)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 0)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 48))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)

	supportPlanStartDate = datainit.BillingDate.Add(time.Hour)
	supportPlanEndDate = nil
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 0)

	supportPlanStartDate = datainit.BillingDate.Add(-time.Hour)
	supportPlanEndDate = nil
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)

	_, _, day = model.GetMeasureDay(supportPlanStartDate, timeutils.GetTimeAddr(datainit.BillingDate.Add(time.Hour*24)), supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)
}

func (s *SupportPlanTestSuite) TestGetMeasureDayInMonth() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	supportPlanStartDate := datainit.BillingDate
	var supportPlanEndDate *time.Time = nil
	measureStartDate := datainit.BillingDate
	measureEndDate := datainit.BillingDate.Add(time.Hour * 24 * 30)
	_, _, day := model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 30)

	expiredDate := timeutils.GetDayDate(datainit.BillingDate.AddDate(0, 1, 0).Add(-time.Second))
	_, _, day = model.GetMeasureDay(supportPlanStartDate, &expiredDate, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 30)

	supportPlanStartDate = datainit.BillingDate.Add(-time.Hour)
	supportPlanEndDate = nil
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24 * 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, &expiredDate, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 30)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour*24*15 + time.Hour*12))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24 * 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 15)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, &expiredDate, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 15)
	expiredDate = timeutils.GetDayDate(datainit.BillingDate.AddDate(0, 0, 12).Add(-time.Second))
	_, _, day = model.GetMeasureDay(supportPlanStartDate, &expiredDate, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 12)

	supportPlanStartDate = datainit.BillingDate
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 24 * 33))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24 * 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 31)

	supportPlanStartDate = datainit.BillingDate.Add(time.Hour*24*15 + time.Hour)
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour * 24 * 15))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24 * 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 0)

	supportPlanStartDate = datainit.BillingDate.Add(time.Hour*24*15 + time.Hour*12)
	supportPlanEndDate = timeAddr(datainit.BillingDate.Add(time.Hour*24*17 + time.Hour*12))
	measureStartDate = datainit.BillingDate
	measureEndDate = datainit.BillingDate.Add(time.Hour * 24 * 31)
	_, _, day = model.GetMeasureDay(supportPlanStartDate, nil, supportPlanEndDate, measureStartDate, measureEndDate)
	assert.Equal(day, 1)
}

func MustFromString(input string) decimal.Decimal {
	res, err := decimal.NewFromString(input)
	if err != nil {
		panic(err)
	}
	return res
}

func (s *SupportPlanTestSuite) TestCreateSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(15),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})

	assert.NoError(err)
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(15))
	assert.Equal(info.Level, entity.SupportPlanLevel_Standard)

	info, err = model.GetSupportPlan(info.SupportPlanID)
	assert.NoError(err)
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(15))
	assert.Equal(info.Level, entity.SupportPlanLevel_Standard)

	infoList, err := model.ListSupportPlan()
	assert.NoError(err)
	assert.Len(infoList, 1)
	info = &infoList[0]
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(15))
	assert.Equal(info.Level, entity.SupportPlanLevel_Standard)

	infoList, err = model.ListPublicSupportPlan()
	assert.NoError(err)
	assert.Len(infoList, 1)
	info = &infoList[0]
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(15))
	assert.Equal(info.Level, entity.SupportPlanLevel_Standard)

	units, err := (&billing.UnitModel{}).GetAllUnits()
	assert.NoError(err)
	assert.Len(units, 4)
	assert.Equal(units[0].Price, MustFromString("0.001"))

	info, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(799),
		SupportPricePercentage: MustFromString("0.7"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Premium,
	})

	assert.NoError(err)
	assert.Equal(info.Level, entity.SupportPlanLevel_Premium)
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(799))
	assert.Equal(info.SupportPricePercentage, MustFromString("0.7"))

	infoList, err = model.ListSupportPlan()
	assert.NoError(err)
	assert.Len(infoList, 2)

	infoList, err = model.ListPublicSupportPlan()
	assert.NoError(err)
	assert.Len(infoList, 1)
}

func (s *SupportPlanTestSuite) TestUpdateSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(15),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})
	assert.NoError(err)

	units, err := (&billing.UnitModel{}).GetAllUnits()
	assert.NoError(err)
	assert.Len(units, 4)
	assert.Equal(units[0].Price, MustFromString("0.001"))

	info, err = model.UpdateSupportPlan(info.SupportPlanID, &forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(100),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})
	assert.NoError(err)
	assert.Equal(info.FixedChargePrice, decimal.NewFromInt(100))

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

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(timeutils.GetDayDate(timeutils.GetNow()))})
	assert.NoError(err)
	assert.Equal(bindedInfo.SupportPlanID, bindedInfo.SupportPlanID)
	assert.Equal(bindedInfo.FixedChargePrice, decimal.NewFromInt(100))
	assert.Equal(bindedInfo.Public, true)
	assert.Equal(bindedInfo.Status, entity.BindedSupportPlanStatus_ACTIVE)
	assert.Equal(bindedInfo.ActiveDate, datainit.BillingDate)

	_, err = model.UpdateSupportPlan(info.SupportPlanID, &forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(150),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})
	assert.NoError(err)

	units, err = (&billing.UnitModel{}).GetAllUnits()
	assert.NoError(err)
	assert.Len(units, 4)
	assert.Equal(units[0].Price, MustFromString("0.001"))

	bindedInfo, err = model.GetBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	assert.Equal(bindedInfo.SupportPlanID, bindedInfo.SupportPlanID)
	assert.Equal(bindedInfo.FixedChargePrice, decimal.NewFromInt(150))
	assert.Equal(bindedInfo.Public, true)
	assert.Equal(bindedInfo.Status, entity.BindedSupportPlanStatus_ACTIVE)
	assert.Equal(bindedInfo.ActiveDate, datainit.BillingDate)
}

func (s *SupportPlanTestSuite) TestCreateOrUpdateSupportPlanScenarioV2() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	// case-1: tiered price is empty
	_, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	// case-2: the lowest tier must start at 0, and the highest tier must have no upper limit
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromFloat(10.0),
				UpperBound:             nil,
				SupportPricePercentage: decimal.NewFromFloat(0.1),
			},
		},
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	// case-3: the lowest tier must start at 0, and the highest tier must have no upper limit
	upperBound := decimal.NewFromInt(900)
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromFloat(10.0),
				UpperBound:             &upperBound,
				SupportPricePercentage: decimal.NewFromFloat(0.1),
			},
		},
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	// case-4: the upper bound must be greater than the lower bound for each tier
	upperBound = decimal.NewFromInt(100)
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromFloat(210.0),
				UpperBound:             &upperBound,
				SupportPricePercentage: decimal.NewFromFloat(0.1),
			},
		},
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	// case-5
	upperBoundV1 := decimal.NewFromInt(200)
	upperBoundV2 := decimal.NewFromInt(400)
	upperBoundV3 := decimal.NewFromInt(100)
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromInt(0),
				UpperBound:             &upperBoundV1,
				SupportPricePercentage: decimal.NewFromFloat(0.3),
			},
			{
				LowerBound:             upperBoundV1,
				UpperBound:             &upperBoundV3,
				SupportPricePercentage: decimal.NewFromFloat(0.4),
			},
			{
				LowerBound:             upperBoundV3,
				UpperBound:             nil,
				SupportPricePercentage: decimal.NewFromFloat(0.2),
			},
		},
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)

	// case-6
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(699),
		Type:             entity.SupportPlanType_TieredPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromInt(0),
				UpperBound:             &upperBoundV1,
				SupportPricePercentage: decimal.NewFromFloat(0.4),
			},
			{
				LowerBound:             decimal.NewFromInt(200),
				UpperBound:             &upperBoundV2,
				SupportPricePercentage: decimal.NewFromFloat(0.3),
			},
			{
				LowerBound:             decimal.NewFromInt(400),
				UpperBound:             nil,
				SupportPricePercentage: decimal.NewFromFloat(0.2),
			},
		},
	})
	assert.NoError(err)
	// query and check
	plans, err := model.ListSupportPlan()
	assert.NoError(err)
	assert.Equal(len(plans), 1)
	assert.Equal(plans[0].TieredPriceRules[0].LowerBound, decimal.NewFromInt(0))
	assert.Equal(plans[0].TieredPriceRules[0].SupportPricePercentage, decimal.NewFromFloat(0.4))
	assert.Equal(plans[0].TieredPriceRules[1].LowerBound, decimal.NewFromInt(200))
	assert.Equal(plans[0].TieredPriceRules[1].SupportPricePercentage, decimal.NewFromFloat(0.3))
	assert.Equal(plans[0].TieredPriceRules[2].LowerBound, decimal.NewFromInt(400))
	assert.Equal(plans[0].TieredPriceRules[2].SupportPricePercentage, decimal.NewFromFloat(0.2))

	// case-7: fixed_price SupportPlan, support_price_percentage != 0
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(699),
		SupportPricePercentage: decimal.NewFromFloat(0.3),
		Type:                   entity.SupportPlanType_FixedPrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)

	// case-8: type not in (fixed_price、percentage_price、tiered_price)
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(699),
		SupportPricePercentage: decimal.NewFromFloat(0.3),
		Type:                   "test",
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)

	// case-9: create percentage price SupportPLan
	_, err = model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(699),
		SupportPricePercentage: decimal.NewFromFloat(0.3),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	// query and check
	plans, err = model.ListSupportPlan()
	assert.NoError(err)
	assert.Equal(len(plans), 2)
}

func (s *SupportPlanTestSuite) TestDeleteSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(15),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})
	assert.NoError(err)

	plans, err := model.ListSupportPlan()
	assert.NoError(err)
	assert.Len(plans, 1)

	err = model.DeleteSupportPlan(info.SupportPlanID)
	assert.NoError(err)

	plans, err = model.ListSupportPlan()
	assert.NoError(err)
	assert.Len(plans, 0)

	packageModel := &billing.PackageModel{}

	packages, err := packageModel.GetAllPackages()
	assert.NoError(err)
	assert.Len(packages, 0)
}

func (s *SupportPlanTestSuite) TestBindSupportPlan() {
	assert := s.Assert()
	tenantID := uint(1)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.1"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	err = datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate // now = "2021-11-01 00:00:00"
	}
	// case-1: activeTime < now()
	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, -1))})
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	// case-2: first time to subscribe SupportPlan and the SupportPlan is support free trial
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	assert.Equal(bindedInfo.Status, entity.BindedSupportPlanStatus_ACTIVE)
	assert.Equal(bindedInfo.ActiveDate, datainit.BillingDate)
	assert.Equal(bindedInfo.IsEligibleForFreeTrial, true)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 5).Add(time.Hour * 2) // now = "2021-11-06 02:00:00"
	}
	// case-3: re-subscribe SupportPlan which is support free trial. activeTime = "2021-11-06 00:00:00"
	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 5))}) // "2021-11-06 00:00:00"
	assert.NoError(err)
	bindedSupportPlans, err := model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_ACTIVE)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 5))
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)
	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_Expired)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate)
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, true)
	assert.Equal(*bindedSupportPlans[0].ExpiredDate, datainit.BillingDate.AddDate(0, 0, 5).Add(-time.Second))

	// case-4: re-subscribe SupportPlan which is support free trial. activeTime = "2021-11-11 00:00:00"
	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 10))}) // "2021-11-11 00:00:00"
	assert.NoError(err)
	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_ACTIVE)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 5))
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)
	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_TOBEACTIVATED)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 10))
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)

	// case-5: cancel to_be_active bindedSupportPlan
	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 20))}) // "2021-11-21 00:00:00"
	assert.NoError(err)

	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_ACTIVE)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 5))
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)
	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_TOBEACTIVATED)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 20))
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)

	bindedSupportPlans, err = model.ListTenantsBindedSupportPlan(tenantID, entity.BindedSupportPlanStatus_CANCELED)
	assert.NoError(err)
	assert.Equal(len(bindedSupportPlans), 1)
	assert.Equal(bindedSupportPlans[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 10))
	assert.Equal(*bindedSupportPlans[0].ClosedDate, timeutils.GetNow())
	assert.Equal(bindedSupportPlans[0].IsEligibleForFreeTrial, false)
}

func (s *SupportPlanTestSuite) TestIsInFreeTrialForBindedSupportPlan() {
	assert := s.Assert()
	tenantID := uint(1)
	/*
		**Subscribe SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan: activeTime = "2021.11.01 00:00:00", private, 'Premium'
	*/
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(699),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate // set now = 2021.11.01 00:00:00
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	// case-1: DirectCustomer subscribe supportPlan for first time and it is support free trial, The 1st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.Add(time.Second) // set now = 2021.11.01 00:00:01
	}
	isInFreeTrial, restDays, err := model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 1st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 29)

	// case-2: DirectCustomer subscribe supportPlan for first time and it is support free trial, The 30st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 29).Add(time.Second) // set now = 2021.11.30 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 30st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 0)

	// case-3: DirectCustomer subscribe supportPlan for first time and it is support free trial, The 31st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 30).Add(time.Second) // set now = 2021.12.01 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 30st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	/*
		**BindedSupportPlan Switch**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan:
			before: "2021.11.01 00:00:00" ~ "2021.12.01 00:00:00", 'Developer', IsSupportFreeTrial = True
			after:  "2021.12.01 00:00:00" ~ , 'Developer', IsSupportFreeTrial = True
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0) // set now = 2021.12.01 00:00:00
	}
	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfo, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	newBindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfo.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(newBindedInfo.TenantID, tenantID)

	// case-4: re-subscribe 'Developer' SupportPlan, The 1st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0).Add(time.Second) // set now = 2021.12.01 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 1st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	// case-5: re-subscribe 'Developer' SupportPlan, The 30st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 29).Add(time.Hour) // set now = 2022.12.30 01:00:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 30st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	// case-6: re-subscribe 'Developer' SupportPlan, The 31st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 30).Add(time.Hour) // set now = 2022.01.31 01:00:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 31st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)
}

func (s *SupportPlanTestSuite) TestIsInFreeTrialForBindedSupportPlanScenarioV2() {
	assert := s.Assert()
	tenantID := uint(1)
	/*
		**Subscribe SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan: activeTime = "2021.11.01 00:00:00", public, 'Standard'
	*/
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(49),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Standard,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate // set now = 2021.11.01 00:00:00
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	// case-6: User subscribe 'Standard', And it's his first time to subscribe 'Standard'
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.Add(time.Minute) // set now = 2021.11.01 00:01:00
	}
	isInFreeTrial, restDays, err := model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 1st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 29).Add(time.Minute) // set now = 2021.11.30 00:01:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 1st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	/*
		**BindedSupportPlan Switch**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan:
			before: activeTime = "2021.11.01 00:00:00", public, 'Standard'
			after:  activeTime = "2021.12.01 00:00:00", public, 'Developer'
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0) // set now = 2021.12.01 00:00:00
	}
	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfo, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	newBindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfo.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(newBindedInfo.TenantID, tenantID)

	// case-7: User subscribe 'Standard' before, now he subscribe 'Developer'
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0).Add(time.Minute) // set now = 2021.12.01 00:01:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 1st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 1).Add(time.Minute) // set now = 2022.12.02 00:01:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 2st day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)
}

func (s *SupportPlanTestSuite) TestIsInFreeTrialForBindedSupportPlanScenarioV3() {
	assert := s.Assert()
	tenantID := uint(1)
	directBillingConfigID := uint(1)
	resellerTenantID := uint(2)
	/*
		**Subscribe SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan: activeTime = "2021.11.01 00:00:00", public, 'Standard'
	*/
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(49),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate // set now = 2021.11.01 00:00:00
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &directBillingConfigID,
	})
	assert.NoError(err)

	// Case-8: DirectCustomer is availabel to Free Trial
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.Add(time.Second) // set now = 2021.11.01 00:00:01
	}
	isInFreeTrial, restDays, err := model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // The 1st day, DirectCustomer
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 29)

	/*
		** BillingConfig Switch **
		BindedSupportPlan: activeTime = "2021.11.01 00:00:00", public, 'Developer'
		BillingConfig:
			Before: DirectCustomer: 2021.10.01 00:00:00 ~ 2021.11.11 00:00:00
			After: CPPOCustomer: 2021.11.11 00:00:00 ~ now
	*/
	cppoCreateQuery := forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:      tenantID,
			EffectiveDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 10)),
			PaymentCode:   "ADCXA12345",
			ResellerId:    resellerTenantID,
		},
	}
	customerBillingConfigID, paymentBillingConfigID, err := (&billingconfig.BillingConfigModel{}).CreateMarketplaceResellerBillingConfig(cppoCreateQuery.MarketplaceResellerBillingConfig)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(tenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              tenantID,
		PaymentType:           entity.PaymentType_AWSCPPOTransfer,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		BindedBillingConfigID: &customerBillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(resellerTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              resellerTenantID,
		PaymentType:           entity.PaymentType_AWS,
		PaymentCode:           cppoCreateQuery.PaymentCode,
		BindedBillingConfigID: &paymentBillingConfigID,
	})
	assert.NoError(err)
	billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetAllScheduledBillingConfigs()
	assert.NoError(err)
	assert.Equal(len(billingConfigs), 2)
	for _, billingConfig := range billingConfigs {
		err = (&billingconfig.BillingConfigModel{}).SwitchBillingConfig(billingConfig, false)
		assert.NoError(err)
	}
	// close the before billingConfig
	err = (&billingconfig.BillingConfigModel{}).ClosedBillingConfig(tenantID, directBillingConfigID)
	assert.NoError(err)

	// Case-9: CPPOCustomer is not availabel to Free Trial
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 17).Add(time.Second) // set now = 2021.11.18 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo) // 18st day, CPPOCustomer
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)
}

func (s *SupportPlanTestSuite) TestIsInFreeTrialForBindedSupportPlanScenarioV4() {
	assert := s.Assert()
	tenantID := uint(1)
	/*
		**Subscribe SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan: activeTime = "2021.11.02 02:00:00", 'Premium', IsSupportFreeTrial = True
	*/
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(49),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate // now = "2021.11.01 00:00:00"
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 1).Add(time.Hour * 2))}) // active_time = "2021.11.02 02:00:00"
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	assert.Equal(bindedInfo.Status, entity.BindedSupportPlanStatus_TOBEACTIVATED)

	// case-10: status = "to_be_active"
	isInFreeTrial, restDays, err := model.IsInSupportPlanFreeTrialForTenant(tenantID, bindedInfo)
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	err = entity.NewBindedSupportPlanQuerySet(db.GetDB()).TenantIDEq(tenantID).IDEq(bindedInfo.BindedSupportPlanID).GetUpdater().SetStatus(entity.BindedSupportPlanStatus_ACTIVE).Update()
	assert.NoError(err)
	newBindedInfo, err := model.GetBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	// case-11: status = 'active', but now < active_time
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 1).Add(time.Second) // now = 2021.11.02 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo)
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	// case-12: now = 2021.11.03 00:00:01
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 2).Add(time.Second) // set now = 2021.11.03 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 2th day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 29)

	// case-13: now = "2021.12.03 00: 00: 01", 31st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 31).Add(time.Second) // set now = 2021.12.03 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 31th day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 0)

	// case-14: now = "2021.12.04 00: 00: 01", 32st day
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 32).Add(time.Second) // set now = 2021.12.04 00:00:01
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo) // The 32th day
	assert.NoError(err)
	assert.Equal(isInFreeTrial, false)
	assert.Equal(restDays, 0)

	// case-15: set expire
	err = entity.NewBindedSupportPlanQuerySet(db.GetDB()).TenantIDEq(tenantID).IDEq(newBindedInfo.BindedSupportPlanID).GetUpdater().SetStatus(entity.BindedSupportPlanStatus_Expired).SetExpiredDate(timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 1, 5))).SetClosedDate(nil).Update()
	assert.NoError(err)
	newBindedInfo, err = model.GetBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 29).Add(time.Hour) // set now = 2021.11.30 01:00:00
	}
	isInFreeTrial, restDays, err = model.IsInSupportPlanFreeTrialForTenant(tenantID, newBindedInfo)
	assert.NoError(err)
	assert.Equal(isInFreeTrial, true)
	assert.Equal(restDays, 2)
}

func (s *SupportPlanTestSuite) TestSendNormalMeasures() {
	assert := s.Assert()
	tenantID := uint(1)
	resellerTenantID := uint(2)
	directBillingConfigID := uint(1)
	/*
		**Bind SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", id = 1
		BindedSupportPlan: directBillingConfigID = 1, activeTime = "2021.11.01 00:00:00", public, 'Developer'
	*/
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &directBillingConfigID,
	})
	assert.NoError(err)

	// case-1: measureDate is not within the effective time range of BindedSupportPlan
	costItem := &entity.UserUnitCost{
		CostID:                "test@-1",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.Add(-time.Hour * 2).Unix(), // the -1th day
		Unit:                  datainit.TestUnit,
		Cost:                  50,
		CostPrice:             decimal.NewFromInt(100),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: directBillingConfigID,
	}
	err = costItem.Create(db.GetDB())
	assert.NoError(err)
	cost, err := model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(-time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-2: measureDate is within the effective time range of BindedSupportPlan, and is in free trial(30days)
	costItem01 := &entity.UserUnitCost{
		CostID:                "test@01",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Hour * 2).Unix(), // the 1th day
		Unit:                  datainit.TestUnit,
		Cost:                  100,
		CostPrice:             decimal.NewFromInt(100),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: directBillingConfigID,
	}
	err = costItem01.Create(db.GetDB())
	assert.NoError(err)

	costItem30 := &entity.UserUnitCost{
		CostID:                "test@30",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 29).Add(time.Hour * 2).Unix(), // the 30th day
		Unit:                  datainit.TestUnit,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(200),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: directBillingConfigID,
	}
	err = costItem30.Create(db.GetDB())
	assert.NoError(err)
	// in free trial, no need to send metering data
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour)) // the 1th day
	assert.NoError(err)
	assert.Equal(cost, 0)
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 29).Add(time.Hour)) // the 30th day
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-3: send reseller normal metering data when it is 'DirectCustomer'
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-4: measureDate is within the effective time range of BindedSupportPlan，but not in free trial
	costItem31 := &entity.UserUnitCost{
		CostID:                "test@31",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 30).Add(time.Hour * 2).Unix(), // the 31th day
		Unit:                  datainit.TestUnit,
		Cost:                  300,
		CostPrice:             decimal.NewFromInt(300),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: directBillingConfigID,
	}
	err = costItem31.Create(db.GetDB())
	assert.NoError(err)

	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 30).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 60000) // 300 * 0.2 * 1000

	/*
		** BillingConfig Switch **
		BindedSupportPlan: "2021.11.01 00:00:00", public, 'Developer'
		BillingConfig:
			Before: DirectCustomer: 2021.10.01 00:00:00 ~ 2021.11.11 00:00:00
			After: CPPOCustomer: 2021.11.11 00:00:00 ~ now
	*/
	cppoCreateQuery := forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:      tenantID,
			EffectiveDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 10)),
			PaymentCode:   "ADCXA12345",
			ResellerId:    resellerTenantID,
		},
	}
	customerBillingConfigID, paymentBillingConfigID, err := (&billingconfig.BillingConfigModel{}).CreateMarketplaceResellerBillingConfig(cppoCreateQuery.MarketplaceResellerBillingConfig)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(tenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              tenantID,
		PaymentType:           entity.PaymentType_AWSCPPOTransfer,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		BindedBillingConfigID: &customerBillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(resellerTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              resellerTenantID,
		PaymentType:           entity.PaymentType_AWS,
		PaymentCode:           cppoCreateQuery.PaymentCode,
		BindedBillingConfigID: &paymentBillingConfigID,
	})
	assert.NoError(err)
	billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetAllScheduledBillingConfigs()
	assert.NoError(err)
	assert.Equal(len(billingConfigs), 2)
	for _, billingConfig := range billingConfigs {
		err = (&billingconfig.BillingConfigModel{}).SwitchBillingConfig(billingConfig, false)
		assert.NoError(err)
	}
	billingConfigs, err = (&billingconfig.BillingConfigModel{}).GetAllScheduledBillingConfigs()
	assert.NoError(err)
	// SwitchBillingConfig will set old billingConfig to 'closing',GetAllScheduledBillingConfigs will query 'closing' billingConfig
	assert.Equal(len(billingConfigs), 1)

	// case-5: measureDate is within the effective time range of BindedSupportPlan, but corresponding billingConfig is closed
	err = (&billingconfig.BillingConfigModel{}).ClosedBillingConfig(tenantID, directBillingConfigID)
	assert.NoError(err)
	_, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour))
	assert.ErrorIs(err, billing_errors.SystemError)

	// case-6: measureDate is within the effective time range of BindedSupportPlan, and in free trial, but it is CPPOCustomer and no cost in user_unit_cost
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 29).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 0)

	costItem15 := &entity.UserUnitCost{
		CostID:                "test@15",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour * 2).Unix(), // the 15th day
		Unit:                  datainit.TestUnit,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(200),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID, // for end customer
	}
	err = costItem15.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller15 := &entity.UserUnitCost{
		CostID:                "test@reseller@15",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour * 2).Unix(), // the 15th day
		Unit:                  datainit.TestUnit,
		Cost:                  150,
		CostPrice:             decimal.NewFromInt(150),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID, // for reseller
	}
	err = costItemReseller15.Create(db.GetDB())
	assert.NoError(err)

	// case-7: CPPOCustomer not available for free trial, need to send metering data
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 40000) // 200 * 0.2 * 1000

	// case-8: send reseller normal metering if needed
	// Reseller push now uses the customer's own usage (costItem15,
	// costPrice=200), matching case-7. The pre-refactor value 30000
	// came from the reseller-side costItemReseller15 (costPrice=150),
	// which would silently re-attribute the customer's usage onto the
	// partner's books in a Resale 1:N setup.
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 40000) // 200 * 0.2 * 1000 (customer's own cost)

	// case-9: Duplicate sending normal metering
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-10: Duplicate sending reseller metering
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour))
	assert.NoError(err)
	assert.Equal(cost, 0)
}

func (s *SupportPlanTestSuite) TestSplitSupportPlanMeteringRangeByTrialStatus() {
	assert := s.Assert()
	tenantID := uint(1)
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 14) // "2021-11-15 00:00:00"
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)

	// case-1: input time range: "2021-11-01 00: 00: 00" ~ "2021-11-15 00: 00: 00"
	_, err = model.SplitSupportPlanMeteringRangeByTrialStatus(datainit.BillingDate, datainit.BillingDate.AddDate(0, 0, 14), bindedInfo)
	assert.ErrorIs(err, billing_errors.TimeTimeRangeConflicted)

	// case-2: input time range: "2021-11-15 00: 00: 00" ~ "2021-11-19 00: 00: 00"
	timeRangeList, err := model.SplitSupportPlanMeteringRangeByTrialStatus(datainit.BillingDate.AddDate(0, 0, 14), datainit.BillingDate.AddDate(0, 0, 18), bindedInfo)
	assert.NoError(err)
	assert.Equal(len(timeRangeList), 2)
	assert.Equal(timeRangeList[0].ActiveDate, datainit.BillingDate.AddDate(0, 0, 14))
	assert.Equal(*timeRangeList[0].ExpiredDate, datainit.BillingDate.AddDate(0, 0, 18))
	assert.Equal(timeRangeList[0].IsInFreeTrial, true)
	assert.Equal(timeRangeList[1].ActiveDate, datainit.BillingDate.AddDate(0, 0, 18))
	assert.Equal(*timeRangeList[1].ExpiredDate, datainit.BillingDate.AddDate(0, 0, 18))
	assert.Equal(timeRangeList[1].IsInFreeTrial, false)

	// case-3: input time range: "2021-12-10 00: 00: 00" ~ "2021-12-19 00: 00: 00"
	timeRangeList, err = model.SplitSupportPlanMeteringRangeByTrialStatus(datainit.BillingDate.AddDate(0, 1, 9), datainit.BillingDate.AddDate(0, 1, 18), bindedInfo)
	assert.NoError(err)
	assert.Equal(len(timeRangeList), 2)
	assert.Equal(timeRangeList[0].ActiveDate, datainit.BillingDate.AddDate(0, 1, 9))
	assert.Equal(*timeRangeList[0].ExpiredDate, datainit.BillingDate.AddDate(0, 1, 14))
	assert.Equal(timeRangeList[0].IsInFreeTrial, true)
	assert.Equal(timeRangeList[1].ActiveDate, datainit.BillingDate.AddDate(0, 1, 14))
	assert.Equal(*timeRangeList[1].ExpiredDate, datainit.BillingDate.AddDate(0, 1, 18))
	assert.Equal(timeRangeList[1].IsInFreeTrial, false)

	// case-4: input time range: "2021-12-16 00: 00: 00" ~ "2021-12-26 00: 00: 00"
	timeRangeList, err = model.SplitSupportPlanMeteringRangeByTrialStatus(datainit.BillingDate.AddDate(0, 1, 15), datainit.BillingDate.AddDate(0, 1, 25), bindedInfo)
	assert.NoError(err)
	assert.Equal(len(timeRangeList), 1)
	assert.Equal(timeRangeList[0].ActiveDate, datainit.BillingDate.AddDate(0, 1, 15))
	assert.Equal(*timeRangeList[0].ExpiredDate, datainit.BillingDate.AddDate(0, 1, 25))
	assert.Equal(timeRangeList[0].IsInFreeTrial, false)

	/*
		**BindedSupportPlan Switch**
		BindedSupportPlan:
			before: "2021.11.15 00:00:00" ~ "2021.12.01 00:00:00", public, 'Developer'
			after:  "2021.12.01 00:00:00" ~ now, public, 'Developer'
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0) // set now = 2021.12.01 00:00:00
	}
	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfo, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
	})
	assert.NoError(err)
	newBindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfo.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(newBindedInfo.TenantID, tenantID)

	// case-5: not first time to subscribe supportPlan, has no free trial, input time range: "2021-12-16 00: 00: 00" ~ "2021-12-26 00: 00: 00"
	timeRangeList, err = model.SplitSupportPlanMeteringRangeByTrialStatus(datainit.BillingDate.AddDate(0, 1, 15), datainit.BillingDate.AddDate(0, 1, 25), newBindedInfo)
	assert.NoError(err)
	assert.Equal(len(timeRangeList), 1)
	assert.Equal(timeRangeList[0].ActiveDate, datainit.BillingDate.AddDate(0, 1, 15))
	assert.Equal(*timeRangeList[0].ExpiredDate, datainit.BillingDate.AddDate(0, 1, 25))
	assert.Equal(timeRangeList[0].IsInFreeTrial, false)
}

func (s *SupportPlanTestSuite) TestSendCommitmentMeasures() {
	assert := s.Assert()
	/*
		**Create BillingConfig And Bind SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00", tenantID = 1
		BindedSupportPlan: activeTime = "2021.12.01 00:00:00", public, 'Developer'
	*/
	tenantID := uint(1)
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 0) // now = 2021-12-01 00:00:00
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	// insert cost
	costItem30 := &entity.UserUnitCost{
		CostID:                "test@30",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 1, 29).Add(time.Hour * 2).Unix(),
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem30.Create(db.GetDB())
	assert.NoError(err)

	costItem31 := &entity.UserUnitCost{
		CostID:                "test@31",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 1, 30).Add(time.Hour * 2).Unix(),
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(0.2),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem31.Create(db.GetDB())
	assert.NoError(err)

	// case-1: free trial (30 days) < 31 days (Dec), and need send Minimum Guarantee Fee
	measureDay, cost, err := model.SendCommitmentMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 1, 3))
	assert.NoError(err)
	assert.Equal(measureDay, 1)
	assert.Equal(cost, 770) // cost of 31st. int(((29/30*1) - 0.2) * 1000)

	costItem31V2 := &entity.UserUnitCost{
		CostID:                "test@31@v2",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 1, 30).Add(time.Hour * 2).Unix(),
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(0.8),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem31V2.Create(db.GetDB())
	assert.NoError(err)
	// case-2: free trial (30 days) < 31 days (Dec), and don't need send Minimum Guarantee Fee
	measureDay, cost, err = model.SendCommitmentMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 1, 3))
	assert.NoError(err)
	assert.Equal(measureDay, 1)
	assert.Equal(cost, 0) // cost of 31st. (29/30*1) < (0.2 + 0.8)

	/*
		**BindedSupportPlan Switch**
		BindedSupportPlan:
			before: "2021.12.01 00:00:00" ~ "2021.12.12 00:00:00", public, 'Developer'
			after:  "2021.12.12 00:00:00" ~ now, public, 'Developer'
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 11) // set now = 2021.12.12 00:00:00
	}
	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfo, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	newBindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfo.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(newBindedInfo.TenantID, tenantID)

	costItem12 := &entity.UserUnitCost{
		CostID:                "test@12",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 1, 11).Add(time.Hour * 2).Unix(),
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(1.2),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem12.Create(db.GetDB())
	assert.NoError(err)

	// case-3: not available for free trial
	measureDay, cost, err = model.SendCommitmentMetering(newBindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 1, 3))
	assert.NoError(err)
	assert.Equal(measureDay, 20) // 2021.12.12 00:00:00 ~ 2022.01.01 00:00:00
	assert.Equal(cost, 16130)    // int(((29/30*20) - (1+1+1.2)) * 1000)

	// case-4: force to send reseller commitment data
	measureDay, cost, err = model.SendResellerCommitmentMeteringIfNeed(newBindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 1, 3))
	assert.NoError(err)
	assert.Equal(measureDay, 20) // 2021.12.12 00:00:00 ~ 2022.01.01 00:00:00
	assert.Equal(cost, 0)        // not CPPOCustomer

	/*
		**BindedSupportPlan Switch**
		BindedSupportPlan:
			v1: "2021.12.01 00:00:00" ~ "2021.12.12 00:00:00", public, 'Developer'
			v2: "2021.12.12 00:00:00" ~ "2021.12.20 00:00:00", public, 'Developer'
			now:  "2021.12.20 00:00:00" ~ now, public, 'Developer', 'fixed_price'
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 1, 19) // set now = 2021.12.20 00:00:00
	}
	_, err = model.CancelBindedSupportPlan(newBindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfoV2, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0"),
		Type:                   entity.SupportPlanType_FixedPrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)
	newBindedInfoV2, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfoV2.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	// case-5: subscribe 'fixed_price' SupportPlan
	measureDay, cost, err = model.SendCommitmentMetering(newBindedInfoV2.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 1, 3))
	assert.NoError(err)
	assert.Equal(measureDay, 0) // type != 'percentage_price'
	assert.Equal(cost, 0)       // type != 'percentage_price'

}

func (s *SupportPlanTestSuite) TestSendCommitmentMeasuresScenarioV2() {
	assert := s.Assert()
	/*
		**Create BillingConfig And Bind SupportPlan**
		BillingConfig: type = 'DirectCustomer', startActiveTime = "2021.10.01 00:00:00" ~ ?, tenantID = 1
		BindedSupportPlan: activeTime = "2021.11.05 00:00:00" ~ ?, public, 'Premium'
	*/
	tenantID := uint(1)
	resellerTenantID := uint(2)
	directBillingConfigID := uint(1)

	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(90),
		SupportPricePercentage: MustFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 4) // now = 2021-11-05 00:00:00
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &directBillingConfigID,
	})
	assert.NoError(err)
	// insert cost
	costItem05 := &entity.UserUnitCost{
		CostID:                "test@05",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 4).Add(time.Hour * 2).Unix(), // 2021-11-05 02:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(1.2),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem05.Create(db.GetDB())
	assert.NoError(err)
	costItem13 := &entity.UserUnitCost{
		CostID:                "test@13",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 12).Add(time.Hour * 2).Unix(), // 2021-11-13 02:00:00
		Unit:                  datainit.TestUnit,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem13.Create(db.GetDB())
	assert.NoError(err)
	costItem14 := &entity.UserUnitCost{
		CostID:                "test@14",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 13).Add(time.Hour * 2).Unix(), // 2021-11-14 02:00:00
		Unit:                  datainit.TestUnit,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem14.Create(db.GetDB())
	assert.NoError(err)
	costItem15 := &entity.UserUnitCost{
		CostID:                "test@15",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour * 2).Unix(), // 2021-11-15 02:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: 1,
	}
	err = costItem15.Create(db.GetDB())
	assert.NoError(err)

	/*
		** BillingConfig Switch **
		BindedSupportPlan: "2021.11.05 00:00:00" ~ "", public, 'Developer'
		BillingConfig:
			Before: DirectCustomer: 2021.10.01 00:00:00 ~ 2021.11.15 15:00:00
			After: CPPOCustomer: 2021.11.15 15:00:00 ~ now
	*/
	cppoCreateQuery := forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:      tenantID,
			EffectiveDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour * 15)),
			PaymentCode:   "ADCXA12345",
			ResellerId:    resellerTenantID,
		},
	}
	customerBillingConfigID, paymentBillingConfigID, err := (&billingconfig.BillingConfigModel{}).CreateMarketplaceResellerBillingConfig(cppoCreateQuery.MarketplaceResellerBillingConfig)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(tenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              tenantID,
		PaymentType:           entity.PaymentType_AWSCPPOTransfer,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		BindedBillingConfigID: &customerBillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(resellerTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              resellerTenantID,
		PaymentType:           entity.PaymentType_AWS,
		PaymentCode:           cppoCreateQuery.PaymentCode,
		BindedBillingConfigID: &paymentBillingConfigID,
	})
	assert.NoError(err)
	billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetAllScheduledBillingConfigs()
	assert.NoError(err)
	assert.Equal(len(billingConfigs), 2)
	for _, billingConfig := range billingConfigs {
		err = (&billingconfig.BillingConfigModel{}).SwitchBillingConfig(billingConfig, false) // directCustomer will be set to 'closing'
		assert.NoError(err)
	}
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 14).Add(time.Hour * 15) // now = 2021-11-15 15:00:00, The Switch time
	}

	// case-1: mock billingConfig Switch, cancel BindedSupportPlan at: 2021.11.15 15:00:00
	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)

	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate.AddDate(0, 0, 13),
		EndDate:   datainit.BillingDate.AddDate(0, 0, 14),
		Units:     []string{"support-plan:percentage_price"},
	})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("0.2")) // 2021-11-14 (now -1), metering: 1 * 0.2 = 0.2

	res, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate.AddDate(0, 0, 12),
		EndDate:   datainit.BillingDate.AddDate(0, 0, 13),
		Units:     []string{"support-plan:percentage_price"},
	})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("0.2")) // 2021-11-13 (now - 2),  metering: 1 * 0.2 = 0.2

	res, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate.AddDate(0, 0, 13),
		EndDate:   datainit.BillingDate.AddDate(0, 0, 14),
		Units:     []string{"support-plan:commitment_price"},
	})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("28.4")) // 2021-11-14, which is the actual billing start day (2021-11-05 00:00:00 ~ 2021-11-15 00:00:00), commitment metering = 28.4

	err = (&billingconfig.BillingConfigModel{}).ClosedBillingConfig(tenantID, directBillingConfigID) // closed directCustomer BillingConfig
	assert.NoError(err)

	/*
		Binde new SupportPLan and inser cost
		BindedSupportPlan: activeTime = "2021.11.16 00:00:00", private, 'Premium'
	*/
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 15) // now = 2021-11-16 00:00:00
	}
	newBindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	costItemCustomer15 := &entity.UserUnitCost{
		CostID:                "test@customer@15",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 14).Unix(), // 2021-11-15 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer15.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller15 := &entity.UserUnitCost{
		CostID:                "test@reseller@15",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 14).Unix(), // 2021-11-15 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(0.5),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller15.Create(db.GetDB())
	assert.NoError(err)

	costItemCustomer16 := &entity.UserUnitCost{
		CostID:                "test@customer@16",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 15).Unix(), // 2021-11-16 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(2),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer16.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller16 := &entity.UserUnitCost{
		CostID:                "test@reseller@16",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 15).Unix(), // 2021-11-16 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(1),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller16.Create(db.GetDB())
	assert.NoError(err)

	costItemCustomer30 := &entity.UserUnitCost{
		CostID:                "test@customer@30",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 29).Unix(), // 2021-11-30 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(3),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer30.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller30 := &entity.UserUnitCost{
		CostID:                "test@reseller@30",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 29).Unix(), // 2021-11-30 00:00:00
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  200,
		CostPrice:             decimal.NewFromFloat(1.5),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller30.Create(db.GetDB())
	assert.NoError(err)

	// case-2: send normal Commitment metering and reseller commitment on CPPOCustomer, now = 2021-11-17
	measureDay, cost, err := model.SendCommitmentMetering(newBindedInfo.BindedSupportPlanID, *timeutils.GetNowAddr())
	assert.NoError(err)
	assert.Equal(measureDay, 15)
	assert.Equal(cost, 40000)

	measureDay, cost, err = model.SendResellerCommitmentMeteringIfNeed(newBindedInfo.BindedSupportPlanID, *timeutils.GetNowAddr())
	assert.NoError(err)
	assert.Equal(measureDay, 15)
	// Reseller commitment now uses the customer's own support-fee
	// spend (costItemCustomer16 costPrice=2 + costItemCustomer30=3 = 5)
	// rather than the reseller-side data, so the gap matches the
	// customer commitment (45 - 5 = 40 -> 40000). The pre-refactor
	// value was 42500, which used the reseller-side cost rows.
	assert.Equal(cost, 40000)

}

func (s *SupportPlanTestSuite) TestSendNormalMeteringTieredPricing() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	tenantID := uint(1)
	resellerTenantID := uint(2)
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, -1, 14) // now = 2021-10-15 00:00:00
	}
	/*
		** BillingConfig Switch **
		CPPOCustomer: "2021-10-15 00: 00: 00" ~ now(),
	*/
	cppoCreateQuery := forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:      tenantID,
			EffectiveDate: timeutils.GetNowAddr(),
			PaymentCode:   "ADCXA12345",
			ResellerId:    resellerTenantID,
		},
	}
	customerBillingConfigID, paymentBillingConfigID, err := (&billingconfig.BillingConfigModel{}).CreateMarketplaceResellerBillingConfig(cppoCreateQuery.MarketplaceResellerBillingConfig)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(tenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              tenantID,
		PaymentType:           entity.PaymentType_AWSCPPOTransfer,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		BindedBillingConfigID: &customerBillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(resellerTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              resellerTenantID,
		PaymentType:           entity.PaymentType_AWS,
		PaymentCode:           cppoCreateQuery.PaymentCode,
		BindedBillingConfigID: &paymentBillingConfigID,
	})
	assert.NoError(err)
	billingConfigs, err := (&billingconfig.BillingConfigModel{}).GetAllScheduledBillingConfigs()
	assert.NoError(err)
	assert.Equal(len(billingConfigs), 2)
	for _, billingConfig := range billingConfigs {
		err = (&billingconfig.BillingConfigModel{}).SwitchBillingConfig(billingConfig, false) // directCustomer will be set to 'closing'
		assert.NoError(err)
	}
	/*
		**Bind SupportPlan**
		BindedSupportPlan: directBillingConfigID = 1, activeTime = "2021.11.01 00:00:00", public, 'Premium'
	*/
	upperBoundV1 := decimal.NewFromInt(200)
	upperBoundV2 := decimal.NewFromInt(400)
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:   decimal.NewFromInt(799),
		Type:               entity.SupportPlanType_TieredPrice,
		Public:             true,
		Level:              entity.SupportPlanLevel_Premium,
		IsSupportFreeTrial: false,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromInt(0),
				UpperBound:             &upperBoundV1,
				SupportPricePercentage: decimal.NewFromFloat(0.4),
			},
			{
				LowerBound:             upperBoundV1,
				UpperBound:             &upperBoundV2,
				SupportPricePercentage: decimal.NewFromFloat(0.3),
			},
			{
				LowerBound:             upperBoundV2,
				UpperBound:             nil,
				SupportPricePercentage: decimal.NewFromFloat(0.2),
			},
		},
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}
	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo.TenantID, tenantID)
	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &customerBillingConfigID,
	})
	assert.NoError(err)
	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(resellerTenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &paymentBillingConfigID,
	})
	assert.NoError(err)

	// insert Cost
	costItemCustomer01 := &entity.UserUnitCost{
		CostID:                "test@customer@01",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(), // 2021-11-01 00:00:01
		Unit:                  "test",
		Cost:                  100,
		CostPrice:             decimal.NewFromInt(100),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer01.Create(db.GetDB())
	assert.NoError(err)
	costItemCustomer03 := &entity.UserUnitCost{
		CostID:                "test@customer@03",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 2).Add(time.Second).Unix(), // 2021-11-03 00:00:01
		Unit:                  "test",
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(200),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer03.Create(db.GetDB())
	assert.NoError(err)
	costItemCustomer04 := &entity.UserUnitCost{
		CostID:                "test@customer@04",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 3).Add(time.Second).Unix(), // 2021-11-04 00:00:01
		Unit:                  "test",
		Cost:                  300,
		CostPrice:             decimal.NewFromInt(300),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBillingConfigID,
	}
	err = costItemCustomer04.Create(db.GetDB())
	assert.NoError(err)

	costItemReseller01 := &entity.UserUnitCost{
		CostID:                "test@reseller@01",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(), // 2021-11-15 00:00:01
		Unit:                  "test",
		Cost:                  80,
		CostPrice:             decimal.NewFromFloat(80),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller01.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller03 := &entity.UserUnitCost{
		CostID:                "test@reseller@03",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 2).Add(time.Second).Unix(), // 2021-11-03 00:00:01
		Unit:                  "test",
		Cost:                  160,
		CostPrice:             decimal.NewFromFloat(160),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller03.Create(db.GetDB())
	assert.NoError(err)
	costItemReseller04 := &entity.UserUnitCost{
		CostID:                "test@reseller@04",
		TenantID:              resellerTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 3).Add(time.Second).Unix(), // 2021-11-04 00:00:00
		Unit:                  "test",
		Cost:                  240,
		CostPrice:             decimal.NewFromFloat(240),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: paymentBillingConfigID,
	}
	err = costItemReseller04.Create(db.GetDB())
	assert.NoError(err)

	// case -1: CPPOCustomer: cost of "2021-11-01"
	cost, err := model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(cost, 40000) // 100 * 1000 * 0.4
	// Reseller. CPPO is 1:1 (customer:partner), so the source billing
	// config (customer) and the target (partner) hold the same
	// aggregate cost for this scenario — the reseller push uses the
	// customer's own cost as the base, not the partner's separately
	// pushed $80 figure.
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(cost, 40000) // 100 * 1000 * 0.4 (customer own cost = reseller own cost for 1:1)

	// case-2: cost of "2021-11-02"
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 1))
	assert.NoError(err)
	assert.Equal(cost, 0)
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 1))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-3: cost of "2021-11-03"
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 2))
	assert.NoError(err)
	assert.Equal(cost, 70000)
	// Reseller. Tiered pricing now bases off customer's own cumulative
	// cost, so this matches SendNormalMetering for the 1:1 case.
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 2))
	assert.NoError(err)
	assert.Equal(cost, 70000)

	// case-4: cost of "2021-11-04"
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 3))
	assert.NoError(err)
	assert.Equal(cost, 70000)
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 3))
	assert.NoError(err)
	assert.Equal(cost, 70000)

	// case-5: cost of "2021-11-05"
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 4))
	assert.NoError(err)
	assert.Equal(cost, 0)
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 4))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-6: cost of "2021-11-06"
	cost, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 5))
	assert.NoError(err)
	assert.Equal(cost, 0)
	cost, err = model.SendResellerNormalMeteringIfNeed(bindedInfo.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 5))
	assert.NoError(err)
	assert.Equal(cost, 0)

	// case-7: cancel BindedSupportPlan: "2021-11-09 01:00:00"
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.AddDate(0, 0, 8).Add(time.Hour)
	}

	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	var record entity.SupportPlanMeteringRecords
	// CPPOCustomer, now() - 1 (2021-11-08), cost = 0
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 0, 7)).TenantIDEq(tenantID).BindedSupportPlanIDEq(bindedInfo.BindedSupportPlanID).One(&record)
	assert.NoError(err)
	assert.Equal(record.Cost, 0)
	assert.Equal(record.Unit, constant.TieredPrice_SKU)

	// CPPOCustomer, now() - 2 (2021-11-07), cost = 0
	var record1 entity.SupportPlanMeteringRecords
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 0, 6)).TenantIDEq(tenantID).BindedSupportPlanIDEq(bindedInfo.BindedSupportPlanID).One(&record1)
	assert.NoError(err)
	assert.Equal(record1.Cost, 0)
	assert.Equal(record1.Unit, constant.TieredPrice_SKU)

	// CPPOCustomer, Commitment, current month (2021-11-08 23:59:59), cost = 33070 (int((799/30*8 - 180) *  1000) = 33070)
	var record2 entity.SupportPlanMeteringRecords
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(timeutils.GetDayDate(timeutils.GetNow()).Add(-1 * time.Second)).TenantIDEq(tenantID).UnitEq(constant.CommintmentPrice_SKU).One(&record2)
	assert.NoError(err)
	assert.Equal(record2.Cost, 33070)
	assert.Equal(record2.Unit, constant.CommintmentPrice_SKU)

	// CPPOResellor, now() - 1 (2021-11-08), cost = 0
	var record3 entity.SupportPlanMeteringRecords
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 0, 7)).TenantIDEq(resellerTenantID).BindedSupportPlanIDEq(bindedInfo.BindedSupportPlanID).One(&record3)
	assert.NoError(err)
	assert.Equal(record3.Cost, 0)
	assert.Equal(record3.Unit, constant.TieredPrice_SKU)

	// CPPOResellor, now() - 2 (2021-11-07), cost = 0
	var record4 entity.SupportPlanMeteringRecords
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 0, 6)).TenantIDEq(resellerTenantID).BindedSupportPlanIDEq(bindedInfo.BindedSupportPlanID).One(&record4)
	assert.NoError(err)
	assert.Equal(record4.Cost, 0)
	assert.Equal(record4.Unit, constant.TieredPrice_SKU)

	// CPPOResellor, Commitment, current month (2021-11-01).
	// With the per-customer base refactor, the reseller commitment row
	// uses the customer's own support-fee sum (40 + 70 + 70 = 180) as
	// costPrice, so the gap matches the customer's commitment gap.
	var record5 entity.SupportPlanMeteringRecords
	err = entity.NewSupportPlanMeteringRecordsQuerySet(db.GetDB()).CostTimeStampEq(timeutils.GetDayDate(timeutils.GetNow()).Add(-1 * time.Second)).TenantIDEq(resellerTenantID).UnitEq(constant.CommintmentPrice_SKU).One(&record5)
	assert.NoError(err)
	assert.Equal(record5.Cost, 33070)
	assert.Equal(record5.Unit, constant.CommintmentPrice_SKU)
}

// TestSendResaleOneToNManyCustomers is the regression test for the
// Resale 1:N bug fix. It binds the same support plan to two
// independent ResaleCustomer billing configs that both point to the
// same ResalePartner, writes distinct per-customer usage rows, and
// verifies each customer's support fee is computed from that
// customer's own cost (not the partner's aggregate). Without the
// source/target split in sendToBillNormalMetering, customer B would
// inherit customer A's larger usage base and produce a fee > its
// own usage warrants.
func (s *SupportPlanTestSuite) TestSendResaleOneToNManyCustomers() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	bcModel := &billingconfig.BillingConfigModel{}

	customerATenantID := uint(3)
	customerBTenantID := uint(4)
	resellerTenantID := uint(5)

	timeutils.GetNowFunc = func() time.Time { return datainit.BillingDate }

	// Two ResaleCustomer billing configs (different tenants, same
	// reseller) plus one ResalePartner billing config. Each customer's
	// payment method is Resale with the partner tenant ID as code,
	// matching how getResellerTenantAndBillingConfig resolves the
	// link for ResaleCustomer billing configs.
	customerABC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerATenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	customerBBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerBTenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	resellerBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: resellerTenantID,
		Type:     entity.BillingConfigType_ResalePartner,
	})
	assert.NoError(err)

	tests.MockCentralHandler()
	paymenthandler.SetPaymentHandler(&impl.PaymentHandlerImpl{})
	defer tests.MockPaymentHandler()

	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerATenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerATenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerABC.BillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerBTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerBTenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerBBC.BillingConfigID,
	})
	assert.NoError(err)

	// NoUsePackage lets cost rows measure through at rawPrice=0.001
	// without invoking credits, matching how the other support-plan
	// tests stage billing data.
	for _, c := range []struct {
		tenantID        uint
		billingConfigID uint
	}{
		{customerATenantID, customerABC.BillingConfigID},
		{customerBTenantID, customerBBC.BillingConfigID},
		{resellerTenantID, resellerBC.BillingConfigID},
	} {
		id := c.billingConfigID
		_, err = (&billing.BindedPackageModel{}).BindPackageExtra(c.tenantID, &forms.BindedPackageInfoInput{
			PackageID:             datainit.NoUsePackageId,
			DiscountEnabled:       true,
			InvoicePayable:        true,
			BindedBillingConfigID: &id,
		})
		assert.NoError(err)
	}

	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(10),
		SupportPricePercentage: decimal.RequireFromString("0.20"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Standard,
		IsSupportFreeTrial:     false,
	})
	assert.NoError(err)

	bindedInfoA, err := model.BindSupportPlanForPayableCustomer(customerATenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	bindedInfoB, err := model.BindSupportPlanForPayableCustomer(customerBTenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	// Distinct per-customer cost rows for day 1. Customer A = 100,
	// customer B = 50. If the support fee accidentally scaled off the
	// partner's aggregate, B would produce 30 instead of 10.
	costA := &entity.UserUnitCost{
		CostID:                "test@a@01",
		TenantID:              customerATenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(),
		Unit:                  datainit.TestUnit,
		Cost:                  100,
		CostPrice:             decimal.NewFromInt(100),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerABC.BillingConfigID,
	}
	err = costA.Create(db.GetDB())
	assert.NoError(err)
	costB := &entity.UserUnitCost{
		CostID:                "test@b@01",
		TenantID:              customerBTenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(),
		Unit:                  datainit.TestUnit,
		Cost:                  50,
		CostPrice:             decimal.NewFromInt(50),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBBC.BillingConfigID,
	}
	err = costB.Create(db.GetDB())
	assert.NoError(err)

	// Customer A: 100 * 1000 * 0.2 = 20000
	costResellerA, err := model.SendResellerNormalMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(costResellerA, 20000)

	// Customer B: 50 * 1000 * 0.2 = 10000. With the pre-refactor
	// CalculateCost-on-partner path, this would have used the
	// partner's aggregate (which includes A's 100) and produced
	// 30000 — silently overcharging B.
	costResellerB, err := model.SendResellerNormalMeteringIfNeed(bindedInfoB.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(costResellerB, 10000)

	// Both support fee rows land on the reseller's billing config,
	// and each carries the per-customer MarketplaceResellerManagedCustomerIDLabel
	// so the partner can attribute the row back to the customer.
	var resellerSupportRows []entity.UserUnitCost
	err = entity.NewUserUnitCostQuerySet(db.GetDB()).
		TenantIDEq(resellerTenantID).
		BindedBillingConfigIDEq(resellerBC.BillingConfigID).
		UnitEq(constant.PercentagePrice_SKU).
		All(&resellerSupportRows)
	assert.NoError(err)
	assert.Len(resellerSupportRows, 2)
	for _, row := range resellerSupportRows {
		tidStr, ok := row.Labels[constant.MarketplaceResellerManagedCustomerIDLabel]
		assert.True(ok, "reseller support row should carry MarketplaceResellerManagedCustomerIDLabel, got labels=%v", row.Labels)
		if ok {
			tid, err := strconv.ParseUint(tidStr, 10, 64)
			assert.NoError(err)
			assert.True(tid == uint64(customerATenantID) || tid == uint64(customerBTenantID),
				"label customer ID should be one of the source customers, got %d", tid)
		}
	}
}

// TestSendResaleOneToNCommitmentMetering verifies that
// SendResellerCommitmentMeteringIfNeed uses each Resale customer's own
// support-fee spend (not the partner's aggregate) to compute the
// commitment gap. Two customers under the same ResalePartner have
// different support-fee spend; each customer's commitment gap must
// reflect only its own spend.
func (s *SupportPlanTestSuite) TestSendResaleOneToNCommitmentMetering() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	bcModel := &billingconfig.BillingConfigModel{}

	customerATenantID := uint(3)
	customerBTenantID := uint(4)
	resellerTenantID := uint(5)

	timeutils.GetNowFunc = func() time.Time { return datainit.BillingDate }

	// Create billing configs and payment methods (same pattern as
	// TestSendResaleOneToNManyCustomers).
	customerABC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerATenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	customerBBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerBTenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	resellerBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: resellerTenantID,
		Type:     entity.BillingConfigType_ResalePartner,
	})
	assert.NoError(err)

	tests.MockCentralHandler()
	paymenthandler.SetPaymentHandler(&impl.PaymentHandlerImpl{})
	defer tests.MockPaymentHandler()

	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerATenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerATenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerABC.BillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerBTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerBTenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerBBC.BillingConfigID,
	})
	assert.NoError(err)

	for _, c := range []struct {
		tenantID        uint
		billingConfigID uint
	}{
		{customerATenantID, customerABC.BillingConfigID},
		{customerBTenantID, customerBBC.BillingConfigID},
		{resellerTenantID, resellerBC.BillingConfigID},
	} {
		id := c.billingConfigID
		_, err = (&billing.BindedPackageModel{}).BindPackageExtra(c.tenantID, &forms.BindedPackageInfoInput{
			PackageID:             datainit.NoUsePackageId,
			DiscountEnabled:       true,
			InvoicePayable:        true,
			BindedBillingConfigID: &id,
		})
		assert.NoError(err)
	}

	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: decimal.RequireFromString("0.20"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     false,
	})
	assert.NoError(err)

	bindedInfoA, err := model.BindSupportPlanForPayableCustomer(customerATenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	bindedInfoB, err := model.BindSupportPlanForPayableCustomer(customerBTenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	// Simulate support-fee spend from SendNormalMetering output.
	// Customer A: two PercentagePrice_SKU rows (costPrice=2.0 + 1.5 = 3.5)
	// Customer B: one PercentagePrice_SKU row (costPrice=0.5)
	//
	// If the commitment gap accidentally read the partner's aggregate
	// or cross-contaminated customer spend, B's gap would be wrong.
	supportFeeA1 := &entity.UserUnitCost{
		CostID:                "commit-test-a-01",
		TenantID:              customerATenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 5).Unix(), // Nov 6
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  2000,
		CostPrice:             decimal.NewFromFloat(2.0),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerABC.BillingConfigID,
	}
	err = supportFeeA1.Create(db.GetDB())
	assert.NoError(err)
	supportFeeA2 := &entity.UserUnitCost{
		CostID:                "commit-test-a-02",
		TenantID:              customerATenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 15).Unix(), // Nov 16
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  1500,
		CostPrice:             decimal.NewFromFloat(1.5),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerABC.BillingConfigID,
	}
	err = supportFeeA2.Create(db.GetDB())
	assert.NoError(err)
	supportFeeB := &entity.UserUnitCost{
		CostID:                "commit-test-b-01",
		TenantID:              customerBTenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 10).Unix(), // Nov 11
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  500,
		CostPrice:             decimal.NewFromFloat(0.5),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBBC.BillingConfigID,
	}
	err = supportFeeB.Create(db.GetDB())
	assert.NoError(err)

	// Call commitment metering for November (the month containing
	// datainit.BillingDate = 2021-11-01). Both plans are active from
	// Nov 1 with no end date, so measureDay = 30.
	// commitment = FixedChargePrice * measureDay / MonthDay = 29 * 30 / 30 = 29
	// Customer A: gap = 29 - 3.5 = 25.5 → cost = 25500
	measureDayA, costA, err := model.SendResellerCommitmentMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(30, measureDayA)
	assert.Equal(25500, costA)

	// Customer B: gap = 29 - 0.5 = 28.5 → cost = 28500
	// With the pre-refactor CalculateCost-on-partner path, this would
	// have included A's 3.5 spend (total costPrice=4.0) and produced
	// 25000 — silently undercharging B's commitment gap.
	measureDayB, costB, err := model.SendResellerCommitmentMeteringIfNeed(bindedInfoB.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(30, measureDayB)
	assert.Equal(28500, costB)

	// Idempotency: second call for the same customer/month returns 0.
	_, costA2, err := model.SendResellerCommitmentMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(0, costA2)

	// Both commitment rows land on the reseller's billing config.
	var resellerCommitRows []entity.UserUnitCost
	err = entity.NewUserUnitCostQuerySet(db.GetDB()).
		TenantIDEq(resellerTenantID).
		BindedBillingConfigIDEq(resellerBC.BillingConfigID).
		UnitEq(constant.CommintmentPrice_SKU).
		All(&resellerCommitRows)
	assert.NoError(err)
	assert.Len(resellerCommitRows, 2)

	// Each commitment row carries the per-customer label so the
	// partner can attribute the gap back to the source customer.
	for _, row := range resellerCommitRows {
		tidStr, ok := row.Labels[constant.MarketplaceResellerManagedCustomerIDLabel]
		assert.True(ok, "reseller commitment row should carry MarketplaceResellerManagedCustomerIDLabel, got labels=%v", row.Labels)
		if ok {
			tid, err := strconv.ParseUint(tidStr, 10, 64)
			assert.NoError(err)
			assert.True(tid == uint64(customerATenantID) || tid == uint64(customerBTenantID),
				"label customer ID should be one of the source customers, got %d", tid)
		}
	}
}

// TestSendResaleOneToNTieredPricing verifies that tiered-pricing
// SendResellerNormalMeteringIfNeed tracks each Resale customer's
// cumulative usage independently. Two customers under the same
// ResalePartner have different usage patterns; tier advancement and
// cost must be driven by each customer's own cumulative usage, not
// the partner's aggregate or the other customer's records.
func (s *SupportPlanTestSuite) TestSendResaleOneToNTieredPricing() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	bcModel := &billingconfig.BillingConfigModel{}

	customerATenantID := uint(3)
	customerBTenantID := uint(4)
	resellerTenantID := uint(5)

	timeutils.GetNowFunc = func() time.Time { return datainit.BillingDate }

	customerABC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerATenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	customerBBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: customerBTenantID,
		Type:     entity.BillingConfigType_ResaleCustomer,
	})
	assert.NoError(err)
	resellerBC, err := bcModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID: resellerTenantID,
		Type:     entity.BillingConfigType_ResalePartner,
	})
	assert.NoError(err)

	tests.MockCentralHandler()
	paymenthandler.SetPaymentHandler(&impl.PaymentHandlerImpl{})
	defer tests.MockPaymentHandler()

	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerATenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerATenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerABC.BillingConfigID,
	})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(customerBTenantID, &forms.PaymentMethodCreateQuery{
		TenantID:              customerBTenantID,
		PaymentType:           entity.PaymentType_Resale,
		PaymentCode:           fmt.Sprint(resellerTenantID),
		SetUpPaymentCode:      true,
		BindedBillingConfigID: &customerBBC.BillingConfigID,
	})
	assert.NoError(err)

	for _, c := range []struct {
		tenantID        uint
		billingConfigID uint
	}{
		{customerATenantID, customerABC.BillingConfigID},
		{customerBTenantID, customerBBC.BillingConfigID},
		{resellerTenantID, resellerBC.BillingConfigID},
	} {
		id := c.billingConfigID
		_, err = (&billing.BindedPackageModel{}).BindPackageExtra(c.tenantID, &forms.BindedPackageInfoInput{
			PackageID:             datainit.NoUsePackageId,
			DiscountEnabled:       true,
			InvoicePayable:        true,
			BindedBillingConfigID: &id,
		})
		assert.NoError(err)
	}

	upperBoundV1 := decimal.NewFromInt(200)
	upperBoundV2 := decimal.NewFromInt(400)
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:   decimal.NewFromInt(799),
		Type:               entity.SupportPlanType_TieredPrice,
		Public:             true,
		Level:              entity.SupportPlanLevel_Premium,
		IsSupportFreeTrial: false,
		TieredPriceRules: []forms.TieredPriceRule{
			{
				LowerBound:             decimal.NewFromInt(0),
				UpperBound:             &upperBoundV1,
				SupportPricePercentage: decimal.NewFromFloat(0.4),
			},
			{
				LowerBound:             upperBoundV1,
				UpperBound:             &upperBoundV2,
				SupportPricePercentage: decimal.NewFromFloat(0.3),
			},
			{
				LowerBound:             upperBoundV2,
				UpperBound:             nil,
				SupportPricePercentage: decimal.NewFromFloat(0.2),
			},
		},
	})
	assert.NoError(err)

	bindedInfoA, err := model.BindSupportPlanForPayableCustomer(customerATenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	bindedInfoB, err := model.BindSupportPlanForPayableCustomer(customerBTenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	// -- Day 1 cost rows --
	// Customer A: costPrice=100 → usage base=100000, tier 1 (0-200)
	costA1 := &entity.UserUnitCost{
		CostID:                "tiered-resale-a-01",
		TenantID:              customerATenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(),
		Unit:                  datainit.TestUnit,
		Cost:                  100,
		CostPrice:             decimal.NewFromInt(100),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerABC.BillingConfigID,
	}
	err = costA1.Create(db.GetDB())
	assert.NoError(err)
	// Customer B: costPrice=50 → usage base=50000, tier 1
	costB1 := &entity.UserUnitCost{
		CostID:                "tiered-resale-b-01",
		TenantID:              customerBTenantID,
		CostTimeStamp:         datainit.BillingDate.Add(time.Second).Unix(),
		Unit:                  datainit.TestUnit,
		Cost:                  50,
		CostPrice:             decimal.NewFromInt(50),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerBBC.BillingConfigID,
	}
	err = costB1.Create(db.GetDB())
	assert.NoError(err)

	// Day 1 customer A: 100 * 1000 * 0.4 = 40000 (tier 1)
	costDay1A, err := model.SendResellerNormalMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(40000, costDay1A)

	// Day 1 customer B: 50 * 1000 * 0.4 = 20000 (tier 1)
	// If B's usage were read from a shared partner cumulative, this
	// would be 150*1000*0.4=60000 (wrong).
	costDay1B, err := model.SendResellerNormalMeteringIfNeed(bindedInfoB.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(20000, costDay1B)

	// Day 2: no new cost rows for either customer → 0
	costDay2A, err := model.SendResellerNormalMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 1))
	assert.NoError(err)
	assert.Equal(0, costDay2A)

	costDay2B, err := model.SendResellerNormalMeteringIfNeed(bindedInfoB.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 1))
	assert.NoError(err)
	assert.Equal(0, costDay2B)

	// -- Day 3: Customer A adds 200 more cost --
	costA3 := &entity.UserUnitCost{
		CostID:                "tiered-resale-a-03",
		TenantID:              customerATenantID,
		CostTimeStamp:         datainit.BillingDate.AddDate(0, 0, 2).Add(time.Second).Unix(),
		Unit:                  datainit.TestUnit,
		Cost:                  200,
		CostPrice:             decimal.NewFromInt(200),
		Status:                entity.UserUnitCostStatus_DONE,
		BindedBillingConfigID: customerABC.BillingConfigID,
	}
	err = costA3.Create(db.GetDB())
	assert.NoError(err)

	// Day 3 customer A: cumulative=300 (100→200 tier1, 200→300 tier2)
	// Incremental: (200-100)*1000*0.4 + (300-200)*1000*0.3 = 40000 + 30000 = 70000
	costDay3A, err := model.SendResellerNormalMeteringIfNeed(bindedInfoA.BindedSupportPlanID, datainit.BillingDate.AddDate(0, 0, 2))
	assert.NoError(err)
	assert.Equal(70000, costDay3A)

	// Verify all tiered-price rows landed on the reseller's billing
	// config. Zero-cost days (no new usage) still emit a
	// SupportPlanMeteringRecords row for cumulative tier tracking but
	// SecondaryMeasure skips the UserUnitCost write when cost=0, so
	// only 3 non-zero rows appear: day-1 A (40000), day-1 B (20000),
	// day-3 A (70000).
	var tieredRows []entity.UserUnitCost
	err = entity.NewUserUnitCostQuerySet(db.GetDB()).
		TenantIDEq(resellerTenantID).
		BindedBillingConfigIDEq(resellerBC.BillingConfigID).
		UnitEq(constant.TieredPrice_SKU).
		All(&tieredRows)
	assert.NoError(err)
	assert.Len(tieredRows, 3)

	// Each row carries the per-customer label so the partner can
	// attribute the tiered-price charge to the source customer.
	for _, row := range tieredRows {
		tidStr, ok := row.Labels[constant.MarketplaceResellerManagedCustomerIDLabel]
		assert.True(ok, "reseller tiered row should carry MarketplaceResellerManagedCustomerIDLabel, got labels=%v", row.Labels)
		if ok {
			tid, err := strconv.ParseUint(tidStr, 10, 64)
			assert.NoError(err)
			assert.True(tid == uint64(customerATenantID) || tid == uint64(customerBTenantID),
				"label customer ID should be one of the source customers, got %d", tid)
		}
	}
}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWhenClosed() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(15),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	billingConfigID := uint(1)

	err = datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	_, err = (&billing.BindedPackageModel{}).BindPackageExtra(tenantID, &forms.BindedPackageInfoInput{
		PackageID:             datainit.NoUsePackageId,
		DiscountEnabled:       true,
		InvoicePayable:        true,
		BindedBillingConfigID: &billingConfigID,
	})
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.Add(time.Hour * 25)
	}

	_, err = model.CancelBindedSupportPlan(bindedInfo.BindedSupportPlanID)
	assert.NoError(err)
	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("0.5"))
}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithPercentage() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	_, err = model.SendNormalMetering(bindedInfo.TenantID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("1.5"))
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("0.5"))
}

func newCreditsPackageWhihTime(tenantID uint, credits int64, date time.Time, generalUnitFilter *forms.UnitFilter) (ret *forms.CreditsPackage, unitFilter *forms.UnitFilter, err error) {
	if nil == generalUnitFilter {
		generalUnitFilter, err = (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
			UnitFilterSlim: forms.UnitFilterSlim{Name: "General", Description: "General"},
			General:        true,
		})
	}
	billingConfigID := uint(1)
	creditsPackage := &forms.CreditsPackage{
		StartDate:             timeutils.GetMonthDate(date),
		ExpiredDate:           date.AddDate(0, 1, 0),
		TotalCredits:          decimal.NewFromInt(credits),
		Usage:                 entity.CreditsPackageType_General,
		UnitFilters:           []*forms.UnitFilterSlim{{TypeID: generalUnitFilter.FilterID, FilterID: generalUnitFilter.FilterID}},
		DiscountEnabled:       true,
		BindedBillingConfigID: &billingConfigID,
	}
	creditsPackage, err = creditsPackageModel.CreateCreditsPackage(tenantID, creditsPackage)
	return creditsPackage, generalUnitFilter, err

}
func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithPercentageWithCostUseCredits() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)

	newCreditsPackageWhihTime(tenantID, 1, datainit.BillingDate, nil)

	measureModel := &billing.MeasureModel{}

	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	_, err = model.SendNormalMetering(bindedInfo.TenantID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("2"))
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("1"))
}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithPercentageWithAllUseCredits() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)

	creditsPackage, _, _ := newCreditsPackageWhihTime(tenantID, 10, datainit.BillingDate, nil)

	measureModel := &billing.MeasureModel{}

	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	_, err = model.SendNormalMetering(bindedInfo.TenantID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	bindCreditsPackage, err := (&billing.BindedPackageModel{}).GetBindedPackage(*creditsPackage.BindedPackageID)
	assert.Equal(bindCreditsPackage.RemainingCredits, MustFromString("8"))

	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("1"))
	assert.Equal(res.CreditsCosts.CostPrice, MustFromString("2"))
	assert.Equal(res.CreditsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("1"))
}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithPercentageWithOnlySupportPlanUseCredits() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)
	generalUnitFilter, err := (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "GeneralSupportPlan", Description: "GeneralSupportPlan"},
		UnitKeyPattern: "^support-plan.*$",
		General:        false,
	})
	assert.NoError(err)

	creditsPackage, _, _ := newCreditsPackageWhihTime(tenantID, 10, datainit.BillingDate, generalUnitFilter)

	measureModel := &billing.MeasureModel{}

	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	_, err = model.SendNormalMetering(bindedInfo.TenantID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	bindCreditsPackage, err := (&billing.BindedPackageModel{}).GetBindedPackage(*creditsPackage.BindedPackageID)
	//pp.Print(bindCreditsPackage)
	assert.Equal(bindCreditsPackage.RemainingCredits, MustFromString("9"))

	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("2"))
	assert.Equal(res.CreditsCosts.CostPrice, MustFromString("1"))
	assert.Equal(res.CreditsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("1"))
}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithPercentageCommitment() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	_, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour))
	assert.NoError(err)
	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("1.5"))
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("0.5"))

	_, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour*24))
	assert.NoError(err)
	res, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("1.5"))
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("0.5"))

	_, err = model.SendNormalMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate.Add(time.Hour*48))
	assert.NoError(err)
	res, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CostPrice, MustFromString("1.5"))
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("0.5"))

	cost := &entity.UserUnitCost{
		CostID:                "xas@222",
		TenantID:              tenantID,
		CostTimeStamp:         datainit.BillingDate.Unix(),
		Unit:                  constant.PercentagePrice_SKU,
		Cost:                  20,
		CostPrice:             decimal.NewFromInt(200),
		Status:                entity.UserUnitCostStatus_DONE,
		ConsumedSource:        entity.ConsumedSource_Credits,
		BindedBillingConfigID: 1,
	}
	err = cost.Create(db.GetDB())
	assert.NoError(err)

	_, _, err = model.SendCommitmentMetering(bindedInfo.BindedSupportPlanID, datainit.BillingDate)
	assert.NoError(err)
	res, err = (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	//pp.Print(res)
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("0.5"))
	assert.Equal(res.CreditsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, decimal.NewFromInt(200))

}

func (s *SupportPlanTestSuite) TestSendNormalMeasuresWithCloseCommitment() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(15),
		SupportPricePercentage: MustFromString("0.5"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		Level:                  entity.SupportPlanLevel_Premium,
	})
	assert.NoError(err)
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	bindedInfo, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate.Add(time.Hour * 24 * 15)
	}

	_, err = model.CancelBindedSupportPlan(bindedInfo.SupportPlanID)
	assert.NoError(err)

	res, err := (&costexplorer.CostExplorerModel{}).CalculateCost(tenantID, 1, &forms.CostExplorerQuery{})
	assert.NoError(err)
	assert.Equal(res.InvoiceCostsCosts.CategoryUnitsCosts[marketing.SKUCategory].CostPrice, MustFromString("7.5"))
}

func (s *SupportPlanTestSuite) TestListTenantsBindSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(15),
		Type:             entity.SupportPlanType_FixedPrice,
		Public:           true,
		Level:            entity.SupportPlanLevel_Standard,
	})

	assert.NoError(err)
	tenantID := uint(1)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}

	err = datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)

	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)

	res, err := model.ListTenantsBindedSupportPlan(tenantID)
	assert.NoError(err)
	assert.Len(res, 1)
	assert.Equal(res[0].Type, entity.SupportPlanType_FixedPrice)

	tenantIDs, err := model.GetBindedSupportPlanTenantIDs()
	assert.Nil(err)
	assert.Len(tenantIDs, 1)

	_, err = model.CancelBindedSupportPlan(res[0].BindedSupportPlanID)
	assert.NoError(err)

	res, err = model.ListTenantsBindedSupportPlan(tenantID)
	assert.NoError(err)
	assert.Len(res, 1)

	tenantIDs, err = model.GetBindedSupportPlanTenantIDs()
	assert.Nil(err)
	assert.Len(tenantIDs, 0)
}

type PayableMockPaymentHandler struct {
	payable bool
	tests.MockPaymentHandlerImpl
}

type MockCentralHandlerImpl struct {
	centralhandler.CentralHandler
	tenants []forms.Tenant
}

func (i *MockCentralHandlerImpl) GetTenant(tenantID uint) (*forms.Tenant, error) {
	for _, t := range i.tenants {
		if t.TenantID == tenantID {
			return &t, nil
		}
	}
	return &forms.Tenant{
		TenantID: tenantID,
		Status:   centralhandler.TenantStatusActive,
	}, nil
}

func (i *MockCentralHandlerImpl) SetAllTenants(tenants []forms.Tenant) {
	i.tenants = tenants
}

func (*MockCentralHandlerImpl) Get(userID uint) (*forms.User, error) {
	return &forms.User{
		UserID: userID,
	}, nil
}

func (i *MockCentralHandlerImpl) GetMspCustomerScheduleInfo(customerID uint) (*forms.MspCustomerScheduleInfo, error) {
	for _, t := range i.tenants {
		if t.TenantID == customerID && t.ThirdPartyAccountProvider == constant.MSPAccountProvider {
			mspTenantID, err := strconv.ParseUint(t.ThirdPartyAccountCode, 10, 64)
			if err != nil {
				return nil, err
			}
			return &forms.MspCustomerScheduleInfo{
				CustomerTenantID: uint64(customerID),
				MspTenantID:      mspTenantID,
			}, nil
		}
	}
	return nil, billing_errors.ObjectNotExistsError
}

func (*MockCentralHandlerImpl) GetProjectInfo(projectID uint) (*forms.Project, error) {
	return &forms.Project{
		ID:   projectID,
		Name: "project",
	}, nil
}

func mockCentral() {
	central := &MockCentralHandlerImpl{}
	central.SetAllTenants([]forms.Tenant{
		{
			TenantID:                  210,
			Status:                    centralhandler.TenantStatusActive,
			ThirdPartyAccountProvider: constant.MSPAccountProvider,
			ThirdPartyAccountCode:     "211",
			IsMspCustomer:             true,
		},
		{
			TenantID: 211,
			Status:   centralhandler.TenantStatusActive,
			IsMsp:    true,
		},
		{
			TenantID:       2222,
			Status:         centralhandler.TenantStatusActive,
			IsCPPOReseller: true,
		},
		{
			TenantID:       2777,
			Status:         centralhandler.TenantStatusActive,
			IsCPPOCustomer: true,
		},
	})
	centralhandler.SetCentralHandler(central)
}

func (s *SupportPlanTestSuite) TestMspCustomerSupportPlanHandler() {
	var err error
	assert := s.Assert()
	datainit.CreateBillingConfig(210, entity.BillingConfigType_MSPCustomer)

	err = datainit.CreatePaymentMock(210)

	tenantID := uint(210)
	mockCentral()
	model := &marketing.SupportPlanModel{}
	eb, err := tests.NewLocalEventBusHandler()
	panicIf.Error(err)
	panicIf.Error(eventbushandler.Get().Close())
	eventbushandler.Set(eb)
	err = model.RegisterSupportPlanEventHandler()
	ch := make(chan struct{})
	err = eventbushandler.Get().RegisterEventHandleFunc(
		constant.PaymentMethodChangedEventType,
		"test-1",
		forms.Tenant{},
		func(event *eventbushandler.Event) error {
			close(ch)
			return nil
		},
	)
	panicIf.Error(eventbushandler.Get().Run())

	err = (&paymentsub.PaymentModel{}).CreateOrReplacePaymentMethodAfterMarketplaceBind(tenantID)
	assert.NoError(err)

	select {
	case <-ch:
	case <-time.After(5 * time.Second):
		panic("timeout")
	}

}

func (s *SupportPlanTestSuite) TestMultiBindedSupportPlans() {
	assert := s.Assert()
	timeutils.SetMockNow(datainit.BillingDate)
	tenantID := uint(1)
	model := &marketing.SupportPlanModel{}
	err := model.CreateSupportPlanUnits()
	assert.NoError(err)
	datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)
	datainit.CreatePaymentMock(tenantID)
	datainit.CreatePricePackageAndBind(tenantID)
	datainit.CreateDefaultUnits()
	fixedPlanInput := forms.SupportPlan{
		FixedChargePrice: decimal.NewFromInt(350),
		Type:             entity.SupportPlanType_FixedPrice,
		Level:            entity.SupportPlanLevel_Standard,
	}
	percentagePlanInput1 := forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(350),
		SupportPricePercentage: decimal.RequireFromString("0.35"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Level:                  entity.SupportPlanLevel_Premium,
	}
	percentagePlanInput2 := forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(250),
		SupportPricePercentage: decimal.RequireFromString("0.25"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Level:                  entity.SupportPlanLevel_Standard,
	}
	fixedPlan, err := model.CreateSupportPlan(&fixedPlanInput)
	assert.NoError(err)
	percentagePlan1, err := model.CreateSupportPlan(&percentagePlanInput1)
	assert.NoError(err)
	percentagePlan2, err := model.CreateSupportPlan(&percentagePlanInput2)
	assert.NoError(err)
	planAfter1stDec, err := model.BindSupportPlanForPayableCustomer(tenantID, fixedPlan.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 1, 0)),
	})
	assert.NoError(err)
	assert.Equal(planAfter1stDec.Status, entity.BindedSupportPlanStatus_TOBEACTIVATED)
	planAfter15thNov, err := model.BindSupportPlanForPayableCustomer(tenantID, percentagePlan2.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 15)),
		EndDate:   timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 1, 0).Add(-time.Second)),
	})
	assert.NoError(err)
	assert.Equal(planAfter15thNov.Status, entity.BindedSupportPlanStatus_TOBEACTIVATED)
	planAfter1stNov, err := model.BindSupportPlanForPayableCustomer(tenantID, percentagePlan1.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 0)),
		EndDate:   timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 15).Add(-time.Second)),
	})
	assert.Equal(planAfter1stNov.Status, entity.BindedSupportPlanStatus_ACTIVE)
	assert.NoError(err)
	for i := 0; i != 36; i++ {
		cost, err := (&billing.MeasureModel{}).CostUnits(tenantID, &forms.UnitCosts{
			Unit:          datainit.AWSNodeUnit,
			Cost:          100,
			CostTimeStamp: datainit.BillingDate.AddDate(0, 0, i).Unix(),
		})
		assert.Nil(err)
		assert.Equal(decimal.NewFromInt(1), cost.CostPrice)
		assert.Equal(entity.ConsumedSource_Invoice, cost.ConsumedSource)
	}
	offset := 2
	for i := 0; i != 30; i++ {
		date := datainit.BillingDate.AddDate(0, 0, i+offset)
		for j := time.Duration(0); j != 24; j += 3 {
			mockNow := date.Add(j * time.Hour)
			timeutils.SetMockNow(mockNow)
			workers.SupportPlanWorkerByTenant(tenantID)
		}
		var costs []entity.UserUnitCost
		err = entity.NewUserUnitCostQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 0, i).Unix()).UnitEq(constant.PercentagePrice_SKU).All(&costs)
		assert.NoError(err)
		assert.Len(costs, 1)
		if i < 15 {
			assert.Equal(decimal.RequireFromString("0.35"), costs[0].CostPrice)
		} else {
			assert.Equal(decimal.RequireFromString("0.25"), costs[0].CostPrice)
		}
		planAfter1stNov, err = model.GetBindedSupportPlan(planAfter1stNov.BindedSupportPlanID)
		assert.NoError(err)
		now := datainit.BillingDate.AddDate(0, 0, i+offset).Add(86399 * time.Second)
		if now.Before(*planAfter1stNov.ExpiredDate) {
			planAfter1stNov.Status = entity.BindedSupportPlanStatus_ACTIVE
		} else {
			planAfter1stNov.Status = entity.BindedSupportPlanStatus_Expired
		}
		planAfter15thNov, err = model.GetBindedSupportPlan(planAfter15thNov.BindedSupportPlanID)
		assert.NoError(err)
		if now.Before(planAfter15thNov.ActiveDate) {
			planAfter15thNov.Status = entity.BindedSupportPlanStatus_TOBEACTIVATED
		} else {
			planAfter15thNov.Status = entity.BindedSupportPlanStatus_ACTIVE
		}
	}
	var costs []entity.UserUnitCost
	err = entity.NewUserUnitCostQuerySet(db.GetDB()).UnitEq(constant.CommintmentPrice_SKU).OrderDescByCost().All(&costs)
	assert.NoError(err)
	assert.Len(costs, 2)
	assert.Equal(decimal.NewFromInt(15*350).Div(decimal.NewFromInt(30)).Sub(decimal.RequireFromString("5.25")), costs[0].RawPrice)
	assert.Equal(decimal.NewFromInt(15*250).Div(decimal.NewFromInt(30)).Sub(decimal.RequireFromString("3.75")), costs[1].RawPrice)
	for i := 0; i != 1; i++ {
		mockNow := datainit.BillingDate.AddDate(0, 1, i+offset).Add(19 * time.Hour)
		timeutils.SetMockNow(mockNow)
		workers.SupportPlanWorkerByTenant(tenantID)
		var cost entity.UserUnitCost
		err = entity.NewUserUnitCostQuerySet(db.GetDB()).CostTimeStampEq(datainit.BillingDate.AddDate(0, 1, i).Unix()).UnitEq(constant.FixedPrice_SKU).One(&cost)
		assert.NoError(err)
		assert.Equal(decimal.RequireFromString("11.67"), cost.CostPrice)
	}
	timeutils.SetMockNow(datainit.BillingDate.AddDate(0, 1, 0).Add(time.Hour*config.Get().GetDuration(config.BindedSupportPlanInactiveDelayHour) + time.Hour))
	workers.SupportPlanWorkerByTenant(tenantID)
	planAfter1stNov, err = model.GetBindedSupportPlan(planAfter1stNov.BindedSupportPlanID)
	assert.NoError(err)
	assert.Equal(entity.BindedSupportPlanStatus_CANCELED, planAfter1stNov.Status)
	planAfter15thNov, err = model.GetBindedSupportPlan(planAfter15thNov.BindedSupportPlanID)
	assert.NoError(err)
	assert.Equal(entity.BindedSupportPlanStatus_CANCELED, planAfter15thNov.Status)
}

func (s *SupportPlanTestSuite) TestMspSupportPlanChangedSync() {
	assert := s.Assert()
	var err error
	mockCentral()
	billingConfigModel := &billingconfig.BillingConfigModel{}
	_, err = billingConfigModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID:        211,
		StartActiveTime: timeutils.GetNowAddr(),
		Type:            entity.BillingConfigType_MSP,
		Status:          entity.BillingConfigStatus_Active,
	})
	assert.NoError(err)
	_, err = billingConfigModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID:        210,
		StartActiveTime: timeutils.GetNowAddr(),
		Type:            entity.BillingConfigType_MSPCustomer,
		Status:          entity.BillingConfigStatus_Active,
	})
	assert.NoError(err)
	paymenthandler.SetPaymentHandler(&impl.PaymentHandlerImpl{})
	defer tests.MockPaymentHandler()
	customerBillingConfigID, err := billingConfigModel.GetActiveBillingConfigID(210)
	assert.NoError(err)
	err = (&paymentsub.PaymentModel{}).PartnerPaymentProcessOnScheduleActivate(210, customerBillingConfigID)
	assert.NoError(err)
	billingConfigID, err := billingConfigModel.GetActiveBillingConfigID(211)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(211, &forms.PaymentMethodCreateQuery{PaymentType: entity.PaymentType_WireTransfer, BindedBillingConfigID: &billingConfigID})
	assert.NoError(err)
	supportPlanModel := &marketing.SupportPlanModel{}
	info, err := supportPlanModel.CreateSupportPlan(&forms.SupportPlan{
		Public: true,
		Type:   entity.SupportPlanType_FixedPrice,
	})
	assert.NoError(err)
	_, err = supportPlanModel.BindSupportPlanForPayableCustomer(211, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(timeutils.GetDayDate(timeutils.GetNow()))})
	assert.NoError(err)
	config.Get().Set(config.JSMEnableSender, false)
	err = supportPlanModel.MspSupportPlanChangedSync(211)
	assert.NoError(err)
	tenantID := uint(2222)
	_, err = billingConfigModel.CreateBillingConfigAndActive(forms.BillingConfig{
		TenantID:        tenantID,
		StartActiveTime: timeutils.GetNowAddr(),
		Type:            entity.BillingConfigType_MarketplaceReseller,
		Status:          entity.BillingConfigStatus_Active,
	})
	assert.NoError(err)
	billingConfig, err := billingConfigModel.GetActiveBillingConfig(tenantID)
	assert.NoError(err)
	err = supportPlanModel.MspCustomerSyncSupportPlan(tenantID, billingConfig.BillingConfigID)
	assert.NoError(err)
}

func (s *SupportPlanTestSuite) TestCreateOrUpdatePublicSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	// case-1: forbiden to create private supportPlan
	_, err := model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Developer, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Developer,
		FixedChargePrice:       decimal.NewFromFloat(29),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
	})
	assert.ErrorIs(err, billing_errors.InvalidInputError)

	// case-2: create public support plan
	standardPlan, err := model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Standard, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Standard,
		FixedChargePrice:       decimal.NewFromFloat(49),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
	})
	assert.NoError(err)
	publicPlans, err := model.ListSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 1)
	assert.Equal(standardPlan.Level, entity.SupportPlanLevel_Standard)
	assert.Equal(standardPlan.FixedChargePrice, decimal.NewFromFloat(49))
	assert.Equal(standardPlan.IsSupportFreeTrial, false)

	// case-3: update public support plan
	standardPlan, err = model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Standard, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Standard,
		FixedChargePrice:       decimal.NewFromFloat(29),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)

	publicPlans, err = model.ListSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 1)
	assert.Equal(standardPlan.Level, entity.SupportPlanLevel_Standard)
	assert.Equal(standardPlan.FixedChargePrice, decimal.NewFromFloat(29))
	assert.Equal(standardPlan.IsSupportFreeTrial, true)

	publicPlans, err = model.ListPublicSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 1)

	// case-4: delete and list public SupportPlan
	err = model.DeleteSupportPlan(standardPlan.SupportPlanID)
	assert.NoError(err)
	publicPlans, err = model.ListPublicSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 0)

	// case-5: create a new public support plan
	deceloperPlan, err := model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Developer, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Developer,
		FixedChargePrice:       decimal.NewFromFloat(29),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		IsSupportFreeTrial:     false,
	})
	assert.NoError(err)

	publicPlans, err = model.ListSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 1)

	publicPlans, err = model.ListPublicSupportPlan()
	assert.NoError(err)
	assert.Equal(len(publicPlans), 1)

	assert.Equal(deceloperPlan.Level, entity.SupportPlanLevel_Developer)
	assert.Equal(deceloperPlan.FixedChargePrice, decimal.NewFromFloat(29))
	assert.Equal(deceloperPlan.IsSupportFreeTrial, false)
}

func (s *SupportPlanTestSuite) TestListAllActiveBindedSupportPlan() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	plan1, err := model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Developer, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Developer,
		FixedChargePrice:       decimal.NewFromFloat(29),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
	})
	assert.NoError(err)
	plan2, err := model.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Premium, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Premium,
		FixedChargePrice:       decimal.NewFromFloat(699),
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
	})
	assert.NoError(err)

	billingconfig2, err := datainit.CreateBillingConfig(uint(2), entity.BillingConfigType_DirectCustomer)
	assert.NoError(err)
	billingconfig3, err := datainit.CreateBillingConfig(uint(3), entity.BillingConfigType_DirectCustomer)
	assert.NoError(err)
	err = datainit.CreatePaymentMock(uint(1))
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(uint(2), &forms.PaymentMethodCreateQuery{PaymentType: entity.PaymentType_AWS, BindedBillingConfigID: &billingconfig2.BillingConfigID})
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(uint(3), &forms.PaymentMethodCreateQuery{PaymentType: entity.PaymentType_AWS, BindedBillingConfigID: &billingconfig3.BillingConfigID})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}
	_, err = model.BindSupportPlanForPayableCustomer(uint(1), plan1.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetNowAddr(), // "2021-11-01", active
	})
	assert.NoError(err)
	bindedPlan2, err := model.BindSupportPlanForPayableCustomer(uint(2), plan2.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetNowAddr(), // "2021-11-01", active
	})
	assert.NoError(err)
	_, err = model.BindSupportPlanForPayableCustomer(uint(3), plan2.SupportPlanID, forms.DateInterval{
		StartDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 1)), // "2021-11-02", to be active
	})
	assert.NoError(err)
	bindedPlanList, err := model.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Developer)
	assert.NoError(err)
	assert.Equal(len(bindedPlanList), 1)
	bindedPlanList, err = model.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Premium)
	assert.NoError(err)
	assert.Equal(len(bindedPlanList), 1)
	bindedPlanList, err = model.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Premium, entity.SupportPlanLevel_Developer)
	assert.NoError(err)
	assert.Equal(len(bindedPlanList), 2)

	// cancel bindedPlan2
	_, err = model.CancelBindedSupportPlan(bindedPlan2.BindedSupportPlanID)
	assert.NoError(err)
	bindedPlanList, err = model.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Premium)
	assert.NoError(err)
	assert.Equal(len(bindedPlanList), 0)
	bindedPlanList, err = model.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Premium, entity.SupportPlanLevel_Developer)
	assert.NoError(err)
	assert.Equal(len(bindedPlanList), 1)
	assert.Equal(bindedPlanList[0].Level, entity.SupportPlanLevel_Developer)
}

func (s *SupportPlanTestSuite) TestCPPOCustomerBindSupportPlan() {
	assert := s.Assert()
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert.NoError(datainit.CreateCompany())
	timeutils.SetMockNow(datainit.BillingDate)
	supportPlanModel := &marketing.SupportPlanModel{}
	info, err := supportPlanModel.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(49),
		SupportPricePercentage: decimal.RequireFromString("0.2"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Standard,
		IsSupportFreeTrial:     false,
	})
	assert.NoError(err)
	var customerTenantID uint = 10
	var resellerTenantID uint = 20
	billingConfigModel := &billingconfig.BillingConfigModel{}
	_, err = billingConfigModel.CreateBillingConfigAndActive(forms.BillingConfig{TenantID: customerTenantID, Type: entity.BillingConfigType_DirectCustomer})
	assert.NoError(err)
	_, err = billingConfigModel.CreateBillingConfigAndActive(forms.BillingConfig{TenantID: resellerTenantID, Type: entity.BillingConfigType_DirectCustomer})
	assert.NoError(err)
	customerDirectBillingConfigID, err := billingConfigModel.GetActiveBillingConfigID(customerTenantID)
	assert.NoError(err)
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(
		customerTenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &customerDirectBillingConfigID,
		},
	)
	assert.NoError(err)
	cppoCreateQuery := forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:      customerTenantID,
			EffectiveDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(0, 0, 10)),
			PaymentCode:   "ADCXA12345",
			ResellerId:    resellerTenantID,
			SupportPlanInfo: &forms.SupportPlanInput{
				SupportPlan: *info,
				ExpiredDate: timeutils.GetTimeAddr(datainit.BillingDate.AddDate(1, 0, 0)),
			}}, PaymentSubCode: "222222",
	}
	customerBillingConfigID, paymentBillingConfigID, err := billingConfigModel.CreateMarketplaceResellerBillingConfig(cppoCreateQuery.MarketplaceResellerBillingConfig)
	assert.NoError(err)
	legacySupport, err := supportPlanModel.BindSupportPlanForPayableCustomer(customerTenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetTimeAddr(datainit.BillingDate)})
	assert.NoError(err)
	assert.Equal(entity.BindedSupportPlanStatus_ACTIVE, legacySupport.Status)
	timeutils.SetMockNow(cppoCreateQuery.EffectiveDate.Add(-time.Second * 60))
	workers.BillingConfigScheduledExecution()
	resellerBillingConfig, err := billingConfigModel.GetBillingConfig(paymentBillingConfigID)
	assert.NoError(err)
	assert.Equal(entity.BillingConfigStatus_Managing, resellerBillingConfig.Status)
	customerBillingConfig, err := billingConfigModel.GetBillingConfig(customerBillingConfigID)
	assert.NoError(err)
	assert.Equal(entity.BillingConfigStatus_Active, customerBillingConfig.Status)
	assert.NotEqual(info.SupportPlanID, customerBillingConfig.SupportPlanID)
	assert.NotNil(customerBillingConfig.SupportPlanExpiredDate)
	assert.Equal(*cppoCreateQuery.SupportPlanInfo.ExpiredDate, *customerBillingConfig.SupportPlanExpiredDate)
	err = supportPlanModel.SupportPlanProcessOnBillingConfigSwitched(paymentBillingConfigID)
	assert.NoError(err)
	err = supportPlanModel.SupportPlanProcessOnBillingConfigSwitched(customerBillingConfigID)
	assert.NoError(err)
	legacySupport, err = supportPlanModel.GetBindedSupportPlan(legacySupport.BindedSupportPlanID)
	assert.NoError(err)
	assert.Equal(entity.BindedSupportPlanStatus_CANCELED, legacySupport.Status)

	timeutils.SetMockNow(*cppoCreateQuery.EffectiveDate)
	workers.SupportPlanWorkerByTenant(customerTenantID)
	newSupportInfo, err := supportPlanModel.GetTenantActiveBindedPlan(customerTenantID)
	assert.NoError(err)
	assert.Equal(*cppoCreateQuery.EffectiveDate, newSupportInfo.ActiveDate)
	assert.Equal(*cppoCreateQuery.SupportPlanInfo.ExpiredDate, *newSupportInfo.ExpiredDate)
}

func (s *SupportPlanTestSuite) TestGetBindedSupportPlansEndingFreeTrialSoon() {
	assert := s.Assert()
	model := &marketing.SupportPlanModel{}
	tenantID := uint(1)

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

	info, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(29),
		SupportPricePercentage: MustFromString("0.1"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Developer,
		IsSupportFreeTrial:     true,
	})
	assert.NoError(err)

	now := datainit.BillingDate // now = "2021-11-01 00:00:00"

	// case-1: ActiveDate = now - 25天 = "2021-10-07 00:00:00"
	// 免费试用将在5天后结束（免费试用结束日期 = ActiveDate + 30天 = "2021-11-06 00:00:00"）
	// 查询范围：ActiveDate >= now - 25天 && ActiveDate < now - 24天
	// 即：2021-10-07 00:00:00 ≤ ActiveDate < 2021-10-08 00:00:00
	// 免费试用结束日期范围：now + 5天 ~ now + 6天（即2021-11-06 ~ 2021-11-07）
	// 应该被查询到
	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -25) // set now = "2021-10-07 00:00:00"
	}
	bindedInfo1, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo1.ActiveDate, now.AddDate(0, 0, -25))

	plans, err := model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	assert.Len(plans, 1)
	assert.Equal(plans[0].ID, bindedInfo1.BindedSupportPlanID)

	// case-2: 取消之前的绑定，重新绑定一个ActiveDate = now - 26天的
	// ActiveDate = "2021-10-06 00:00:00"，不在查询范围 [now-25天, now-24天)
	// 不应该被查询到
	_, err = model.CancelBindedSupportPlan(bindedInfo1.BindedSupportPlanID)
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -26) // set now = "2021-10-06 00:00:00"
	}
	bindedInfo2, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo2.ActiveDate, now.AddDate(0, 0, -26))

	plans, err = model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	assert.Len(plans, 0)

	// case-3: 取消之前的绑定，重新绑定一个ActiveDate = now - 24天的
	// ActiveDate = "2021-10-08 00:00:00"，不在查询范围（因为条件是 ActiveDate < now - 24天）
	// 不应该被查询到
	_, err = model.CancelBindedSupportPlan(bindedInfo2.BindedSupportPlanID)
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -24) // set now = "2021-10-08 00:00:00"
	}
	bindedInfo3, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo3.ActiveDate, now.AddDate(0, 0, -24))

	plans, err = model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	assert.Len(plans, 0)

	// case-4: 取消之前的绑定，创建一个IsEligibleForFreeTrial = false的计划
	// 不应该被查询到
	_, err = model.CancelBindedSupportPlan(bindedInfo3.BindedSupportPlanID)
	assert.NoError(err)
	newPlanInfo, err := model.CreateSupportPlan(&forms.SupportPlan{
		FixedChargePrice:       decimal.NewFromInt(49),
		SupportPricePercentage: MustFromString("0.1"),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 false,
		Level:                  entity.SupportPlanLevel_Standard,
		IsSupportFreeTrial:     false,
	})
	assert.NoError(err)

	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -25) // set now = "2021-10-07 00:00:00"
	}
	bindedInfo4, err := model.BindSupportPlanForPayableCustomer(tenantID, newPlanInfo.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	assert.Equal(bindedInfo4.ActiveDate, now.AddDate(0, 0, -25))
	assert.Equal(bindedInfo4.IsEligibleForFreeTrial, false)

	plans, err = model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	assert.Len(plans, 0)

	// case-5: 取消之前的绑定，绑定一个计划然后取消它
	// Status != ACTIVE，不应该被查询到
	_, err = model.CancelBindedSupportPlan(bindedInfo4.BindedSupportPlanID)
	assert.NoError(err)
	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -25) // set now = "2021-10-07 00:00:00"
	}
	bindedInfo5, err := model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)
	err = entity.NewBindedSupportPlanQuerySet(db.GetDB()).IDEq(bindedInfo5.BindedSupportPlanID).GetUpdater().SetStatus(entity.BindedSupportPlanStatus_CANCELED).SetClosedDate(&now).Update()
	assert.NoError(err)

	plans, err = model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	assert.Len(plans, 0)

	// case-6: 重新绑定一个ActiveDate = now - 25天 + 1小时的计划
	// ActiveDate = "2021-10-07 01:00:00"
	// 查询条件：ActiveDate >= "2021-10-07 00:00:00" && ActiveDate < "2021-10-08 00:00:00"
	// 应该被查询到
	// 注意：取消 bindedInfo5 后，系统会自动绑定一个默认的 Basic 支持计划，导致 IsEligibleForFreeTrial 为 false
	timeutils.GetNowFunc = func() time.Time {
		return now.AddDate(0, 0, -25).Add(time.Hour) // set now = "2021-10-07 01:00:00"
	}
	_, err = model.BindSupportPlanForPayableCustomer(tenantID, info.SupportPlanID, forms.DateInterval{StartDate: timeutils.GetNowAddr()})
	assert.NoError(err)

	plans, err = model.GetBindedSupportPlansEndingFreeTrialSoon(now)
	assert.NoError(err)
	// 由于之前多次绑定和取消，这个计划可能不再有免费试用资格
	// 所以查询结果可能是0个，这是正常的实现行为
	assert.Len(plans, 0)
}
