package utils

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/form3tech-oss/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func GinBizFail(c *gin.Context, err error, message string) {
	GinFail(c, http.StatusBadRequest, err, message)
}

func GinUnauthFail(c *gin.Context, err error, message string) {
	GinFail(c, http.StatusUnauthorized, err, message)
	c.Abort()
}
func GinFail(c *gin.Context, httpCode int, err error, message string) {
	tilog.Error(c.Request.Context(), "GinBizFail", zap.Any("err", err), zap.String("message", message))
	c.JSON(httpCode, gin.H{"code": httpCode, "message": message})
}

func GinBizSucc(c *gin.Context, result interface{}) {
	if result == nil {
		result = gin.H{}
	}
	c.JSON(http.StatusOK, result)
}

func getAuth0Token(c *gin.Context) (string, error) {
	header := c.GetHeader("authorization")
	if header == "" {
		header = c.GetHeader("Authorization")
	}

	if header == "" {
		return "", errs.NewUnauthorized("No token found in header.", nil)
	}

	tokenSlice := strings.Split(header, " ")
	if len(tokenSlice) != 2 || strings.ToLower(tokenSlice[0]) != "bearer" {
		return "", errs.NewUnauthorized("Parse token failed.", nil)
	}

	return tokenSlice[1], nil
}

func parseAuth0Token(tokenValidator *auth0.RawTokenValidator, rawToken string) (string, error) {
	var claims jwt.MapClaims
	var err error
	claims, err = tokenValidator.ValidateFor(auth0.AuthValidatorIDCentral, rawToken)
	if err != nil {
		return "", err
	}

	// Auth0 m2m
	if claims["gty"] == auth0.ClientCredentialsAuthType {
		return fmt.Sprintf("%v@m2m-client", claims["azp"]), nil
	}

	// login as user
	email, err := auth0.GetEmailClaim(claims)
	if err != nil {
		return "", err
	}
	return email, nil
}

func ParseConsoleAuthToken(tokenValidator *auth0.RawTokenValidator) gin.HandlerFunc {
	return func(c *gin.Context) {
		ctx := c.Request.Context()
		token, err := getAuth0Token(c)
		if err != nil {
			GinUnauthFail(c, err, "Get Auth0Token failed.")
			return
		}
		email, err := parseAuth0Token(tokenValidator, token)
		if err != nil {
			GinUnauthFail(c, err, "parse Auth0Token failed.")
			return
		}
		c.Set(string(auth0.AuthTokenClaimKey), email)
		tilog.Info(ctx, "Parse Auth token succeed. ", zap.String("email", email))
		c.Next()
	}
}

// Supoort Rest-API CORS
func Cors() gin.HandlerFunc {

	return func(c *gin.Context) {
		method := c.Request.Method
		if method == "OPTIONS" {
			c.Header("Access-Control-Allow-Headers", "accept, content-type, authorization")
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
			c.Header("Access-Control-Allow-Origin", "*")
			c.AbortWithStatus(http.StatusNoContent)
		} else {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Next()
		}
	}

}
