package alert

import (
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/pingcap-inc/pchub/internal/pkg/middleware"

	modelv1 "github.com/pingcap-inc/pchub/model/v1"

	srvv1 "github.com/pingcap-inc/pchub/internal/apiserver/service/v1"
	"github.com/pingcap-inc/pchub/internal/apiserver/store"
	"github.com/pingcap-inc/pchub/internal/pkg/code"
	"github.com/pingcap-inc/pchub/pkg/errors"
	"github.com/pingcap-inc/pchub/pkg/log"
	"github.com/pingcap-inc/pchub/pkg/util/ginres"
)

type AlertController struct {
	srv srvv1.Service
}

func NewAlertController(store store.Factory) *AlertController {
	return &AlertController{
		srv: srvv1.NewService(store),
	}
}

func (a *AlertController) ConfirmAlert(c *gin.Context) {
	log.L(c).Info("AlertController.ConfirmAlert function called.")

	params := ginres.ParseQuery(c, "ard")
	if params["ard"] == "" {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "ard is required"), nil)

		return
	}

	recordID, err := strconv.ParseUint(params["ard"], 10, 64)
	if err != nil {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "ard should be a number"), nil)

		return
	}

	err = a.srv.Alert().ConfirmAlert(c, recordID)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	ginres.WriteResponse(c, nil, "ok")
}

//nolint:gocyclo,cyclop // acceptable
func (a *AlertController) CreateAlert(c *gin.Context) {
	log.L(c).Info("AlertController.CreateAlert function called.")

	var rb modelv1.CostAlertCreateBody
	if err := c.ShouldBindJSON(&rb); err != nil {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, err.Error()), nil)

		return
	}

	if rb.ResourceMID == "" {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "resourceMID is required"), nil)

		return
	}

	resource, err := a.srv.CloudResource().GetByManagementID(c, rb.ResourceMID)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	members, err := a.srv.CloudResource().DetailMembers(c, resource.ID, "")
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	authingId, err := middleware.GetAuthingId(c)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	user, err := a.srv.User().GetUserByID(c, authingId)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	isMem := false
	for _, m := range *members {
		if m.Member.LarkUserID == user.LarkUserID {
			isMem = true

			break
		}
	}

	if resource.OwnerLarkID != user.LarkUserID && !isMem {
		log.L(c).Error("only owner can create alert, " + err.Error())
		ginres.WriteResponse(c, errors.WithCode(code.ErrPermissionDenied, "only member can create alert"), nil)

		return
	}

	if rb.Type != 1 && rb.Type != 2 && rb.Type != 3 && rb.Type != 4 {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "type must be in (1, 2, 3, 4)"), nil)

		return
	}

	if rb.Threshold <= 0 {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "threshold must > 0"), nil)

		return
	}

	alertR, err := a.srv.Alert().CreateAlert(c, &rb, resource)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	ginres.WriteResponse(c, nil, alertR)
}

func (a *AlertController) UpdateAlert(c *gin.Context) {
	log.L(c).Info("AlertController.UpdateAlert function called.")

	var rb modelv1.CostAlertUpdateBody
	if err := c.ShouldBindJSON(&rb); err != nil {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, err.Error()), nil)

		return
	}

	if rb.ID == 0 {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "id is required"), nil)

		return
	}

	alertR, err := a.srv.Alert().GetAlertRuleByID(c, rb.ID)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	authingId, err := middleware.GetAuthingId(c)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	user, err := a.srv.User().GetUserByID(c, authingId)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	members, err := a.srv.CloudResource().DetailMembers(c, alertR.ResourceID, "")
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	isMem := false
	for _, m := range *members {
		if m.Member.LarkUserID == user.LarkUserID {
			isMem = true

			break
		}
	}
	if alertR.Resource.OwnerLarkID != user.LarkUserID && !isMem {
		log.L(c).Error("only owner can update alert, " + err.Error())
		ginres.WriteResponse(c, errors.WithCode(code.ErrPermissionDenied, "only member can update alert"), nil)

		return
	}

	res, err := a.srv.Alert().UpdateAlert(c, &rb, alertR)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	ginres.WriteResponse(c, nil, res)
}

func (a *AlertController) DeleteAlert(c *gin.Context) {
	log.L(c).Info("AlertController.DeleteAlert function called.")

	params := ginres.ParseQuery(c, "id")
	if params["id"] == "" {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "id is required"), nil)

		return
	}

	id, err := strconv.ParseUint(params["id"], 10, 64)
	if err != nil {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "id should be a number"), nil)

		return
	}

	alertR, err := a.srv.Alert().GetAlertRuleByID(c, id)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	authingId, err := middleware.GetAuthingId(c)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	user, err := a.srv.User().GetUserByID(c, authingId)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	members, err := a.srv.CloudResource().DetailMembers(c, alertR.ResourceID, "")
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}
	isMem := false
	for _, m := range *members {
		if m.Member.LarkUserID == user.LarkUserID {
			isMem = true

			break
		}
	}
	if alertR.Resource.OwnerLarkID != user.LarkUserID && !isMem {
		log.L(c).Error("only owner can delete alert, " + err.Error())
		ginres.WriteResponse(c, errors.WithCode(code.ErrPermissionDenied, "only member can delete alert"), nil)

		return
	}

	err = a.srv.Alert().DeleteAlert(c, id)
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	ginres.WriteResponse(c, nil, "ok")
}

func (a *AlertController) ListAlerts(c *gin.Context) {
	log.L(c).Info("AlertController.ListAlerts function called.")

	params := ginres.ParseQuery(c, "mid")
	if params["mid"] == "" {
		ginres.WriteResponse(c, errors.WithCode(code.ErrValidation, "mid is required"), nil)

		return
	}

	alerts, err := a.srv.Alert().ListAlertsByResourceMngID(c, params["mid"])
	if err != nil {
		ginres.WriteResponse(c, err, nil)

		return
	}

	ginres.WriteResponse(c, nil, alerts)
}
