package middleware

import (
	"fmt"
	"net/url"
	"strings"

	log "app.yhyue.com/moapp/jylog"
	"github.com/gogf/gf/v2/net/gtrace"

	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/vars"

	"regexp"

	. "bp.jydev.jianyu360.cn/BaseService/gateway/common/gatecode"
	"bp.jydev.jianyu360.cn/BaseService/gateway/core/proxy/rpc"
	"bp.jydev.jianyu360.cn/BaseService/gateway/core/router"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/util/gconv"
)

// filterBefore 前置拦截处理
// step 1：session校验
// step 2: 账户状态校验
// step 3: 接口权益校验
func filterBefore(r *ghttp.Request) error {
	ctx, span := gtrace.NewSpan(r.Context(), "filterBefore")
	defer span.End()
	//r.SetCtx(ctx)

	context := router.GetGContext(ctx)
	rule := context.RouterRule

	if rule.SessCheck.NeedCheck() {
		uCheck, eCheck := rule.SessCheck.CheckUserSession(), rule.SessCheck.CheckEntSession()
		if uCheck && context.Sess.UserId == "" {
			return NewErrorWithCode(GLOBAL_ERR_NOTLOGIN)
		}
		if eCheck && context.Sess.EntId == 0 {
			return NewErrorWithCode(GLOBAL_ERR_NOENT_SELECT)
		}
	}

	//校验账户状态,校验认证状态
	if rule.AccountCheck.NeedCheck() || rule.AuthCheck.NeedCheck() {
		if err := rpc.CheckAccountStatus(ctx, context.Sess.EntId, context.Sess.UserId, rule); err != nil {
			return err
		}
	}
	var useCount, surplusCount int64
	//校验权益
	var powerErr error
	if rule.PowerCheck == 1 {
		useCount, surplusCount, powerErr = rpc.CheckResourcePower(ctx, context.Sess.AccountId, context.Sess.EntAccountId, context.Sess.EntId, context.Sess.EntUserId, rule.FuncCode, r.GetHeader("functionCode"), rule.AppId)
	}
	//老剑鱼用户身份判断
	if powerErr == nil && rule.PowerType > 0 {
		powerErr = rpc.CheckUserPower(ctx, rule.PowerType, rule.AppId, context.Sess.MgoUserId, context.Sess.NewUid, context.Sess.AccountId, context.Sess.EntId, context.Sess.PositionType, context.Sess.PositionId)
	}
	if rule.NoPowerUrl == "" && powerErr != nil {
		return powerErr
	} else if powerErr != nil && rule.NoPowerUrl != "" { //无权限替换请求地址
		newUrl, urlSwitchErr := url.Parse(rule.NoPowerUrl)
		if urlSwitchErr != nil {
			return NewErrorWithCode(GLOBAL_ERR_CONFIG_FAIL)
		}
		powerErr = nil //无权限异常置空
		r.URL = newUrl
	}

	//注入用户身份
	infusionHeader(r, context.Sess, useCount, surplusCount, rule.AppId)
	//外部程序过滤器
	if rule.IsOutServer {
		//赛思用户身份注入
		if err := vars.BManager.OutSeverFilter(rule.MiddleCode, r); err != nil {
			return err
		}
	}

	return nil
}

// filterAfter 后置扣减逻辑。
// 同步处理，若扣减异常，接口返回错误信息。
func filterAfter(r *ghttp.Request) (err error) {
	ctx, span := gtrace.NewSpan(r.Context(), "filterAfter")
	defer span.End()
	//r.SetCtx(ctx)

	context := router.GetGContext(ctx)
	if r.GetError() != nil {
		return
	}
	rule := context.RouterRule
	// 响应完成，判断是否扣除
	if rule.Deduct != 0 {
		var ids []string
		var deductNum int64 = 1
		if rule.Deduct == 2 || rule.Deduct == -2 { //后端应用
			var respErr error
			deductNum, ids, respErr = GetResponseJsonAndClear(r)
			if respErr != nil { //扣减接口异常 不修改接口内容
				log.WithContext(r.Context()).Errorln(respErr)
			}
		} else if rule.Deduct != 1 {
			deductNum = gconv.Int64(rule.Deduct)
		}

		if deductNum > 0 { //资源接口异常
			if rule.Deduct > 0 { //扣减
				if err = rpc.ResourcePowerDeduct(ctx, context.Sess.AccountId, context.Sess.EntAccountId, rule.FuncCode, rule.AppId, deductNum, ids); err != nil {
					return err
				}
			} else { //充值
				if err = rpc.ResourcePowerRecharge(ctx, context.Sess.AccountId, context.Sess.EntAccountId, rule.FuncCode, rule.AppId, deductNum, ids); err != nil {
					return err
				}
			}
		}

	}
	return nil
}

