// Package migrate this only use in migrate data from v1 to v2, will delete after migrated data.
package migrate

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/unc"

	"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/infrahandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/creditriskcontrol"
	"github.com/pingcap/dbaas/billing2/model/marketing"
	"github.com/pingcap/dbaas/billing2/model/marketing/plan"
	"github.com/pingcap/dbaas/billing2/model/marketing/unit_filter"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"github.com/pingcap/dbaas/billing2/model/payment/spend_limit"
	"github.com/pingcap/dbaas/billing2/utils/jsm"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/pingcap/dbaas/billing2/utils/transformer"
	"github.com/shopspring/decimal"
	"github.com/stripe/stripe-go/v76"
	scclient "github.com/stripe/stripe-go/v76/client"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type MigrateModel struct{}

func (m *MigrateModel) CreateNewPackages() error {
	// must called first and keep orders to have same id config in central
	packageModel := &billing.PackageModel{}
	_, err := packageModel.CreatePackage(&forms.PackageInfo{
		Description:   "POC Package",
		CreditsEnable: true,
		CreditsOnly:   true,
		Labels:        map[string]string{},
		Discount:      decimal.NewFromInt(1),
	})
	if err != nil {
		return err
	}
	_, err = packageModel.CreatePackage(&forms.PackageInfo{
		Description:          "OnDemand Package",
		CreditsEnable:        false,
		CreditsOnly:          false,
		TenantMaxBindedTimes: 1,
		Labels:               map[string]string{},
		Discount:             decimal.RequireFromString("0.7"),
	})
	if err != nil {
		return err
	}
	_, err = packageModel.CreatePackage(&forms.PackageInfo{
		Description:          "DevTier Package",
		CreditsEnable:        true,
		CreditsOnly:          true,
		TenantMaxBindedTimes: 1,
		Labels: map[string]string{
			"devTier": "true",
		},
		Discount: decimal.NewFromInt(0),
	})
	if err != nil {
		return err
	}
	return nil
}

func (m *MigrateModel) CreateGlobalPricePackage() error {
	// should not be binded to customer, only for show price in dryrun
	packageModel := &billing.PackageModel{}
	_, err := packageModel.CreatePackage(&forms.PackageInfo{
		Description:   "Global Price Package",
		CreditsEnable: false,
		CreditsOnly:   false,
		Discount:      decimal.NewFromInt(1),
	})
	return err
}

func (*MigrateModel) CreateMigratePackageForLegacyData() (uint, error) {
	unitModel := &billing.UnitModel{}

	_, err := unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "LegacyNodeUsage",
		Description: "LegacyNodeUsage",
		Category:    "Node Usage Cost",
	})
	if err != nil {
		return 0, err
	}
	_, err = unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "LegacyBackupUsage",
		Description: "LegacyBackupUsage",
		Category:    "Data Backup Cost",
	})
	if err != nil {
		return 0, err
	}
	_, err = unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "LegacyDataTransferUsage",
		Description: "LegacyDataTransferUsage",
		Category:    "Data Transfer Cost",
	})
	if err != nil {
		return 0, err
	}

	packageModel := &billing.PackageModel{}
	info, err := packageModel.CreatePackage(
		&forms.PackageInfo{
			UnitPrices: forms.UnitPrices{
				"LegacyNodeUsage":         {Price: decimal.RequireFromString("0.001")},
				"LegacyBackupUsage":       {Price: decimal.RequireFromString("0.001")},
				"LegacyDataTransferUsage": {Price: decimal.RequireFromString("0.001")},
			},
			Discount:    decimal.NewFromInt(1),
			Description: "Migrate Package from v1 billing",
			Labels: map[string]string{
				"legacyData": "true",
			},
		})
	if err != nil {
		return 0, err
	}
	return info.PackageID, nil
}

func (*MigrateModel) CreateSystemSKU() error {
	unitModel := &billing.UnitModel{}

	_, err := unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "network-traffic:all",
		Description: "Data Transfer Cost - Load Balancing",
		Category:    "Data Transfer Cost - Load Balancing",
	})
	if err != nil {
		return err
	}
	_, err = unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "backup-storage:all",
		Description: "Data Backup Cost",
		Category:    "Data Backup Cost",
	})
	if err != nil {
		return err
	}
	return nil
}

func (*MigrateModel) UpdateDataTransferCategory() error {
	_, err := (&billing.UnitModel{}).UpdateUnit(&forms.UnitInfo{
		UnitKey:     "network-traffic:all",
		Description: "Data Transfer Cost - Load Balancing",
		Category:    "Data Transfer Cost - Load Balancing",
	})
	return err
}

func (*MigrateModel) CreateCDCSKU() error {
	unitModel := &billing.UnitModel{}
	_, err := unitModel.CreateUnit(&forms.UnitInfo{
		UnitKey:     "ticdc:network-traffic",
		Description: "TiCDC Data Transfer Cost",
		Category:    "TiCDC Data Transfer Cost",
	})
	if err != nil {
		return err
	}
	return nil
}
func (*MigrateModel) CreateNetworkDetailSKU() error {
	unitModel := &billing.UnitModel{}
	providers := []string{"aws", "gcp"}
	categories := map[string]string{"lb": "Load Balancing", "cross_az": "Same Region", "cross_region": "Cross Region", "internet": "Internet"}
	for _, provider := range providers {
		for key, value := range categories {
			_, err := unitModel.CreateUnit(&forms.UnitInfo{
				UnitKey:     fmt.Sprintf("network-traffic:%s:%s", provider, key),
				Description: fmt.Sprintf("Data Transfer Cost - %s %s", strings.ToUpper(provider), value),
				Category:    fmt.Sprintf("Data Transfer Cost - %s %s", strings.ToUpper(provider), value),
			})
			if err != nil {
				return err
			}
		}
	}

	return nil
}

const (
	DefaultCompanyName = "PINGCAP US"
)

func (*MigrateModel) CreateDefaultBillingCompany() error {
	company := &forms.BillingCompany{
		Name:           DefaultCompanyName,
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		PaymentSubCode: "",
	}
	billingInfoModel := paymentsub.BillingInformationModel{}
	err := billingInfoModel.CreateBillingCompany(company)
	if err != nil {
		return err
	}
	return nil
}

func (*MigrateModel) CreateSupportPlanUnit() error {
	supportPlanModel := &marketing.SupportPlanModel{}
	return supportPlanModel.CreateSupportPlanUnits()
}

const POCPackageID = 1

var BeginDate = time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
var EndDate = time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC)

func (*MigrateModel) SetupAllCreditRiskLevel() error {
	creditRiskLevelInfo, err := (&creditriskcontrol.CreditRiskLevelModel{}).CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "New User",
	})
	if err != nil {
		return err
	}
	_, err = (&creditriskcontrol.CostQuotaModel{}).SetCreditRiskLevelQuota(creditRiskLevelInfo.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: decimal.NewFromInt(500),
	})
	if err != nil {
		return err
	}
	creditRiskLevelInfo, err = (&creditriskcontrol.CreditRiskLevelModel{}).CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "Sales Managed",
	})
	if err != nil {
		return err
	}
	_, err = (&creditriskcontrol.CostQuotaModel{}).SetCreditRiskLevelQuota(creditRiskLevelInfo.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: entity.NoLimitCostQuota,
	})
	if err != nil {
		return err
	}
	return nil
}

func (*MigrateModel) SetupAllTenantsCreditRiskLevel() error {

	results, err := (&creditriskcontrol.CreditRiskLevelModel{}).ListAllCreditRiskLevels()
	if err != nil {
		return err
	}
	var creditRiskLevelInfo *forms.CreditRiskLevel = nil

	for _, riskLevelInfo := range results {
		if riskLevelInfo.Name == "Sales Managed" {
			creditRiskLevelInfo = &riskLevelInfo
		}
	}

	if creditRiskLevelInfo == nil {
		return billing_errors.NewSystemError(errors.New("no invalid credit risk level"))
	}

	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}

	for _, tenant := range tenants {
		err = (&creditriskcontrol.CreditRiskLevelModel{}).SetTenantCreditRiskLevel(tenant.TenantID, creditRiskLevelInfo.CreditRiskLevelID)
		if err != nil {
			return err
		}
	}

	return nil
}

