package api

import (
	"errors"
	"fmt"
	"slices"
	"sort"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"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/payment/spend_limit"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
)

type BudgetAPI struct {
}

type BudgetQuery struct {
	TenantIDStruct
	BudgetID uint `uri:"budget_id" binding:"required"`
}

type BudgetThresholdQuery struct {
	BudgetQuery
	ThresholdID uint `uri:"threshold_id" binding:"required"`
}

func getBudgetQueryFromURI(c *gin.Context) (*BudgetQuery, error) {
	query := &BudgetQuery{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return query, billing_errors.NewInvalidInputError(err)
	}
	return query, nil
}

func getBudgetThresholdQueryFromURI(c *gin.Context) (*BudgetThresholdQuery, error) {
	query := &BudgetThresholdQuery{}
	err := c.ShouldBindUri(query)
	if err != nil {
		return query, billing_errors.NewInvalidInputError(err)
	}
	return query, nil
}

// @Summary List All Tenant Budgets
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} []forms.Budget
// @Router /tenants/{tenant_id}/budgets [get]
func (b *BudgetAPI) ListTenantBudgets(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenant_id, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	settingList, err := model.ListTenantSpendLimitSettings(tenant_id, false)
	if err != nil {
		return nil, err
	}
	var ret []*forms.Budget
	for _, setting := range settingList {
		isFreeQuotaOnlySpendingLimit, err := model.IsFreeQuotaOnlySpendingLimit(&setting)
		if err != nil {
			return nil, err
		}
		if isFreeQuotaOnlySpendingLimit || setting.BizType == "" {
			continue
		}
		budget, err := model.SpendingLimitToBudgetView(setting)
		if err != nil {
			if errors.Is(err, billing_errors.ObjectNotExistsError) {
				continue
			}
			return nil, err
		}
		ret = append(ret, budget)
	}
	orderMap := map[string]int{entity.SpendLimitBizType_Normal: 0, entity.SpendLimitBizType_ServerlessCluster: 1}
	sort.Slice(ret, func(i, j int) bool {
		if ret[i].BizType == ret[j].BizType {
			return ret[i].CreatedAt.After(ret[j].CreatedAt)
		}
		return orderMap[ret[i].BizType] < orderMap[ret[j].BizType]
	})
	return ret, nil
}

// @Summary Get Tenant Budget
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Success 200 {object} forms.Budget
// @Router /tenants/{tenant_id}/budgets/{budget_id} [get]
func (b *BudgetAPI) GetTenantBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	setting, err := model.GetSpendLimitSetting(query.TenantID, query.BudgetID)
	if err != nil {
		return nil, err
	}
	ignore, err := model.IsFreeQuotaOnlySpendingLimit(setting)
	if err != nil {
		return nil, err
	}
	if ignore {
		return nil, billing_errors.NewObjectNotExistsError(nil)
	}
	budget, err := model.SpendingLimitToBudgetView(*setting)
	if err != nil {
		return nil, err
	}
	return budget, nil
}

// @Summary Create Budget
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget body forms.Budget true "budget"
// @Success 200 {object} forms.Budget
// @Router /tenants/{tenant_id}/budgets [post]
func (b *BudgetAPI) CreateTenantBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	var budget forms.Budget
	err = c.ShouldBindJSON(&budget)
	if err != nil {
		return nil, err
	}
	if budget.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	if len(budget.Thresholds) > config.Get().GetInt(config.MaxBudgetThresholds) || len(budget.Thresholds) <= 1 {
		return nil, billing_errors.NewInvalidInputError(errors.New("invalidated threshold length"))
	}
	if budget.RangeIDStr != 0 {
		budget.RangeID = budget.RangeIDStr
	}
	setting, err := model.CreateSpendLimitSetting(tenantID, &budget.SpendLimitSetting)
	if err != nil {
		return nil, err
	}
	err = model.CreateThresholds(setting.TenantID, setting.SpendLimitSettingID, budget.Thresholds)
	if err != nil {
		return nil, err
	}
	return model.SpendingLimitToBudgetView(*setting)
}