// GetResponseJsonAndClear 获取接口返回参数，并删除扣减预留字段
func GetResponseJsonAndClear(r *ghttp.Request) (deductNum int64, ids []string, err error) {
	_, span := gtrace.NewSpan(r.Context(), "GetResponseJsonAndClear")
	defer span.End()
	respJson := gconv.Map(r.Response.Buffer())
	defer func() {
		r.Response.ClearBuffer()
		r.Response.WriteJson(respJson)
		r.Response.Header().Set("Content-Length", gconv.String(len(r.Response.Buffer())))
		log.WithContext(r.Context()).Debugf("填充 %s 相应头长度%s\n", gconv.String(respJson), r.Response.Header().Get("Content-Length"))
	}()
	log.WithContext(r.Context()).Debugf("获取 %s 相应头长度%s", gconv.String(respJson), r.Response.Header().Get("Content-Length"))

	//仅接口返回成功后 进行资源操作
	if code, exists := respJson["error_code"]; !(exists && code == 0) {
		err = fmt.Errorf("扣减接口返回异常 %v", gconv.String(respJson))
	}

	deductNum = gconv.Int64(respJson["resourceNum"])
	if idStr := gconv.String(respJson["resourceIds"]); idStr != "" {
		ids = strings.Split(idStr, ",")
	}

	for _, field := range []string{"resourceNum", "resourceIds"} {
		if _, ok := respJson[field]; ok {
			delete(respJson, field)
		}
	}
	return
}

// infusionHeader 用户身份注入Header  有则注入
func infusionHeader(r *ghttp.Request, sess *router.JySession, useCount, surplusCount int64, appId string) {
	_, span := gtrace.NewSpan(r.GetCtx(), "infusionHeader")
	defer span.End()
	r.Header.Set("appId", appId)
	if sess.UserId != "" {
		r.Header.Set("userId", sess.UserId)
	}
	if sess.EntName != "" {
		r.Header.Set("entName", sess.EntName)
	}
	if sess.EntId > 0 {
		r.Header.Set("entId", gconv.String(sess.EntId))
	}
	if sess.Phone != "" {
		r.Header.Set("phone", sess.Phone)
	}
	if sess.EntUserId > 0 {
		r.Header.Set("entUserId", gconv.String(sess.EntUserId))
	}
	if sess.NewUid > 0 {
		r.Header.Set("newUserId", gconv.String(sess.NewUid))
	}
	if useCount > 0 {
		r.Header.Set("resourceUseCount", gconv.String(useCount))
	}
	if surplusCount > 0 {
		r.Header.Set("resourceSurplusCount", gconv.String(surplusCount))
	}
	//个人年终报告添加
	if sess.UserPositionId > 0 {
		r.Header.Set("userPositionId", gconv.String(sess.UserPositionId))
	}
	if sess.UserAccountId > 0 {
		r.Header.Set("userAccountId", gconv.String(sess.UserAccountId))
	}
	if sess.EntUserPositionId > 0 {
		r.Header.Set("entPositionId", gconv.String(sess.EntUserPositionId))
	}
	if sess.EntAccountId > 0 {
		r.Header.Set("entAccountId", gconv.String(sess.EntAccountId))
	}

	if sess.PersonId > 0 {
		r.Header.Set("personId", gconv.String(sess.PersonId))
	}
	if sess.AccountId > 0 {
		r.Header.Set("accountId", gconv.String(sess.AccountId))
	}
	if sess.PositionId > 0 {
		r.Header.Set("positionId", gconv.String(sess.PositionId))
	}
	if sess.PositionType > 0 {
		r.Header.Set("positionType", gconv.String(sess.PositionType))
	}
	if sess.MgoUserId != "" {
		r.Header.Set("mgoUserId", gconv.String(sess.MgoUserId))
	}
	if sess.UserName != "" {
		reg := regexp.MustCompile("[^ -~\u2E80-\u2FDF\u3040-\u318F\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FFF\uA960-\uA97F\uAC00-\uD7FF\u3002\u00a5\uff1f\uff01\uff0c\u3001\uff1b\uff1a\u201c\u201d\u2018\u2019\uff08\uff09\u300a\u300b\u3008\u3009\u3010\u3011\u300e\u300f\u300c\u300d\ufe43\ufe44\u3014\u3015\u2026\u2014\uff5e\ufe4f\uffe5\u00a5]+")
		userName := strings.TrimSpace(reg.ReplaceAllString(sess.UserName, ""))
		r.Header.Set("userName", userName)
	}
	if sess.EntUserName != "" {
		r.Header.Set("entUserName", sess.EntUserName)
	}
	if sess.EntRole > 0 {
		r.Header.Set("entRole", gconv.String(sess.EntRole))
	}
	if sess.EntNicheDis > 0 {
		r.Header.Set("entNicheDis", gconv.String(sess.EntNicheDis))
	}
	if sess.EntDeptId > 0 {
		r.Header.Set("entDeptId", gconv.String(sess.EntDeptId))
	}
	if sess.EntUserRole != "" {
		r.Header.Set("entUserRole", gconv.String(sess.EntUserRole))
	}
}