func (*MigrateModel) SetupAllUnitProvider() error {

	results, err := (&billing.UnitModel{}).GetAllUnits()
	if err != nil {
		return err
	}

	for _, unitInfo := range results {
		_, err := (&billing.UnitModel{}).CreateAndUpdateUnit(&unitInfo)
		if err != nil {
			return err
		}
	}

	return nil
}
func (*MigrateModel) MigrateCreditsFilter() error {
	generalProduct := "General Product"
	generalProductExp := "^(?!support-plan).*$"
	supportPlan := "supportPlan"
	supportPlanExp := "support-plan.*$"

	err := entity.NewUnitFilterQuerySet(db.GetDB()).GeneralEq(true).GetUpdater().
		SetGeneral(false).SetName(generalProduct).SetDescription(generalProduct).
		SetUnitKeyPattern(generalProductExp).Update()
	if err != nil {
		return err
	}
	_, err = (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{
			Name:        supportPlan,
			Description: supportPlan,
			FilterID:    31,
			ParentID:    0,
			Internal:    false,
			Priority:    100000,
		},
		General:  false,
		Provider: "all",
		Region:   "all",
		Category: "all",

		UnitKeyPattern: supportPlanExp,
	})
	if err != nil {
		log.Error(" MigrateCreditsFilter CreateUnitFilter err", zap.Error(err))

		return err
	}
	return nil

}
func (*MigrateModel) SetBindedPackageInvoicePayable() error {
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}

	onDemandPackageID := config.Get().GetUint(config.OnDemandPackage)

	log.Info("all tenants num", zap.Int("num", len(tenants)))

	for _, tenant := range tenants {
		tenantID := tenant.TenantID

		payable, err := (&paymentsub.PaymentModel{}).Payable(tenantID)
		if err != nil {
			return err
		}

		if !payable {
			log.Info("skipped tenant ondemand plan migrate, not payable", zap.Uint("tenantID", tenantID))
			continue
		}

		bindedPackages, err := (&billing.BindedPackageModel{}).GetTenantPackageBindedPackage(tenantID, onDemandPackageID)
		if err != nil {
			return err
		}
		if len(bindedPackages) != 1 {
			log.Error("invalid tenant ondemand plan bindedPackages", zap.Uint("tenantID", tenantID), zap.Int("bindedPackages", len(bindedPackages)))
			//	return billing_errors.NewSystemError(errors.New("invalid tenant ondemand plan bindedPackages"))
			continue
		}

		entity.NewBindedPackageQuerySet(db.GetDB()).IDEq(bindedPackages[0].BindedPackageID).
			TenantIDEq(tenantID).GetUpdater().SetInvoicePayable(true).Update()

	}
	return nil
}

var testingUnits = []string{"tcds-request-unit:aws:us-west-2",
	"tcds-request-network:aws:us-west-2",
	"tcds-request-unit:aws:ap-northeast-1",
	"tcds-request-network:aws:ap-northeast-1",
	"tcds-request-unit:aws:ap-northeast-2",
	"tcds-request-network:aws:ap-northeast-2",
	"tcds-request-unit:aws:ap-southeast-1",
	"tcds-request-network:aws:ap-southeast-1",
	"tcds-request-unit:aws:eu-central-1",
	"tcds-request-network:aws:eu-central-1",
	"tcds-request-unit:aws:us-east-1",
	"tcds-request-network:aws:us-east-1"}

func (*MigrateModel) CreateSKU() error {
	err := (&marketing.SupportPlanModel{}).CreateSupportPlanUnits()
	if err != nil {
		return err
	}
	for _, unitKey := range testingUnits {
		_, err = (&billing.UnitModel{}).CreateAndUpdateUnit(&forms.UnitInfo{
			UnitKey:   unitKey,
			Category:  "tcds",
			Price:     decimal.RequireFromString("0.000"),
			PriceType: entity.PriceType_Normal,
			Provider:  "PingCAP",
			SkuType:   entity.SkuType_Test,
		})
		if err != nil {
			return err
		}
	}

	return nil

}

func (*MigrateModel) SetSKUPrice() error {
	packagesMap := map[string]bool{
		"POC Package":          true,
		"OnDemand Package":     true,
		"Global Price Package": true,
	}
	setPriceMap := map[string]bool{}
	unitMap := map[string]entity.Unit{}
	version := 1

	packages, err := (&billing.PackageModel{}).GetAllPackages()
	if err != nil {
		return err
	}
	var units []entity.Unit
	err = entity.NewUnitQuerySet(db.GetDB()).All(&units)
	if err != nil {
		return err
	}
	for _, unit := range units {
		unitMap[unit.UnitKey] = unit
	}

	for _, packagePrice := range packages {
		if !packagesMap[packagePrice.Description] {
			continue
		}
		dealSKU(packagePrice.UnitPrices, setPriceMap, version, unitMap)

		if packagePrice.ExtraPricingInfos != nil {
			unitPrices := packagePrice.ExtraPricingInfos[0].UnitPrices
			dealSKU(unitPrices, setPriceMap, version, unitMap)

		}

	}
	return nil
}

func dealSKU(unitPrices forms.UnitPrices, setPriceMap map[string]bool, version int, unitMap map[string]entity.Unit) {
	for _, uintPrice := range unitPrices {
		if !setPriceMap[uintPrice.Unit] {
			unitEntity := entity.Unit{}
			unitEntity.Version = uint(version)
			unitEntity.Price = uintPrice.Price
			unitEntity.PriceType = entity.PriceType_Normal
			unitEntity.SkuType = entity.SkuType_Normal
			if len(uintPrice.MultipleFactorLabel) > 0 {
				unitEntity.PriceType = entity.PriceType_Threshold
				unitEntity.PriceExpression = map[string]string{
					entity.PriceType_Threshold_label: uintPrice.MultipleFactorLabel,
					entity.PriceType_Threshold_value: strconv.Itoa(uintPrice.MultipleFactorThreshold),
				}
			}
			db.GetDB().Model(unitMap[uintPrice.Unit]).Updates(unitEntity)
			setPriceMap[uintPrice.Unit] = true
		}

	}
}

func (*MigrateModel) SetupAllCategoryItem() error {
	var categoryItemInfoDb []entity.CategoryItemShow
	err := entity.NewCategoryItemShowQuerySet(db.GetDB()).All(&categoryItemInfoDb)
	if err != nil {
		log.Error("getAllCategoryItemInfo ", zap.Error(err))
		return err
	}
	categoryItemInfoDbMap := map[string]bool{}

	for _, categoryItem := range categoryItemInfoDb {
		categoryItemInfoDbMap[categoryItem.Category] = true
	}

	categoryLabels := GetCategoryLabelFromConfig()

	for _, categoryLabel := range categoryLabels {
		if categoryItemInfoDbMap[categoryLabel.Category] {
			err = entity.NewCategoryItemShowQuerySet(db.GetDB()).CategoryEq(categoryLabel.Category).GetUpdater().SetShowKindName(categoryLabel.ShowKindName).SetShowCategoryName(categoryLabel.ShowCategoryName).Update()
			if err != nil {
				return err
			}
			err = entity.NewCategoryItemShowQuerySet(db.GetDB()).CategoryEq(categoryLabel.Category).GetUpdater().SetMeasureUnit(categoryLabel.MeasureUnit).Update()
			if err != nil {
				return err
			}
			err = db.GetDB().Model(categoryLabel).Where("category", categoryLabel.Category).Update("order_map", categoryLabel.OrderMap).Error
			if err != nil {
				return err
			}
			err = db.GetDB().Model(categoryLabel).Where("category", categoryLabel.Category).Update("labels", categoryLabel.Labels).Error
			if err != nil {
				return err
			}
			continue

		}

		el := &entity.CategoryItemShow{
			ShowKindName:      categoryLabel.ShowKindName,
			Category:          categoryLabel.Category,
			Labels:            categoryLabel.Labels,
			LabelKey:          categoryLabel.LabelKey,
			OrderMap:          categoryLabel.OrderMap,
			ShowServicePrefix: categoryLabel.ShowServicePrefix,
			ShowCategoryName:  categoryLabel.ShowCategoryName,
			UseCateGory:       categoryLabel.UseCateGory,
			MeasureUnit:       categoryLabel.MeasureUnit,
		}
		err := el.Create(db.GetDB())
		if err != nil {
			log.Error("getAllCategoryItemInfo ", zap.Error(err))
			return err
		}
	}

	return nil
}

// ShowKindName{ShowCategoryName { ShowService|| Labels-value}}
func GetCategoryLabelFromConfig() map[string]entity.CategoryItemShow {

	res := map[string]entity.CategoryItemShow{}
	//3
	res["Backup Cost"] = entity.CategoryItemShow{
		ShowKindName: "TiDB Dedicated",
		Category:     "Backup Cost",
		Labels: map[string]string{
			"Storage Cost":     "Single Region Storage",
			"default":          "Single Region Storage",
			"Replication Cost": "Replication",
		},
		OrderMap: map[string]int32{
			"TiDB Dedicated":        2,
			"Backup":                3,
			"Single Region Storage": 1,
			"Replication":           4,
		},
		LabelKey:          "component",
		ShowCategoryName:  "Backup",
		ShowServicePrefix: "Backup - ",
		UseCateGory:       false,
	}
	//3 -backup -dual
	res["Dual Region Storage"] = entity.CategoryItemShow{
		ShowKindName: "TiDB Dedicated",
		Category:     "Dual Region Storage",
		Labels: map[string]string{
			"Dual Region Storage": "Dual Region Storage",
		},
		OrderMap: map[string]int32{
			"TiDB Dedicated":      2,
			"Backup":              3,
			"Dual Region Storage": 2,
		},
		LabelKey:          "Dual Region Storage",
		ShowCategoryName:  "Backup",
		ShowServicePrefix: "Backup - ",
		UseCateGory:       true,
	}

	return res
}

