package handler

import (
	"cold-delivery/app/admin/model"
	"cold-delivery/common"
	"cold-delivery/common/global"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v7"
	"github.com/go-sql-driver/mysql"
	"github.com/mssola/user_agent"
	"gogs.baozhida.cn/zoie/OAuth-core/api"
	"gogs.baozhida.cn/zoie/OAuth-core/pkg"
	jwt "gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth"
	"gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth/user"
	"gogs.baozhida.cn/zoie/OAuth-core/pkg/response"
	"gogs.baozhida.cn/zoie/OAuth-core/sdk"
	"gogs.baozhida.cn/zoie/OAuth-core/sdk/config"
	"gorm.io/gorm"
	"net/http"
	"strings"
)

func PayloadFunc(data interface{}) jwt.MapClaims {
	if v, ok := data.(map[string]interface{}); ok {
		u, _ := v["user"].(SysUser)
		r, _ := v["role"].(SysRole)
		d, _ := v["dept"].(SysDept)
		p, _ := v["post"].(SysPost)
		single, _ := v["single"].(bool)
		return jwt.MapClaims{
			jwt.UUIDKey:      u.Uuid,
			jwt.IdentityKey:  u.Id,
			jwt.RoleIdKey:    r.Id,
			jwt.RoleKey:      r.RoleKey,
			jwt.UserNameKey:  u.Username,
			jwt.DataScopeKey: r.DataScope,
			jwt.RoleNameKey:  r.Name,
			jwt.SingleKey:    single,
			jwt.DeptIdKey:    u.DeptId,
			jwt.DeptNameKey:  d.DeptName,
			"postId":         u.PostId,
			"postCode":       p.PostCode,
		}
	}
	return jwt.MapClaims{}
}

func IdentityHandler(c *gin.Context) interface{} {
	claims := jwt.ExtractClaims(c)
	return map[string]interface{}{
		"UUIDKey":     claims["uuid"],
		"IdentityKey": claims["identity"],
		"UserName":    claims["username"],
		"RoleName":    claims["roleName"],
		"RoleKey":     claims["roleKey"],
		"Id":          claims["identity"],
		"RoleId":      claims["roleId"],
		"DataScope":   claims["dataScope"],
		"single":      claims["single"],
		"DeptId":      claims["deptId"],
		"DeptName":    claims["deptName"],
		"PostId":      claims["postId"],
		"PostCode":    claims["postCode"],
	}
}

// Authenticator 登录认证
// Update 登录认证
// @Summary 登录认证
// @Description 登录认证
// @Tags 登录
// @Accept  application/json
// @Product application/json
// @Param data body Login true "body"
// @Success 200 {object} response.Response "{"code": 200, "data": [...]}"
// @Router /api/login [post]
func Authenticator(c *gin.Context) (interface{}, error) {
	log := api.GetRequestLogger(c)
	ormDB, err := pkg.GetOrm(c)
	if err != nil {
		log.Errorf("get db error, %s", err.Error())
		response.Error(c, 500, err, "数据库连接获取失败")
		return nil, jwt.ErrFailedAuthentication
	}

	var loginVals Login
	var status = "2"
	var msg = "登录成功"
	var username = ""
	defer func() {
		LoginLogToDB(c, status, msg, username)
	}()

	if err = c.ShouldBind(&loginVals); err != nil {
		username = loginVals.Username
		msg = "数据解析失败"
		status = "1"

		return nil, jwt.ErrFailedAuthentication
	}
	//if config.ApplicationConfig.Mode != "dev" {
	//	if !captcha.Verify(loginVals.UUID, loginVals.Code, true) {
	//		username = loginVals.Username
	//		msg = "验证码错误"
	//		status = "1"
	//
	//		return nil, jwt.ErrInvalidVerificationCode
	//	}
	//}
	var u SysUser
	var role SysRole
	var dept SysDept
	var e error
	if loginVals.Type == 1 {
		u, role, dept, e = loginVals.GetUser(ormDB)
		if e != nil {
			msg = e.Error()
			status = "1"
			log.Warnf("%s login failed!", username)
			return nil, jwt.ErrFailedAuthentication
		}
	}
	if loginVals.Type == 2 {
		u, role, dept, e = loginVals.GetUserByCode(ormDB)
		if e != nil {
			msg = e.Error()
			status = "1"
			log.Warnf("%s login failed!", username)
			return nil, jwt.ErrFailedSmsVerifyCode
		}
	}

	if loginVals.Type == 3 {
		u, role, dept, e = loginVals.GetUserByWX(ormDB)
		if e != nil {
			msg = e.Error()
			status = "1"
			log.Warnf("%s login failed!", username)
			return nil, jwt.ErrFailedSmsVerifyCode
		}
	}
	if loginVals.Type == 0 {
		return nil, jwt.ErrFailedAuthentication
	}

	username = loginVals.Username

	var single bool
	//single, err = GetSingleLogin(c)
	//if err != nil {
	//	return nil, err
	//}

	// 加载用户岗位
	var post SysPost
	if u.PostId > 0 {
		ormDB.Table("sys_post").Where("id = ? AND status = '2'", u.PostId).First(&post)
	}
	return map[string]interface{}{"user": u, "role": role, "dept": dept, "post": post, "single": single, "mobile": loginVals.Mobile}, nil
}

