package api

import (
	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/validator"
	"go.uber.org/multierr"
)

// @Summary Create Unit
// @Schemes
// @Accept json
// @Produce json
// @Param unit body forms.UnitInfo true "unit"
// @Success 200 {object} forms.UnitInfo
// @Router /units [post]
func (s *API) CreateUnit(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	unitform := &forms.UnitInfo{}
	if err := c.ShouldBindJSON(unitform); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	err := validator.ValidateStruct(unitform)
	if err != nil {
		return nil, err
	}

	model := &billing.UnitModel{}

	info, err := model.CreateUnit(unitform)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary Bulk CreateOrUpdate Unit
// @Schemes
// @Accept json
// @Produce json
// @Param unit body []forms.UnitInfo true "units"
// @Success 200 {object} forms.OKType
// @Router /units/_bulk [post]
func (s *API) BulkCreateOrUpdateUnit(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	unitforms := []forms.UnitInfo{}
	if err := c.ShouldBindJSON(&unitforms); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	for _, unitInfo := range unitforms {
		err := validator.ValidateStruct(&unitInfo)
		if err != nil {
			return nil, err
		}
	}
	model := &billing.UnitModel{}

	errs := []error{}

	for _, unitInfo := range unitforms {
		_, err := model.CreateAndUpdateUnit(&unitInfo)
		if err != nil {
			errs = append(errs, err)
		}
	}

	err := multierr.Combine(errs...)

	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Update Unit
// @Schemes
// @Accept json
// @Produce json
// @Param unit_key path string true "UnitKey"
// @Success 200 {object} forms.UnitInfo
// @Router /units/{unit_key} [put]
func (s *API) UpdateUnit(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	uriForm := &UnitURIForm{}

	if err := c.ShouldBindUri(&uriForm); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	unitform := &forms.UnitInfo{}
	if err := c.ShouldBindJSON(unitform); err != nil {
		return nil, err
	}

	unitform.UnitKey = uriForm.UnitKey
	err := validator.ValidateStruct(unitform)
	if err != nil {
		return nil, err
	}
	model := &billing.UnitModel{}

	info, err := model.UpdateUnit(unitform)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// @Summary List Units
// @Schemes
// @Accept json
// @Produce json
// @Success 200 {object} []forms.UnitInfo
// @Router /units [get]
func (s *API) ListUnits(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	model := &billing.UnitModel{}
	units, err := model.GetAllUnits()
	if err != nil {
		return nil, err
	}
	return units, nil
}

type UnitURIForm struct {
	UnitKey string `uri:"unit_key"` //nolint
}

// @Summary Get Unit
// @Schemes
// @Accept json
// @Produce json
// @Param unit_key path string true "UnitKey"
// @Success 200 {object} forms.UnitInfo
// @Router /units/{unit_key} [get]
func (s *API) GetUnit(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	uriForm := &UnitURIForm{}

	if err := c.ShouldBindUri(&uriForm); err != nil {
		return nil, err
	}

	model := &billing.UnitModel{}
	unitInfo, err := model.GetUnit(uriForm.UnitKey)
	if err != nil {
		return nil, err
	}
	return unitInfo, nil
}

// @Summary Delete Unit
// @Schemes
// @Accept json
// @Produce json
// @Param unit_key path string true "UnitKey"
// @Success 200 {object} forms.OKType
// @Router /units/{unit_key} [delete]
func (s *API) DeleteUnit(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	uriForm := &UnitURIForm{}

	if err := c.ShouldBindUri(&uriForm); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	model := &billing.UnitModel{}
	err := model.DeleteUnit(uriForm.UnitKey)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

func InitializeUnitRouter(router ginutils.MyRoute) {
	api := &API{}
	router.HandleWithError("GET", "/units", api.ListUnits)
	router.HandleWithError("POST", "/units", api.CreateUnit)
	router.HandleWithError("POST", "/units/_bulk", api.BulkCreateOrUpdateUnit)
	router.HandleWithError("GET", "/units/:unit_key", api.GetUnit)
	router.HandleWithError("PUT", "/units/:unit_key", api.UpdateUnit)
	router.HandleWithError("DELETE", "/units/:unit_key", api.DeleteUnit)
}
