package dapi

import (
	"encoding/json"
	"epur-pay/cache"
	"epur-pay/model"
	"epur-pay/pkg/config"
	"epur-pay/pkg/logger"
	"epur-pay/pkg/rds"
	"epur-pay/pkg/utils"
	"fmt"
	"github.com/deatil/go-cryptobin/cryptobin/crypto"
	"net/url"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

var LockPool sync.Map

func (a *ApiBase) apiHandler() error {
	if a.paramNum == 1 {
		a.funcValue.Call(valOf(a))
	} else {
		if err := a.getBody(); err != nil {
			return err
		}
		a.funcValue.Call(valOf(a, a.body))
	}
	return nil
}

func (a *ApiBase) limitRouterRuleHandler() error {

	if cache.Global.LimitRouter.Api != nil {
		key := cache.Global.LimitRouter.Api.Key(a.C)
		if bucket, ok := cache.Global.LimitRouter.Api.GetBucket(key); ok {
			count := bucket.TakeAvailable(1)
			if count == 0 {
				return a.ReturnPublicErrorResponse(a.Translate("rateLimit"))
			}
		}
	}

	return nil
}

func (a *ApiBase) headerHandler() error {

	//站点处理
	a.siteHandler()
	//币种处理
	a.currencyHander()
	//时区处理
	a.localHandler()
	//分页参数处理
	a.pageHandler()
	//语言处理
	a.getLangByAppRequest()
	//版本管理
	if err := a.versionHander(); err != nil {
		return err
	}

	return nil
}

func (a *ApiBase) siteHandler() {
	a.Site = a.C.GetHeader("site")
}

func (a *ApiBase) currencyHander() {
	a.Currency = a.C.GetHeader("currency")
	if len(a.Currency) <= 0 {
		a.Currency = "USD"
	}
}

func (a *ApiBase) GetPage() int {
	page1, _ := a.C.GetQuery("page")
	page, _ := strconv.Atoi(page1)
	if page <= 0 {
		page = 1
	}
	return page
}
func (a *ApiBase) pageHandler() {
	page1, _ := a.C.GetQuery("page")
	size1, _ := a.C.GetQuery("size")

	page, _ := strconv.Atoi(page1)
	limit, _ := strconv.Atoi(size1)

	if page <= 0 {
		page = 1
	}
	if limit >= 100 {
		limit = 100
	} else if limit <= 0 {
		limit = 20
	}

	a.Limit = limit
	a.Offset = (page - 1) * limit
}

func (a *ApiBase) localHandler() {
	var err error
	a.Local, err = time.LoadLocation(a.C.GetHeader("Timezone")) //等同于"UTC"
	if err != nil {
		a.Local = nil
	}

	if len(a.C.GetHeader("Timezone")) == 0 {
		a.Local = nil
	}
}

func (a *ApiBase) getBody() error {

	if a.C.Request.Method == "POST" ||
		a.C.Request.Method == "PUT" ||
		a.C.Request.Method == "PATCH" {

		row, err := a.C.GetRawData()
		if err != nil {
			logger.ErrorLogger.Errorln(err.Error())
			return err
		}

		var data string

		data = string(row)

		if config.Cf.Common.RunMode == "debug" {
			logger.AccessLogger.Infof("%s 请求数据 -> %s", a.C.FullPath(), data)
		}

		if a.Log != nil && a.Log.Event != "登陆" {
			a.Log.Data = data
		}

		err = json.Unmarshal([]byte(data), &a.body)
		if err != nil {

			logger.AccessLogger.Errorf("===> BindJSON: %s", a.Translate("param_fail"))

			return a.ReturnErrorResponse(InvalidParams, a.Translate("param_fail"))
		}
		if a.InParams.IsErrors {
			var errMsg []string
			ValidateField(a.body, func(typeOfCat reflect.Type, fieldName string) {
				field, ok := typeOfCat.FieldByName(fieldName) //通过反射获取filed
				if ok {
					errMsg = append(errMsg, a.Translate(field.Tag.Get("label")))
				}
			})
			if len(errMsg) > 0 {
				return a.ReturnErrorResponse(InvalidParams, strings.Join(errMsg, ","))
			}
		} else {
			var errMsg string
			ValidateField(a.body, func(typeOfCat reflect.Type, fieldName string) {
				field, ok := typeOfCat.FieldByName(fieldName) //通过反射获取filed
				if ok {
					errMsg = field.Tag.Get("label")
					return
				}
			})
			if len(errMsg) > 0 {
				return a.ReturnErrorResponse(InvalidParams, a.Translate(errMsg))
			}
		}
	}
	return nil
}

func valOf(i ...interface{}) []reflect.Value {
	var rt []reflect.Value
	for _, i2 := range i {
		rt = append(rt, reflect.ValueOf(i2))
	}
	return rt
}

func (a *ApiBase) ClientIp() string {

	ip := a.C.Request.Header.Get("Cf-Connecting-Ip")

	if len(ip) <= 0 {
		ip = a.C.Request.Header.Get("X-Real-Ip")
	}

	if len(ip) <= 0 {
		ip = a.C.ClientIP()
	}

	return ip
}

func (a *ApiBase) Translate(key string) string {
	return cache.Global.Caches.Language.GetLang(a.Lang, key)
}

func (a *ApiBase) RefreshCache(key string) error {
	return Cf.ApiRefreshCache(key)
}

func (a *ApiBase) authHandler() error {
	if a.InParams.IsTicket || a.InParams.IsTicketSkip {
		if err := a.getUser(a.C.Request.Header.Get("Token")); err != nil {
			return err
		}
	}

	return nil
}

func (a *ApiBase) getUser(token string) error {

	var uid int64
	a.Token = token

	if a.InParams.IsTicket && len(a.Token) <= 0 {
		return a.ReturnErrorResponse(600, a.Translate("reset_login"))
	} else if len(a.Token) > 0 {
		//logger.AccessLogger.Infoln("---> getUser DecryptToken ", a.Token)
		t, e := DecryptToken(a.Token)
		if !e {
			if a.InParams.IsTicket {
				return a.ReturnErrorResponse(600, a.Translate("not_found"))
			}
		} else {
			uid, _ = strconv.ParseInt(strings.Split(t, ",")[0], 10, 64)
		}
	}

	if uid > 0 {
		a.User = cache.Global.Caches.User.Get(uid)

		if a.User == nil { //解决用户被删除后token无效的问题
			if a.InParams.IsTicket {
				return a.ReturnErrorResponse(600, a.Translate("reset_login"))
			} else {
				return nil
			}
		}

		if a.User.Detail.Token != a.Token {
			if a.InParams.IsTicket {
				return a.ReturnErrorResponse(600, a.Translate("reset_login"))
			} else {
				a.User = nil
				return nil
			}
		}

		if a.User.Uid <= 0 {
			if a.InParams.IsTicket {
				return a.ReturnErrorResponse(600, a.Translate("not_found"))
			} else {
				a.User = nil
				return nil
			}
		}

		// 如果必须是超级权限操作,需要判断是否存在权限ID=1的数据 //!a.User.RoleIds.Of(1)
		if a.InParams.IsSuperOnly && a.User.Uid != 1 {
			logger.AccessLogger.Errorf("---> Uid: [%d] Super权限错误 %s", a.User.Uid, a.InParams.Permission)
			return a.ReturnErrorResponse(602, a.Translate("AT004"))
		}

		// 这里验证用户是否拥有接口操作权限
		if len(a.InParams.Permission) > 0 {
			if !cache.Global.Caches.RolePermission.Check(a.User.RoleIds, a.InParams.Permission) {
				logger.AccessLogger.Errorf("---> Uid: [%d] 尚无权限: [%s] %v", a.User.Uid, a.InParams.Permission, a.User.RoleIds)
				return a.ReturnPublicErrorResponse(a.Translate("AT058"))
			}
		}

		if a.User.Status != "0" {
			if a.InParams.IsTicket || (a.InParams.IsTicketSkip && a.User != nil) {
				if !a.InParams.IsNoCheckBlackList {
					return a.ReturnErrorResponse(602, a.Translate("black_list"))
				}
			} else {
				a.User = nil
				return nil
			}
		}
	}

	return nil
}

func (a *ApiBase) lockTrade() error {
	//logger.AccessLogger.Infof("params:%+v user:%+v", a.InParams.IsLockTradeForMerchant, a.User)
	if a.InParams.IsForUpdate && a.User != nil {
		if _, ok := LockPool.Load(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid)); ok {
			return a.ReturnPublicErrorResponse(a.Translate("LLLP"))
		} else {
			LockPool.Store(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid), true)
		}
	}
	return nil

}

