package api

import (
	"context"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/admin/forms"
	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/dbutil"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/dbaas/central/grpc/grpcli"
	"github.com/pingcap/dbaas/central/model"
	"github.com/pingcap/dbaas/central/model/admin"
	"github.com/tidbcloud/idl/pbgen/proto/base"
	"github.com/tidbcloud/idl/pbgen/proto/core"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

var (
	NonDeletedAndClearedCLusterStatuses = []core.ClusterStatus{
		core.ClusterStatus_CLUSTER_STATUS_INITIALIZING, core.ClusterStatus_CLUSTER_STATUS_PREPARING,
		core.ClusterStatus_CLUSTER_STATUS_LAUNCHING, core.ClusterStatus_CLUSTER_STATUS_NORMAL,
		core.ClusterStatus_CLUSTER_STATUS_SCALING, core.ClusterStatus_CLUSTER_STATUS_RECOVERING,
	}
)

// @Summary List Tenant Users
// @tags TenantUser
// @Description List tenant user
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Success 200 {object} forms.ListTenantUsersResp
// @Router /orgs/{org_id}/users [get]
func ListTenantUsers(c *gin.Context) {
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	limitOffset := GetLimitAndOffset(c)
	userContext := GetUserContext(c)

	dbRecords, err := userContext.ListTenantUsers(req.OrgID, limitOffset.Limit, limitOffset.Offset)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	total, err := userContext.GetTenantUsersCount(req.OrgID)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	var items = make([]forms.TenantUser, len(dbRecords))
	for idx, item := range dbRecords {
		items[idx] = buildAPITenantUserItem(item)
	}

	result := &forms.ListTenantUsersResp{
		Total: total,
		Items: items,
	}
	Success(c, result, http.StatusOK)
}

// @Summary Sync Salesforce Leads
// @Tags Salesforce
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/salesforce [post]
func SalesforceSyncLeads(c *gin.Context) {
	ctx := c.Request.Context()
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	userContext := GetUserContext(c)

	err := userContext.SalesforceSyncLeads(ctx, entity.ID(req.OrgID))
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary Add Tenant User
// @tags TenantUser
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param user body forms.TenantUser true "Add User"
// @Success 200 {object} forms.TenantUser
// @Router /orgs/{org_id}/users [post]
func AddTenantUser(c *gin.Context) {
	reqUri := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	req := forms.TenantUser{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	req.OrgID = reqUri.OrgID

	internalTenantUser := buildInternalTenantUser(&req)
	internalTenantUser.Id = 0
	item, err := userContext.UpsertTenantUser(internalTenantUser)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	result := buildAPITenantUserItem(item)
	Success(c, result, http.StatusOK)
}

// @Summary Update Tenant User
// @tags TenantUser
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param user_id path string true "user_id"
// @Param user body forms.TenantUser true "update User"
// @Success 200 {object} forms.TenantUser
// @Router /orgs/{org_id}/users/{user_id} [put]
func UpdateTenantUser(c *gin.Context) {
	reqUri := forms.GetOrgIDAndUserIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	req := forms.TenantUser{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	req.ID = reqUri.UserID
	req.OrgID = reqUri.OrgID
	userContext := GetUserContext(c)

	internalUser, err := userContext.UpsertTenantUser(buildInternalTenantUser(&req))
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	result := buildAPITenantUserItem(internalUser)

	Success(c, result, http.StatusOK)
}

// @Summary Delete Tenant User
// @tags TenantUser
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param user_id path string true "user_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/users/{user_id} [delete]
func DeleteTenantUser(c *gin.Context) {
	req := forms.GetOrgIDAndUserIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	if err := userContext.DeleteTenantUser(req.OrgID, req.UserID); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary Get Tenant User
// @tags TenantUser
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param user_id path string true "user_id"
// @Success 200 {object} forms.TenantUser
// @Router /orgs/{org_id}/users/{user_id} [get]
func GetTenantUser(c *gin.Context) {
	req := forms.GetOrgIDAndUserIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	item, err := userContext.GetTenantUserByID(req.OrgID, req.UserID)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	result := buildAPITenantUserItem(item)
	Success(c, result, http.StatusOK)
}

// @Summary List Tenant Cluster
// @tags TenantCuster
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param page query forms.PaginationReq  true "pagination"
// @Success 200 {object} forms.ListTenantClustersResp
// @Router /orgs/{org_id}/clusters [get]
func ListTenantClusters(c *gin.Context) {
	ctx := c.Request.Context()
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	logger := log.WithContextS(ctx).With(zap.Uint64("tenantID", req.OrgID))
	logger.Info("get clusters of tenant, begin")

	total, clusters, err := listTenantClusters(ctx, req.OrgID, GetPager(c))
	resp := forms.ListTenantClustersResp{}
	statusCode := http.StatusOK
	// add a middleware
	if err != nil {
		if err.IsException() {
			logger.Errorf("failed: %s", err)
		} else {
			logger.Infof("failed: %s", err)
		}
		err = err.Redact()
		resp.Code = int64(err.ErrCode())
		resp.Msg = err.ErrMsg()
		statusCode = err.HttpStatus()
	} else {
		resp.Total = total
		resp.Items = clusters
	}
	c.JSON(statusCode, resp)
}

func listTenantClusters(ctx context.Context, tenantID uint64, pager *base.Pager) (int64, []forms.TenantCluster, bizerr.BizError) {
	fitler := &core.ClusterFilter{
		TenantIds: []uint64{tenantID},
		Statuses:  NonDeletedAndClearedCLusterStatuses,
	}
	sorter := &base.Sorter{
		OrderBy: "created_at",
		Sort:    base.Sort_SORT_DESC,
	}
	listReq := &core.ListClustersReq{
		Filter: fitler,
		Pager:  pager,
		Sorter: sorter,
	}
	listResp, err := grpcli.ListClusters(ctx, listReq)
	if err != nil {
		return 0, nil, err
	}
	countReq := &core.CountClustersReq{
		Filter: fitler,
	}
	countResp, err := grpcli.CountClusters(ctx, countReq)
	if err != nil {
		return 0, nil, err
	}

	// TODO(chenfei): opt, use rpc to instead
	id2project := map[uint64]*entity.Project{}
	if err := db.Transaction(func(tx *gorm.DB) error {
		projects, err := dbutil.GetActiveProjects(ctx, tx, entity.ID(tenantID))
		if err != nil {
			return err
		}
		for _, each := range projects {
			project := each
			id2project[each.ID.Uint64()] = &project
		}
		return nil
	}); err != nil {
		return 0, nil, bizerr.WrapGormError(err, "get active projects")
	}

	out := make([]forms.TenantCluster, 0, len(listResp.Clusters))
	for _, id := range listResp.OrderedClusterIds {
		item := listResp.Clusters[id]
		out = append(out, buildAPITenantClusterItem(item, id2project[item.ProjectId]))
	}
	return countResp.Count, out, nil
}

// @Summary Delete Tenant Cluster
// @tags TenantCuster
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param cluster_id path string true "cluster_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/clusters/{cluster_id} [delete]
func DeleteTenantCluster(c *gin.Context) {
	req := forms.GetOrgIDAndClusterIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	if err := userContext.DeleteTenantCluster(req.OrgID, req.ClusterID); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary Scale Tenant Cluster
// @tags TenantCuster
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Param cluster_id path string true "cluster_id"
// @Param scale body forms.ScaleComponentReq true "scale cluster component"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/clusters/{cluster_id}/components [put]
func ScaleTenantCluster(c *gin.Context) {
	ctx := c.Request.Context()
	orgIDAndClusterID := forms.GetOrgIDAndClusterIDReq{}
	if err := c.ShouldBindUri(&orgIDAndClusterID); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	scaleReq := forms.ScaleComponentReq{}
	if err := c.ShouldBindJSON(&scaleReq); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	err := scaleTenantCluster(ctx, orgIDAndClusterID.ClusterID, &scaleReq)
	resp := forms.CodeMsgResponse{}
	statusCode := http.StatusOK
	if err != nil {
		err = err.Redact()
		resp.Code = int64(err.ErrCode())
		resp.Msg = err.ErrMsg()
		statusCode = err.HttpStatus()
	}
	c.JSON(statusCode, resp)
}

func scaleTenantCluster(ctx context.Context, clusterID uint64, scaleReq *forms.ScaleComponentReq) bizerr.BizError {
	components := make([]*core.ClusterComponent, 0, len(scaleReq.Components))
	for _, c := range scaleReq.Components {
		components = append(components, &core.ClusterComponent{
			ComponentType: core.ComponentType(core.ComponentType_value[c.ComponentType]),
			NodeProfileId: c.NodeProfileId,
			Replicas:      c.Replicas,
			StorageSizeGi: c.StorageSizeGi,
		})
	}
	coreReq := &core.ScaleClusterReq{
		ClusterId:           clusterID,
		NewComponents:       components,
		AllowScaleIn:        true,
		AllowScaleSecondary: false,
		BaseReq:             nil,
	}
	_, err := grpcli.ScaleCluster(ctx, coreReq)
	return err
}

// @Summary Add Management Portal User
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param user body forms.CentralUser true "Add management portal user"
// @Success 200 {object} forms.CentralUser
// @Router /users [post]
func AddCentralUser(c *gin.Context) {
	req := forms.CentralUser{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	internalCentralUser := buildInternalCentralUser(req)

	item, err := userContext.UpsertCentralUser(internalCentralUser)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	result := buildAPICentralUserItem(item)

	Success(c, result, http.StatusOK)
}

// @Summary ListCentralUsers
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param page query forms.PaginationReq  true "pagination"
// @Param search query forms.ListCentralUser  true "search"
// @Success 200 {object} forms.ListCentralUsersResp
// @Router /users [get]
func ListCentralUsers(c *gin.Context) {
	userContext := GetUserContext(c)
	limitOffset := GetLimitAndOffset(c)
	req := forms.ListCentralUser{}
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	filter := admin.CentralUserFilter{
		Email: req.CentralUserFilter.Email,
	}
	dbRecords, err := userContext.ListCentralUsers(filter, limitOffset.Limit, limitOffset.Offset)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	total, err := userContext.GetCentralUsersCount()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	items := []forms.CentralUser{}
	for _, item := range dbRecords {
		items = append(items, buildAPICentralUserItem(item))
	}

	result := forms.ListCentralUsersResp{
		Total: total,
		Items: items,
	}

	Success(c, result, http.StatusOK)
}

// @Summary Delete Management Portal User
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param user_id path string true "user_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /users/{user_id} [delete]
func DeleteCentralUser(c *gin.Context) {
	req := forms.GetUserIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	if err := userContext.DeleteCentralUser(req.UserID); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary Get Management Portal User
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param user_id path string true "user_id"
// @Success 200 {object} forms.CentralUser
// @Router /users/{user_id} [get]
func GetCentralUser(c *gin.Context) {
	req := forms.GetUserIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	item, err := userContext.GetCentralUserByID(req.UserID)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, buildAPICentralUserItem(item), http.StatusOK)
}

// @Summary Update Management Portal User
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Param user_id path string true "user_id"
// @Param user body forms.CentralUser true "update management portal user"
// @Success 200 {object} forms.CentralUser
// @Router /users/{user_id} [put]
func UpdateCentralUser(c *gin.Context) {
	reqUri := forms.GetUserIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	req := forms.CentralUser{}
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	req.ID = reqUri.UserID

	userContext := GetUserContext(c)
	centralUser := buildInternalCentralUser(req)
	item, err := userContext.UpsertCentralUser(centralUser)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, buildAPICentralUserItem(item), http.StatusOK)
}

// @Summary List Tenant
// @tags Tenant
// @Accept  json
// @Produce json
// @Param page query forms.PaginationReq  true "pagination"
// @Param search query forms.ListTenant  true "search"
// @Success 200 {object} forms.ListTenantResp
// @Router /orgs [get]
func ListTenants(c *gin.Context) {
	req := forms.ListTenant{}
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	limitOffset := GetLimitAndOffset(c)
	userContext := GetUserContext(c)
	dbRecords, total, err := userContext.ListTenants(&admin.ListTenantsReq{
		Filter: admin.TenantFilter{
			ID:     (*entity.ID)(req.TenantFilter.ID),
			Plan:   ParseTenantPlans(req.TenantFilter.Plan),
			Kind:   ParseTenantKinds(req.TenantFilter.Kind),
			Name:   req.TenantFilter.Name,
			Alias:  req.TenantFilter.Alias,
			Email:  req.TenantFilter.Email,
			Status: ParseTenantStatuses(req.TenantFilter.Status),
		},
		Sorter:  admin.BuildSorter(req.SortBy, req.Order),
		Limit:   limitOffset.Limit,
		Offset:  limitOffset.Offset,
		NoLimit: req.NoLimit,
		Brief:   req.Brief,
	})
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	items := []forms.Tenant{}
	for _, item := range dbRecords {
		items = append(items, buildAPITenantItem(item))
	}
	result := forms.ListTenantResp{
		Total: total,
		Items: items,
	}

	Success(c, result, http.StatusOK)
}

// @Summary Get Tenant By ID
// @tags Tenant
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Success 200 {object} forms.Tenant
// @Router /orgs/{org_id} [get]
func GetTenantById(c *gin.Context) {
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	items, _, err := userContext.ListTenants(&admin.ListTenantsReq{Filter: admin.TenantFilter{ID: (*entity.ID)(&req.OrgID)}, NoLimit: true})
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	if len(items) > 0 {
		Success(c, buildAPITenantItem(items[0]), http.StatusOK)
		return
	}

	Fail(c, nil, http.StatusNotFound)
}

// @Summary AddImpersonation
// @tags Impersonation
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/impersonation [post]
func AddImpersonation(c *gin.Context) {
	req := forms.GetOrgIDReq{}
	ctx := c.Request.Context()
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	impersonate := admin.ImpersonateReq{OrgID: req.OrgID}
	if err := userContext.AddImpersonation(ctx, &impersonate); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	Success(c, nil, http.StatusOK)
}

// @Summary Delete Impersonation
// @tags Impersonation
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id}/impersonation [delete]
func DeleteImpersonation(c *gin.Context) {
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	if err := userContext.DeleteImpersonation(req.OrgID); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	Success(c, nil, http.StatusOK)
}

// @Summary Delete Tenant By ID
// @tags Tenant
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id} [delete]
func DeleteTenant(c *gin.Context) {
	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	if err := userContext.DeleteTenant(req.OrgID); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}

// @Summary Serach Tenant access logs
// @tags AccessLog
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Param search query forms.ListAccessLog  true "search"
// @Param page query forms.PaginationReq  true "page"
// @Success 200 {object} forms.ListAccessLogResp
// @Router /orgs/{org_id}/access-logs [get]
func TenantAccessLogs(c *gin.Context) {
	reqUri := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	req := forms.ListAccessLog{}
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	req.OrgID = reqUri.OrgID

	limitOffset := GetLimitAndOffset(c)

	userContext := GetUserContext(c)

	tenantID := reqUri.OrgID
	query := model.ListAccessLogQuery{
		StartTimeInSec: req.StartTime,
		EndTimeInSec:   req.EndTime,
		User:           req.User,
		Content:        req.Content,
		From:           limitOffset.Offset,
		Size:           limitOffset.Limit,
	}
	dbRecords, count, err := userContext.ListAccessLog(tenantID, &query)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	items := []forms.AccessLog{}
	for _, item := range dbRecords {
		items = append(items, buildAPIAccessLog(item))
	}

	result := forms.ListAccessLogResp{
		Total: count,
		Items: items,
	}

	Success(c, result, http.StatusOK)
}

// @Summary Search Management Portal access logs
// @tags AccessLog
// @Accept  json
// @Produce json
// @Param search query forms.ListAccessLog  true "search"
// @Param page query forms.PaginationReq  true "page"
// @Success 200 {object} forms.ListAccessLogResp
// @Router /access-logs [get]
func ListAccessLogs(c *gin.Context) {
	req := forms.ListAccessLog{}
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	limitOffset := GetLimitAndOffset(c)
	userContext := GetUserContext(c)

	tenantID := entity.PingCAPTenantID.Uint64()
	query := model.ListAccessLogQuery{
		StartTimeInSec: req.StartTime,
		EndTimeInSec:   req.EndTime,
		User:           req.User,
		Content:        req.Content,
		From:           limitOffset.Offset,
		Size:           limitOffset.Limit,
	}
	dbRecords, count, err := userContext.ListAccessLog(tenantID, &query)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	items := []forms.AccessLog{}
	for _, item := range dbRecords {
		items = append(items, buildAPIAccessLog(item))
	}

	result := forms.ListAccessLogResp{
		Total: count,
		Items: items,
	}

	Success(c, result, http.StatusOK)
}

// @Summary GetCentralUserProfile
// @tags ManagementPortalUser
// @Accept  json
// @Produce json
// @Success 200 {object} forms.CentralUserProfile
// @Router /me [get]
func GetCentralUserProfile(c *gin.Context) {
	userContext := GetUserContext(c)

	userProfile, err := userContext.GetCentralUserProfile()
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, buildAPICentralUserProfile(userProfile), http.StatusOK)
}

// @Summary ListTenantClusterLifeCycle
// @tags Tenant
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @Param search query forms.ListTenantClusterLifeCycleReq  true "search"
// @Success 200 {object} forms.ListTenantClusterLifeCycleResp
// @Router /orgs/{org_id}/cluster-lifecycles [get]
func ListTenantClusterLifeCycle(c *gin.Context) {
	reqUri := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	req := forms.ListTenantClusterLifeCycleReq{}
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)

	cf, err := userContext.ListTenantClusterLifeCycle(reqUri.OrgID, req.Start, req.End)
	if err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	resp := forms.ListTenantClusterLifeCycleResp{Total: int64(len(cf))}
	for _, item := range cf {
		resp.Items = append(resp.Items, forms.TenantClusterLifecycle{
			Id:        item.ID,
			ProjectID: item.ProjectID,
			CreatedAt: item.CreatedAt,
			DeletedAt: item.DeletedAt,
		})
	}
	Success(c, resp, http.StatusOK)
}

// @Summary PatchTenant
// @tags Tenant
// @Accept  json
// @Produce json
// @Param org_id path string  true "org_id"
// @param req body forms.PatchTenantReq true "patch tenant"
// @Success 200 {object} forms.EmptyResponse
// @Router /orgs/{org_id} [PATCH]
func PatchTenant(c *gin.Context) {
	reqUri := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&reqUri); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	reqBody := forms.PatchTenantReq{}
	if err := c.ShouldBindJSON(&reqBody); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}

	userContext := GetUserContext(c)
	req := &admin.PatchTenantReq{
		OrgID:          reqUri.OrgID,
		Kind:           reqBody.Kind,
		Alias:          reqBody.Alias,
		AccountManager: reqBody.AccountManager,
	}
	if err := userContext.PatchTenant(req); err != nil {
		Fail(c, err, http.StatusInternalServerError)
		return
	}

	Success(c, nil, http.StatusOK)
}
