package controllers

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/plugins/cors"
	"gitlab.fjmaimaimai.com/mmm-go/gocomm/time"
	"strconv"
	"strings"

	"opp/protocol"
	"opp/services/auth"

	"github.com/astaxie/beego/context"
	"github.com/astaxie/beego/validation"
	"github.com/prometheus/client_golang/prometheus"
	"gitlab.fjmaimaimai.com/mmm-go/gocomm/common"
	"gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
	"gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/mybeego"
)

var (
	//prometheus 监控endpoint
	HTTPReqTotal *prometheus.CounterVec
)

type BaseController struct {
	Header *protocol.RequestHeader
	mybeego.BaseController
}

func init() {
	// HistogramVec 是一组Histogram
	HTTPReqTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "request_count_vec", //http_requests_total
		Help: "total number of http requests made.",
	}, []string{"method", "path"})
	// 这里的"method"、"path"、"status" 都是label , "status"
	prometheus.MustRegister(
		HTTPReqTotal,
	)
	//if err:=prometheus.Register(HTTPReqTotal);err!=nil{
	//	log.Error(err)
	//}
}

var DefaultController *BaseController = &BaseController{}

//Valid  valid struct
func (this *BaseController) Valid(obj interface{}) (result bool, msg *protocol.ResponseMessage) {
	/*校验*/
	var err error
	valid := validation.Validation{}
	result, err = valid.Valid(obj)
	//if err != nil {
	//	log.Error(err)
	//	msg = protocol.BadRequestParam(2)
	//	return
	//}
	if err != nil {
	}
	if !result {
		for _, err := range valid.Errors {
			if strings.HasSuffix(err.Key, ".Mobile") {
				msg = protocol.BadRequestParam(2001)
				return
			}
			log.Error(err.Key, err.Message)
		}
		msg = protocol.BadRequestParam(2)
		return
	}

	return
}

func (this *BaseController) Resp(msg *protocol.ResponseMessage) {
	this.Data["json"] = msg
	this.ServeJSON()
}

func (this *BaseController) RespH5(msg *protocol.ResponseMessage) {
	if msg.Errno != 0 {
		msg.Errno = -1
	}
	this.Data["json"] = msg
	this.ServeJSON()
}

//获取请求头信息
func GetRequestHeader(ctx *context.Context) *protocol.RequestHeader {
	h := &protocol.RequestHeader{}
	h.AccessToken = ctx.Input.Header("x-mmm-accesstoken")
	h.AppProject = ctx.Input.Header("x-mmm-appproject")
	h.DeviceType, _ = strconv.Atoi(ctx.Input.Header("x-mmm-devicetype"))
	h.Sign = ctx.Input.Header("x-mmm-sign")
	h.Uuid = ctx.Input.Header("x-mmm-uuid")
	h.TimeStamp = ctx.Input.Header("x-mmm-timestamp")
	h.Version = ctx.Input.Header("x-mmm-version")
	h.Uid, _ = strconv.ParseInt(ctx.Input.Header("uid"), 10, 64) //需要uid写入到header里面
	if h.Uid == 0 {
		h.Uid, _ = strconv.ParseInt(ctx.Input.Header("x-mmm-uid"), 10, 64)
	}
	h.CompanyId, _ = strconv.ParseInt(ctx.Input.Header("x-mmm-cid"), 10, 64)
	h.UserId, _ = strconv.ParseInt(ctx.Input.Header("x-mmm-id"), 10, 64)
	return h
}