func (a *ApiBase) freeTrade() {
	if a.InParams.IsForUpdate && a.User != nil {
		LockPool.Delete(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid))
	}
}

func (a *ApiBase) saveLog() {

	defer func() {
		err := recover()
		if err != nil {
			buf := make([]byte, 1<<16)
			runtime.Stack(buf, true)
			e := reflect.ValueOf(err)
			logger.ErrorLogger.Errorln(e.String())
		}
	}()

	//logger.AccessLogger.Warnln(a.Error, a.InParams.IsSkipErrorSaveLog)

	if a.Log != nil && a.Log.Uid > 0 &&
		(a.Error == nil || a.InParams.IsSkipErrorSaveLog) {
		// 如果是super操作也记录
		if a.Log.Name == "super" && a.Log.UserType == "1" {
			a.Log.IsSuper = "1"
			a.Log.Ip = ""
		}
		a.Log.A1 = cache.GetIpAddress(a.Log.Ip)

		if a.Error != nil {
			a.Log.Event = fmt.Sprintf("%s(失败)", a.Log.Event)
		}

		if err := rds.DB.Create(a.Log).Error; err != nil {
			logger.ErrorLogger.Errorln(err.Error())
		}
	}
}

func (a *ApiBase) getLangByAppRequest() {

	lan := a.C.Request.Header.Get("Language")

	languageCode := ""
	countryCode := ""
	if len(strings.Split(lan, "_")) > 1 {
		languageCode = strings.Split(lan, "_")[0]
		countryCode = strings.Split(lan, "_")[1]
	}

	row := ""

	tmpLanguageCodes := make([]model.SysLanguage, 0)
	for _, v := range cache.Global.Caches.Language.List {
		if v.LanguageCode == languageCode {
			tmpLanguageCodes = append(tmpLanguageCodes, v)
		}
	}
	isFind := false
	for _, v := range tmpLanguageCodes {
		if v.CountryCode == countryCode {
			isFind = true
			row = fmt.Sprintf("%s_%s", v.LanguageCode, v.CountryCode)
			break
		}
	}
	if !isFind && len(tmpLanguageCodes) > 0 {
		row = fmt.Sprintf("%s_%s", tmpLanguageCodes[0].LanguageCode, tmpLanguageCodes[0].CountryCode)
	}

	if len(row) <= 0 {
		row = cache.Global.Caches.Language.DefaultLanguage
	}

	if len(row) <= 0 {
		row = "en_US"
	}
	a.Lang = row
}

