package api

import (
	"context"
	"net/http"

	dbjwt2 "github.com/tidbcloud/account/internel/svc/dbjwt"

	"github.com/tidbcloud/account/central/server"
	"github.com/tidbcloud/tiinf/errs"

	"github.com/tidbcloud/account/commons/config"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/central/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"github.com/tidbcloud/tiinf/sdk/auth"
	"go.uber.org/zap"
)

// create swag doc command: project root directory exec the command
// swag init -d ./ -g central/api/openapi_msp.go --tags Account --output central/docs/openapi/msp

func RegisterDbusertRouter(e gin.IRouter) {

	r := e.Group("/v1beta1/clusters/:clusterId/dbuser")
	//b := e.Group("/v1beta1/projects/clusters/:cluster_id/dbuser")

	r.Use(server.LocalTestIdentity)
	if shouldUseGatewayAuth(config.IsLocalEnv(), config.Global().OpenAPIGatewayConfig.EnableGatewayAuth) {

		//auth + PermissionCheck

		pathWhitelist := []string{}
		auth.InitAccountClientByEndpoint(config.Global().GrpcConfig.AccountSvcAddr, 0)

		r.Use(
			auth.GinGetIdentityInfoInterceptor(&auth.IdentityAuthOption{
				EnableLog: true,
			}),
			auth.GinPermissionCheckInterceptor(&auth.PermissionCheckerCommonParam{
				IdentityF:               auth.GinGetIdentityFunc,
				PathPattern2ResourceMap: pathPattern2ResourceMap,
				PathWhitelist:           pathWhitelist,
				GenAccessParam4GinF:     GenAccessParam4GinWithPrefixOrgIdProjectId,
				EnableLog:               true,
			}),
		)

	}

	r.GET("", GetDbuserHandler)

}

func shouldUseGatewayAuth(local, enabledInLocal bool) bool {
	return !local || enabledInLocal
}

// https://iam.tidbapi.com/v1beta1/clusters/{clusterId}/dbuser
// @Tags Account
// @Summary get one dbuser
// @Description  This endpoint retrieves a db user by apikey or access token.
// @Schemes
// @Accept json
// @Produce json
// @Param clusterId path string true "The id of the cluster."
// @Success 200 {object} GetDbuserRsp
// @Failure 400  {object}  OpenApiError
// @Router /v1beta1/clusters/{clusterId}/dbuser [GET]
func GetDbuserHandler(c *gin.Context) {
	ctx := c.Request.Context()
	bm := utils.Begin(ctx, "GetDbuserHandler")
	defer bm.OnExit(ctx)
	req := SqlUserUriReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		tilog.Warn(ctx, "GetDbuserHandler ShouldBindUri err", zap.Error(err))
		writeError(ctx, c.Writer, err, http.StatusBadRequest, paramInvalidErrorCode, "param invalid")
		return
	}
	if req.ClusterId == 0 {
		tilog.Warn(ctx, "ListProjectHandler input param", zap.Any("cluster_id", req.ClusterId))
		writeError(ctx, c.Writer, nil, http.StatusBadRequest, paramInvalidErrorCode, "param invalid")
		return

	}
	var authInfo *auth.AuthnIdentityInfo
	var err error

	authInfo, err = auth.GetIdentityInfoFromGinContext(c)
	if err != nil {
		tilog.Info(ctx, "get auth Info failed %v", zap.Error(err))
	}

	var jwtInfo *dbjwt2.DbJwtInfo

	// 1、call by access token
	if authInfo.IdentityType == auth.AuthnIdentityTypeAccessToken {

		jwtInfo, err = getJwtByAccessToken(ctx, authInfo, req.ClusterId)

		// 2、call by apikey
	} else if authInfo.IdentityType == auth.AuthnIdentityTypeApiKeyAK {
		jwtInfo, err = getJwtByApikey(ctx, authInfo, req.ClusterId)
	}
	if err != nil {
		httpcode := http.StatusInternalServerError
		codeStr := internalServerErrorCode
		if te, ok := errs.AsTiError(err); ok {
			httpcode = te.HTTPCode()
			codeStr = paramInvalidErrorCode
		} else {
			tilog.Error(ctx, "getJwt err", zap.Any("req", req), zap.Error(err))
		}
		tilog.Info(ctx, "getJwt err", zap.Error(err))
		writeError(ctx, c.Writer, err, httpcode, codeStr, err.Error())
		return
	}

	tilog.Info(ctx, "GetDbuserHandler result success")

	resp := &GetDbuserRsp{
		DbUser: jwtInfo.DbUser,
		Jwt:    jwtInfo.Jwt,
	}
	openApiWriteResult(c.Writer, resp)
}

