package apikey

import (
	"context"
	"fmt"

	"github.com/tidbcloud/account/central/utils"
	apikey2 "github.com/tidbcloud/account/internel/svc/apikey"
	"github.com/tidbcloud/account/internel/svc/authn"
	"github.com/tidbcloud/account/internel/svc/comsvc"
	"github.com/tidbcloud/account/internel/svc/org/tenantctx"
	"github.com/tidbcloud/tiinf/errs"
	"google.golang.org/protobuf/types/known/timestamppb"

	"github.com/tidbcloud/account/idl/pbgen/proto/central"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"google.golang.org/protobuf/types/known/emptypb"
)

type openapikeyService struct {
	central.UnimplementedOpenAPIKeyServiceServer
	authn.OperationsService
	tenantModel tenantctx.Model
}

// NewService constructs a apikeyService
func NewOpenApiKeyService(s authn.OperationsService, tenantModel tenantctx.Model) central.OpenAPIKeyServiceServer {
	return &openapikeyService{
		OperationsService: s,
		tenantModel:       tenantModel,
	}
}

func (o *openapikeyService) userContext(ctx context.Context) tenantctx.Context {
	return o.tenantModel.User(ctx, o.CurrentUser(ctx))
}

// func (o *openapikeyService) validateTrafficFilters(ctx context.Context, trafficFilters *central.TrafficFilters) error {
// 	if trafficFilters == nil {
// 		return nil
// 	}

// 	existMap := map[string]bool{}
// 	for idx, trafficFilter := range trafficFilters.Cidrs {
// 		if strings.Contains(trafficFilter.Cidr, "/") {
// 			_, _, err := net.ParseCIDR(trafficFilter.Cidr)
// 			if err != nil {
// 				return fmt.Errorf("invalid CIDR: %s", trafficFilter.Cidr)
// 			}
// 			if val, ok := existMap[trafficFilter.Cidr]; ok && val {
// 				return fmt.Errorf("duplicate CIDR: %s", trafficFilter.Cidr)
// 			}
// 			existMap[trafficFilter.Cidr] = true
// 		} else {
// 			ip := net.ParseIP(trafficFilter.Cidr)
// 			if ip == nil {
// 				return fmt.Errorf("invalid IP address: %s", trafficFilter.Cidr)
// 			}
// 			// currently our load balancer does not support IPv6
// 			trafficFilters.Cidrs[idx].Cidr = fmt.Sprintf("%s/32", trafficFilter.Cidr)
// 			if val, ok := existMap[trafficFilters.Cidrs[idx].Cidr]; ok && val {
// 				return fmt.Errorf("duplicate IP: %s", trafficFilter.Cidr)
// 			}
// 			existMap[trafficFilters.Cidrs[idx].Cidr] = true
// 		}
// 	}
// 	return nil
// }

func (o *openapikeyService) CreateOpenAPIKey(ctx context.Context, req *central.CreateOpenAPIKeyReq) (*central.OpenAPIKey, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//if !apikey2.IsValidApiKeyRole(entity.RoleBindingScopeOrg.String(), req.Role) {
	//	return nil, errs.NewBadRequest("role invalid", nil)
	//}
	//param := &apikey2.CreateApiKeyParam{
	//	Source:      "dbaas-console",
	//	Description: req.Description,
	//	ScopeType:   string(entity.RoleBindingScopeOrg),
	//	ResourceIds: []uint64{req.OrgId},
	//	Operator:    o.CurrentUser(ctx).Email,
	//	Role:        req.Role,
	//}
	//svc, _ := apikey2.GetApiKeySvc()
	//createKey, err := svc.CreateApiKey(ctx, param)
	//
	//details := auditlog.Details{
	//	auditlog.KeyDescription: req.Description,
	//	auditlog.KeyRole:        req.Role,
	//	auditlog.KeyError:       err,
	//}
	//auditlog.NewEvent(ctx, auditlog.EventCreateAPIKey).SetResult(err == nil).SetOrgID(req.OrgId).SetDetails(details.Build()).Submit() //nolint:errcheck
	//
	//if err != nil {
	//	return nil, err
	//}
	//role := req.Role
	//if role == entity.RoleApiKeyOrgOwner {
	//	role = entity.RoleOrgOwner
	//}
	//retKey := convSvcApiKey2CentralApiKey(createKey, createKey.SecretKey, req.OrgId, 0, role)
	//return retKey, nil

}