//过滤器
func FilterComm(ctx *context.Context) {
	//if strings.HasSuffix(ctx.Request.RequestURI,"login"){
	//	return
	//}

	//统计
	MetricCounter(ctx)

	//if beego.BConfig.RunMode == "dev" && (ctx.Input.Header("x-mmm-uid") != "" || ctx.Input.Header("uid") != "") {
	//	return
	//}
	//TODO:注入账号,后期移除掉
	//if ctx.Input.Header("x-mmm-accesstoken") == "" {
	//	ctx.Request.Header.Set("x-mmm-accesstoken", "6839602f1d8211eabd85000c29ad8d6d")
	//	if ctx.Input.Header("x-mmm-accesstoken") == "" {
	//		ctx.Request.Header.Add("x-mmm-accesstoken", "6839602f1d8211eabd85000c29ad8d6d")
	//	}
	//} else {
	//	//1.检查签名
	//	if !CheckSign(ctx) {
	//		return
	//	}
	//}
	//if !CheckToken(ctx) {
	//	return
	//}

	//if !CheckSign(ctx) {
	//	return
	//}
	//3.查重uuid
	if !CheckUuid(ctx) {
		return
	}
	//2.检查token是否有效
	if !CheckToken(ctx) {
		return
	}
	return
}

func MetricCounter(ctx *context.Context) {
	// 请求数加1
	HTTPReqTotal.With(prometheus.Labels{
		"method": ctx.Request.Method,
		"path":   ctx.Request.RequestURI,
		//"status": strconv.Itoa(c.Writer.Status()),
	}).Inc()
}

//检查签名
func CheckSign(ctx *context.Context) (result bool) {
	var (
		h       *protocol.RequestHeader
		sign    string
		signHex string
	)
	result = true
	h = GetRequestHeader(ctx)
	//1.检查签名
	sign = fmt.Sprintf("v!(MmM%v%v%vMmM)i^", h.TimeStamp, h.Uuid, h.AccessToken)
	sha256 := sha256.New()
	sha256.Write([]byte(sign))
	signHex = hex.EncodeToString(sha256.Sum(nil))
	if strings.Compare(signHex, h.Sign) != 0 {
		msg := protocol.BadRequestParam(113)
		log.Error(fmt.Sprintf("%v req:%v resp:%v %v", ctx.Request.RequestURI, common.AssertJson(h), common.AssertJson(msg), signHex))
		ctx.Output.JSON(msg, false, false)
		result = false
		return
	}
	return
}

//检查access_token
func CheckToken(ctx *context.Context) (result bool) {
	var (
		msg *protocol.ResponseMessage
	)
	token := ctx.Input.Header("x-mmm-accesstoken")
	if strings.HasSuffix(ctx.Request.RequestURI, "loginModule") ||
		strings.HasSuffix(ctx.Request.RequestURI, "accessToken") ||
		strings.HasSuffix(ctx.Request.RequestURI, "refreshToken") ||
		strings.HasSuffix(ctx.Request.RequestURI, "smsCode") {
		return true
	}
	result = true
	defer func() {
		if msg != nil {
			result = false
			ctx.Output.JSON(msg, false, false)
		}
	}()
	if rsp, err := auth.CheckToken(&protocol.CheckTokenRequest{Token: token}); err != nil || rsp.UserInfo == nil {
		msg = protocol.NewReturnResponse(rsp, err)
		log.Error(fmt.Sprintf("%v req:%v resp:%v", ctx.Request.RequestURI, token, common.AssertJson(msg)))
		return
	} else {
		if rsp.UserInfo != nil {
			//设置附加数据
			ctx.Request.Header.Set("x-mmm-uid", fmt.Sprintf("%v", rsp.UserInfo.UserId))
			ctx.Request.Header.Set("x-mmm-cid", fmt.Sprintf("%v", rsp.UserInfo.CurrentCompanyId))
			ctx.Request.Header.Set("x-mmm-id", fmt.Sprintf("%v", rsp.UserInfo.CurrentUserCompanyId))
		}
	}
	return
}

//检查Uuid
func CheckUuid(ctx *context.Context) (result bool) {
	var (
		msg *protocol.ResponseMessage
	)
	result = true
	defer func() {
		if msg.Errno != 0 {
			ctx.Output.JSON(msg, false, false)
		}
	}()
	uuid := ctx.Input.Header("x-mmm-uuid")
	msg = protocol.NewReturnResponse(auth.CheckUuid(&protocol.CheckUuidRequest{Uuid: uuid}))
	if msg.Errno != 0 {
		log.Error(fmt.Sprintf("%v req:%v resp:%v", ctx.Request.RequestURI, uuid, common.AssertJson(msg)))
	}
	return
}