// LoginLogToDB Write log to database
func LoginLogToDB(c *gin.Context, status string, msg string, username string) {
	if !config.LoggerConfig.EnabledDB {
		return
	}
	log := api.GetRequestLogger(c)
	l := make(map[string]interface{})

	ua := user_agent.New(c.Request.UserAgent())
	l["ipaddr"] = common.GetClientIP(c)
	l["loginTime"] = pkg.GetCurrentTime()
	l["status"] = status
	l["remark"] = c.Request.UserAgent()
	browserName, browserVersion := ua.Browser()
	l["browser"] = browserName + " " + browserVersion
	l["os"] = ua.OS()
	l["platform"] = ua.Platform()
	l["username"] = username
	l["msg"] = msg

	q := sdk.Runtime.GetMemoryQueue(c.Request.Host)
	message, err := sdk.Runtime.GetStreamMessage("", global.LoginLog, l)
	if err != nil {
		log.Errorf("GetStreamMessage error, %s", err.Error())
		//日志报错错误，不中断请求
	} else {
		err = q.Append(message)
		if err != nil {
			log.Errorf("Append message error, %s", err.Error())
		}
	}
}

// LogOut 退出登录
// @Summary 退出登录
// @Description 退出登录
// @Description LoginHandler can be used by clients to get a jwt token.
// @Description Reply will be of the form {"token": "TOKEN"}.
// @Tags 登录
// @Accept  application/json
// @Product application/json
// @Success 200 {string} string "{"code": 200, "msg": "成功退出系统"}"
// @Router /logout [post]
// @Security Bearer
func LogOut(c *gin.Context) {
	LoginLogToDB(c, "2", "退出成功", user.GetUserName(c))

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"msg":  "退出成功",
	})

}

func Authorizator(data interface{}, c *gin.Context) bool {

	if v, ok := data.(map[string]interface{}); ok {
		u, _ := v["user"].(model.SysUser)
		r, _ := v["role"].(model.SysRole)
		d, _ := v["dept"].(model.SysDept)
		single, _ := v["single"].(bool)
		c.Set("uuid", u.Uuid)
		c.Set("identity", u.Id)
		c.Set("userName", u.Username)
		c.Set("roleName", r.Name)
		c.Set("roleKey", r.RoleKey)
		c.Set("userId", u.Id)
		c.Set("roleId", r.Id)
		c.Set("single", single)
		c.Set("dataScope", r.DataScope)
		c.Set("deptId", u.DeptId)
		c.Set("deptName", d.Name)
		c.Set("postId", u.PostId)
		c.Set("postCode", "")
		return true
	}
	return false
}

func Unauthorized(c *gin.Context, code int, message string) {
	c.JSON(http.StatusOK, gin.H{
		"code": code,
		"msg":  message,
	})
}

// 保存token到redis
func SaveNewestToken(c *gin.Context, userId int64, token string, expire int64) error {
	key := fmt.Sprintf("%s:%d", "bzd.oauth.token", userId)
	return sdk.Runtime.GetCacheAdapter().Set(key, token, int(expire))
}

// redis从redis获取token
func GetNewestToken(c *gin.Context, userId int64) (string, error) {
	key := fmt.Sprintf("%s:%d", "bzd.oauth.token", userId)
	return sdk.Runtime.GetCacheAdapter().Get(key)
}

func GetSingleLogin(c *gin.Context) (bool, error) {
	log := api.GetRequestLogger(c)
	ormDB, err := pkg.GetOrm(c)
	if err != nil {
		log.Errorf("get db error, %s", err.Error())
		response.Error(c, 500, err, "数据库连接获取失败")
		return false, err
	}
	//result := map[string]interface{}{}
	var result string
	err = ormDB.Table("sys_config").Select("config_value").Where("config_key = ? ", "sys_single_login").Scan(&result).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) || err.(*mysql.MySQLError).Number == 1146 {
			// 默认为非单一登录
			return false, nil
		}
		log.Errorf("get sys_config error, %s", err.Error())
		return false, err
	}

	if result == "是" {
		return true, nil
	}
	return false, nil

}

func SetEnterDeptId(c *gin.Context, newToken string, userId int64) error {
	oldToken := ""
	list := strings.Split(c.Request.Header.Get("Authorization"), ".")
	if len(list) > 0 {
		oldToken = list[len(list)-1]
	} else {
		return errors.New("token is null")
	}
	list2 := strings.Split(newToken, ".")
	newToken2 := list2[len(list2)-1]
	deptIdStr, err := sdk.Runtime.GetCacheAdapter().Get(fmt.Sprintf("enter-dept-%s-%d", oldToken, userId))
	if err == nil {
		sdk.Runtime.GetCacheAdapter().Set(fmt.Sprintf("enter-dept-%s-%d", newToken2, userId), deptIdStr, int(config.JwtConfig.Timeout)+7200)
		sdk.Runtime.GetCacheAdapter().Del(fmt.Sprintf("enter-dept-%s-%d", oldToken, userId))
	}
	deptName, err := sdk.Runtime.GetCacheAdapter().Get(fmt.Sprintf("enter-dept-name-%s-%d", oldToken, userId))
	if err == nil {
		sdk.Runtime.GetCacheAdapter().Set(fmt.Sprintf("enter-dept-name-%s-%d", newToken2, userId), deptName, int(config.JwtConfig.Timeout)+7200)
		sdk.Runtime.GetCacheAdapter().Del(fmt.Sprintf("enter-dept-name-%s-%d", oldToken, userId))
	}
	if err == redis.Nil {
		return nil
	}
	return err
}
