package comsvc

import (
	"context"

	"github.com/tidbcloud/account/central/utils"
	"github.com/tidbcloud/account/commons/database"
	"github.com/tidbcloud/account/internel/dao"
	"github.com/tidbcloud/account/internel/dao/entity"
	daorole "github.com/tidbcloud/account/internel/dao/role"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type AssignIdentRolesParam struct {
	IdentTye entity.TargetType
	IdentID  uint64
	IdentVal string
	OrgID    uint64
	ORole    *string           // org role
	PRoles   map[uint64]string // project role
	CRoles   map[uint64]string // cluster role
	DRoles   map[uint64]string // dataapp role, only for patch action
	Status   string
	NoDBUser bool // default to create db user
}

type Operator struct {
	OpTye entity.TargetType
	OpID  uint64
	OpVal string
	OpIP  string
}

type AssignIdentRolesRet struct {
	InsOBinding *ToChangeBindingInfo
	UpdOBinding *ToChangeBindingInfo
	DelOBinding *ToChangeBindingInfo

	InsPBindings []ToChangeBindingInfo
	UpdPBindings []ToChangeBindingInfo
	DelPBindings []ToChangeBindingInfo

	InsCBindings []ToChangeBindingInfo
	UpdCBindings []ToChangeBindingInfo
	DelCBindings []ToChangeBindingInfo

	InsDBindings []ToChangeBindingInfo
	UpdDBindings []ToChangeBindingInfo
	DelDBindings []ToChangeBindingInfo
}

type ToChangeBindingInfo struct {
	BindingID  uint64
	ScopeId    uint64
	FromRole   string
	ToRole     string
	FromStatus string
}

// UpdateIdentitySpecRoles for specific scope id, to change role
func UpdateIdentitySpecRoles(ctx context.Context, p *AssignIdentRolesParam) (*AssignIdentRolesRet, error) {
	bm := utils.Begin(ctx, "PatchIdentityRoles", zap.Any("param", p))
	defer bm.OnExit(ctx)

	if p == nil {
		return nil, errs.NewBadRequest("param is nil", nil)
	}
	if p.IdentTye == "" || p.IdentID == 0 || p.IdentVal == "" {
		return nil, errs.NewBadRequest("identity value is blank", nil)
	}
	if p.Status == "" {
		return nil, errs.NewBadRequest("status is blank", nil)
	}

	// create to change org/project/cluster/dataapp role info
	var (
		toInsOBinding, toUpdOBinding   *ToChangeBindingInfo
		toInsPBindings, toUpdPBindings []ToChangeBindingInfo
		toInsCBindings, toUpdCBindings []ToChangeBindingInfo
		toInsDBindings, toUpdDBindings []ToChangeBindingInfo
	)

	if p.OrgID != 0 && p.ORole != nil {
		oBindingMap, err := listScopeRoleBindings(ctx, p.IdentTye, p.IdentID, entity.RoleBindingScopeOrg, []entity.ID{entity.ID(p.OrgID)})
		if err != nil {
			tilog.Error(ctx, "PatchIdentityRoles list org role binding error", zap.Any("err", err))
			return nil, errs.NewInternalUnknown("list role bindings internal error", err)
		}
		toInsOBinding, toUpdOBinding = diffRoleChange(p.OrgID, *p.ORole, oBindingMap)
	}
	if len(p.PRoles) > 0 {
		var projectIds []entity.ID
		for id := range p.PRoles {
			projectIds = append(projectIds, entity.ID(id))
		}
		pBindingMap, err := listScopeRoleBindings(ctx, p.IdentTye, p.IdentID, entity.RoleBindingScopeProject, projectIds)
		if err != nil {
			tilog.Error(ctx, "PatchIdentityRoles list project role binding error", zap.Any("err", err))
			return nil, errs.NewInternalUnknown("list role bindings internal error", err)
		}
		for pid, prole := range p.PRoles {
			toInsBinding, toUpdBinding := diffRoleChange(pid, prole, pBindingMap)
			if toInsBinding != nil {
				toInsPBindings = append(toInsPBindings, *toInsBinding)
			}
			if toUpdBinding != nil {
				toUpdPBindings = append(toUpdPBindings, *toUpdBinding)
			}
		}
	}
	if len(p.CRoles) > 0 {
		var clusterIds []entity.ID
		for id := range p.CRoles {
			clusterIds = append(clusterIds, entity.ID(id))
		}
		cBindingMap, err := listScopeRoleBindings(ctx, p.IdentTye, p.IdentID, entity.RoleBindingScopeTiDB, clusterIds)
		if err != nil {
			tilog.Error(ctx, "PatchIdentityRoles list cluster role binding error", zap.Any("err", err))
			return nil, errs.NewInternalUnknown("list role bindings internal error", err)
		}
		for cid, crole := range p.CRoles {
			toInsBinding, toUpdBinding := diffRoleChange(cid, crole, cBindingMap)
			if toInsBinding != nil {
				toInsCBindings = append(toInsCBindings, *toInsBinding)
			}
			if toUpdBinding != nil {
				toUpdCBindings = append(toUpdCBindings, *toUpdBinding)
			}
		}
	}
	if len(p.DRoles) > 0 {
		var dataappIds []entity.ID
		for id := range p.DRoles {
			dataappIds = append(dataappIds, entity.ID(id))
		}
		dBindingMap, err := listScopeRoleBindings(ctx, p.IdentTye, p.IdentID, entity.RoleBindingScopeDataAPP, dataappIds)
		if err != nil {
			tilog.Error(ctx, "PatchIdentityRoles list dataapp role binding error", zap.Any("err", err))
			return nil, errs.NewInternalUnknown("list role bindings internal error", err)
		}
		for did, drole := range p.DRoles {
			toInsBinding, toUpdBinding := diffRoleChange(did, drole, dBindingMap)
			if toInsBinding != nil {
				toInsDBindings = append(toInsDBindings, *toInsBinding)
			}
			if toUpdBinding != nil {
				toUpdDBindings = append(toUpdDBindings, *toUpdBinding)
			}
		}
	}

	err := transactionHandleDB(ctx, p, toInsOBinding, toUpdOBinding,
		toInsPBindings, toUpdPBindings, []ToChangeBindingInfo{},
		toInsCBindings, toUpdCBindings, []ToChangeBindingInfo{},
		toInsDBindings, toUpdDBindings, []ToChangeBindingInfo{})

	r := &AssignIdentRolesRet{
		InsOBinding:  toInsOBinding,
		UpdOBinding:  toUpdOBinding,
		DelOBinding:  nil,
		InsPBindings: toInsPBindings,
		UpdPBindings: toUpdPBindings,
		DelPBindings: []ToChangeBindingInfo{},
		InsCBindings: toInsCBindings,
		UpdCBindings: toUpdCBindings,
		DelCBindings: []ToChangeBindingInfo{},
		InsDBindings: toInsDBindings,
		UpdDBindings: toUpdDBindings,
		DelDBindings: []ToChangeBindingInfo{},
	}

	if err == nil {
		postChangeBindings(ctx, p, r)
	}

	return r, err

}

func listScopeRoleBindings(ctx context.Context, identType entity.TargetType, identId uint64, scopeType entity.ScopeType, scopeIds []entity.ID) (map[uint64]entity.RoleBinding, error) {
	bindings, err := dao.GetDaoHub().Role.ListRoleBindings(ctx, database.DBConn.DB, daorole.RoleBindingFilter{
		TargetType: []entity.TargetType{identType},
		TargetIDs:  []entity.ID{entity.ID(identId)},
		ScopeTypes: []entity.ScopeType{scopeType},
		ScopeIDs:   scopeIds,
	})
	if err != nil {
		return nil, err
	}
	var bindingMap = map[uint64]entity.RoleBinding{}
	for _, b := range bindings {
		bindingMap[b.ScopeID.Uint64()] = b
	}
	return bindingMap, nil
}

func diffRoleChange(scopeId uint64, scopeRole string, eBindings map[uint64]entity.RoleBinding) (toInsertBinding, toUpdateBinding *ToChangeBindingInfo) {
	if binding, ok := eBindings[scopeId]; ok {
		if binding.Role != scopeRole {
			toUpdateBinding = &ToChangeBindingInfo{
				BindingID: binding.ID.Uint64(),
				ScopeId:   scopeId,
				FromRole:  binding.Role,
				ToRole:    scopeRole,
			}
		}
	} else {
		toInsertBinding = &ToChangeBindingInfo{
			ScopeId: scopeId,
			ToRole:  scopeRole,
		}
	}
	return
}

func transactionHandleDB(ctx context.Context, p *AssignIdentRolesParam,
	toInsOBinding, toUpdOBinding *ToChangeBindingInfo,
	toInsPBindings, toUpdPBindings, toDelPBindings []ToChangeBindingInfo,
	toInsCBindings, toUpdCBindings, toDelCBindings []ToChangeBindingInfo,
	toInsDBindings, toUpdDBindings, toDelDBindings []ToChangeBindingInfo) error {

	return database.Transaction(func(tx *gorm.DB) error {
		// create org role
		if toInsOBinding != nil {
			_, err := dao.GetDaoHub().Role.CreateRole(ctx, tx, p.IdentTye, p.IdentID, entity.RoleBindingScopeOrg, toInsOBinding.ScopeId, toInsOBinding.ToRole, p.Status)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB create org role binding error", zap.Any("change_binding_info", toInsOBinding), zap.Any("err", err))
				return errs.NewInternalUnknown("create org role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB create org role binding success", zap.Any("change_binding_info", toInsOBinding))
		}
		// update org role
		if toUpdOBinding != nil {
			err := dao.GetDaoHub().Role.UpdateRoleBindingByID(ctx, tx, entity.RoleBinding{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID: entity.ID(toUpdOBinding.BindingID),
					},
				},
				Role: toUpdOBinding.ToRole,
			})
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB update org role binding error", zap.Any("err", err))
				return errs.NewInternalUnknown("update org role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB update org role binding success", zap.Any("change_binding_info", toUpdOBinding))
		}

		// create project role
		for _, b := range toInsPBindings {
			_, err := dao.GetDaoHub().Role.CreateRole(ctx, tx, p.IdentTye, p.IdentID, entity.RoleBindingScopeProject, b.ScopeId, b.ToRole, p.Status)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB create project role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("create project role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB create project role binding success", zap.Any("change_binding_info", b))
		}
		// update project role
		for _, b := range toUpdPBindings {
			err := dao.GetDaoHub().Role.UpdateRoleBindingByID(ctx, tx, entity.RoleBinding{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID: entity.ID(b.BindingID),
					},
				},
				Role: b.ToRole,
			})
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB update project role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("update project role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB update project role binding success", zap.Any("change_binding_info", b))

		}
		// delete project role
		for _, b := range toDelPBindings {
			err := dao.GetDaoHub().Role.DeleteRoleByID(ctx, tx, b.BindingID)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB delete project role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("delete project role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB delete project role binding success", zap.Any("change_binding_info", b))
		}

		// create cluster role
		for _, b := range toInsCBindings {
			_, err := dao.GetDaoHub().Role.CreateRole(ctx, tx, p.IdentTye, p.IdentID, entity.RoleBindingScopeTiDB, b.ScopeId, b.ToRole, p.Status)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB create cluster role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("create cluster role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB create cluster role binding success", zap.Any("change_binding_info", b))
		}
		// update cluster role
		for _, b := range toUpdCBindings {
			err := dao.GetDaoHub().Role.UpdateRoleBindingByID(ctx, tx, entity.RoleBinding{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID: entity.ID(b.BindingID),
					},
				},
				Role: b.ToRole,
			})
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB update cluster role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("update cluster role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB update cluster role binding success", zap.Any("change_binding_info", b))
		}
		// delete cluster role
		for _, b := range toDelCBindings {
			err := dao.GetDaoHub().Role.DeleteRoleByID(ctx, tx, b.BindingID)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB delete cluster role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("delete cluster role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB delete cluster role binding success", zap.Any("change_binding_info", b))
		}

		// create dataapp role
		for _, b := range toInsDBindings {
			_, err := dao.GetDaoHub().Role.CreateRole(ctx, tx, p.IdentTye, p.IdentID, entity.RoleBindingScopeDataAPP, b.ScopeId, b.ToRole, p.Status)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB create dataapp role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("create dataapp role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB create dataapp role binding success", zap.Any("change_binding_info", b))
		}
		// update cluster role
		for _, b := range toUpdDBindings {
			err := dao.GetDaoHub().Role.UpdateRoleBindingByID(ctx, tx, entity.RoleBinding{
				Updatable: entity.Updatable{
					Base: entity.Base{
						ID: entity.ID(b.BindingID),
					},
				},
				Role: b.ToRole,
			})
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB update dataapp role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("update dataapp role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB update dataapp role binding success", zap.Any("change_binding_info", b))
		}
		// delete cluster role
		for _, b := range toDelDBindings {
			err := dao.GetDaoHub().Role.DeleteRoleByID(ctx, tx, b.BindingID)
			if err != nil {
				tilog.Error(ctx, "transactionHandleDB delete dataapp role binding error", zap.Any("change_binding_info", b), zap.Any("err", err))
				return errs.NewInternalUnknown("delete dataapp role binding internal error", err)
			}
			tilog.Info(ctx, "transactionHandleDB delete dataapp role binding success", zap.Any("change_binding_info", b))
		}

		return nil
	})
}