//func convSvcApiKey2CentralApiKey(createKey *apikey2.ApiKey, secretKey string, orgId, projectId uint64, role string) *central.OpenAPIKey {
//	return &central.OpenAPIKey{
//		Id:           createKey.Id,
//		OrgId:        orgId,
//		ProjectId:    projectId,
//		PublicKey:    createKey.AccessKey,
//		PrivateKey:   secretKey,
//		Role:         role,
//		Description:  createKey.Description,
//		CreatorEmail: createKey.CreatedBy,
//		CreatedAt:    timestamppb.New(*createKey.CreatedAt),
//		UpdatedAt:    timestamppb.New(*createKey.UpdatedAt),
//	}
//}

func (o *openapikeyService) CreateProjectOpenAPIKey(ctx context.Context, req *central.CreateProjectOpenAPIKeyReq) (*central.OpenAPIKey, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//param := &apikey2.CreateApiKeyParam{
	//	Source:      "dbaas-console",
	//	Description: req.Description,
	//	ScopeType:   string(entity.RoleBindingScopeProject),
	//	ResourceIds: []uint64{req.ProjectId},
	//	Operator:    o.CurrentUser(ctx).Email,
	//	Role:        req.Role,
	//}
	//svc, _ := apikey2.GetApiKeySvc()
	//createKey, err := svc.CreateApiKey(ctx, param)
	//
	//details := auditlog.Details{
	//	auditlog.KeyDescription: req.Description,
	//	auditlog.KeyRole:        req.Role,
	//	auditlog.KeyError:       err,
	//}
	//auditlog.NewEvent(ctx, auditlog.EventCreateAPIKey).SetResult(err == nil).SetOrgID(req.OrgId).SetProjectID(req.ProjectId).SetDetails(details.Build()).Submit() //nolint:errcheck
	//
	//if err != nil {
	//	return nil, err
	//}
	//retKey := convSvcApiKey2CentralApiKey(createKey, createKey.SecretKey, req.OrgId, req.ProjectId, req.Role)
	//return retKey, nil
}

func (o *openapikeyService) ListOpenAPIKeys(ctx context.Context, req *central.ListOpenAPIKeysReq) (*central.OpenAPIKeys, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//pageNo := req.Page
	//pageSize := req.PerPage
	//if pageNo <= 0 {
	//	pageNo = 1
	//}
	//if pageSize <= 0 {
	//	pageSize = 10
	//}
	//svc, _ := apikey2.GetApiKeySvc()
	//param := apikey2.ListKeysByResourceIDParam{
	//	ScopeType:  string(entity.RoleBindingScopeOrg),
	//	ResourceId: req.OrgId,
	//	PageNo:     pageNo,
	//	PageSize:   pageSize,
	//	AccessKey:  req.PublicKey,
	//	Desc:       req.Description,
	//	Roles:      req.Roles,
	//}
	//keys, err := svc.ListApiKeysByResourceID(ctx, &param)
	//if err != nil {
	//	return nil, err
	//}
	//retOpenAPIKeys := &central.OpenAPIKeys{
	//	Total: keys.Total,
	//	Items: []*central.OpenAPIKey{},
	//}
	//for _, key := range keys.Items {
	//	openAPIKey := convSvcApiKey2CentralApiKey(key, "", req.OrgId, 0, key.ResourceInfos[0].Role)
	//	retOpenAPIKeys.Items = append(retOpenAPIKeys.Items, openAPIKey)
	//}
	//
	//return retOpenAPIKeys, nil
}

func (o *openapikeyService) ListProjectOpenAPIKeys(ctx context.Context, req *central.ListProjectOpenAPIKeysReq) (*central.OpenAPIKeys, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//pageNo := req.Page
	//pageSize := req.PerPage
	//if pageNo <= 0 {
	//	pageNo = 1
	//}
	//if pageSize <= 0 {
	//	pageSize = 10
	//}
	//svc, _ := apikey2.GetApiKeySvc()
	//param := apikey2.ListKeysByResourceIDParam{
	//	ScopeType:  string(entity.RoleBindingScopeProject),
	//	ResourceId: req.ProjectId,
	//	PageNo:     pageNo,
	//	PageSize:   pageSize,
	//}
	//keys, err := svc.ListApiKeysByResourceID(ctx, &param)
	//if err != nil {
	//	return nil, err
	//}
	//retOpenAPIKeys := &central.OpenAPIKeys{
	//	Total: keys.Total,
	//	Items: []*central.OpenAPIKey{},
	//}
	//for _, key := range keys.Items {
	//	openAPIKey := convSvcApiKey2CentralApiKey(key, "", req.OrgId, key.ResourceInfos[0].ResourceId, key.ResourceInfos[0].Role)
	//	retOpenAPIKeys.Items = append(retOpenAPIKeys.Items, openAPIKey)
	//}
	//
	//return retOpenAPIKeys, nil
}

