package billing

import (
	"errors"
	"github.com/pingcap/dbaas/cost-metering-service/billing_errors"
	"github.com/pingcap/dbaas/cost-metering-service/db"
	"github.com/pingcap/dbaas/cost-metering-service/db/entity"
	"github.com/pingcap/dbaas/cost-metering-service/forms"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

type MeasureModel struct {
}

func checkInput(input *forms.UnitCostsInput) error {

	if input.CostID == "" {
		return billing_errors.NewInvalidInputError(errors.New("cost ID must be have"))

	}
	// if input.Labels check
	// todo

	return nil
}

func (model *MeasureModel) ProcessMeasure(input *forms.UnitCostsInput) (*forms.UnitCosts, error) {
	//1 check
	err := checkInput(input)
	if err != nil {
		return nil, err

	}
	if input.Cost < 0 {
		return nil, billing_errors.NewInvalidInputError(errors.New("cost must be greater than 0"))
	}
	//2. unique
	var ret entity.Measure
	err = entity.NewMeasureQuerySet(db.GetDB()).CostIDEq(input.CostID).One(&ret)
	if err != nil {
		return nil, err
	}
	//3. unit
	unit, err := (&UnitModel{}).GetUnit(input.Unit)
	if err != nil {
		return nil, err
	}

	costEntity := &entity.Measure{
		CostID:   input.CostID,
		Labels:   input.Labels,
		CostTime: input.CostTime,
		Cost:     input.Cost,
		Unit:     input.Unit,
		Status:   entity.MeasureStatus_INIT,
	}

	if unit.SkuType == entity.UnitSkuType_COST {
		costEntity.CostPrice = decimal.NewFromInt(int64(input.Cost))
	}
	err = costEntity.Create(db.GetDB().Clauses(clause.Insert{Modifier: "IGNORE"}))
	if err != nil {
		return nil, err
	}

	return nil, nil

}

func (model *MeasureModel) GetMeasure(CalculateDate time.Time, units []string) ([]forms.UnitCosts, error) {
	var measures []entity.Measure
	err := entity.NewMeasureQuerySet(db.GetDB()).UnitIn(units...).StatusEq(entity.MeasureStatus_INIT).CostTimeGte(CalculateDate).
		CostTimeLt(CalculateDate.AddDate(0, 0, 1)).All(&measures)
	if err != nil {
		return nil, err
	}
	costMap := make(map[string]decimal.Decimal)

	for _, measure := range measures {
		costMap[measure.Unit] = costMap[measure.Unit].Add(measure.CostPrice)
	}
	var res []forms.UnitCosts
	for unit, totalCost := range costMap {
		res = append(res, forms.UnitCosts{
			Unit:      unit,
			CostPrice: totalCost,
		})
	}
	return res, nil
}
func (model *MeasureModel) DealMeasureByCost(CalculateDate time.Time, costUnit string, units []string, cost decimal.Decimal) error {
	return db.GetDB().Transaction(func(tx *gorm.DB) error {
		var measures []entity.Measure
		err := entity.NewMeasureQuerySet(db.GetDB()).UnitIn(units...).StatusEq(entity.MeasureStatus_INIT).CostTimeGte(CalculateDate).
			CostTimeLt(CalculateDate.AddDate(0, 0, 1)).All(&measures)
		if err != nil {
			return err
		}
		var costMeasures []entity.Measure
		err = entity.NewMeasureQuerySet(db.GetDB()).UnitEq(costUnit).StatusEq(entity.MeasureStatus_INIT).CostTimeGte(CalculateDate).
			CostTimeLt(CalculateDate.AddDate(0, 0, 1)).All(&costMeasures)
		if err != nil {
			return err
		}

		var sumCost int
		for _, measure := range measures {
			sumCost += measure.Cost
		}
		if sumCost == 0 {
			return errors.New("sumCost cannot be zero")
		}

		for index := range measures {
			measures[index].CostPrice = cost.Div(decimal.NewFromInt(int64(sumCost)))
		}
		err = entity.NewMeasureQuerySet(db.GetDB()).UnitEq(costUnit).CostTimeGte(CalculateDate).
			CostTimeLt(CalculateDate.AddDate(0, 0, 1)).GetUpdater().SetStatus(entity.CostCenterStatus_DONE).Update()
		if err != nil {
			return err
		}
		for _, measure := range measures {
			err := tx.Model(&entity.Measure{}).Where("cost_id = ?", measure.CostID).Updates(measure).Error
			if err != nil {
				return err
			}
		}

		return nil
	})
}