func UpdateIdentityRoles(ctx context.Context, p *AssignIdentRolesParam) (*AssignIdentRolesRet, error) {
	bm := utils.Begin(ctx, "UpdateIdentityRoles", zap.Any("param", p))
	defer bm.OnExit(ctx)

	if p == nil {
		return nil, errs.NewBadRequest("param is nil", nil)
	}
	if p.IdentTye == "" || p.IdentID == 0 || p.IdentVal == "" {
		return nil, errs.NewBadRequest("identity value is blank", nil)
	}
	if p.OrgID == 0 {
		return nil, errs.NewBadRequest("org value is blank", nil)
	}
	if p.Status == "" {
		return nil, errs.NewBadRequest("status is blank", nil)
	}

	// list identity exist org/project/cluster role bindings
	eOBindingMap, ePBindingMap, eCBindingMap, err := listIdentExistRoleBindings(ctx, p.IdentTye, p.IdentID, p.OrgID)
	if err != nil {
		tilog.Error(ctx, "UpdateIdentityRoles list identity exist org project cluster role binding error", zap.Any("err", err))
		return nil, err
	}

	// create to change org/project/cluster role info
	var toInsOBinding, toUpdOBinding *ToChangeBindingInfo
	var toInsPBindings, toUpdPBindings, toDelPBindings []ToChangeBindingInfo
	var toInsCBindings, toUpdCBindings, toDelCBindings []ToChangeBindingInfo

	if p.ORole != nil {
		toInsOBindings, toUpdOBindings, _ := diffRolesChange(eOBindingMap, map[uint64]string{p.OrgID: *p.ORole})
		if len(toInsOBindings) > 0 {
			toInsOBinding = &toInsOBindings[0]
		}
		if len(toUpdOBindings) > 0 {
			toUpdOBinding = &toUpdOBindings[0]
		}
	}
	// nil means not to change, empty means to remove scope roles
	if p.PRoles != nil {
		toInsPBindings, toUpdPBindings, toDelPBindings = diffRolesChange(ePBindingMap, p.PRoles)
	}
	if p.CRoles != nil {
		toInsCBindings, toUpdCBindings, toDelCBindings = diffRolesChange(eCBindingMap, p.CRoles)
	}

	err = transactionHandleDB(ctx, p, toInsOBinding, toUpdOBinding,
		toInsPBindings, toUpdPBindings, toDelPBindings,
		toInsCBindings, toUpdCBindings, toDelCBindings,
		[]ToChangeBindingInfo{}, []ToChangeBindingInfo{}, []ToChangeBindingInfo{})

	r := &AssignIdentRolesRet{
		InsOBinding:  toInsOBinding,
		UpdOBinding:  toUpdOBinding,
		DelOBinding:  nil,
		InsPBindings: toInsPBindings,
		UpdPBindings: toUpdPBindings,
		DelPBindings: toDelPBindings,
		InsCBindings: toInsCBindings,
		UpdCBindings: toUpdCBindings,
		DelCBindings: toDelCBindings,
		InsDBindings: []ToChangeBindingInfo{},
		UpdDBindings: []ToChangeBindingInfo{},
		DelDBindings: []ToChangeBindingInfo{},
	}

	if err == nil {
		postChangeBindings(ctx, p, r)
	}

	return r, err
}