func (o *openapikeyService) UpdateOpenAPIKey(ctx context.Context, req *central.UpdateOpenAPIKeyReq) (*emptypb.Empty, error) {
	svc, _ := apikey2.GetApiKeySvc()
	checkParam := apikey2.CheckApiKeyBelongToResourceParam{
		ScopeType:  entity.RoleBindingScopeOrg.String(),
		ResourceId: req.OrgId,
		ApiKeyId:   req.ApiKeyId,
	}
	belong, err := svc.CheckApiKeyBelongToResource(ctx, &checkParam)
	if err != nil {
		return nil, err
	}
	if !belong {
		return nil, utils.ConvertErr2GrpcErr(errs.NewForbidden("no permission", nil))
	}
	if len(req.Description) > 500 {
		return nil, errs.NewBadRequest("description is invalid", nil)
	}
	user := o.CurrentUser(ctx)

	param := &apikey2.UpdateOneConsoleApiKeyParam{
		KeyID: req.ApiKeyId,
		Attrs: &apikey2.ApiKeyAttr{
			Description: req.Description,
		},
		RecAuditLog:   true,
		AuditLogOrgID: req.OrgId,
	}
	oper := &comsvc.Operator{
		OpTye: entity.RoleBindingTargetUser,
		OpID:  user.ID.Uint64(),
		OpVal: user.Email,
	}
	err = svc.UpdateOneConsoleApiKey(ctx, param, oper)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil

}

func (o *openapikeyService) UpdateProjectOpenAPIKey(ctx context.Context, req *central.UpdateProjectOpenAPIKeyReq) (*emptypb.Empty, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//if req.Role != "" && !apikey2.IsValidApiKeyRole(entity.RoleBindingScopeProject.String(), req.Role) {
	//	return nil, errs.NewBadRequest("role invalid", nil)
	//}
	//svc, _ := apikey2.GetApiKeySvc()
	//checkParam := apikey2.CheckApiKeyBelongToResourceParam{
	//	ScopeType:  entity.RoleBindingScopeProject.String(),
	//	ResourceId: req.ProjectId,
	//	ApiKeyId:   req.ApiKeyId,
	//}
	//belong, err := svc.CheckApiKeyBelongToResource(ctx, &checkParam)
	//if err != nil {
	//	return nil, err
	//}
	//if !belong {
	//	return nil, utils.ConvertErr2GrpcErr(errs.NewForbidden("no permission", nil))
	//}
	//param := apikey2.UpdateApiKeyParam{
	//	ApiKeyId:    req.ApiKeyId,
	//	Description: req.Description,
	//	Operator:    o.CurrentUser(ctx).Email,
	//}
	//err = svc.UpdateApiKey(ctx, &param)
	//
	//if err == nil && req.Role != "" {
	//	err = svc.ChangeApiKeyRefResourceRole(ctx, &apikey2.ChangeApiKeyRefResourceRoleParam{
	//		ApiKeyId:   req.ApiKeyId,
	//		ScopeType:  entity.RoleBindingScopeProject.String(),
	//		ResourceId: req.ProjectId,
	//		NewRole:    req.Role,
	//	})
	//}
	//
	//details := auditlog.Details{
	//	auditlog.KeyNewDescription: req.Description,
	//	auditlog.KeyRole:           req.Role,
	//	auditlog.KeyError:          err,
	//}
	//auditlog.NewEvent(ctx, auditlog.EventEditAPIKey).SetOrgID(req.OrgId).SetProjectID(req.ProjectId).SetDetails(details.Build()).SetResult(err == nil).Submit() //nolint:errcheck
	//
	//if err != nil {
	//	return nil, err
	//}
	//return &emptypb.Empty{}, nil
}

