package oauth2

import (
	"github.com/duke-git/lancet/v2/slice"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/spf13/viper"
	oauth2_dto "github.com/tidbcloud/auth-server/internal/dto/oauth2"
	"github.com/tidbcloud/auth-server/internal/entity"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"net/http"
)

// InnerQueryUserAuthorizedApplications godoc
//
//	@Summary		query user authorized application request.
//	@Description	Inner query user authorized application  for console request.
//	@Tags			console_inner
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	oauth2_dto.InnerQueryUserAuthorizedAppsResponse
//	@Failure		400	{object}	oauth2_dto.InnerBasicResponse
//	@Failure		404	{object}	oauth2_dto.InnerBasicResponse
//	@Router			/api/v1/oauth/console/application/user-authorized  [get]
func (o *Oauth2ControllerImpl) InnerQueryUserAuthorizedApplications(c *gin.Context) {
	ctx := c.Request.Context()
	user, err := getAccountUser(c)
	if err != nil {
		abort(c, err)
		return
	}

	tilog.Info(ctx, "InnerQueryUserAuthorizedApplications", zap.Any("req", user.Email))

	// query all apps
	queryAppsParam := oauth2_dto.QueryAppsParam{Types: []entity.ApplicationType{entity.ApplicationTypeCloud}}
	allApps, err := o.oauth2Svc.QueryOauth2Applications(ctx, queryAppsParam)

	//  this result maybe contain expired access token
	authorizedApps := o.oauth2Svc.InnerQueryUserAuthorizedApplications(ctx, oauth2_dto.UserAuthorizedAppsFilter{UserId: user.Id})
	tilog.Info(ctx, "InnerQueryUserAuthorizedApplications", zap.Any("result", err == nil))
	var appResp []oauth2_dto.UserAuthorizedApp
	for _, app := range allApps {
		isEven := func(authorizedApp oauth2_dto.InnerApplication) bool {
			return authorizedApp.Id == uint64(app.ID)
		}
		isAuthorized := slice.ContainBy(authorizedApps, isEven)
		bean := oauth2_dto.UserAuthorizedApp{}
		_ = copier.Copy(&bean, app)

		// set last usedAt equal the token createAt
		if isAuthorized {

			filter := oauth2_dto.TokenFilter{
				UserId:        user.Id,
				ApplicationId: uint64(app.ID),
			}
			tilog.Info(ctx, "query the user last token in the client", zap.Any("filter", filter))
			token, err := o.oauth2Svc.GetUserLastAccessToken(ctx, filter)
			if err != nil {
				tilog.Info(ctx, "GetUserLastAccessToken fail", zap.Error(err))
				abort(c, err)
				return
			}
			// the last access token is not expired
			if token.ID != 0 {
				tilog.Info(ctx, "last token", zap.Any("token id", token.ID))
				bean.LastUsedAt = &token.CreatedAt
				bean.Authorized = true
			}
		}
		appResp = append(appResp, bean)
	}

	var resp = &oauth2_dto.InnerQueryUserAuthorizedAppsResponse{
		InnerBasicResponse: oauth2_dto.InnerDefaultSuccessResponse(),
		Applications:       appResp,
	}

	c.JSON(http.StatusOK, resp)
}

const (
	InnerCreateApplicationEmailWhitelistKey = "auth-server.InnerCreateApplicationEmailWhitelist"
)

// test create
func (o *Oauth2ControllerImpl) InnerCreateApplications(c *gin.Context) {
	ctx := c.Request.Context()
	user, err := getAccountUser(c)
	if err != nil {
		abort(c, err)
		return
	}

	tilog.Info(ctx, "InnerCreateApplications", zap.Any("operator", user.Email))

	emailWhitelist := viper.GetStringSlice(InnerCreateApplicationEmailWhitelistKey)
	tilog.Info(ctx, "InnerCreateApplications, get create app email whitelist", zap.Any("whitelist", emailWhitelist))
	if len(emailWhitelist) == 0 || !slice.Contain(emailWhitelist, user.Email) {
		c.JSON(http.StatusForbidden, "please contact server owner to create application")
		return
	}

	createAppReq := oauth2_dto.InnerCreateApplicationRequest{}
	err = c.ShouldBindJSON(&createAppReq)
	if err != nil {
		tilog.Info(ctx, "InnerCreateApplications parse request error", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, "param invalid")
		return
	}
	tilog.Info(ctx, "InnerCreateApplications request", zap.Any("req", createAppReq))

	if createAppReq.Name == "" {
		c.JSON(http.StatusBadRequest, "invalid name")
		return
	}
	if createAppReq.RedirectURI == "" {
		c.JSON(http.StatusBadRequest, "invalid redirect_uri")
		return
	}
	grantType, ok := entity.GrantTypeFormString[createAppReq.GrantType]
	if !ok {
		c.JSON(http.StatusBadRequest, "invalid grant_type")
		return
	}
	name := createAppReq.Name
	redirectURI := createAppReq.RedirectURI
	orgRole := createAppReq.OrgRole
	projectRole := createAppReq.ProjectRole
	app, sk, err := o.oauth2Svc.CreateApplication(ctx, name, redirectURI, grantType, orgRole, projectRole)

	tilog.Info(ctx, "InnerCreateApplications", zap.Any("app", app), zap.Any("result", err == nil))

	c.JSON(http.StatusOK, sk)
}