func getJwtByAccessToken(ctx context.Context, authInfo *auth.AuthnIdentityInfo, clusterId uint64) (*dbjwt2.DbJwtInfo, error) {

	//tilog.Info(ctx, "test HasPermissionForAccessToken: get access token info ", zap.Any("accessToken", accessToken), zap.Any("AccessTokenIdentity", authInfo.AccessTokenRsp.AccessTokenIdentity))
	tilog.Info(ctx, "HasPermissionForAccessToken: get access token info", zap.Any("accessToken", authInfo.IdentityVal), zap.Any("AccessTokenIdentity", authInfo.AccessTokenRsp.AccessTokenIdentity))
	//accessToken = authInfo.IdentityVal
	orgId := authInfo.AccessTokenRsp.AccessTokenIdentity.OrgId
	userId := authInfo.AccessTokenRsp.AccessTokenIdentity.UserId

	tilog.Info(ctx, "get access token info", zap.Any("orgId", orgId), zap.Any("userId", userId))
	jwtSvc := dbjwt2.GlobalDBJwtSvc
	param := &dbjwt2.GetJwtByUserParam{
		ClusterID: clusterId,
		UserID:    userId,
	}
	jwt, err := jwtSvc.GetJwtByUser(ctx, param)
	return jwt, err

}

func getJwtByApikey(ctx context.Context, authInfo *auth.AuthnIdentityInfo, clusterId uint64) (*dbjwt2.DbJwtInfo, error) {
	var projects *dbjwt2.DbJwtInfo
	apiKeyRsp := authInfo.ApiKeyRsp
	orgId, err := auth.GetOrgIdFromApiKeyRsp(ctx, apiKeyRsp)

	if err != nil {
		tilog.Error(ctx, "GetOrgIdFromApiKeyRsp err", zap.Error(err))
		return projects, err
	}

	tilog.Info(ctx, "HasPermissionForApiKey: get apiKey info", zap.Any("apiKeyRsp", apiKeyRsp), zap.Any("orgId", orgId))
	jwtSvc := dbjwt2.GlobalDBJwtSvc
	param := &dbjwt2.GetJwtByApiKeyParam{
		ClusterID: clusterId,
		ApiKeyID:  apiKeyRsp.ApiKey.Id,
		AccessKey: apiKeyRsp.ApiKey.AccessKey,
	}
	jwt, err := jwtSvc.GetJwtByApiKey(ctx, param)
	return jwt, err

}

/*
func checkAccessPermission(ctx context.Context, accessKey string, resourceID uint64) (bool, string, error) {
	resource := "account/openapi/orgs/{org_id}/list_projects"
	resourceParam := map[string]string{
		"org_id": fmt.Sprintf("%d", resourceID),
	}
	auth.CheckIdentityPermission(ctx)
	return useadapter.CheckApiKeyPermission(ctx, accessKey, resource, resourceParam)
}


func CheckIdentityPermission22(ctx context.Context) {
	param := auth.CheckPermissionParam{
		IdentityTye: auth.AccessIdentityTypeUserID,
		IdentityVal: "12345",
		ResourceUri: "/orgs/{org_id}/projects/{pid}/list",
		ResourceParam: map[string]string{
			"org_id": "11",
			"pid":    "22",
		},
	}
	info, err := auth.CheckIdentityPermission(context.Background(), &param)
	log.Println(fmt.Sprintf("CheckIdentityPermission ret, info: %v, err: %s", info, err))
}
*/