//AllowOption 允许跨域请求
var AllowOption = func(ctx *context.Context) {
	if ctx.Request.Method != "OPTIONS" {
		return
	}
	f := cors.Allow(&cors.Options{
		AllowMethods: []string{"POST", "GET", "OPTIONS", "PUT", "DELETE"}, //允许的请求类型
		AllowHeaders: []string{"Origin", "Accept", "Content-Type", "Authorization",
			"x-mmm-cid", "x-mmm-uid", "x-mmm-accesstoken", "x-mmm-refreshtoken", "x-requested-with"}, //允许的头部信息
		ExposeHeaders:    []string{"Content-Length"}, //允许暴露的头信息
		AllowCredentials: false,                      //不允许共享AuthTuffic证书
		AllowAllOrigins:  true,                       //允许的请求来源
	})
	f(ctx)
	ctx.Output.Body([]byte("{}"))
	ctx.Output.SetStatus(204)
	return
}

//LogRequestData Before Router
var LogRequestData = func(ctx *context.Context) {
	log.Info("====>Recv Request:%s", ctx.Input.URI())
	hmap := map[string]string{
		//protocol.HeaderAccessToken:  ctx.Input.Header(protocol.HeaderAccessToken),
		//protocol.HeaderRefreshToken: ctx.Input.Header(protocol.HeaderRefreshToken),
	}
	if ctx.Input.RequestBody != nil {
		log.Info("====>Recv data from client:\nHeadData: %v \nBodyData: %s", hmap, string(ctx.Input.RequestBody))
	} else {
		log.Info("====>Recv data from client:\nHeadData: %v ", hmap)
	}
}

func (this *BaseController) Prepare() {
	this.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*")
	this.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "*")
	if this.Ctx.Input.Method() == "OPTIONS" {
		this.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		return
	}
	this.Query = map[string]string{}
	input := this.Input()
	for k := range input {
		this.Query[k] = input.Get(k)
	}
	if this.Ctx.Input.RequestBody != nil {
		this.ByteBody = this.Ctx.Input.RequestBody[:]
		if len(this.ByteBody) < 1 {
			this.ByteBody = []byte("{}")
		}
		this.RequestHead = this.GetRequestHead()
		this.Header = GetRequestHeader(this.Ctx)
		this.Header.SetRequestId(fmt.Sprintf("%v.%v.%s", this.Header.Uid, time.GetTimeByYyyymmddhhmmss(), this.Ctx.Request.URL))
		log.Debug(fmt.Sprintf("====>Recv data from uid(%d) ucid(%v) client:\nHeadData: %s\nRequestId:%s BodyData: %s", this.Header.Uid, this.Header.UserId, common.AssertJson(this.Header), this.Header.GetRequestId(), string(this.ByteBody)))
	}
}

func (this *BaseController) Finish() {
	if this.Ctx.Input.Method() == "OPTIONS" {
		return
	}
	strByte, _ := json.Marshal(this.Data["json"])
	length := len(strByte)
	if length > 5000 {
		log.Debug(fmt.Sprintf("<====Send to uid(%d) ucid(%v) client: %d byte\nRequestId:%s RspBodyData: %s......", this.Header.Uid, this.Header.UserId, length, this.Header.GetRequestId(), string(strByte[:5000])))
	} else {
		log.Debug(fmt.Sprintf("<====Send to uid(%d) ucid(%v) client: %d byte\nRequestId:%s RspBodyData: %s", this.Header.Uid, this.Header.UserId, length, this.Header.GetRequestId(), string(strByte)))
	}
}

//func(this *BaseController)Get(){
//	if strings.HasSuffix(this.Ctx.Request.RequestURI,".mp3"){
//		this.Ctx.ResponseWriter.Header().Set("Content-Type","audio/mpeg")
//	}
//}