func (o *openapikeyService) DeleteOpenAPIKey(ctx context.Context, req *central.DeleteOpenAPIKeyReq) (*emptypb.Empty, error) {
	svc, _ := apikey2.GetApiKeySvc()
	checkParam := apikey2.CheckApiKeyBelongToResourceParam{
		ScopeType:  entity.RoleBindingScopeOrg.String(),
		ResourceId: req.OrgId,
		ApiKeyId:   req.ApiKeyId,
	}
	belong, err := svc.CheckApiKeyBelongToResource(ctx, &checkParam)
	if err != nil {
		return nil, err
	}
	if !belong {
		return nil, utils.ConvertErr2GrpcErr(errs.NewForbidden("no permission", nil))
	}
	keyEntity, err := svc.GetApiKeyByID(ctx, req.ApiKeyId)
	if err != nil {
		return nil, err
	}
	if keyEntity == nil {
		return nil, utils.ConvertErr2GrpcErr(errs.NewNotFound("api_key_id invalid", nil))
	}
	param := apikey2.DeleteApiKeyParam{
		ApiKeyId: req.ApiKeyId,
	}
	operator := &comsvc.Operator{
		OpTye: entity.RoleBindingTargetUser,
		OpID:  o.CurrentUser(ctx).ID.Uint64(),
		OpVal: o.CurrentUser(ctx).Email,
	}
	err = svc.DeleteApiKey(ctx, &param, operator)

	details := auditlog.Details{
		auditlog.KeyDescription: keyEntity.Description,
		auditlog.KeyError:       err,
	}
	auditlog.NewEvent(ctx, auditlog.EventDeleteAPIKey).SetOrgID(req.OrgId).SetDetails(details.Build()).SetResult(err == nil).Submit() //nolint:errcheck

	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil

}

// DeleteProjectOpenAPIKey as project:owner can delete project level key, add the project level interface
func (o *openapikeyService) DeleteProjectOpenAPIKey(ctx context.Context, req *central.DeleteProjectOpenAPIKeyReq) (*emptypb.Empty, error) {
	// todo mock offline unused api
	return nil, errs.NewBadRequest("The API has been taken offline", nil)
	//svc, _ := apikey2.GetApiKeySvc()
	//checkParam := apikey2.CheckApiKeyBelongToResourceParam{
	//	ScopeType:  entity.RoleBindingScopeProject.String(),
	//	ResourceId: req.ProjectId,
	//	ApiKeyId:   req.ApiKeyId,
	//}
	//belong, err := svc.CheckApiKeyBelongToResource(ctx, &checkParam)
	//if err != nil {
	//	return nil, err
	//}
	//if !belong {
	//	return nil, utils.ConvertErr2GrpcErr(errs.NewForbidden("no permission", nil))
	//}
	//keyEntity, err := svc.GetApiKeyByID(ctx, req.ApiKeyId)
	//if err != nil {
	//	return nil, err
	//}
	//if keyEntity == nil {
	//	return nil, utils.ConvertErr2GrpcErr(errs.NewNotFound("api_key_id invalid", nil))
	//}
	//param := apikey2.DeleteApiKeyParam{
	//	ApiKeyId: req.ApiKeyId,
	//	Operator: o.CurrentUser(ctx).Email,
	//}
	//err = svc.DeleteApiKey(ctx, &param)
	//
	//details := auditlog.Details{
	//	auditlog.KeyDescription: keyEntity.Description,
	//	auditlog.KeyError:       err,
	//}
	//auditlog.NewEvent(ctx, auditlog.EventDeleteAPIKey).SetOrgID(req.OrgId).SetProjectID(req.ProjectId).SetDetails(details.Build()).SetResult(err == nil).Submit() //nolint:errcheck
	//
	//if err != nil {
	//	return nil, err
	//}
	//return &emptypb.Empty{}, nil
}

// interface for one console

