package workers

import (
	"strconv"
	"time"

	"github.com/pingcap/dbaas/billing2/constant"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/costexplorer"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/marketing"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
)

const (
	checkTypeHistory       = "history"
	checkTypePreviousMonth = "previousMonth"
	checkDay               = 5
)

func CheckSupportPlanAccuracy() error {

	start := timeutils.GetNow()
	log.Info("worker CheckBillingAccuracy start ", zap.Time("startTime", start))

	switch config.Get().GetString(config.CheckSupportPlanAccuracyType) {

	case checkTypePreviousMonth:
		checkPreviousMonth()
		log.Info("worker CheckBillingAccuracy  ", zap.String("checkType", checkTypePreviousMonth))

	case "", checkTypeHistory:
		log.Info("worker CheckBillingAccuracy  ", zap.String("checkType", checkTypeHistory))
		checkHistory()
	}
	log.Info("worker CheckBillingAccuracy end ", zap.Time("endTime", timeutils.GetNow()))

	return nil

}
func checkHistory() {

	model := &marketing.SupportPlanModel{}
	bindedSupportPlans, err := model.ListAllBindedSupportPlan()
	if err != nil {
		log.Error("CheckBillingAccuracy failed to get  support plan", zap.Error(err))
	}
	for _, bindedSupportPlan := range bindedSupportPlans {

		if bindedSupportPlan.Type == entity.SupportPlanType_PercentagePrice {
			timeCol := timeutils.GetMonthDate(bindedSupportPlan.ActiveDate)
			endTime := timeutils.GetMonthDate(timeutils.GetNow())

			for ; timeCol.Before(endTime); timeCol = timeCol.AddDate(0, 1, 0) {
				if bindedSupportPlan.ClosedDate != nil && timeCol.After(*bindedSupportPlan.ClosedDate) {
					break
				}

				check(timeCol.Year(), int(timeCol.Month()), bindedSupportPlan)

			}

		} else {
			//support-plan:fixed
			continue
		}

	}

}
func checkPreviousMonth() {
	now := time.Now()
	year, month, day := now.Date()
	if day != checkDay {
		return
	}
	model := &marketing.SupportPlanModel{}
	bindedSupportPlans, err := model.ListAllBindedSupportPlan()
	if err != nil {
		log.Error("failed to get  support plan", zap.Error(err))
	}
	for _, bindedSupportPlan := range bindedSupportPlans {

		if bindedSupportPlan.Status == entity.BindedSupportPlanStatus_CANCELED {
			continue
		}
		if bindedSupportPlan.Type == entity.SupportPlanType_PercentagePrice {

			check(year, int(month), bindedSupportPlan)

		} else {
			//support-plan:fixed
			continue
		}

	}

}
func check(year int, month int, bindedSupportPlan forms.BindedSupportPlan) {
	measureModel := &billing.MeasureModel{}
	costexplorerModel := &costexplorer.CostExplorerModel{}
	start := timeutils.GetMonthDateByYearMonth(year, month)
	billingConfigID, err := (&billingconfig.BillingConfigModel{}).GetUseBillingConfigIDFromAllByTimeStampWithCache(bindedSupportPlan.TenantID, start.Unix())

	res, err := costexplorerModel.CalculateCost(bindedSupportPlan.TenantID, billingConfigID, &forms.CostExplorerQuery{
		StartDate: start,
		EndDate:   start.AddDate(0, 1, 0),
	})
	if err != nil {
		log.Error(" CheckBillingAccuracy failed to get  costs plan", zap.Error(err), zap.Uint("tenant_id", bindedSupportPlan.TenantID))
		return
	}
	defaultLabelValue := config.Get().GetString(config.DefaultLabelValueCategory)

	costSupportPlan := res.InvoiceCostsCosts.LabelsUnitsCosts["project"][defaultLabelValue].CostPrice
	costProject := res.InvoiceCostsCosts.CostPrice.Add(res.CreditsCosts.CostPrice).Sub(costSupportPlan)
	cost := int(costProject.Mul(decimal.NewFromInt(marketing.SupportPlan_PercentagePriceBase)).Mul(bindedSupportPlan.SupportPricePercentage).IntPart())
	costParam := &forms.UnitCosts{
		CostID:        "check-" + strconv.FormatUint(uint64(bindedSupportPlan.TenantID), 10) + strconv.Itoa(year) + strconv.Itoa(month),
		CostTimeStamp: timeutils.GetNow().Unix(),
		Unit:          constant.PercentagePrice_SKU,
		Cost:          cost,
	}

	newCosts, err := measureModel.CostUnitsDryrun(bindedSupportPlan.TenantID, costParam)
	if err != nil {
		log.Info("CheckBillingAccuracy CostUnitsDryrun error ", zap.Uint("tenant_id", bindedSupportPlan.TenantID), zap.Any("costParam", costParam), zap.Error(err))
		return
	}

	log.Info("CheckBillingAccuracy Checkout  info", zap.Uint("tenant_id", bindedSupportPlan.TenantID),
		zap.Int("year", year), zap.Int("month", month), zap.String("costSupportPlanIndb", costSupportPlan.String()),
		zap.String("costProjectIndb", costProject.String()), zap.String("newCosts", newCosts.CostPrice.String()),
		zap.String("planStatus", bindedSupportPlan.Status))

	if costSupportPlan.Cmp(newCosts.CostPrice) > 10 {
		needCost := costSupportPlan.Sub(newCosts.CostPrice)
		log.Info("CheckBillingAccuracy needDeal result  ", zap.Uint("tenant_id", bindedSupportPlan.TenantID),
			zap.Int("year", year), zap.Int("month", month), zap.String("needCost", needCost.String()))
	}
	if costSupportPlan.Cmp(newCosts.CostPrice) < 10 {
		needDealCost := newCosts.CostPrice.Sub(costSupportPlan)
		log.Info("CheckBillingAccuracy error result  ", zap.Uint("tenant_id", bindedSupportPlan.TenantID),
			zap.Int("year", year), zap.Int("month", month), zap.String("noCost", needDealCost.String()),
			zap.String("fixfee", bindedSupportPlan.FixedChargePrice.String()))
	}
}
