package middleware

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/go-sonic/sonic/cache"
	"github.com/go-sonic/sonic/consts"
	"github.com/go-sonic/sonic/model/dto"
	"github.com/go-sonic/sonic/model/property"
	"github.com/go-sonic/sonic/service"
	"github.com/go-sonic/sonic/util/xerr"
)

type AuthMiddleware struct {
	OptionService       service.OptionService
	OneTimeTokenService service.OneTimeTokenService
	UserService         service.UserService
	Cache               cache.Cache
}

func NewAuthMiddleware(optionService service.OptionService, oneTimeTokenService service.OneTimeTokenService, cache cache.Cache, userService service.UserService) *AuthMiddleware {
	authMiddleware := &AuthMiddleware{
		OptionService:       optionService,
		OneTimeTokenService: oneTimeTokenService,
		Cache:               cache,
		UserService:         userService,
	}
	return authMiddleware
}

func (a *AuthMiddleware) GetWrapHandler() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		isInstalled, err := a.OptionService.GetOrByDefaultWithErr(ctx, property.IsInstalled, false)
		if err != nil {
			abortWithStatusJSON(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
			return
		}
		if !isInstalled.(bool) {
			abortWithStatusJSON(ctx, http.StatusBadRequest, "Blog is not initialized")
			return
		}

		oneTimeToken, ok := ctx.GetQuery(consts.OneTimeTokenQueryName)
		if ok {
			allowedURL, ok := a.OneTimeTokenService.Get(oneTimeToken)
			if !ok {
				abortWithStatusJSON(ctx, http.StatusBadRequest, "OneTimeToken is not exist or expired")
				return
			}
			currentURL := ctx.Request.URL.Path
			if currentURL != allowedURL {
				abortWithStatusJSON(ctx, http.StatusBadRequest, "The one-time token does not correspond the request uri")
				return
			}
			return
		}

		token := ctx.GetHeader(consts.AdminTokenHeaderName)
		if token == "" {
			abortWithStatusJSON(ctx, http.StatusUnauthorized, "未登录,请登录后访问")
			return
		}
		userID, ok := a.Cache.Get(cache.BuildTokenAccessKey(token))

		if !ok || userID == nil {
			abortWithStatusJSON(ctx, http.StatusUnauthorized, "Token 已过期或不存在")
			return
		}

		user, err := a.UserService.GetByID(ctx, userID.(int32))
		if xerr.GetType(err) == xerr.NoRecord {
			_ = ctx.Error(err)
			abortWithStatusJSON(ctx, http.StatusUnauthorized, "用户不存在")
			return
		}
		if err != nil {
			_ = ctx.Error(err)
			abortWithStatusJSON(ctx, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
			return
		}
		ctx.Set(consts.AuthorizedUser, user)
	}
}

func abortWithStatusJSON(ctx *gin.Context, status int, message string) {
	ctx.AbortWithStatusJSON(status, &dto.BaseDTO{
		Status:  status,
		Message: message,
	})
}