// @Summary Update Budget
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Param budget body forms.Budget false "name"
// @Success 200 {object} forms.Budget
// @Router /tenants/{tenant_id}/budgets/{budget_id} [put]
func (b *BudgetAPI) UpdateTenantBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	params := &forms.Budget{}
	err = c.ShouldBindJSON(&params)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	spendingLimit, err := model.GetSpendLimitSetting(query.TenantID, query.BudgetID)
	if err != nil {
		return nil, err
	}
	if spendingLimit.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	if params.RangeIDStr != 0 || params.RangeID != 0 {
		spendingLimit.RangeID = params.RangeID
		if params.RangeIDStr != 0 {
			spendingLimit.RangeID = params.RangeIDStr
		}
		if params.RangeType != "" {
			spendingLimit.RangeType = params.RangeType
		}
	}
	if params.Name != "" {
		spendingLimit.Name = params.Name
	}
	if !params.Limit.IsZero() {
		spendingLimit.Limit = params.Limit
	}
	if params.BizData != "" {
		spendingLimit.BizData = params.BizData
	}
	if len(params.Thresholds) != 0 {
		err = model.UpdateThresholds(query.TenantID, query.BudgetID, params.Thresholds)
		if err != nil {
			return nil, err
		}
	}
	settings, err := model.UpdateSpendLimitSettings(query.TenantID, query.BudgetID, spendingLimit)
	if err != nil {
		return nil, err
	}
	return model.SpendingLimitToBudgetView(*settings)
}