func diffRolesChange(originRoles map[uint64]entity.RoleBinding, finalRoles map[uint64]string) (toInsertBindings, toUpdateBindings, toDeleteBindings []ToChangeBindingInfo) {
	for scopeId, role := range finalRoles {
		if binding, ok := originRoles[scopeId]; ok {
			if binding.Role != role {
				toUpdateBindings = append(toUpdateBindings, ToChangeBindingInfo{
					BindingID: binding.ID.Uint64(),
					ScopeId:   scopeId,
					FromRole:  binding.Role,
					ToRole:    role,
				})
			}
		} else {
			toInsertBindings = append(toInsertBindings, ToChangeBindingInfo{
				ScopeId: scopeId,
				ToRole:  role,
			})
		}
	}
	for scopeId, binding := range originRoles {
		if _, ok := finalRoles[scopeId]; !ok {
			toDeleteBindings = append(toDeleteBindings, ToChangeBindingInfo{
				BindingID:  binding.ID.Uint64(),
				ScopeId:    scopeId,
				FromRole:   binding.Role,
				FromStatus: binding.Status,
			})
		}
	}

	return
}

func listIdentExistRoleBindings(ctx context.Context, identType entity.TargetType, identId, orgId uint64) (map[uint64]entity.RoleBinding, map[uint64]entity.RoleBinding, map[uint64]entity.RoleBinding, error) {
	// list exist role bindings
	bindings, err := ListIdentRoleBindings(ctx, identType, identId, orgId,
		[]entity.ScopeType{entity.RoleBindingScopeOrg, entity.RoleBindingScopeProject, entity.RoleBindingScopeTiDB}, []string{})
	if err != nil {
		return nil, nil, nil, errs.NewInternalUnknown("list role bindings internal error", err)
	}

	var oBindingMap, pBindingMap, cBindingMap = map[uint64]entity.RoleBinding{}, map[uint64]entity.RoleBinding{}, map[uint64]entity.RoleBinding{}

	for _, b := range bindings {
		if b.ScopeType == entity.RoleBindingScopeOrg {
			oBindingMap[b.ScopeID.Uint64()] = b
		} else if b.ScopeType == entity.RoleBindingScopeProject {
			pBindingMap[b.ScopeID.Uint64()] = b
		} else if b.ScopeType == entity.RoleBindingScopeTiDB {
			cBindingMap[b.ScopeID.Uint64()] = b
		}
	}

	return oBindingMap, pBindingMap, cBindingMap, nil
}

