package api

import (
	"errors"
	"fmt"
	"slices"

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

type SpendLimitAPI struct {
}

type SpendLimitURIStruct struct {
	TenantID            uint `uri:"tenant_id" binding:"required"`
	SpendLimitSettingID uint `uri:"spend_limit_setting_id" binding:"required"`
}

func getSpendLimitURIParams(c *gin.Context) (*SpendLimitURIStruct, error) {
	spendLimitUriStruct := &SpendLimitURIStruct{}
	err := c.ShouldBindUri(spendLimitUriStruct)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	return spendLimitUriStruct, nil
}

// @Summary Create Spend Limit Settings
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param setting body forms.SpendLimitSettingInput true "setting"
// @Success 200 {object} forms.SpendLimitSetting
// @Router /tenants/{tenant_id}/spend_limits [post]
func (s *SpendLimitAPI) CreateSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}

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

	model := &spend_limit.SpendLimitModel{}

	return model.CreateSpendLimitSetting(tenantID, input)
}

// @Summary Update Spend Limit Settings
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param spend_limit_setting_id path string true "spend_limit_setting_id"
// @Param setting body forms.SpendLimitSettingInput true "setting"
// @Success 200 {object} forms.SpendLimitSetting
// @Router /tenants/{tenant_id}/spend_limits/{spend_limit_setting_id} [put]
func (s *SpendLimitAPI) UpdateSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	spendLimitURIStruct, err := getSpendLimitURIParams(c)
	if err != nil {
		return nil, err
	}

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

	model := &spend_limit.SpendLimitModel{}

	return model.UpdateSpendLimitSettings(spendLimitURIStruct.TenantID, spendLimitURIStruct.SpendLimitSettingID, input)
}

// @Summary List All Spend Limits Settings
// @Schemes
// @Accept json
// @Produce json
// @Param q query forms.SpendLimitFilter true "q"
// @Success 200 {object} []forms.SpendLimitSetting
// @Router /spend_limits [get]
func (s *SpendLimitAPI) ListSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {

	model := &spend_limit.SpendLimitModel{}
	query := &forms.SpendLimitFilter{}
	if err := c.ShouldBindQuery(query); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	return model.ListSpendLimitSettings(0, query, nil)
}

// @Summary List All Tenant Spend Limit Settings
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param q query forms.SpendLimitFilter true "q"
// @Success 200 {object} []forms.SpendLimitSetting
// @Router /tenants/{tenant_id}/spend_limits [get]
func (s *SpendLimitAPI) ListTenantSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	bcfg, err := getBillingCfgFromURI(c)
	if err != nil {
		return nil, err
	}
	tenantID := bcfg.TenantID
	query := &forms.SpendLimitFilter{}
	if err := c.ShouldBindQuery(query); err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	//if query.PageSize > 100 {
	//	return nil, billing_errors.NewInvalidInputError(fmt.Errorf("page_size should be less than 100"))
	//}
	if query.RangeID == 0 && slices.Contains(config.Get().GetStringSlice(config.BillsInvisibleList), fmt.Sprintf("%d-%d", bcfg.TenantID, bcfg.BillingConfigID)) {
		err := errors.New("you are restricted view spending limits, please contact our support")
		return nil, billing_errors.NewInvalidInputError(err)
	}
	model := &spend_limit.SpendLimitModel{}

	return model.ListSpendLimitSettings(tenantID, query, nil)
}

// @Summary List All Tenant Spend Limits Info
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param q query forms.SpendLimitFilter true "q"
// @Success 200 {object} []forms.SpendLimitInfo
// @Router /tenants/{tenant_id}/spend_limits/info [get]
func (s *SpendLimitAPI) ListTenantSpendLimitSettingInfo(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	settingList, err := s.ListTenantSpendLimitSetting(c)
	if err != nil {
		return nil, err
	}
	model := &spend_limit.SpendLimitModel{}
	var ret []forms.SpendLimitInfo
	for _, setting := range settingList.([]forms.SpendLimitSetting) {
		info, err := model.SpendLimitSettingWithCost(&setting)
		if err != nil {
			return nil, err
		}
		ret = append(ret, *info)
	}
	return ret, nil
}

// @Summary Get Spend Limit Setting
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param spend_limit_setting_id path string true "spend_limit_setting_id"
// @Success 200 {object} forms.SpendLimitSetting
// @Router /tenants/{tenant_id}/spend_limits/{spend_limit_setting_id} [get]
func (s *SpendLimitAPI) GetSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	spendLimitURIStruct, err := getSpendLimitURIParams(c)
	if err != nil {
		return nil, err
	}

	model := &spend_limit.SpendLimitModel{}

	return model.GetSpendLimitSetting(spendLimitURIStruct.TenantID, spendLimitURIStruct.SpendLimitSettingID)
}

// @Summary Get Spend Limit Info
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param spend_limit_setting_id path string true "spend_limit_setting_id"
// @Success 200 {object} forms.SpendLimitInfo
// @Router /tenants/{tenant_id}/spend_limits/{spend_limit_setting_id}/info [get]
func (s *SpendLimitAPI) GetSpendLimitInfo(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	spendLimitURIStruct, err := getSpendLimitURIParams(c)
	if err != nil {
		return nil, err
	}

	model := &spend_limit.SpendLimitModel{}

	return model.GetSpendLimitInfo(spendLimitURIStruct.TenantID, spendLimitURIStruct.SpendLimitSettingID)
}

// @Summary Delete Spend Limit Setting
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param spend_limit_setting_id path string true "spend_limit_setting_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/spend_limits/{spend_limit_setting_id} [delete]
func (s *SpendLimitAPI) DeleteSpendLimitSetting(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	spendLimitURIStruct, err := getSpendLimitURIParams(c)
	if err != nil {
		return nil, err
	}

	model := &spend_limit.SpendLimitModel{}

	err = model.DeleteSpendLimitSetting(spendLimitURIStruct.TenantID, spendLimitURIStruct.SpendLimitSettingID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

func InitializeSpendLimitRouter(router ginutils.MyRoute) {
	api := &SpendLimitAPI{}
	router.HandleWithError("GET", "/spend_limits", api.ListSpendLimitSetting)
	router.HandleWithError("GET", "/tenants/:tenant_id/spend_limits", api.ListTenantSpendLimitSetting)
	router.HandleWithError("GET", "/tenants/:tenant_id/spend_limits/info", api.ListTenantSpendLimitSettingInfo)
	router.HandleWithError("POST", "/tenants/:tenant_id/spend_limits", api.CreateSpendLimitSetting)
	router.HandleWithError("PUT", "/tenants/:tenant_id/spend_limits/:spend_limit_setting_id", api.UpdateSpendLimitSetting)
	router.HandleWithError("GET", "/tenants/:tenant_id/spend_limits/:spend_limit_setting_id", api.GetSpendLimitSetting)
	router.HandleWithError("GET", "/tenants/:tenant_id/spend_limits/:spend_limit_setting_id/info", api.GetSpendLimitInfo)
	router.HandleWithError("DELETE", "/tenants/:tenant_id/spend_limits/:spend_limit_setting_id", api.DeleteSpendLimitSetting)
}