func (*MigrateModel) MigrateTenantOnDemandPlan() error {
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}
	planModel := (&plan.TenantPlanModel{})

	onDemandPackageID := config.Get().GetUint(config.OnDemandPackage)

	now := timeutils.GetNow()

	log.Info("all tenants num", zap.Int("num", len(tenants)))

	for _, tenant := range tenants {
		tenantID := tenant.TenantID
		billingConfigID, err := (&billingconfig.BillingConfigModel{}).GetActiveBillingConfigID(tenantID)
		if err != nil {
			return err
		}
		info, err := planModel.GetTenantOnDemandPlanInfo(tenantID, billingConfigID)
		if err != nil {
			return err
		}
		if info.Status == entity.TenantPlanStatus_Active {
			log.Info("skipped tenant ondemand plan migrate, already active", zap.Uint("tenantID", tenantID))
			continue
		}
		if info.Status != entity.TenantPlanStatus_NotStarted {
			log.Error("invalid tenant ondemand plan status", zap.Uint("tenantID", tenantID), zap.String("status", info.Status))
			return billing_errors.NewSystemError(errors.New("invalid tenant ondemand plan status"))
		}

		payable, err := (&paymentsub.PaymentModel{}).Payable(tenantID)
		if err != nil {
			return err
		}

		if !payable {
			log.Info("skipped tenant ondemand plan migrate, not payable", zap.Uint("tenantID", tenantID))
			continue
		}

		bindedPackages, err := (&billing.BindedPackageModel{}).GetTenantPackageBindedPackage(tenantID, onDemandPackageID)
		if err != nil {
			return err
		}
		if len(bindedPackages) != 1 {
			log.Error("invalid tenant ondemand plan bindedPackages", zap.Uint("tenantID", tenantID), zap.Int("bindedPackages", len(bindedPackages)))
			return billing_errors.NewSystemError(errors.New("invalid tenant ondemand plan bindedPackages"))
		}

		tenantPlanEntity := entity.TenantPlan{
			TenantID:        tenant.TenantID,
			Plan:            entity.TenantPlan_OnDemand,
			Status:          entity.TenantPlanStatus_Active,
			StartedAt:       &now,
			BindedPackageID: bindedPackages[0].BindedPackageID,
		}
		err = tenantPlanEntity.Create(db.GetDB())
		if err != nil {
			log.Error("failed to create ondemand tenant plan", zap.Uint("tenantID", tenantID), zap.Error(err))
			return err
		}

		log.Info("successfully create ondemand tenant plan", zap.Uint("tenantID", tenantID))
	}
	return nil
}

func (*MigrateModel) MigrateTenantDevTierPlan() error {
	// We need manullay migrate tenant dev tier plan for db permission reason.
	// It has no lifecycle problem in dev tier plan management.
	return nil
}

func (*MigrateModel) MigrateTenantPOCPlan() error {
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}

	creditsModel := &marketing.CreditsPackageModel{}
	planModel := (&plan.TenantPlanModel{})

	for _, tenant := range tenants {
		tenantID := tenant.TenantID
		info, err := planModel.GetTenantPOCPlanInfo(tenantID)
		if err != nil {
			return err
		}

		if info.Status != entity.TenantPlanStatus_NotStarted {
			log.Info("just skipped migrate poc plan", zap.Uint("tenantID", tenantID))
			continue
		}

		credits, err := creditsModel.GetTenantCreditsSummary(tenantID, 0)
		if err != nil {
			return err
		}
		if credits.LastCreditsClosedDate == nil {
			err = planModel.EnableTenantPOCPlanAfterCreditsOpened(tenantID)
			if err != nil {
				return err
			}
		} else {
			if *credits.LastCreditsClosedDate == marketing.DefaultClosedDate {
				log.Info("not enabled poc, just skipped", zap.Uint("tenantID", tenantID))
				continue
			}
			status := entity.TenantPlanStatus_Closing

			if credits.LastCreditsClosedDate.Add(time.Hour * 24 * 14).Before(timeutils.GetNow()) {
				log.Info("poc already closed", zap.Uint("tenantID", tenantID))
				status = entity.TenantPlanStatus_Closed
			}

			tenantPlanEntity := entity.TenantPlan{
				TenantID:        tenantID,
				Plan:            entity.TenantPlan_POC,
				Status:          status,
				StartedAt:       timeutils.GetNowAddr(),
				ClosedAt:        credits.LastCreditsClosedDate,
				BindedPackageID: 0,
			}
			err = tenantPlanEntity.Create(db.GetDB())
			if err != nil {
				log.Error("failed to update tenant poc plan entity", zap.Uint("tenantID", tenantID), zap.Error(err))
				return err
			}

		}
		log.Info("successful migrate tenant poc plan", zap.Uint("tenantID", tenantID))
	}
	return nil
}

func (m *MigrateModel) MigrateTenantPlan() error {
	err := m.MigrateTenantOnDemandPlan()
	if err != nil {
		return err
	}

	err = m.MigrateTenantDevTierPlan()
	if err != nil {
		return err
	}

	err = m.MigrateTenantPOCPlan()
	if err != nil {
		return err
	}

	return nil
}

func (m *MigrateModel) SetupAllTenantClosePolicy() error {

	results, err := (&creditriskcontrol.CreditRiskLevelModel{}).ListAllCreditRiskLevels()
	if err != nil {
		return err
	}
	var salesManagedLevel, newUserLevel *forms.CreditRiskLevel = nil, nil

	for _, riskLevelInfo := range results {
		if riskLevelInfo.Name == "Sales Managed" {
			salesManagedLevel = &riskLevelInfo
		}
		if riskLevelInfo.Name == "New User" {
			newUserLevel = &riskLevelInfo
		}
	}

	if salesManagedLevel == nil {
		log.Info("no sales managed credit risk level")
	} else {
		err = (&creditriskcontrol.TenantCloseModel{}).CreateTenantRiskClosePolicy(salesManagedLevel.CreditRiskLevelID, &forms.TenantClosePolicy{
			ClosePolicy: forms.ClosePolicy{
				Actions: []*forms.CloseAction{
					{
						Action:           "cleanup_tenant_dedicate_clusters",
						ActionTimeInHour: 0,
					},
					{
						Action:           "close_tenant",
						ActionTimeInHour: 45 * 24,
					},
				},
			},
		})
		if err != nil {
			if errors.Is(err, billing_errors.ObjectAlreadyExists) {
				log.Info("sales managed credit risk level tenant close policy already set")
			} else {
				return err
			}
		}
		log.Info("sales managed credit risk level tenant close policy set")
	}

	if newUserLevel == nil {
		log.Info("no new user credit risk level")
	} else {
		err = (&creditriskcontrol.TenantCloseModel{}).CreateTenantRiskClosePolicy(newUserLevel.CreditRiskLevelID, &forms.TenantClosePolicy{
			ClosePolicy: forms.ClosePolicy{
				Actions: []*forms.CloseAction{
					{
						Action:           "cleanup_tenant_dedicate_clusters",
						ActionTimeInHour: 0,
					},
					{
						Action:           "close_tenant",
						ActionTimeInHour: 60 * 24,
					},
				},
			},
		})
		if err != nil {
			if errors.Is(err, billing_errors.ObjectAlreadyExists) {
				log.Info("new user credit risk level tenant close policy already set")
			} else {
				return err
			}
		}
		log.Info("new user credit risk level tenant close policy set")
	}

	return nil
}

func (m *MigrateModel) NewSpendLimitAlertThreshold(gDB *gorm.DB) (err error) {
	err = gDB.AutoMigrate(entity.SpendLimitAlertThreshold{})
	if err != nil {
		return
	}
	for _, val := range []string{"0.9", "1", "0.75"} {
		err = (&entity.SpendLimitAlertThreshold{
			BizType: entity.SpendLimitBizType_ServerlessCluster, Proportion: decimal.RequireFromString(val),
		}).Create(db.GetDB())
		if err != nil {
			return
		}
	}
	return
}

func (m *MigrateModel) MigrateUnitFilterPriority() (err error) {
	priorityMap := map[string]int{
		//constant.GeneralTypeName:           constant.GeneralPriority,
		constant.ServerlessTierTypeName: constant.ServerlessTierPriority,
		//constant.NodeComputeTypeName:       constant.NodeComputePriority,
		//constant.TiDBNodeTypeName:          constant.TiDBNodePriority,
		//constant.TiKVNodeTypeName:          constant.TiKVNodePriority,
		//constant.TiFlashNodeTypeName:       constant.TiFlashNodePriority,
		//constant.StorageTypeName:           constant.StoragePriority,
		//constant.TiKVStorageTypeName:       constant.TiKVStoragePriority,
		//constant.TiFlashStorageTypeName:    constant.TiFlashStoragePriority,
		//constant.BackupTypeName:            constant.BackupPriority,
		//constant.BackupStorageTypeName:     constant.BackupStoragePriority,
		//constant.BackupReplicationTypeName: constant.BackupReplicationPriority,
		//constant.TiCDC:                     constant.TiCDCPriority,
		//constant.DM:                        constant.DMPriority,
	}
	for name, priority := range priorityMap {
		err = entity.NewUnitFilterQuerySet(db.GetDB()).NameEq(name).GetUpdater().SetPriority(priority).Update()
		if err != nil {
			return
		}
	}
	for key, uf := range map[string]*forms.UnitFilter{
		constant.ServerlessTierTypeName:       {UnitKeyPattern: `serverless-\b(request-unit|row-storage|column-storage)\b:[\w\-]+:[\w\-]+$`},
		constant.ServerlessRUTypeName:         {UnitKeyPattern: `serverless\-request\-unit:+[\w\-]+:[\w\-]+$`},
		constant.ServerlessRowStorageTypeName: {UnitKeyPattern: `serverless\-row\-storage:+[\w\-]+:[\w\-]+$`},
		constant.ServerlessColStorageTypeName: {UnitKeyPattern: `serverless\-column\-storage:+[\w\-]+:[\w\-]+$`},
	} {
		err = entity.NewUnitFilterQuerySet(db.GetDB()).NameEq(key).GetUpdater().SetUnitKeyPattern(uf.UnitKeyPattern).Update()
		if err != nil {
			return
		}
	}

	return
}