func postChangeBindings(ctx context.Context, p *AssignIdentRolesParam, r *AssignIdentRolesRet) {
	if !p.NoDBUser {
		// db user
		if (r.InsOBinding != nil && r.InsOBinding.ToRole == entity.RoleOrgOwner) ||
			(r.UpdOBinding != nil && r.UpdOBinding.ToRole == entity.RoleOrgOwner) {
			e := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, database.DBConn.DB, 0, p.IdentID, p.IdentVal, p.OrgID, p.IdentID, entity.CommandStatusWaiting, entity.CommandActionCreate, p.IdentTye, p.IdentTye)
			tilog.Info(ctx, "UpdateIdentityRoles rec create/update org role binding to create db user result", zap.Any("org_id", p.OrgID), zap.Any("err", e))
		} else {
			for _, b := range r.InsPBindings {
				e := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, database.DBConn.DB, p.IdentID, p.IdentID, p.IdentVal, p.OrgID, b.ScopeId, entity.CommandStatusWaiting, entity.CommandActionCreate, p.IdentTye, p.IdentTye)
				tilog.Info(ctx, "UpdateIdentityRoles rec create project role binding to create db user result", zap.Any("org_id", p.OrgID), zap.Any("project_id", b.ScopeId), zap.Any("err", e))
			}
			for _, b := range r.DelPBindings {
				e := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, database.DBConn.DB, p.IdentID, p.IdentID, p.IdentVal, p.OrgID, b.ScopeId, entity.CommandStatusWaiting, entity.CommandActionDelete, p.IdentTye, p.IdentTye)
				tilog.Info(ctx, "UpdateIdentityRoles rec delete project role binding to delete db user result", zap.Any("org_id", p.OrgID), zap.Any("project_id", b.ScopeId), zap.Any("err", e))
			}
			for _, b := range r.UpdPBindings {
				e := dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, database.DBConn.DB, p.IdentID, p.IdentID, p.IdentVal, p.OrgID, b.ScopeId, entity.CommandStatusWaiting, entity.CommandActionDelete, p.IdentTye, p.IdentTye)
				tilog.Info(ctx, "UpdateIdentityRoles rec update project role binding first delete db user result", zap.Any("org_id", p.OrgID), zap.Any("project_id", b.ScopeId), zap.Any("err", e))
				e = dao.GetDaoHub().DBUser.CreateCommandUserAction(ctx, database.DBConn.DB, p.IdentID, p.IdentID, p.IdentVal, p.OrgID, b.ScopeId, entity.CommandStatusWaiting, entity.CommandActionCreate, p.IdentTye, p.IdentTye)
				tilog.Info(ctx, "UpdateIdentityRoles rec update project role binding second create db user result", zap.Any("org_id", p.OrgID), zap.Any("project_id", b.ScopeId), zap.Any("err", e))
			}

			targetType := entity.ClusterUserTargetUser
			if p.IdentTye == entity.RoleBindingTargetAPIKey {
				targetType = entity.ClusterUserTargetApiKey
			}
			for _, b := range r.InsCBindings {
				if b.ToRole == entity.RoleClusterAdmin {
					var isDedicated = false // only next gen cluster can assign cluster level role
					req := utils.GetDBUserReq{
						Email:       p.IdentVal,
						ClusterId:   &b.ScopeId,
						IsDedicated: &isDedicated,
						TargetType:  p.IdentTye.String(),
					}
					dbUser, e := utils.GetDBUser(ctx, req)
					if e != nil {
						tilog.Warn(ctx, "UpdateIdentityRoles rec create cluster role binding to create db user get db user name error", zap.Any("cluster_id", b.ScopeId), zap.Any("email", p.IdentVal), zap.Any("err", e))
						continue
					}
					e = dao.GetDaoHub().DBUser.CreateCommandDBUserCluster(ctx, database.DBConn.DB, b.ScopeId, p.OrgID, p.IdentID, p.IdentID, *dbUser, entity.CommandStatusWaiting, entity.CommandActionCreate, targetType, entity.DbRoleAdmin, targetType)
					tilog.Info(ctx, "UpdateIdentityRoles rec create cluster role binding to create db user result", zap.Any("org_id", p.OrgID), zap.Any("cluster_id", b.ScopeId), zap.Any("db_user", dbUser), zap.Any("err", e))
				}
			}
			for _, b := range r.UpdCBindings {
				var isDedicated = false // only next gen cluster can assign cluster level role
				req := utils.GetDBUserReq{
					Email:       p.IdentVal,
					ClusterId:   &b.ScopeId,
					IsDedicated: &isDedicated,
					TargetType:  p.IdentTye.String(),
				}
				dbUser, e := utils.GetDBUser(ctx, req)
				if e != nil {
					tilog.Warn(ctx, "UpdateIdentityRoles rec update cluster role binding to create db user get db user name error", zap.Any("cluster_id", b.ScopeId), zap.Any("email", p.IdentVal), zap.Any("err", e))
					continue
				}
				e = dao.GetDaoHub().DBUser.CreateCommandDBUserCluster(ctx, database.DBConn.DB, b.ScopeId, p.OrgID, p.IdentID, p.IdentID, *dbUser, entity.CommandStatusWaiting, entity.CommandActionDelete, targetType, entity.DbRoleAdmin, targetType)
				tilog.Info(ctx, "UpdateIdentityRoles rec update cluster role binding first delete db user result", zap.Any("org_id", p.OrgID), zap.Any("cluster_id", b.ScopeId), zap.Any("err", e))
				if b.ToRole == entity.RoleClusterAdmin {
					e = dao.GetDaoHub().DBUser.CreateCommandDBUserCluster(ctx, database.DBConn.DB, b.ScopeId, p.OrgID, p.IdentID, p.IdentID, *dbUser, entity.CommandStatusWaiting, entity.CommandActionCreate, targetType, entity.DbRoleAdmin, targetType)
					tilog.Info(ctx, "UpdateIdentityRoles rec update cluster role binding second create db user result", zap.Any("org_id", p.OrgID), zap.Any("cluster_id", b.ScopeId), zap.Any("err", e))
				}
			}
			for _, b := range r.DelCBindings {
				var isDedicated = false // only next gen cluster can assign cluster level role
				req := utils.GetDBUserReq{
					Email:       p.IdentVal,
					ClusterId:   &b.ScopeId,
					IsDedicated: &isDedicated,
					TargetType:  p.IdentTye.String(),
				}
				dbUser, e := utils.GetDBUser(ctx, req)
				if e != nil {
					tilog.Warn(ctx, "UpdateIdentityRoles rec delete cluster role binding to delete db user get db user name err", zap.Any("cluster_id", b.ScopeId), zap.Any("email", p.IdentVal), zap.Any("err", e))
					continue
				}
				e = dao.GetDaoHub().DBUser.CreateCommandDBUserCluster(ctx, database.DBConn.DB, b.ScopeId, p.OrgID, p.IdentID, p.IdentID, *dbUser, entity.CommandStatusWaiting, entity.CommandActionDelete, entity.ClusterUserTargetUser, entity.DbRoleAdmin, entity.ClusterUserTargetUser)
				tilog.Info(ctx, "UpdateIdentityRoles rec delete cluster role binding to delete db user result", zap.Any("org_id", p.OrgID), zap.Any("cluster_id", b.ScopeId), zap.Any("err", e))
			}

		}
	}

}