// @Summary Delete Budget
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/budgets/{budget_id} [delete]
func (b *BudgetAPI) DeleteTenantBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	spendingLimit, err := model.GetSpendLimitSetting(query.TenantID, query.BudgetID)
	if spendingLimit.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	err = model.DeleteSpendLimitSetting(query.TenantID, query.BudgetID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Add Budget Threshold
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Success 200 {object} []forms.SpendLimitThreshold
// @Router /tenants/{tenant_id}/budgets/{budget_id}/thresholds [post]
func (b *BudgetAPI) AddThreshold(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	threshold := &forms.SpendLimitThreshold{}
	err = c.ShouldBindJSON(threshold)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	res, err := model.CreateTenantSpendingLimitThreshold(query.TenantID, query.BudgetID, threshold)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// @Summary Update Budget Threshold
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Param threshold_id path string true "threshold_id"
// @Success 200 {object} []forms.SpendLimitThreshold
// @Router /tenants/{tenant_id}/budgets/{budget_id}/thresholds/{threshold_id} [post]
func (b *BudgetAPI) UpdateThreshold(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetThresholdQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	threshold := &forms.SpendLimitThreshold{}
	err = c.ShouldBindJSON(threshold)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	res, err := model.UpdateTenantSpendingLimitThreshold(query.TenantID, query.BudgetID, query.ThresholdID, threshold)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// @Summary Delete Budget Threshold
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param budget_id path string true "budget_id"
// @Param threshold_id path string true "threshold_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/budgets/{budget_id}/thresholds/{threshold_id} [delete]
func (b *BudgetAPI) DeleteThreshold(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getBudgetThresholdQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	err = model.DeleteTenantThreshold(query.TenantID, query.BudgetID, query.ThresholdID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

func budgetToOpenAPI(budget *forms.Budget) (*forms.BudgetForOpenAPI, error) {
	applyCredits := false
	applyDiscounts := false
	enableEmail := true
	if budget.BizType == entity.SpendLimitBizType_Normal {
		bizData, err := (&spend_limit.SpendLimitModel{}).UnmarshallNormalBizData(budget.BizData)
		if err != nil {
			return nil, err
		}
		applyCredits = bizData.ApplyCredits
		applyDiscounts = bizData.ApplyDiscount
		enableEmail = bizData.EnableEmailNotification
	}
	thresholds := make([]forms.BudgetThreshold, len(budget.Thresholds))
	for i, t := range budget.Thresholds {
		thresholds[i] = forms.BudgetThreshold{Proportion: t.Proportion}
	}
	return &forms.BudgetForOpenAPI{
		BudgetId:                budget.SpendLimitSettingID,
		Name:                    budget.Name,
		BizType:                 forms.BudgetBizType(budget.BizType),
		Interval:                forms.BudgetInterval(budget.Interval),
		RangeType:               forms.BudgetRangeType(budget.RangeType),
		RangeId:                 budget.RangeID,
		CostType:                forms.BudgetCostType(budget.CostType),
		Limit:                   budget.Limit,
		Cost:                    budget.Costs,
		ApplyCredits:            applyCredits,
		ApplyDiscounts:          applyDiscounts,
		EnableEmailNotification: enableEmail,
		Thresholds:              thresholds,
	}, nil
}

func openAPIToBudget(input *forms.BudgetForOpenAPI) (*forms.Budget, error) {
	var budget forms.Budget
	var err error
	budget.Name = input.Name
	budget.Interval = string(input.Interval)
	budget.Limit = input.Limit
	budget.CostType = string(input.CostType)
	budget.RangeType = string(input.RangeType)
	budget.RangeID = input.RangeId
	budget.BizType = string(input.BizType)
	if budget.BizType == entity.SpendLimitBizType_Normal {
		budget.BizData, err = (&spend_limit.SpendLimitModel{}).MarshallNormalBizData(&forms.NormalBizData{
			ApplyDiscount:           input.ApplyDiscounts,
			ApplyCredits:            input.ApplyCredits,
			EnableEmailNotification: input.EnableEmailNotification,
		})
		if err != nil {
			return nil, err
		}
	}
	budget.Thresholds = make([]*forms.SpendLimitThreshold, len(input.Thresholds))
	for i, t := range input.Thresholds {
		budget.Thresholds[i] = &forms.SpendLimitThreshold{Proportion: t.Proportion}
	}
	return &budget, nil
}

// @Id Billing_ListOrgBudgets
// @Tags Partner, PartnerBilling
// @Summary List organization budgets
// @Description This endpoint retrieves a list of budgets for an organization.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Success 200 {object} []forms.BudgetForOpenAPI
// @Router /budgets [get]
func (b *BudgetAPI) ListOrgBudgets(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	if slices.Contains(config.Get().GetStringSlice(config.BillsInvisibleList), fmt.Sprintf("%d-%d", billingCfgStruct.TenantID, billingCfgStruct.BillingConfigID)) {
		err := errors.New("you are restricted view budgets, please contact our support")
		return nil, billing_errors.NewInvalidInputError(err)
	}
	model := &spend_limit.SpendLimitModel{}
	settingList, err := model.ListTenantSpendLimitSettings(billingCfgStruct.TenantID, false)
	if err != nil {
		return nil, err
	}
	var budgets []*forms.Budget
	for _, setting := range settingList {
		isFreeQuotaOnlySpendingLimit, err := model.IsFreeQuotaOnlySpendingLimit(&setting)
		if err != nil {
			return nil, err
		}
		if isFreeQuotaOnlySpendingLimit || setting.BizType == "" {
			continue
		}
		budget, err := model.SpendingLimitToBudgetView(setting)
		if err != nil {
			if errors.Is(err, billing_errors.ObjectNotExistsError) {
				continue
			}
			return nil, err
		}
		budgets = append(budgets, budget)
	}
	orderMap := map[string]int{entity.SpendLimitBizType_Normal: 0, entity.SpendLimitBizType_ServerlessCluster: 1}
	sort.Slice(budgets, func(i, j int) bool {
		if budgets[i].BizType == budgets[j].BizType {
			return budgets[i].CreatedAt.After(budgets[j].CreatedAt)
		}
		return orderMap[budgets[i].BizType] < orderMap[budgets[j].BizType]
	})
	ret := make([]*forms.BudgetForOpenAPI, len(budgets))
	for i, budget := range budgets {
		ret[i], err = budgetToOpenAPI(budget)
		if err != nil {
			return nil, err
		}
	}
	return ret, nil
}

func parseBudgetId(c *gin.Context) (uint, error) {
	var q struct {
		BudgetID uint `uri:"budgetId" binding:"required"`
	}
	err := c.ShouldBindUri(&q)
	if err != nil {
		return 0, err
	}
	return q.BudgetID, nil
}

// @Id Billing_CreateBudget
// @Tags Partner, PartnerBilling
// @Summary Create a budget for an organization
// @Description This endpoint creates a new budget for an organization.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param budget body forms.BudgetForOpenAPI true "budget"
// @Success 200 {object} forms.BudgetForOpenAPI
// @Router /budgets [post]
func (b *BudgetAPI) CreateBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	if billingCfgStruct.Type != entity.BillingConfigType_MSP {
		return nil, billing_errors.NewInvalidInputError(errors.New("partner type is unsupported"))
	}
	model := &spend_limit.SpendLimitModel{}
	var input forms.BudgetForOpenAPI
	err = c.ShouldBindJSON(&input)
	if err != nil {
		return nil, err
	}
	budget, err := openAPIToBudget(&input)

	if budget.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	if len(budget.Thresholds) > config.Get().GetInt(config.MaxBudgetThresholds) || len(budget.Thresholds) <= 1 {
		return nil, billing_errors.NewInvalidInputError(errors.New("invalidated threshold length"))
	}
	if budget.RangeIDStr != 0 {
		budget.RangeID = budget.RangeIDStr
	}
	setting, err := model.CreateSpendLimitSetting(billingCfgStruct.TenantID, &budget.SpendLimitSetting)
	if err != nil {
		return nil, err
	}
	err = model.CreateThresholds(setting.TenantID, setting.SpendLimitSettingID, budget.Thresholds)
	if err != nil {
		return nil, err
	}
	retBudget, err := model.SpendingLimitToBudgetView(*setting)
	if err != nil {
		return nil, err
	}
	return budgetToOpenAPI(retBudget)
}

// @Id Billing_GetBudget
// @Tags Partner, PartnerBilling
// @Summary Get a budget
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param budgetId path string true "The ID of the budget."
// @Success 200 {object} forms.BudgetForOpenAPI
// @Router /budgets/{budgetId} [get]
func (b *BudgetAPI) GetBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	budgetID, err := parseBudgetId(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	spendingLimit, err := model.GetSpendLimitSetting(billingCfgStruct.TenantID, budgetID)
	if err != nil {
		return nil, err
	}
	retBudget, err := model.SpendingLimitToBudgetView(*spendingLimit)
	if err != nil {
		return nil, err
	}
	return budgetToOpenAPI(retBudget)
}

// @Id Billing_UpdateBudget
// @Tags Partner, PartnerBilling
// @Summary Update a budget for an organization
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param budgetId path string true "The ID of the budget to delete. You can get the budget ID from the response of [List organization budgets](#tag/PartnerBilling/operation/Billing_ListOrgBudgets)."
// @Param budget body forms.BudgetForOpenAPI true "budget"
// @Success 200 {object} forms.BudgetForOpenAPI
// @Router /budgets/{budgetId} [patch]
func (b *BudgetAPI) UpdateBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	budgetID, err := parseBudgetId(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	var input forms.BudgetForOpenAPI
	err = c.ShouldBindJSON(&input)
	if err != nil {
		return nil, err
	}
	params, err := openAPIToBudget(&input)
	spendingLimit, err := model.GetSpendLimitSetting(billingCfgStruct.TenantID, budgetID)
	if err != nil {
		return nil, err
	}
	if spendingLimit.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	if params.RangeIDStr != 0 || params.RangeID != 0 {
		spendingLimit.RangeID = params.RangeID
		if params.RangeIDStr != 0 {
			spendingLimit.RangeID = params.RangeIDStr
		}
		if params.RangeType != "" {
			spendingLimit.RangeType = params.RangeType
		}
	}
	if params.Name != "" {
		spendingLimit.Name = params.Name
	}
	if !params.Limit.IsZero() {
		spendingLimit.Limit = params.Limit
	}
	if params.BizData != "" {
		spendingLimit.BizData = params.BizData
	}
	if len(params.Thresholds) != 0 {
		err = model.UpdateThresholds(billingCfgStruct.TenantID, budgetID, params.Thresholds)
		if err != nil {
			return nil, err
		}
	}
	settings, err := model.UpdateSpendLimitSettings(billingCfgStruct.TenantID, budgetID, spendingLimit)
	if err != nil {
		return nil, err
	}
	retBudget, err := model.SpendingLimitToBudgetView(*settings)
	if err != nil {
		return nil, err
	}
	return budgetToOpenAPI(retBudget)
}

// @Id Billing_DeleteBudget
// @Tags Partner, PartnerBilling
// @Summary Delete a budget for an organization
// @Description This endpoint deletes a specific budget for an organization.
// @Schemes
// @Accept json
// @Produce json
// @Param accessOrgId query string true "org_id"
// @Param budgetId path string true "The ID of the budget to delete. You can get the budget ID from the response of [List organization budgets](#tag/PartnerBilling/operation/Billing_ListOrgBudgets)."
// @Success 200 {object} forms.OKType
// @Router /budgets/{budgetId} [delete]
func (b *BudgetAPI) DeleteBudget(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	billingCfgStruct, err := getBillingCfgFromAuth(c)
	if err != nil {
		return nil, err
	}
	budgetID, err := parseBudgetId(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	spendingLimit, err := model.GetSpendLimitSetting(billingCfgStruct.TenantID, budgetID)
	if spendingLimit.BizType != entity.SpendLimitBizType_Normal {
		return nil, billing_errors.NewInvalidInputError(errors.New("forbidden"))
	}
	err = model.DeleteSpendLimitSetting(billingCfgStruct.TenantID, budgetID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

func InitializeBudgetRouter(router ginutils.MyRoute) {
	api := &BudgetAPI{}
	router.HandleWithError("GET", "/tenants/:tenant_id/budgets", api.ListTenantBudgets)
	router.HandleWithError("POST", "/tenants/:tenant_id/budgets", api.CreateTenantBudget)
	router.HandleWithError("GET", "/tenants/:tenant_id/budgets/:budget_id", api.GetTenantBudget)
	router.HandleWithError("PUT", "/tenants/:tenant_id/budgets/:budget_id", api.UpdateTenantBudget)
	router.HandleWithError("DELETE", "/tenants/:tenant_id/budgets/:budget_id", api.DeleteTenantBudget)
	router.HandleWithError("POST", "/tenants/:tenant_id/budgets/:budget_id/thresholds", api.AddThreshold)
	router.HandleWithError("UPDATE", "/tenants/:tenant_id/budgets/:budget_id/thresholds/:threshold_id", api.UpdateThreshold)
	router.HandleWithError("DELETE", "/tenants/:tenant_id/budgets/:budget_id/thresholds/:threshold_id", api.DeleteThreshold)
}

func InitializeBudgetRouterForOpenAPI(router ginutils.MyRoute) {
	api := &BudgetAPI{}
	router.HandleWithError("GET", "/budgets", api.ListOrgBudgets)
	router.HandleWithError("GET", "/budgets/:budgetId", api.GetBudget)
	router.HandleWithError("POST", "/budgets", api.CreateBudget)
	router.HandleWithError("PATCH", "/budgets/:budgetId", api.UpdateBudget)
	router.HandleWithError("DELETE", "/budgets/:budgetId", api.DeleteBudget)
}
