package middleware

import (
	"fmt"
	"strings"

	"github.com/gin-gonic/gin"

	"be.ems/src/framework/config"
	"be.ems/src/framework/constants"
	"be.ems/src/framework/reqctx"
	"be.ems/src/framework/resp"
	"be.ems/src/framework/token"
	"be.ems/src/framework/utils/parse"
)

/**无Token可访问白名单 */
var URL_WHITE_LIST = []string{
	"/performanceManagement",
	"/faultManagement",
	"/systemState",
	"/omcNeConfig",
	"/cdrEvent",
	"/ueEvent",
	"/objectType/nbState",
	"/upload-ue",
	"/oauth/token",
}

// AuthorizeUser 用户身份授权认证校验
//
// 只需含有其中角色 "hasRoles": {"xxx"},
//
// 只需含有其中权限 "hasPerms": {"xxx"},
//
// 同时匹配其中角色 "matchRoles": {"xxx"},
//
// 同时匹配其中权限 "matchPerms": {"xxx"},
func AuthorizeUser(options map[string][]string) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 登录认证，默认打开
		enable := parse.Boolean(config.Get("serverLoginAuth"))
		if !enable {
			loginUser, _ := reqctx.LoginUser(c)
			loginUser.UserId = 2
			loginUser.User.UserId = 2
			loginUser.User.UserName = "admin"
			loginUser.User.NickName = "admin"
			c.Set(constants.CTX_LOGIN_USER, loginUser)
			c.Next()
			return
		}

		// 判断白名单
		isWhite := false
		requestURI := c.Request.RequestURI
		for _, w := range URL_WHITE_LIST {
			if strings.Contains(requestURI, w) {
				isWhite = true
				break
			}
		}
		if isWhite {
			c.Next()
			return
		}

		// 获取请求头标识信息
		tokenStr := reqctx.Authorization(c)
		if tokenStr == "" {
			c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_NOTOKEN, "authorization token is empty"))
			c.Abort() // 停止执行后续的处理函数
			return
		}

		// 验证令牌
		claims, err := token.UserTokenVerify(tokenStr, "access")
		if err != nil {
			c.JSON(401, resp.CodeMsg(resp.CODE_AUTH, err.Error()))
			c.Abort() // 停止执行后续的处理函数
			return
		}

		// 获取缓存的用户信息
		info := token.UserInfoGet(claims)
		if info.UserId <= 0 {
			c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_INVALID, "invalid login user information"))
			c.Abort() // 停止执行后续的处理函数
			return
		}
		c.Set(constants.CTX_LOGIN_USER, info)

		// 登录用户角色权限校验
		if options != nil {
			var roles []string
			for _, item := range info.User.Roles {
				roles = append(roles, item.RoleKey)
			}
			perms := info.Permissions
			verifyOk := verifyRolePermission(roles, perms, options)
			if !verifyOk {
				msg := fmt.Sprintf("unauthorized access %s %s", c.Request.Method, c.Request.RequestURI)
				c.JSON(403, resp.CodeMsg(resp.CODE_PERMISSION, msg))
				c.Abort() // 停止执行后续的处理函数
				return
			}
		}

		// 调用下一个处理程序
		c.Next()
	}
}

// verifyRolePermission 校验角色权限是否满足
//
// roles 角色字符数组
//
// perms 权限字符数组
//
// options 参数
func verifyRolePermission(roles, perms []string, options map[string][]string) bool {
	// 直接放行 系统管理员角色或任意权限
	if contains(roles, constants.SYS_ROLE_SYSTEM_KEY) || contains(perms, constants.SYS_PERMISSION_SYSTEM) {
		return true
	}
	opts := make([]bool, 4)

	// 只需含有其中角色
	hasRole := false
	if arr, ok := options["hasRoles"]; ok && len(arr) > 0 {
		hasRole = some(roles, arr)
		opts[0] = true
	}

	// 只需含有其中权限
	hasPerms := false
	if arr, ok := options["hasPerms"]; ok && len(arr) > 0 {
		hasPerms = some(perms, arr)
		opts[1] = true
	}

	// 同时匹配其中角色
	matchRoles := false
	if arr, ok := options["matchRoles"]; ok && len(arr) > 0 {
		matchRoles = every(roles, arr)
		opts[2] = true
	}

	// 同时匹配其中权限
	matchPerms := false
	if arr, ok := options["matchPerms"]; ok && len(arr) > 0 {
		matchPerms = every(perms, arr)
		opts[3] = true
	}

	// 同时判断 含有其中
	if opts[0] && opts[1] {
		return hasRole || hasPerms
	}
	// 同时判断 匹配其中
	if opts[2] && opts[3] {
		return matchRoles && matchPerms
	}
	// 同时判断 含有其中且匹配其中
	if opts[0] && opts[3] {
		return hasRole && matchPerms
	}
	if opts[1] && opts[2] {
		return hasPerms && matchRoles
	}

	return hasRole || hasPerms || matchRoles || matchPerms
}

// contains 检查字符串数组中是否包含指定的字符串
func contains(arr []string, target string) bool {
	for _, str := range arr {
		if str == target {
			return true
		}
	}
	return false
}

// some 检查字符串数组中含有其中一项
func some(origin []string, target []string) bool {
	has := false
	for _, t := range target {
		for _, o := range origin {
			if t == o {
				has = true
				break
			}
		}
		if has {
			break
		}
	}
	return has
}

// every 检查字符串数组中同时包含所有项
func every(origin []string, target []string) bool {
	match := true
	for _, t := range target {
		found := false
		for _, o := range origin {
			if t == o {
				found = true
				break
			}
		}
		if !found {
			match = false
			break
		}
	}
	return match
}