func (o *openapikeyService) CreateConsoleApiKey(ctx context.Context, req *central.UpdateConsoleApiKeyReq) (*central.ConsoleApiKey, error) {
	if req.Description == "" {
		return nil, errs.NewBadRequest("description is blank", nil)
	}
	if len(req.Description) > 500 {
		return nil, errs.NewBadRequest("description is invalid", nil)
	}
	if req.OrgId == 0 || req.OrgRole == "" {
		return nil, errs.NewBadRequest("org role is blank", nil)
	}
	if err := checkProjectAndClusterRolesInvalid(req.ProjectRoles, req.ClusterRoles); err != nil {
		return nil, err
	}
	pRoles, cRoles := map[uint64]string{}, map[uint64]string{}
	for _, pr := range req.ProjectRoles {
		pRoles[pr.ScopeId] = pr.RbacRole
	}
	for _, cr := range req.ClusterRoles {
		cRoles[cr.ScopeId] = cr.RbacRole
	}
	operator := o.CurrentUser(ctx)

	param := &apikey2.CreateOneConsoleApiKeyParam{
		Attrs: &apikey2.ApiKeyAttr{
			Source:      "dbaas-console",
			Type:        entity.ApiKeyTypeNormalKey,
			Description: req.Description,
		},
		AssignRoleParam: &comsvc.AssignIdentRolesParam{
			OrgID:  req.OrgId,
			ORole:  &req.OrgRole,
			PRoles: pRoles,
			CRoles: cRoles,
		},
		RecAuditLog: true,
	}
	oper := &comsvc.Operator{
		OpTye: entity.RoleBindingTargetUser,
		OpID:  operator.ID.Uint64(),
		OpVal: operator.Email,
	}
	svc, _ := apikey2.GetApiKeySvc()
	key, err := svc.CreateOneConsoleApiKey(ctx, param, oper)
	if err != nil {
		return nil, err
	}

	rKey := convApiKeyV2ToConsoleApiKey(key)
	return rKey, nil
}

func checkProjectAndClusterRolesInvalid(projectRoles, clusterRoles []*central.ConsoleUserRole) error {
	if len(projectRoles) > 0 {
		var existPids = map[uint64]struct{}{}
		for _, pr := range projectRoles {
			if pr.ScopeId == 0 {
				return errs.NewBadRequest(fmt.Sprintf("project id %d invalid", pr.ScopeId), nil)
			}
			if _, ok := existPids[pr.ScopeId]; ok {
				return errs.NewBadRequest(fmt.Sprintf("project %d repeated", pr.ScopeId), nil)
			}
			if pr.RbacRole == "" {
				return errs.NewBadRequest(fmt.Sprintf("project role %s invlaid", pr.RbacRole), nil)
			}
			existPids[pr.ScopeId] = struct{}{}
		}
	}
	if len(clusterRoles) > 0 {
		var existCids = map[uint64]struct{}{}
		for _, cr := range clusterRoles {
			if cr.ScopeId == 0 {
				return errs.NewBadRequest(fmt.Sprintf("cluster id %d invalid", cr.ScopeId), nil)
			}
			if _, ok := existCids[cr.ScopeId]; ok {
				return errs.NewBadRequest(fmt.Sprintf("cluster %d repeated", cr.ScopeId), nil)
			}
			if cr.RbacRole == "" {
				return errs.NewBadRequest(fmt.Sprintf("cluster role %s invlaid", cr.RbacRole), nil)
			}
			existCids[cr.ScopeId] = struct{}{}
		}
	}
	return nil
}

func convApiKeyV2ToConsoleApiKey(k *apikey2.OneConsoleApiKey) *central.ConsoleApiKey {
	var ror *central.ConsoleUserRole
	var rprs, rcrs []*central.ConsoleUserRole
	if k.OrgRole != nil {
		ror = &central.ConsoleUserRole{
			ScopeType: k.OrgRole.ScopeType,
			ScopeId:   k.OrgRole.ResourceId,
			RbacRole:  k.OrgRole.Role,
		}
	}
	if len(k.ProjectRoles) > 0 {
		for _, pr := range k.ProjectRoles {
			rpr := &central.ConsoleUserRole{
				ScopeType: pr.ScopeType,
				ScopeId:   pr.ResourceId,
				RbacRole:  pr.Role,
			}
			rprs = append(rprs, rpr)
		}
	}
	if len(k.ClusterRoles) > 0 {
		for _, cr := range k.ClusterRoles {
			rcr := &central.ConsoleUserRole{
				ScopeType: cr.ScopeType,
				ScopeId:   cr.ResourceId,
				RbacRole:  cr.Role,
			}
			rcrs = append(rcrs, rcr)
		}
	}
	return &central.ConsoleApiKey{
		Id:           k.Id,
		PublicKey:    k.AccessKey,
		PrivateKey:   k.SecretKey,
		Description:  k.Description,
		OrgRole:      ror,
		ProjectRoles: rprs,
		ClusterRoles: rcrs,
		CreatedAt:    timestamppb.New(*k.CreatedAt),
		UpdatedAt:    timestamppb.New(*k.UpdatedAt),
		CreatedBy:    k.CreatedBy,
		UpdatedBy:    k.UpdatedBy,
	}
}

