package api

import (
	"net/http"
	"strings"

	"github.com/tidbcloud/account/internel/svc/rbac"

	"github.com/tidbcloud/account/internel/dao/role"
	"github.com/tidbcloud/tiinf/errs"
	"go.uber.org/zap"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/monitor/tilog"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/admin/forms"
)

// @Summary CreateRbacRole
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param role body forms.RbacRole true "CreateRbacRole"
// @Success 200 {object} forms.EmptyResponse
// @Router /rbac/role/create [post]
func CreateRbacRole(c *gin.Context) {

	ctx := c.Request.Context()
	req := forms.RbacRole{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	scopeType := entity.ParseScopeType(strings.ToUpper(req.ScopeType))
	if scopeType == entity.RoleBindingScopeInvalid {
		Fail(c, errors.Errorf("scope_type '%s' invalid", req.ScopeType), http.StatusBadRequest)
		return
	}

	rbacRoleSrv, err := rbac.GetRbacRoleSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	//ctx := context.Background()
	//logger := tilog.WithContextL(ctx)
	err = rbacRoleSrv.CreateRbacRole(ctx, req.Name, req.Description, scopeType, req.DisplayName)

	if err != nil {
		tilog.Error(ctx, "CreateRbacRole fail", zap.Error(err))

		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary UpdateRbacRole
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param role body forms.RbacRole true "UpdateRbacRole"
// @Success 200 {object} forms.EmptyResponse
// @Router /rbac/role/update [post]
func UpdateRbacRole(c *gin.Context) {
	ctx := c.Request.Context()
	req := forms.RbacRole{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	scopeType := entity.ParseScopeType(strings.ToUpper(req.ScopeType))
	if scopeType == entity.RoleBindingScopeInvalid {
		Fail(c, errors.Errorf("scope_type '%s' invalid", req.ScopeType), http.StatusBadRequest)
		return
	}

	rbacRoleSrv, err := rbac.GetRbacRoleSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	err = rbacRoleSrv.UpdateRbacRole(ctx, req.ID, req.Name, req.Description, scopeType, req.DisplayName)
	tilog.Info(ctx, "UpdateRbacPrivilege", zap.Any("result", err == nil), zap.Any("ID", req.ID), zap.Any("Name", req.Name), zap.Any("DisplayName", req.DisplayName), zap.Any("Description", req.Description))

	if err != nil {
		tilog.Error(ctx, "UpdateRbacRole fail", zap.Error(err))

		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary ListRbacRoles
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Success 200 {object} []forms.RbacRoleResp
// @Router /rbac/role/list [get]
func ListRbacRoles(c *gin.Context) {
	ctx := c.Request.Context()
	rbacRoleSrv, err := rbac.GetRbacRoleSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	rbacRoles, err := rbacRoleSrv.ListRbacRoles(ctx)

	if err != nil {

		tilog.Error(ctx, "ListRbacRoles", zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, rbacRoles, http.StatusOK)
}

// @Summary GetRbacRoleById
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param id path uint64 true "id"
// @Success 200 {object} forms.RbacRole
// @Router /rbac/role/{id} [get]
func GetRbacRoleById(c *gin.Context) {
	ctx := c.Request.Context()
	req := GetIDReq(c)

	rbacRoleSrv, err := rbac.GetRbacRoleSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	rbacRole, err := rbacRoleSrv.GetRbacRoleByID(ctx, req.ID)

	if err != nil {
		tilog.Error(ctx, "GetRbacRoleById", zap.Error(err))

		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, rbacRole, http.StatusOK)
}

// @Summary DeleteRbacRole
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param id path uint64 true "id"
// @Success 200 {object} forms.EmptyResponse
// @Router /rbac/role/delete/{id} [post]
func DeleteRbacRole(c *gin.Context) {
	ctx := c.Request.Context()
	req := GetIDReq(c)
	roleBindingSvc, err := rbac.GetRbacRoleBindingSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	filter := role.RoleBindingFilter{
		RoleIDs: []entity.ID{entity.ID(req.ID)},
		Status:  []string{entity.RoleBindingStatusActive},
	}
	bindings, err := roleBindingSvc.ListRoleBindings(ctx, filter)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	if len(bindings) > 0 {
		tilog.Info(ctx, "can not delete role: exist role_bindings used role", zap.Any("role_id", req.ID))

		Fail(c, errs.NewForbidden("role_bindings exist active role", nil), http.StatusForbidden)
		return
	}
	rbacRoleSrv, err := rbac.GetRbacRoleSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	err = rbacRoleSrv.DeleteRbacRole(ctx, req.ID)

	if err != nil {
		tilog.Error(ctx, "DeleteRbacRole", zap.Error(err))

		Fail(c, err, http.StatusInternalServerError)
		return
	}

	rbacRolePrivilegeSvc, err := rbac.GetRbacRolePrivilegeSvc()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	err = rbacRolePrivilegeSvc.DeleteRbacRolePrivilegeByRoleID(ctx, req.ID)
	if err != nil {

		tilog.Error(ctx, "DeleteRbacRolePrivilegeByRoleID", zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	tilog.Info(ctx, "DeleteRbacRole", zap.Any("Role ID", req.ID))

	Success(c, nil, http.StatusOK)
}

func GetIDReq(c *gin.Context) forms.GetIDReq {
	req := forms.GetIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return forms.GetIDReq{}
	}
	return req
}
