package privilege

import (
	"fmt"
	"strconv"

	"github.com/tidbcloud/devops/api"
	privapi "github.com/tidbcloud/devops/api/privilege"
	privent "github.com/tidbcloud/devops/entity/privilege"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	privsvcapi "github.com/tidbcloud/devops/pkg/service/privilege/api"

	"github.com/gin-gonic/gin"
)

// ListPrivileges
//
//	@Id				PrivilegeControllerListPrivileges
//	@Description	list privileges
//	@Tags			PrivilegeController
//	@Accept			json
//	@Produce		json
//	@Param			req		query		privapi.ListPrivilegesReq	false	"request param"
//	@Success		200		{object}	privapi.ListPrivilegesResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/privilege/privileges [get]
func (c *PrivilegeController) ListPrivileges(ctx *gin.Context) {
	req := &privapi.ListPrivilegesReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := privsvcapi.ListPrivilegesParam{
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}

	privileges, total, err := c.svc.ListPrivileges(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &privapi.ListPrivilegesResp{
		Total:      total,
		Privileges: privileges,
	}
	ctrl.Success(ctx, resp)
}

// RequestPrivilege
//
//	@Id				PrivilegeControllerRequestPrivilege
//	@Description	request privileges
//	@Tags			PrivilegeController
//	@Accept			json
//	@Produce		json
//	@Param			req		body		privapi.RequestPrivilegeReq	true	"request param"
//	@Success		200		{object}	privapi.RequestPrivilegeResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/privilege/privileges [post]
func (c *PrivilegeController) RequestPrivilege(ctx *gin.Context) {
	email := ctx.GetString(consts.CtxUserEmail)
	if email == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("account not found in ctx"), api.ErrCodeInternal)
		return
	}

	req := &privapi.RequestPrivilegeReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := privsvcapi.RequestPrivilegeParam{
		Source:       privent.PrivilegeSourceOpsPortal,
		Reason:       req.Reason,
		ExpiredAt:    req.ExpiredAt,
		Applicant:    email,
		BindingUsers: req.BindingUsers,
	}
	if req.Approval != nil {
		param.Approval = &privsvcapi.RequestApprovalPrivilegeParam{
			Scopes: req.Approval.Scopes,
		}
	}

	id, err := c.svc.RequestPrivilege(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &privapi.RequestPrivilegeResp{PrivilegeID: *id}
	ctrl.Success(ctx, resp)
}

// GetPrivilege
//
//	@Id				PrivilegeControllerGetPrivilege
//	@Description	get a privilege
//	@Tags			PrivilegeController
//	@Accept			json
//	@Produce		json
//	@Param			priv_id	path		string	true	"id"
//	@Success		200		{object}	privapi.GetPrivilegeResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/privilege/privileges/{priv_id} [get]
func (c *PrivilegeController) GetPrivilege(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("priv_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	priv, err := c.svc.GetPrivilege(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &privapi.GetPrivilegeResp{
		Privilege: priv,
	}
	ctrl.Success(ctx, resp)
}

// RevokePrivilege
//
//	@Id				PrivilegeControllerRevokePrivilege
//	@Description	revoke a privilege
//	@Tags			PrivilegeController
//	@Accept			json
//	@Produce		json
//	@Param			priv_id	path		string	true	"id"
//	@Success		200		{object}	api.BaseResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/privilege/privileges/{priv_id}/revoke [post]
func (c *PrivilegeController) RevokePrivilege(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("priv_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.svc.RevokePrivilege(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}