func (o *openapikeyService) UpdateConsoleApiKey(ctx context.Context, req *central.UpdateConsoleApiKeyReq) (*emptypb.Empty, error) {
	if req.ApiKeyId == 0 {
		return nil, errs.NewBadRequest("key id is blank", nil)
	}
	if req.OrgId == 0 || req.OrgRole == "" {
		return nil, errs.NewBadRequest("org role is blank", nil)
	}
	if len(req.Description) > 500 {
		return nil, errs.NewBadRequest("description is invalid", nil)
	}
	if err := checkProjectAndClusterRolesInvalid(req.ProjectRoles, req.ClusterRoles); err != nil {
		return nil, err
	}
	pRoles, cRoles := map[uint64]string{}, map[uint64]string{}
	for _, pr := range req.ProjectRoles {
		pRoles[pr.ScopeId] = pr.RbacRole
	}
	for _, cr := range req.ClusterRoles {
		cRoles[cr.ScopeId] = cr.RbacRole
	}
	svc, _ := apikey2.GetApiKeySvc()
	belong, err := svc.CheckApiKeyBelongToResource(ctx, &apikey2.CheckApiKeyBelongToResourceParam{
		ScopeType:  entity.RoleBindingScopeOrg.String(),
		ResourceId: req.OrgId,
		ApiKeyId:   req.ApiKeyId,
	})
	if err != nil {
		return nil, err
	}
	if !belong {
		return nil, utils.ConvertErr2GrpcErr(errs.NewForbidden("no permission", nil))
	}
	operator := o.CurrentUser(ctx)
	oper := &comsvc.Operator{
		OpTye: entity.RoleBindingTargetUser,
		OpID:  operator.ID.Uint64(),
		OpVal: operator.Email,
	}
	param := &apikey2.UpdateOneConsoleApiKeyParam{
		KeyID: req.ApiKeyId,
		Attrs: &apikey2.ApiKeyAttr{
			Description: req.Description,
		},
		AssignRoleParam: &comsvc.AssignIdentRolesParam{
			OrgID:  req.OrgId,
			ORole:  &req.OrgRole,
			PRoles: pRoles,
			CRoles: cRoles,
		},
		RecAuditLog:   true,
		AuditLogOrgID: req.OrgId,
	}
	err = svc.UpdateOneConsoleApiKey(ctx, param, oper)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil

}

func (o *openapikeyService) ListConsoleApiKeys(ctx context.Context, req *central.ListConsoleApiKeysReq) (*central.ListConsoleApiKeysResp, error) {
	if req.OrgId == 0 {
		return nil, errs.NewBadRequest("org id is blank", nil)
	}
	pageNo := req.Page
	pageSize := req.PerPage
	if pageNo <= 0 {
		pageNo = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}

	param := &apikey2.ListApiKeysParam{
		Type:         entity.ApiKeyTypeNormalKey,
		KeyLike:      req.Search,
		OrgId:        req.OrgId,
		OrgRoles:     req.OrgRoles,
		ProjectId:    req.ProjectId,
		ProjectRoles: req.ProjectRoles,
		ClusterId:    req.ClusterId,
		ClusterRoles: req.ClusterRoles,
		Page:         pageNo,
		PerPage:      pageSize,
	}
	svc, _ := apikey2.GetApiKeySvc()
	total, keys, err := svc.ListOneConsoleApiKeys(ctx, param)
	if err != nil {
		return nil, err
	}

	var items []*central.ConsoleApiKey
	for _, k := range keys {
		it := convApiKeyV2ToConsoleApiKey(&k)
		it.PrivateKey = ""
		items = append(items, it)
	}

	resp := &central.ListConsoleApiKeysResp{
		Total: total,
		Items: items,
	}

	return resp, nil
}