func (m *MigrateModel) MigrateUnitFilterQuickSelection(gDB *gorm.DB) (err error) {
	var unitFilters []entity.UnitFilter
	err = entity.NewUnitFilterQuerySet(gDB).NameIn(config.Get().GetStringSlice(config.DefaultDiscountUnitFilters)...).All(&unitFilters)
	if err != nil {
		return err
	}
	unitFilterModel := &unit_filter.Model{}
	targetIDs := make([]uint, len(unitFilters))
	for i, s := range unitFilters {
		targetIDs[i] = s.ID
	}
	target := constant.DiscountDefaultUnitFilterTargetName
	err = unitFilterModel.CreateUnitFilterQuickSelection(target, targetIDs...)
	return
}

func (m *MigrateModel) getUnitFilter(uniqueKey string) (ret *forms.UnitFilterSlim, err error) {
	var entities []entity.UnitFilter
	err = entity.NewUnitFilterQuerySet(db.GetDB()).UniqueKeyEq(uniqueKey).All(&entities)
	if err != nil {
		return
	} else if len(entities) == 0 {
		return
	}
	return transformer.UnitFilterSlimFromEntity(&entities[0]), nil
}

func (m *MigrateModel) getOrCreateUnitFilter(uniqueKey string, unitFilter *forms.UnitFilter) (ret *forms.UnitFilterSlim, err error) {
	ufModel := &unit_filter.Model{}
	ret, err = m.getUnitFilter(uniqueKey)
	if err != nil {
		return
	} else if ret != nil {
		return
	}
	r, err := ufModel.CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: uniqueKey, Description: uniqueKey, ParentID: unitFilter.ParentID, Priority: unitFilter.Priority},
		General:        false, UnitKeyPattern: unitFilter.UnitKeyPattern,
	})
	if err != nil {
		return
	}
	return &r.UnitFilterSlim, nil
}