func (a *ApiBase) versionHander() error {

	appVersion := a.C.Request.Header.Get("appversion")
	{

		var version *model.Version

		if a.Site == "h5Pc" {
			version = cache.Global.Caches.Version.Get("2")
		} else if a.Site == "h5App" {
			version = cache.Global.Caches.Version.Get("1")
		} else if a.Site == "h5Admin" {
			version = cache.Global.Caches.Version.Get("0")
		}

		if version != nil && version.Id > 0 {
			if len(appVersion) > 0 {

				requestVersion, err := strconv.ParseInt(appVersion, 10, 64)
				if err != nil {
					logger.AccessLogger.Errorln("requestVersion version error", appVersion)
					return nil
				}
				currVersion, err := strconv.ParseInt(version.Version, 10, 64)
				if err != nil {
					logger.AccessLogger.Errorln("currVersion version error", version.Version)
					return nil
				}
				if currVersion > requestVersion && version.Force == "0" {
					return a.ReturnErrorResponse(920, a.Translate("versionUpdateing"))
				}
			}
		}
	}
	return nil
}

// 加密
func EncryptToken(uid int64) string {

	return url.QueryEscape(crypto.FromString(fmt.Sprintf("%d,%d", uid, utils.Time2StampSecond()+24*60*60)).
		SetKey("dfertf12dfertf12").
		SetIv("dfertf12dfertf12").
		Aes().
		CBC().
		PKCS7Padding().
		Encrypt().
		ToBase64String())
}

func DecryptToken(token string) (string, bool) {
	token, _ = url.QueryUnescape(token)

	cyptde := crypto.
		FromBase64String(token).
		SetKey("dfertf12dfertf12").
		SetIv("dfertf12dfertf12").
		Aes().
		CBC().
		PKCS7Padding().
		Decrypt().
		ToString()
	if len(cyptde) <= 0 {
		return "", false
	}
	return cyptde, true
}