package api

import (
	"errors"

	"go.uber.org/zap"

	"github.com/google/uuid"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/sqsmeasurehandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/log"
)

type measureAPI struct {
}

// @Summary Cost Units
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Cost body forms.UnitCostsInput true "Cost"
// @Success 200 {object} forms.UnitCosts
// @Router /tenants/{tenant_id}/measures [post]
func (s *measureAPI) CostUnits(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	costForm := &forms.UnitCosts{}
	if err := c.ShouldBindJSON(costForm); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	if config.Get().GetBool(config.HTTPMeasuresToQueue) {
		if costForm.Cost == 0 {
			return costForm, nil
		}
		if costForm.Cost < 0 {
			return nil, billing_errors.NewInvalidInputError(errors.New("cost must be greater than 0"))
		}
		if costForm.CostID == "" {
			costForm.CostID = uuid.NewString()
		}
		err := sqsmeasurehandler.GetMeasureSender().SendMeasure(tenantID, costForm)
		if err != nil {
			log.Error("failed to send measure to sqs", zap.Uint("tenantID", tenantID), zap.String("unit", costForm.Unit))
			return nil, err
		}
		if config.Get().GetBool(config.MeasureDisableSuccessResponse) {
			return nil, nil
		}
		return costForm, nil
	}
	model := &billing.MeasureModel{}

	info, err := model.CostUnits(tenantID, costForm)
	if err != nil {
		return nil, err
	}
	if config.Get().GetBool(config.MeasureDisableSuccessResponse) {
		return nil, nil
	}
	return info, nil
}

// @Summary Cost Units Dryrun
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param Cost body forms.UnitCostsInput true "Cost"
// @Success 200 {object} forms.UnitCosts
// @Router /tenants/{tenant_id}/measures/dryrun [post]
func (s *measureAPI) CostUnitsDryrun(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

	costForm := &forms.UnitCosts{}
	if err := c.ShouldBindJSON(costForm); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	model := &billing.MeasureModel{}

	info, err := model.CostUnitsDryrun(tenantID, costForm)
	if err != nil {
		return nil, err
	}
	return info, nil
}

func InitializeMeasureRouter(router ginutils.MyRoute) {
	s := &measureAPI{}
	router.HandleWithError("POST", "/tenants/:tenant_id/measures", s.CostUnits)
	router.HandleWithError("POST", "/tenants/:tenant_id/measures/dryrun", s.CostUnitsDryrun)
}

func InitializeMeasureRouter2Stress(router ginutils.MyRoute) {
	s := &measureAPI{}
	router.HandleWithError("POST", "/tenants/:tenant_id/measures", s.CostUnits)
}

func InitializeInnerMeasureRouter(router ginutils.MyRoute) {
	s := &measureAPI{}
	router.HandleWithError("POST", "/orgs/:tenant_id/measures", s.CostUnits)
}
