package api

import (
	"errors"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/constant"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/cachehandler"
	"github.com/pingcap/dbaas/billing2/model/creditriskcontrol"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
)

type CloseQueryStruct struct {
	TenantID uint   `uri:"tenant_id" binding:"required"`
	Reason   string `uri:"reason" binding:"required"`
}

func getCloseQueryFromURI(c *gin.Context) (*CloseQueryStruct, error) {
	q := &CloseQueryStruct{}
	err := c.ShouldBindUri(q)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}
	return q, nil
}

type TenantCreditRiskCloseAPI struct{}

var closeModel = &creditriskcontrol.TenantCloseModel{}

// @Summary Get Credit Risk Close Policy
// @Schemes
// @Accept json
// @Produce json
// @Param credit_risk_level_id path string true "credit_risk_level_id"
// @Success 200 {object} forms.TenantClosePolicy
// @Router /credit_risk_levels/{credit_risk_level_id}/risk_close_policy [get]
func (*TenantCreditRiskCloseAPI) GetRiskClosePolicy(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	creditRiskLevelID, err := getCreditRiskLevelIDFromURI(c)
	if err != nil {
		return nil, err
	}
	return closeModel.GetTenantClosePolicyByCreditRiskLevel(creditRiskLevelID)
}

// @Summary Create Or Update Credit Risk Close Policy
// @Schemes
// @Accept json
// @Produce json
// @Param credit_risk_level_id path string true "credit_risk_level_id"
// @Param policy body forms.TenantClosePolicyInput true "policy"
// @Success 200 {object} forms.OKType
// @Router /credit_risk_levels/{credit_risk_level_id}/risk_close_policy [put]
func (*TenantCreditRiskCloseAPI) CreateOrUpdateRiskClosePolicy(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	creditRiskLevelID, err := getCreditRiskLevelIDFromURI(c)
	if err != nil {
		return nil, err
	}

	policy := &forms.TenantClosePolicy{}
	err = c.ShouldBindJSON(policy)
	if err != nil {
		return nil, billing_errors.NewInvalidInputError(err)
	}

	_, err = closeModel.GetTenantClosePolicyByCreditRiskLevel(creditRiskLevelID)
	if err != nil {
		if errors.Is(err, billing_errors.ObjectNotExistsError) {
			err = closeModel.CreateTenantRiskClosePolicy(creditRiskLevelID, policy)
			if err != nil {
				return nil, err
			}
			return forms.OK, nil
		}
	}

	err = closeModel.UpdateTenantRiskClosePolicy(creditRiskLevelID, policy)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Delete Credit Risk Close Policy
// @Schemes
// @Accept json
// @Produce json
// @Param credit_risk_level_id path string true "credit_risk_level_id"
// @Success 200 {object} forms.OKType
// @Router /credit_risk_levels/{credit_risk_level_id}/risk_close_policy [delete]
func (*TenantCreditRiskCloseAPI) DeleteRiskClosePolicy(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	creditRiskLevelID, err := getCreditRiskLevelIDFromURI(c)
	if err != nil {
		return nil, err
	}
	err = closeModel.DeleteTenantRiskClosePolicy(creditRiskLevelID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Get Tenant Credit Risk Close Info
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.TenantCloseInfo
// @Router /tenants/{tenant_id}/tenant_risk_close [get]
func (*TenantCreditRiskCloseAPI) GetTenantRiskCloseInfo(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	info, err := closeModel.GetTenantCloseInfo(tenantID)
	if err != nil {
		if errors.Is(err, billing_errors.ObjectNotExistsError) {
			closePolicyInfo, err := closeModel.GetTenantClosePolicyByCreditRiskLevel(tenantID)
			if err != nil {
				return nil, err
			}
			return &forms.TenantCloseInfo{
				TenantID:    tenantID,
				ClosePolicy: closePolicyInfo.ClosePolicy,
				Status:      entity.TenantCloseStatus_NOTSTARTED,
			}, nil
		} else {
			return nil, err
		}
	}
	return info, nil
}

// @Summary Get Tenant Credit Risk Close Policy
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.TenantClosePolicy
// @Router /tenants/{tenant_id}/risk_close_policy [get]
func (*TenantCreditRiskCloseAPI) GetTenantRiskClosePolicy(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	return closeModel.GetTenantClosePolicyByCreditRiskLevel(tenantID)
}

// @Summary Run Tenant Credit Risk Close
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/tenant_risk_close/actions/run [post]
func (*TenantCreditRiskCloseAPI) RunTenantRiskClose(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	err = closeModel.RunTenantClose(tenantID, entity.TenantCloseReason_Risky)
	if err != nil {
		return nil, err
	}
	cachehandler.Get().Delete(constant.GetTenantEffectivePlanCacheName, tenantID)
	return forms.OK, nil
}

// @Summary Run Tenant Credit Risk Close
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/tenant_risk_close/actions/cancel [post]
func (*TenantCreditRiskCloseAPI) CancelTenantRiskClose(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	err = closeModel.CancelTenantClose(tenantID)
	if err != nil {
		return nil, err
	}
	cachehandler.Get().Delete(constant.GetTenantEffectivePlanCacheName, tenantID)
	return forms.OK, nil
}

// @Summary Get Tenant Closure Reason
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} []string
// @Router /tenants/{tenant_id}/closing/reasons [get]
func (*TenantCreditRiskCloseAPI) ListTenantCloseReasons(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	return closeModel.ListCloseReasons()
}

// @Summary Get Tenant Closure Policy
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param reason path string true "reason"
// @Success 200 {object} forms.TenantClosePolicy
// @Router /tenants/{tenant_id}/closing/reasons/{reason}/policies [get]
func (*TenantCreditRiskCloseAPI) GetTenantClosePolicy(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getCloseQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	policy, err := closeModel.GetTenantClosePolicyByTenantID(query.TenantID, query.Reason)
	if err != nil {
		return nil, err
	}
	return policy, nil
}

// @Summary Run Tenant Closure Process
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Param reason path string true "reason"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/closing/reasons/{reason}/actions/run [post]
func (*TenantCreditRiskCloseAPI) RunTenantClose(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	query, err := getCloseQueryFromURI(c)
	if err != nil {
		return nil, err
	}
	err = closeModel.RunTenantClose(query.TenantID, query.Reason)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Cancel Tenant Closure Process
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.OKType
// @Router /tenants/{tenant_id}/closing/actions/cancel [post]
func (*TenantCreditRiskCloseAPI) CancelTenantClose(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	err = closeModel.CancelTenantClose(tenantID)
	if err != nil {
		return nil, err
	}
	return forms.OK, nil
}

// @Summary Get Tenant Credit Closure Process Info
// @Schemes
// @Accept json
// @Produce json
// @Param tenant_id path string true "tenant_id"
// @Success 200 {object} forms.TenantCloseInfo
// @Router /tenants/{tenant_id}/closing [get]
func (*TenantCreditRiskCloseAPI) GetTenantCloseInfo(c *gin.Context) (ginutils.HandlerFuncResult, error) {
	tenantID, err := getTenantIDFromURI(c)
	if err != nil {
		return nil, err
	}
	info, err := closeModel.GetTenantCloseInfo(tenantID)
	if err != nil {
		if errors.Is(err, billing_errors.ObjectNotExistsError) {
			return &forms.TenantCloseInfo{
				TenantID: tenantID,
				Status:   entity.TenantCloseStatus_NOTSTARTED,
			}, nil
		} else {
			return nil, err
		}
	}
	return info, nil
}

func InitializeTenantCreditRiskCloseRouter(router ginutils.MyRoute) {
	s := &TenantCreditRiskCloseAPI{}
	router.HandleWithError("GET", "/credit_risk_levels/:credit_risk_level_id/risk_close_policy", s.GetRiskClosePolicy)
	router.HandleWithError("PUT", "/credit_risk_levels/:credit_risk_level_id/risk_close_policy", s.CreateOrUpdateRiskClosePolicy)
	router.HandleWithError("DELETE", "/credit_risk_levels/:credit_risk_level_id/risk_close_policy", s.DeleteRiskClosePolicy)
	router.HandleWithError("GET", "/tenants/:tenant_id/risk_close_policy", s.GetTenantRiskClosePolicy)
	router.HandleWithError("GET", "/tenants/:tenant_id/tenant_risk_close", s.GetTenantRiskCloseInfo)
	router.HandleWithError("POST", "/tenants/:tenant_id/tenant_risk_close/actions/run", s.RunTenantRiskClose)
	router.HandleWithError("POST", "/tenants/:tenant_id/tenant_risk_close/actions/cancel", s.CancelTenantRiskClose)

	router.HandleWithError("GET", "/tenants/:tenant_id/closing", s.GetTenantCloseInfo)
	router.HandleWithError("GET", "/tenants/:tenant_id/closing/reasons", s.ListTenantCloseReasons)
	router.HandleWithError("GET", "/tenants/:tenant_id/closing/reasons/:reason/policies", s.GetTenantClosePolicy)
	router.HandleWithError("POST", "/tenants/:tenant_id/closing/reasons/:reason/actions/run", s.RunTenantClose)
	router.HandleWithError("POST", "/tenants/:tenant_id/closing/actions/cancel", s.CancelTenantClose)
}