func (m *MigrateModel) MigrateServerlessUnitFilter() (err error) {
	parent, err := m.getUnitFilter(constant.ServerlessTierTypeName)
	if err != nil {
		return err
	} else if parent == nil {
		return errors.New("serverless uf is missing")
	}
	for key, uf := range map[string]*forms.UnitFilter{
		constant.ServerlessRUTypeName:         {UnitKeyPattern: `serverless\-request\-unit.*$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessRUPriority}},
		constant.ServerlessRowStorageTypeName: {UnitKeyPattern: `serverless\-row\-storage.*`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessRowStoragePriority}},
		constant.ServerlessColStorageTypeName: {UnitKeyPattern: `serverless\-column\-storage.*$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessColStoragePriority}},
	} {
		_, err = m.getOrCreateUnitFilter(key, uf)
		if err != nil {
			return err
		}
	}
	return
}
func (m *MigrateModel) MigrateServerlessUnitFilter20250606() (err error) {
	parent, err := m.getUnitFilter(constant.GeneralTypeName)
	if err != nil {
		return err
	} else if parent == nil {
		return errors.New("serverless uf is missing")
	}
	for key, uf := range map[string]*forms.UnitFilter{
		constant.ServerlessStarterTypeName:   {UnitKeyPattern: `serverless\-.*\:starter$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessStarterTierPriority}},
		constant.ServerlessEssentialTypeName: {UnitKeyPattern: `serverless\-.*\:essential$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessEssentialPriority}},
	} {
		_, err = m.getOrCreateUnitFilter(key, uf)
		if err != nil {
			return err
		}
	}

	parent, err = m.getUnitFilter(constant.ServerlessStarterTypeName)
	if err != nil {
		return err
	} else if parent == nil {
		return errors.New("serverless uf is missing")
	}
	for key, uf := range map[string]*forms.UnitFilter{
		constant.ServerlessStarterRUTypeName:         {UnitKeyPattern: `serverless\-request\-unit.*\:starter$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessStarterRUPriority}},
		constant.ServerlessStarterRowStorageTypeName: {UnitKeyPattern: `serverless\-row\-storage.*\:starter$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessStarterRowStoragePriority}},
		constant.ServerlessStarterColStorageTypeName: {UnitKeyPattern: `serverless\-column\-storage.*\:starter$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessStarterColStoragePriority}},
	} {
		_, err = m.getOrCreateUnitFilter(key, uf)
		if err != nil {
			return err
		}
	}

	parent, err = m.getUnitFilter(constant.ServerlessEssentialTypeName)
	if err != nil {
		return err
	} else if parent == nil {
		return errors.New("serverless uf is missing")
	}
	for key, uf := range map[string]*forms.UnitFilter{
		constant.ServerlessEssentialRCUTypeName:        {UnitKeyPattern: `serverless\-request\-capacity\-unit.*\:essential$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessEssentialRCUPriority}},
		constant.ServerlessEssentialRowStorageTypeName: {UnitKeyPattern: `serverless\-row\-storage.*\:essential$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessEssentialRowStoragePriority}},
		constant.ServerlessEssentialColStorageTypeName: {UnitKeyPattern: `serverless\-column\-storage.*\:essential$`, UnitFilterSlim: forms.UnitFilterSlim{ParentID: parent.FilterID, Priority: constant.ServerlessEssentialColStoragePriority}},
	} {
		_, err = m.getOrCreateUnitFilter(key, uf)
		if err != nil {
			return err
		}
	}

	return
}
func (m *MigrateModel) UpdateStripeEmail() error {
	var err error
	var payments []entity.PaymentMethod
	err = entity.NewPaymentMethodQuerySet(db.GetDB()).PaymentTypeEq(entity.PaymentType_STRIPE).PaymentCodeNe("").All(&payments)
	if err != nil {
		return err
	}
	if !config.Get().GetBool(config.StripePaymentEnable) {
		log.Warn("strip not enabled, do nothing")
		return nil
	}
	sc := &scclient.API{}
	sc.Init(config.Get().GetString(config.StripeSecretKey), nil)
	var lastErr error
	for _, p := range payments {
		if p.ManagedBySales {
			continue
		}
		tenant, err := centralhandler.GetCentralHandler().GetTenant(p.TenantID)
		if err != nil {
			if errors.Is(err, billing_errors.ObjectNotExistsError) {
				continue
			}
			log.Error("Failed to get tenant info", zap.Error(err), zap.Uint("tenantID", tenant.TenantID))
			lastErr = err
			continue
		}
		if tenant.Status != centralhandler.TenantStatusActive {
			continue
		}
		customerID := p.PaymentCode
		customer, err := sc.Customers.Get(customerID, nil)
		if err != nil {
			log.Error("Failed to get customer info", zap.Error(err), zap.Uint("tenantID", tenant.TenantID), zap.String("customerID", customerID))
			lastErr = err
			continue
		}
		if customer.Email != tenant.Email {
			log.Info("Start to update tenant email", zap.Uint("tenantID", tenant.TenantID), zap.String("customerID", customerID), zap.String("raw", customer.Email), zap.String("target", tenant.Email))
			_, err = sc.Customers.Update(customerID, &stripe.CustomerParams{Email: &tenant.Email})
			if err != nil {
				log.Error("Failed to update tenant email", zap.Error(err), zap.Uint("tenantID", tenant.TenantID), zap.String("customerID", customerID), zap.String("raw", customer.Email), zap.String("target", tenant.Email))
				lastErr = err
				continue
			}
			log.Info("Success to update tenant email", zap.Uint("tenantID", tenant.TenantID), zap.String("customerID", customerID), zap.String("raw", customer.Email), zap.String("target", tenant.Email))
		}
	}
	return lastErr
}

func (m *MigrateModel) ReCalculateMeasures(gDB *gorm.DB, start int64, end int64) (err error) {
	var costUnits []entity.UserUnitCost
	err = entity.NewUserUnitCostQuerySet(gDB).StatusEq(entity.UserUnitCostStatus_Recal).CostTimeStampGte(start).CostTimeStampLt(end).All(&costUnits)
	if err != nil {
		return err
	}
	for i, cost := range costUnits {
		_, err = (&billing.MeasureModel{}).CostUnits(cost.TenantID, &forms.UnitCosts{
			CostID:        cost.CostID,
			Unit:          cost.Unit,
			Cost:          cost.Cost,
			Labels:        cost.Labels,
			CostTimeStamp: cost.CostTimeStamp,
		})
		if err != nil {
			log.Error("cost failed", zap.Error(err))
			continue
		}
		log.Info("progress", zap.Int("done", i+1), zap.Int("all", len(costUnits)))
	}
	return
}

func (m *MigrateModel) CreateReCalculateInvoices(date time.Time, tenantIDs []uint) (err error) {
	for _, tenantID := range tenantIDs {
		billingConfigID, err := (&billingconfig.BillingConfigModel{}).GetUseBillingConfigIDByTimeStampWithCache(tenantID, date.Unix())
		invoice, err := (&payment.InvoiceModel{}).CreateConfirmedRecalInvoice(tenantID, billingConfigID, date)
		if err != nil {
			return err
		}
		log.Info("Recalc invoice has been created", zap.Uint("InvoiceID", invoice.InvoiceID))
	}
	return
}

func (m *MigrateModel) MigrateRechargePlanUnitFilter() error {
	return db.GetDB().Exec("insert into unit_filter_tenant_recharge_credits_plan select" +
		"  tenant_recharge_credits_plans.id as tenant_recharge_credits_plan_id,  " +
		"  credit_type_credit_cycle_recharge_credits_plan.credit_type_id as unit_filter_id " +
		"from credit_type_credit_cycle_recharge_credits_plan " +
		"left join recharge_credits_plans on credit_type_credit_cycle_recharge_credits_plan.recharge_credits_plan_id=recharge_credits_plans.id " +
		"left join tenant_recharge_credits_plans on tenant_recharge_credits_plans.main_plan_id=recharge_credits_plans.id " +
		"where tenant_recharge_credits_plans.id not in (select tenant_recharge_credits_plan_id from unit_filter_tenant_recharge_credits_plan)",
	).Error
}

func (m *MigrateModel) MigrateSpendLimitRechargePlanID() error {
	var slList []entity.SpendLimitSetting
	err := db.GetDB().Exec(
		`select * from spend_limit_settings where biz_type=? and json_extract(biz_data, '$.recharge_plan_id') not in (0, 500000, 500001, 500002, 500003, 500004) and status != ?;`,
		entity.SpendLimitBizType_ServerlessCluster, entity.SpendLimitStatus_Deprecated,
	).Find(&slList).Error
	if err != nil {
		return err
	}
	spendLimitModel := &spend_limit.SpendLimitModel{}
	infraClient := infrahandler.Get()
	regionRechargePlanMap := map[string]uint{
		"us-east-1":      500000,
		"us-west-2":      500001,
		"eu-central-1":   500002,
		"ap-southeast-1": 500003,
		"ap-northeast-1": 500004,
	}
	for _, sl := range slList {
		bizData, err := spendLimitModel.UnmarshallServerlessBizData(sl.BizData)
		if err != nil {
			log.Error("failed to migrate spend limit cause by unmarshalling BizData", zap.Error(err), zap.Uint("id", sl.ID))
			continue
		}
		if bizData.RechargePlanID == 0 || (bizData.RechargePlanID >= 500000 && bizData.RechargePlanID <= 500004) {
			// json_extract(biz_data, '$.recharge_plan_id') not in (0, 500000, 500001, 500002, 500003, 500004) may not filter all, but the reason is unknown
			continue
		}
		cluster, err := infraClient.GetCluster(sl.TenantID, sl.RangeID)
		if err != nil {
			log.Error("failed to migrate spend limit cause by getting cluster", zap.Error(err), zap.Uint("id", sl.ID), zap.Uint("clusterID", sl.RangeID))
			continue
		}
		mainPlanID, ok := regionRechargePlanMap[cluster.RegionName]
		if !ok {
			log.Error("failed to migrate spend limit cause by getting main plan id", zap.Error(err), zap.Uint("id", sl.ID), zap.Uint("clusterID", sl.RangeID))
			continue
		}
		err = db.GetDB().Transaction(func(tx *gorm.DB) error {
			err := tx.Exec(`UPDATE spend_limit_settings SET biz_data=JSON_SET(biz_data, '$.recharge_plan_id', ?) WHERE  id = ?`, mainPlanID, sl.ID).Error
			if err != nil {
				return err
			}
			err = entity.NewTenantRechargeCreditsPlanQuerySet(tx).IDEq(bizData.TenantRechargePlanID).GetUpdater().SetMainPlanID(mainPlanID).Update()
			if err != nil {
				return err
			}
			return nil
		})
		if err != nil {
			log.Error("failed to migrate spend limit cause by processing update", zap.Error(err), zap.Uint("id", sl.ID))
			continue
		}
	}
	return nil
}
func (m *MigrateModel) CreateAllTenantIDBillingConfig() error {
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}
	model := &billingconfig.BillingConfigModel{}
	for _, tenantInfo := range tenants {
		_, err := model.GetActiveBillingConfigID(tenantInfo.TenantID)
		if err == nil {
			log.Warn("CreateAllTenantIDBillingConfig tenantID had billing config id", zap.Error(err), zap.Uint("id", tenantInfo.TenantID))
			continue
		}

		billingConfigType := entity.BillingConfigType_DirectCustomer
		if tenantInfo.IsMsp {
			billingConfigType = entity.BillingConfigType_MSP
		}
		if tenantInfo.IsMspCustomer {
			billingConfigType = entity.BillingConfigType_MSPCustomer
		}
		var startActiveTime = time.Unix(0, 0)
		_, err = model.CreateBillingConfigAndActive(forms.BillingConfig{
			TenantID:        tenantInfo.TenantID,
			StartActiveTime: &startActiveTime,
			EndActiveTime:   nil,
			Type:            billingConfigType,
		})
		if err != nil {
			log.Warn("CreateAllTenantIDBillingConfig CreateBillingConfigAndActive error", zap.Error(err), zap.Uint("id", tenantInfo.TenantID))
		}
		billingConfigID, err := model.GetActiveBillingConfigID(tenantInfo.TenantID)
		if err != nil {
			log.Warn("CreateAllTenantIDBillingConfig tenantID had billing config id", zap.Error(err), zap.Uint("id", tenantInfo.TenantID))
			continue
		}
		log.Info("CreateAllTenantIDBillingConfig success create billing config", zap.Uint("id", tenantInfo.TenantID), zap.Uint("billingConfigID", billingConfigID))
	}
	return nil
}
func (m *MigrateModel) CreateAllTenantIDNovuSubscriber() error {
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		return err
	}
	model := &unc.NotificationModel{}

	for _, tenantInfo := range tenants {
		err := model.CreateNovuSubscribersByTenantID(tenantInfo.TenantID)
		log.Info("CreateAllTenantIDNovuSubscriber ", zap.Uint("TenantID", tenantInfo.TenantID))
		if err != nil {
			log.Info("CreateAllTenantIDNovuSubscriber CreateNovuSubscribersByTenantID  error ", zap.Error(err))
		}

	}
	return nil
}

func (m *MigrateModel) MigrateDataTrafficUnitFilters() error {
	err := db.GetDB().Transaction(func(tx *gorm.DB) error {
		err := entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer").GetUpdater().SetUnitKeyPattern(
			`backup\-dual\-region\-replication\:.*\:.*$|dm-internet\:.*\:.*$|dm\-nat\:.*\:.*$|dm-network\:.*$|network\-traffic\:.*$|pl\-network\:.*$|vpcpeering\:.*\:.*$`,
		).SetInternal(false).SetPriority(6000).Update()
		if err != nil {
			return err
		}
		err = entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer (Same Region)").GetUpdater().SetUnitKeyPattern(
			`dm-network\:.*$|network-traffic\:.*\:cross_az$|pl\-network\:.*$|vpcpeering\:.*\:CrossAZ$`,
		).SetInternal(false).SetPriority(6400).Update()
		if err != nil {
			return err
		}
		err = entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer (Cross Region)").GetUpdater().SetUnitKeyPattern(
			`backup\-dual\-region\-replication\:.*\:.*$|network\-traffic\:.*\:cross_region$|vpcpeering\:.*\:CrossRegion$`,
		).SetInternal(false).SetPriority(6300).Update()
		if err != nil {
			return err
		}
		err = entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer (Internet)").GetUpdater().SetUnitKeyPattern(
			`dm\-internet\:.*\:.*$|network\-traffic\:.*\:internet$`,
		).SetInternal(false).SetPriority(6200).Update()
		if err != nil {
			return err
		}
		err = entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer (Load Balancing)").GetUpdater().SetUnitKeyPattern(
			`network\-traffic\:.*\:lb$`,
		).SetInternal(false).SetPriority(6100).Update()
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

func (m *MigrateModel) MigrateDataTrafficUnitFiltersAddIpv4() error {
	err := db.GetDB().Transaction(func(tx *gorm.DB) error {
		err := entity.NewUnitFilterQuerySet(tx).NameEq("Data Transfer (Load Balancing)").GetUpdater().SetUnitKeyPattern(
			`network\-traffic\:.*\:lb$|network\-traffic\:.*\:ipv4$`,
		).SetInternal(false).SetPriority(6100).Update()
		if err != nil {
			return err
		}
		return nil
	})
	return err
}
func (m *MigrateModel) MigrateDataTrafficUnit() error {
	unitModel := &billing.UnitModel{}
	//12 *5
	awsRegion := []string{"ap-northeast-2", "ap-southeast-3", "ap-southeast-2", "us-east-1", "us-west-2", "ap-northeast-1", "ap-southeast-1", "eu-central-1", "ap-south-1", "sa-east-1", "us-east-2", "ap-east-1"}
	//10 *4
	gcpRegion := []string{"us-central1", "us-west1", "asia-northeast1", "asia-southeast1", "asia-southeast2", "asia-east1", "asia-northeast2", "us-east4", "us-west2", "asia-east2"}

	for _, region := range awsRegion {
		err := unitModel.AddRegionNetWorkUnit("aws", region)
		if err != nil {
			log.Error("failed to MigrateDataTrafficUnit ", zap.Error(err), zap.String("aws region", region))
		}
	}
	for _, region := range gcpRegion {
		err := unitModel.AddRegionNetWorkUnit("gcp", region)
		if err != nil {
			log.Error("failed to MigrateDataTrafficUnit ", zap.Error(err), zap.String("gcp region", region))
		}
	}
	log.Info("MigrateDataTrafficUnit end")
	return nil
}

func (m *MigrateModel) MigrateTenantClosePolicy() error {
	var err error
	baseReasons := []string{"marketplace_unsubscribed", "credit_card_unpayable"}
	var reasons []string
	for _, r := range baseReasons {
		for ka, firstCleanTime := range map[string]uint{"non_KA": 0, "KA": 360} {
			reason := fmt.Sprintf("%s_for_%s", r, ka)
			reasons = append(reasons, reason)
			var policy entity.TenantClosePolicy
			err = entity.NewTenantClosePolicyQuerySet(db.GetDB()).ReasonEq(reason).One(&policy)
			fmt.Println(reason, err)
			if err != nil {
				if errors.Is(err, billing_errors.ObjectNotExistsError) {
					policy = entity.TenantClosePolicy{Reason: reason}
					err = policy.Create(db.GetDB())
					if err != nil {
						return err
					}
				} else {
					return err
				}
			}
			policy.ClosePolicy = entity.ClosePolicy{
				Actions: []*forms.CloseAction{
					{Action: fmt.Sprintf("send_suspend_email_for_%s", r), ActionTimeInHour: 0},
					{Action: "cleanup_tenant_dedicate_clusters", ActionTimeInHour: firstCleanTime},
					{Action: "throttle_serverless_scalable_clusters", ActionTimeInHour: firstCleanTime},
					{Action: "cleanup_tenant_support_plan", ActionTimeInHour: firstCleanTime},
					{Action: "cleanup_tenant_serverless_clusters", ActionTimeInHour: 1440},
					{Action: "cleanup_backups", ActionTimeInHour: 1440},
					{Action: fmt.Sprintf("send_closed_email_for_%s", r), ActionTimeInHour: 1440},
					{Action: "close_tenant", ActionTimeInHour: 1440},
				},
				CancelAction: &forms.CloseAction{
					Action:           "cancel_closing_org",
					ActionTimeInHour: 0,
				},
			}
			err = db.GetDB().Model(&entity.TenantClosePolicy{}).Where("id = ?", policy.ID).Select("*").Updates(policy).Error
			if err != nil {
				return err
			}
		}
	}
	err = entity.NewTenantClosePolicyQuerySet(db.GetDB()).ReasonNotIn(reasons...).Delete()
	if err != nil {
		return err
	}
	return nil
}

func (m *MigrateModel) MigrateCAPCOM() error {
	t := time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC)
	for i := 0; i != 30; i++ {
		sql := `update user_unit_costs set consumed_source="invoice", internal=0, binded_package_id=1440196 where tenant_id=1372813089209216864 and binded_billing_config_id=52332 and cost_time_stamp=? and consumed_source="none" and internal`
		err := db.GetDB().Exec(sql, t.AddDate(0, 0, i).Unix()).Error
		if err != nil {
			return err
		}
		_, err = (&payment.InvoiceModel{}).CreateDailyInvoice(1372813089209216864, 52332, t.AddDate(0, 0, i), entity.InvoiceStatus_GENERATED)
		if err != nil {
			if errors.Is(err, billing_errors.InvoiceNoCostsError) {
				continue
			}
			return err
		}
	}
	return nil
}

func (m *MigrateModel) MigratePublicSupportPlan() error {
	// 1. create Developer
	supportPlanModel := &marketing.SupportPlanModel{}
	_, err := supportPlanModel.CreateOrUpdatePublicSupportPlanWithLevel(entity.SupportPlanLevel_Developer, &forms.SupportPlan{
		Level:                  entity.SupportPlanLevel_Developer, // developer
		FixedChargePrice:       decimal.NewFromFloat(29),          // 29
		SupportPricePercentage: decimal.NewFromFloat(0.1),
		Type:                   entity.SupportPlanType_PercentagePrice,
		Public:                 true,
		IsSupportFreeTrial:     true,
	})
	if err != nil {
		return err
	}
	// 2. update public 'Standard' SupportPlan: 'Standard' -> 'Developer' and soft delete
	// 3. update private 'Standard' SupportPlan: 'Standard' -> 'Developer'
	err = db.GetDB().Transaction(func(tx *gorm.DB) error {
		standardPlan := entity.SupportPlan{}
		err := entity.NewSupportPlanQuerySet(db.GetDB()).LevelEq(entity.SupportPlanLevel_Standard).PublicEq(true).One(&standardPlan)
		if err != nil {
			if !errors.Is(err, billing_errors.ObjectNotExistsError) {
				return err
			}
		} else {
			err = entity.NewSupportPlanQuerySet(db.GetDB()).IDEq(standardPlan.ID).GetUpdater().SetLevel(entity.SupportPlanLevel_Developer).SetFixedChargePrice(decimal.NewFromFloat(29)).Update()
			if err != nil {
				return err
			}
			err = entity.NewSupportPlanQuerySet(db.GetDB()).IDEq(standardPlan.ID).Delete()
			if err != nil {
				return err
			}
		}
		return entity.NewSupportPlanQuerySet(db.GetDB()).LevelEq(entity.SupportPlanLevel_Standard).PublicEq(false).GetUpdater().SetLevel(entity.SupportPlanLevel_Developer).Update()
	})
	if err != nil {
		return err
	}
	// 4. Query active users in the 'Developer' level and send a JSM notification
	bindedSupportPlans, err := supportPlanModel.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Developer)
	if err != nil {
		return err
	}
	for _, plan := range bindedSupportPlans {
		jsm.SendJSMSupport(&plan)
	}
	return nil
}

func (m *MigrateModel) SyncAllSupportPlanInfoToEventBus() error {
	supportPlanModel := &marketing.SupportPlanModel{}
	// 1. Query all active users
	bindedSupportPlans, err := supportPlanModel.ListAllActiveBindedSupportPlan(entity.SupportPlanLevel_Developer, entity.SupportPlanLevel_Enterprise, entity.SupportPlanLevel_Premium)
	if err != nil {
		return err
	}
	// 2. sync all active bindedSupportPlan info to eventbus
	for _, plan := range bindedSupportPlans {
		supportPlanModel.SyncCurrentSupportToEventBus(&plan)
	}
	// 3. Sync all active MSPCustomer bindedSupportPlan to eventbus
	type QueryResult struct {
		BindedSupportPlanID uint
		TenantID            uint
		Level               string
	}
	sql := `
	WITH msp_bindedSupportplans AS (
    	SELECT 
			a.id as 'binded_support_plan_id',
        	a.tenant_id, 
        	c.level 
    	FROM 
        	binded_support_plans a
    	JOIN 
       	 billing_configs b ON a.tenant_id = b.tenant_id
    	JOIN 
        	support_plans c ON a.support_plan_id = c.id
   	 	WHERE 
        	a.status = 'active' 
        	AND b.status = 'active' 
        	AND b.type = 'MSP'
		)
	SELECT 
    	pm.tenant_id, 
    	msp.level ,
		msp.binded_support_plan_id
	FROM 
    	payment_methods pm
	JOIN 
    	msp_bindedSupportplans msp ON CAST(pm.payment_code AS SIGNED) = msp.tenant_id 
	WHERE pm.payment_type = 'msp' and pm.status = 'active';
	`
	var entities []QueryResult
	err = db.GetDB().Raw(sql).Scan(&entities).Error
	if err != nil {
		return err
	}
	log.Info("get all binded support plan of msp customer", zap.Any("bindedPlanInfo", entities))
	for _, entity := range entities {
		supportPlanModel.SyncCurrentSupportToEventBus(&forms.BindedSupportPlan{
			BindedSupportPlanID: entity.BindedSupportPlanID,
			TenantID:            entity.TenantID,
			SupportPlan: forms.SupportPlan{
				Level: entity.Level,
			},
		})
	}
	return nil
}

func (m *MigrateModel) SyncOnDemandOrgsToJSM() error {
	var entities []entity.TenantPlan
	sql := `SELECT DISTINCT tenant_id FROM tenant_plans WHERE plan = 'on_demand' and status = 'active' ORDER BY created_at DESC;`
	// 1. Query all orgs who has an active on demand record on db, but it's effective plan may be not ondemand
	err := db.GetDB().Raw(sql).Scan(&entities).Error
	if err != nil {
		log.Error("error to get all active on-demand orgs", zap.Error(err))
		return err
	}
	log.Info("success to get all active on-demand orgs", zap.Int("number", len(entities)))
	plan := &plan.TenantPlanModel{}
	for _, e := range entities {
		// 2. Get effective plan of the org
		effectivePlan, err := plan.GetTenantEffectivePlan(e.TenantID)
		if err != nil {
			log.Error("failed to get effective plan for syncing ondemand org to jsm", zap.Uint("tenantID", e.TenantID), zap.Error(err))
			continue
		}
		if effectivePlan.EffectivePlan != entity.TenantPlan_OnDemand {
			log.Info("no need to sync ondemand org to jsm because its effective plan is not on demand", zap.Uint("tenantID", e.TenantID), zap.String("effectivePlan", effectivePlan.EffectivePlan))
			continue
		}
		// 3. Sync orgId to jsm
		jsm.SyncOnDemandInfoToJSM(e.TenantID, true)
	}
	return nil
}

func (m *MigrateModel) MigrateSupportPlanSKU() error {
	supportPlanModel := &marketing.SupportPlanModel{}
	return supportPlanModel.CreateSupportPlanUnits()
}

func unitsNodeMigrate(group *forms.SKUGroup) error {
	var entityList []entity.Unit
	model := &billing.UnitModel{}
	groupLabels := map[string]string{}
	entity.NewUnitQuerySet(db.GetDB()).CategoryEq(group.Category).All(&entityList)
	for _, unitEntity := range entityList {
		requst := forms.UnitByGroup{
			GroupID:     group.GroupID,
			Provider:    "aws",
			Region:      unitEntity.Region,
			GroupLabels: groupLabels,
		}

		model.CreateUnitByGroup(&requst, true)
	}
	return nil
}

func (m *MigrateModel) MigrateNodeSKUGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "node:{provider}:{region}:DEPLOY_TYPE_DEDICATED:{running_instance_type}",
		Name:           "AWS Node-0",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][instanceType={cost_instance_type}]} * 2.3",
		Category:       "Node Compute Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateNodeSKUGroups ", zap.Error(err))
	}
	SKUGroupNode1 := forms.SKUGroup{
		UnitKeyPattern: "node:{provider}:{region}:DEPLOY_TYPE_DEDICATED:{running_instance_type}",
		Name:           "AWS Node-1",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][instanceType={cost_instance_type}]} * 2.3 *2",
		Category:       "Node Compute Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupNode1)
	if err != nil {
		log.Error("failed to MigrateNodeSKUGroups ", zap.Error(err))
	}
	return nil

}
func (m *MigrateModel) MigrateStorageSKUGroups() error {
	model := &billing.SKUGroupModel{}

	SKUGroupStorage := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:gp3",
		Name:           "AWS Storage Disk V0 gp3",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupStorage)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage ", zap.Error(err))
	}

	SKUGroupStorage1 := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:io2",
		Name:           "AWS Storage Disk V0 io2",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=io2]}",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage1", zap.Error(err))
	}

	SKUGroupStorage2 := forms.SKUGroup{
		UnitKeyPattern:  "disk-iops:aws:{region}:gp3",
		Name:            "AWS Storage Disk V0 gp3",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "iops", "multiple_factor_threshold": "3000"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2 ", zap.Error(err))
	}
	SKUGroupStorage3 := forms.SKUGroup{
		UnitKeyPattern:  "disk-iops:aws:{region}:io2",
		Name:            "AWS Storage Disk V0 io2",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=io2]} * 1.5",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "iops", "multiple_factor_threshold": "0"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3 ", zap.Error(err))
	}
	//throughput
	SKUGroupStorage3_1 := forms.SKUGroup{
		UnitKeyPattern:  "disk-throughput:aws:{region}:gp3",
		Name:            "AWS Storage Disk V0 gp3",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "throughput", "multiple_factor_threshold": "125"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3_1)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3_1 ", zap.Error(err))
	}

	SKUGroupStorage3_2 := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:io2",
		Name:           "AWS Storage Disk V0 gp3",
		PriceFormula:   "0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3_2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3_2 ", zap.Error(err))
	}
	return nil

}
func (m *MigrateModel) MigrateDataTransferSKUGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroup_1 := forms.SKUGroup{
		UnitKeyPattern: "network-traffic:aws:{region}:internet",
		Name:           "AWS Data Transfer Cost",
		PriceFormula:   "0.00000001",
		Category:       "Data Transfer Cost - AWS Internet",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroup_1)
	if err != nil {
		log.Error("failed to MigrateDataTransferSKUGroups SKUGroup_1 ", zap.Error(err))
	}
	SKUGroup_2 := forms.SKUGroup{
		UnitKeyPattern: "network-traffic:aws:{region}:cross_az",
		Name:           "AWS Data Transfer Cost",
		PriceFormula:   "0.00000001",
		Category:       "Data Transfer Cost - AWS Same Region",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroup_2)
	if err != nil {
		log.Error("failed to MigrateDataTransferSKUGroups SKUGroup_2 ", zap.Error(err))
	}
	SKUGroup_3 := forms.SKUGroup{
		UnitKeyPattern: "network-traffic:aws:{region}:lb",
		Name:           "AWS Data Transfer Cost",
		PriceFormula:   "0.00000001",
		Category:       "Data Transfer Cost - AWS Load Balancing",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroup_3)
	if err != nil {
		log.Error("failed to MigrateDataTransferSKUGroups SKUGroup_3 ", zap.Error(err))
	}
	//todo
	SKUGroup_4 := forms.SKUGroup{
		UnitKeyPattern: "network-traffic:aws:{region}:ipv4",
		Name:           "AWS Data Transfer Cost",
		PriceFormula:   "0.00000001",
		Category:       "Data Transfer Cost - AWS Load Balancing",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroup_4)
	if err != nil {
		log.Error("failed to MigrateDataTransferSKUGroups SKUGroup_4 ", zap.Error(err))
	}
	SKUGroup_5 := forms.SKUGroup{
		UnitKeyPattern: "network-traffic:aws:{region}:cross_region",
		Name:           "AWS Data Transfer Cost",
		PriceFormula:   "0.00000001",
		Category:       "Data Transfer Cost - AWS Cross Region",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroup_5)
	if err != nil {
		log.Error("failed to MigrateDataTransferSKUGroups SKUGroup_5 ", zap.Error(err))
	}
	return nil

}

func (m *MigrateModel) MigrateBrSKUGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "backup-storage:{provider}:{region}",
		Name:           "Backup Cost",
		PriceFormula:   "provider_price[service_code={service_code}][provider={provider}][region={region}]",
		Category:       "Backup Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateBrSKUGroups SKUGroupNode ", zap.Error(err))
	}
	return nil

}
func (m *MigrateModel) MigrateNewStorageSKUGroups() error {
	model := &billing.SKUGroupModel{}

	SKUGroupStorage1 := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Basic:data:gp3",
		Name:           "AWS Storage Disk Basic - data disk -1",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupStorage1)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage ", zap.Error(err))
	}

	SKUGroupStorage2 := forms.SKUGroup{
		UnitKeyPattern: "disk-additional-iops:aws:{region}:Basic:data:gp3",
		Name:           "AWS Storage Disk Basic - data disk -2",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2 ", zap.Error(err))
	}
	SKUGroupStorage3 := forms.SKUGroup{
		UnitKeyPattern:  "disk-iops:aws:{region}:Basic:data:gp3",
		Name:            "AWS Storage Disk Basic - data disk -3",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "iops", "multiple_factor_threshold": "3000"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3 ", zap.Error(err))
	}
	SKUGroupStorage4 := forms.SKUGroup{
		UnitKeyPattern:  "disk-throughput:aws:{region}:Basic:data:gp3",
		Name:            "AWS Storage Disk Basic - data disk -4",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "throughput", "multiple_factor_threshold": "125"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4 ", zap.Error(err))
	}

	//Standard V2-data disk 2
	SKUGroupStorage1V2 := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Standard:data:gp3",
		Name:           "AWS Storage Disk Standard V2 - data disk -1",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]} *1.8",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1V2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorageV2  ", zap.Error(err))
	}

	SKUGroupStorage2V2 := forms.SKUGroup{
		UnitKeyPattern:  "disk-additional-iops:aws:{region}:Standard:data:gp3",
		Name:            "AWS Storage Disk Standard V2 - data disk -2",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}*5 ",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "iops", "multiple_factor_threshold": "0"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2V2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2V2  ", zap.Error(err))
	}
	SKUGroupStorage3V2 := forms.SKUGroup{
		UnitKeyPattern: "disk-iops:aws:{region}:Standard:data:gp3",
		Name:           "AWS Storage Disk Standard V2 - data disk -3",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3V2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3V2  ", zap.Error(err))
	}
	SKUGroupStorage4V2 := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:Standard:data:gp3",
		Name:           "AWS Storage Disk Standard V2 - data disk -4",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4V2)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4V2  ", zap.Error(err))
	}

	//Standard - logdisk
	SKUGroupStorage1V2L := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Standard:raftlog:gp3",
		Name:           "AWS Storage Disk Standard - logdisk -1",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1V2L)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorageV2L  ", zap.Error(err))
	}

	SKUGroupStorage2V2L := forms.SKUGroup{
		UnitKeyPattern: "disk-additional-iops:aws:{region}:Standard:raftlog:gp3",
		Name:           "AWS Storage Disk Standard - logdisk -2",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2V2L)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2V2L  ", zap.Error(err))
	}
	SKUGroupStorage3V2L := forms.SKUGroup{
		UnitKeyPattern: "disk-iops:aws:{region}:Standard:raftlog:gp3",
		Name:           "AWS Storage Disk Standard - logdisk -3L",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3V2L)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3V2L  ", zap.Error(err))
	}
	SKUGroupStorage4V2L := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:Standard:raftlog:gp3",
		Name:           "AWS Storage Disk Standard - logdisk -4",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4V2L)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4V2L  ", zap.Error(err))
	}

	//Performance-datadisk 3
	SKUGroupStorage1V2P := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Performance:data:gp3",
		Name:           "AWS Storage Disk Performance-datadisk -1",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]} *1.8",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1V2P)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorageV2P  ", zap.Error(err))
	}

	SKUGroupStorage2V2P := forms.SKUGroup{
		UnitKeyPattern:  "disk-additional-iops:aws:{region}:Performance:data:gp3",
		Name:            "AWS Storage Disk Performance-datadisk -2",
		PriceFormula:    "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=gp3]}*5 ",
		Category:        "Node Storage Cost",
		PriceType:       "threshold",
		PriceExpression: map[string]string{"multiple_factor_label": "iops", "multiple_factor_threshold": "0"},
		MeasureUnit:     "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2V2P)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2V2P  ", zap.Error(err))
	}
	SKUGroupStorage3V2P := forms.SKUGroup{
		UnitKeyPattern: "disk-iops:aws:{region}:Performance:data:gp3",
		Name:           "AWS Storage Disk Performance-datadisk -3",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3V2P)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3V2P  ", zap.Error(err))
	}
	SKUGroupStorage4V2P := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:Performance:data:gp3",
		Name:           "AWS Storage Disk Performance-datadisk -4",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4V2P)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4V2P  ", zap.Error(err))
	}

	//Performance-logdisk 4
	SKUGroupStorage1V2PL := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Performance:raftlog:io2",
		Name:           "AWS Storage Disk Performance-logdisk -1",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1V2PL)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorageV2PL  ", zap.Error(err))
	}

	SKUGroupStorage2V2PL := forms.SKUGroup{
		UnitKeyPattern: "disk-additional-iops:aws:{region}:Performance:raftlog:io2",
		Name:           "AWS Storage Disk Performance-logdisk -2",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2V2PL)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2V2PL  ", zap.Error(err))
	}
	SKUGroupStorage3V2PL := forms.SKUGroup{
		UnitKeyPattern: "disk-iops:aws:{region}:Performance:raftlog:io2",
		Name:           "AWS Storage Disk Performance-logdisk -3",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=io2]} * 1.8",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3V2PL)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3V2PL  ", zap.Error(err))
	}
	SKUGroupStorage4V2PL := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:Performance:raftlog:io2",
		Name:           "AWS Storage Disk Performance-logdisk -4",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4V2PL)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4V2PL  ", zap.Error(err))
	}

	//Plus-datadisk 5
	SKUGroupStorage1V2PLD := forms.SKUGroup{
		UnitKeyPattern: "disk-storage:aws:{region}:Plus:data:io2",
		Name:           "AWS Storage Disk Plus-datadisk -1",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=io2]}",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage1V2PLD)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorageV2PLD   ", zap.Error(err))
	}

	SKUGroupStorage2V2PLD := forms.SKUGroup{
		UnitKeyPattern: "disk-additional-iops:aws:{region}:Plus:data:io2",
		Name:           "AWS Storage Disk Plus-datadisk -2 ",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage2V2PLD)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage2V2PL  ", zap.Error(err))
	}
	SKUGroupStorage3V2PLD := forms.SKUGroup{
		UnitKeyPattern: "disk-iops:aws:{region}:Plus:data:io2",
		Name:           "AWS Storage Disk Plus-datadisk -3",
		PriceFormula:   "{provider_price[service_code={service_code}][provider={provider}][region={region}][volumeType=io2]} *1.5",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage3V2PLD)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage3V2PLD   ", zap.Error(err))
	}
	SKUGroupStorage4V2PLD := forms.SKUGroup{
		UnitKeyPattern: "disk-throughput:aws:{region}:Plus:data:io2",
		Name:           "AWS Storage Disk Plus-datadisk -4",
		PriceFormula:   "0.0",
		Category:       "Node Storage Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupStorage4V2PLD)
	if err != nil {
		log.Error("failed to MigrateStorageSKUGroups SKUGroupStorage4V2PLD   ", zap.Error(err))
	}

	return nil
}

func (m *MigrateModel) MigrateSKUGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "backup-storage:{provider}:{region}",
		Name:           "Backup Cost",
		PriceFormula:   "provider_price[service_code={service_code}][provider={provider}][region={region}]",
		Category:       "Backup Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateBrSKUGroups SKUGroupNode ", zap.Error(err))
	}
	return nil

}

func (m *MigrateModel) MigrateDualRegionStorageGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "backup-dual-region-storage:aws:{region}",
		Name:           "AWS Dual Region BR",
		PriceFormula:   "{provider_price[service_code=S3][provider={provider}][region={region}]}* 8.7 / 2",
		Category:       "Dual Region Storage",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateDualRegionStorageGroups SKUGroupNode ", zap.Error(err))
	}
	return nil

}
func (m *MigrateModel) MigrateDualRegionDTGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "backup-dual-region-replication:{provider}:{region}",
		Name:           "AWS Dual Region DT",
		PriceFormula:   "{provider_price[service_code=S3_RegionOut_MAX][provider={provider}][region={region}]}",
		Category:       "Data Transfer Cost - AWS Cross Region",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateDualRegionDTGroups SKUGroupNode ", zap.Error(err))
	}
	return nil

}
func (m *MigrateModel) MigrateTiCDCGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "ticdc-tcu:{provider}:{region}",
		Name:           "TiCDC Capacity Units",
		PriceFormula:   "({provider_price[service_code=EC2_Compute][provider={provider}][region={region}][instanceType={cost_instance_type}]} + {provider_price[service_code=EC2_Storage_Size][provider={provider}][region={region}][volumeType={cost_volume_type}]} * 500 + {provider_price[service_code=EC2_Storage_IOPS][provider={provider}][region={region}][volumeType={cost_volume_type}]} * (4000-3000) + {provider_price[service_code=EC2_Storage_Throughput][provider={provider}][region={region}][volumeType={cost_volume_type}]} * (288-125) ) / 8 * 2.3",
		Category:       "TiCDC Capacity Units",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateTiCDCGroups SKUGroupNode ", zap.Error(err))
	}

	SKUGroupNode1 := forms.SKUGroup{
		UnitKeyPattern: "changefeed-pl-data-processing:{provider}:{region}",
		Name:           "Data Transfer Cost - AWS Private Data Link Cost",
		PriceFormula:   "{provider_price[service_code=PrivateLink][provider={provider}][region={region}]}",
		Category:       "Data Transfer Cost - AWS Private Data Link Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupNode1)
	if err != nil {
		log.Error("failed to MigrateTiCDCGroups SKUGroupNode1 ", zap.Error(err))
	}
	return nil

}

func (m *MigrateModel) MigrateRecoveryGroupServiceGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "rg-base-access:{provider}:{region}",
		Name:           "Recovery Group Service",
		PriceFormula:   "0.00035",
		Category:       "Recovery Group Service",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateRecoveryGroupServiceGroups SKUGroupNode ", zap.Error(err))
	}
	//SKUGroupNode1 := forms.SKUGroup{
	//	UnitKeyPattern: "rg-base-access:{provider}:{region}",
	//	Name:           "Recovery Group Service",
	//	PriceFormula:   "0.00035",
	//	Category:       "Recovery Group Service",
	//	PriceType:      "normal",
	//	MeasureUnit:    "min",
	//}
	//_, err = model.CreateGroup(&SKUGroupNode1)
	//if err != nil {
	//	log.Error("failed to MigrateRecoveryGroupServiceGroups SKUGroupNode1", zap.Error(err))
	//}

	SKUGroupNode2 := forms.SKUGroup{
		UnitKeyPattern: "rg-data-processing:{provider}:{region}",
		Name:           "Same Region Data Processing",
		PriceFormula:   "{provider_price[service_code=Data_Transfer_Within_SameRegion][provider={provider}][region={costRegion}]} *2",
		Category:       "Same Region Data Processing",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupNode2)
	if err != nil {
		log.Error("failed to MigrateRecoveryGroupServiceGroups SKUGroupNode2", zap.Error(err))
	}

	SKUGroupNode3 := forms.SKUGroup{
		UnitKeyPattern: "rg-x-data-processing:{provider}:{region}",
		Name:           "Cross Region Data Processing",
		PriceFormula:   "{provider_price[service_code=Data_Transfer_Within_SameRegion][provider={provider}][region={costRegion}]} + {provider_price[service_code=Data_Transfer_RegionOut_MAX][provider={provider}][region={region}]}",
		Category:       "Cross Region Data Processing",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupNode3)
	if err != nil {
		log.Error("failed to MigrateRecoveryGroupServiceGroups SKUGroupNode3", zap.Error(err))
	}

	return nil

}

func (m *MigrateModel) MigrateDMGroups() error {
	model := &billing.SKUGroupModel{}
	// aws node
	SKUGroupNode := forms.SKUGroup{
		UnitKeyPattern: "dm-rcu:{provider}:{region}",
		Name:           "DM RCU Cost",
		PriceFormula:   "({provider_price[service_code=EC2_Compute][provider={provider}][region={region}][instanceType={cost_instance_type}]} + {provider_price[service_code=EC2_Storage_Size][provider={provider}][region={region}][volumeType={cost_volume_type}]} * 500 + {provider_price[service_code=EC2_Storage_IOPS][provider={provider}][region={region}][volumeType={cost_volume_type}]} * (4000-3000) + {provider_price[service_code=EC2_Storage_Throughput][provider={provider}][region={region}][volumeType={cost_volume_type}]} * (288-125) ) / 8 * 2.3",
		Category:       "DM RCU Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err := model.CreateGroup(&SKUGroupNode)
	if err != nil {
		log.Error("failed to MigrateDMGroups SKUGroupNode ", zap.Error(err))
	}

	SKUGroupNode1 := forms.SKUGroup{
		UnitKeyPattern: "dm-nat:aws:{region}",
		Name:           "Data Transfer Cost - AWS DM NAT Gateway Cost",
		PriceFormula:   "{provider_price[service_code=NatGateway][provider={provider}][region={region}]}",
		Category:       "Data Transfer Cost - AWS DM NAT Gateway Cost",
		PriceType:      "normal",
		MeasureUnit:    "min",
	}
	_, err = model.CreateGroup(&SKUGroupNode1)
	if err != nil {
		log.Error("failed to MigrateDMGroups SKUGroupNode1", zap.Error(err))
	}

	return nil

}
