// 微信全网接入测试
// https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318611&token=&lang=zh_CN

package controllers

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"wechat-conf/models"
	"wechat-conf/models/model"
	"wechat-conf/service/autoreply"
	"wechat-conf/service/wechat"
	"wechat-conf/utils"

	"github.com/astaxie/beego"

	"github.com/kinisky564477/wechat/component"

	"github.com/kinisky564477/wechat/core"
)

// WechatController 用户
type WechatController struct {
	BaseController
	serv       *autoreply.AutoreplyServ
	wechatServ *wechat.WechatServ
	compConf   *model.SysComponentConf
	cipher     core.IOCipher
}

const (
	INFOTYPE_TICKET           = "component_verify_ticket"
	INFOTYPE_AUTHORIZED       = "authorized"
	INFOTYPE_UNAUTHORIZED     = "unauthorized"
	INFOTYPE_UPDATEAUTHORIZED = "updateauthorized"
)

const (
	WX_OFFICE_TEST_APPID    = "wx570bc396a51b8ff8"
	WX_OFFICE_TEST_USERNAME = "gh_3c884a361561"
)

const (
	RESPONSE_CUSTOM_MESSAGE = "response-custom-message"
)

var nilData = []byte("success")

// Constructor 初始化 Controller
// @Title Constructor
// @Description 初始化 Controller, 系统自动调用
func (c *WechatController) Constructor() {
	c.serv = autoreply.NewAutoreplyServ(c.Context)
	c.wechatServ = wechat.NewWechatServ(c.Context)

	var err error
	c.compConf, err = c.wechatServ.GetComponentInfo()
	if err != nil {
		utils.LogError("获取平台配置失败", err)
		return
	}

	c.cipher, err = core.NewCipher(c.compConf.OriginToken, c.compConf.Aeskey, c.compConf.Appid)
	if err != nil {
		utils.LogError("初始化加解密算法失败", err)
		return
	}
}

// preCheck 如果有需要用到加解密之前, 请先调用这个方法
func (c *WechatController) preCheck(resp []byte) {
	if c.compConf == nil || c.compConf.Appid == "" {
		c.ResponseRaw(resp)
	}

	if c.cipher == nil {
		c.ResponseRaw(resp)
	}
}

// ComponentPush 接收第三方平台推送
// 主要有 ticket, 取消授权等
func (c *WechatController) ComponentPush() {
	utils.LogInfo("接收平台推送")
	c.preCheck(nilData)

	// 接收内容
	receiveData := string(c.Ctx.Input.RequestBody)
	utils.LogInfo("接收内容", receiveData)
	if receiveData == "" {
		c.ResponseRaw(nilData)
	}

	// 解密内容
	r := bytes.NewBufferString(receiveData)
	dt, err := c.cipher.Decrypt(r)
	if err != nil {
		utils.LogError("解密数据失败", err)
		c.ResponseRaw(nilData)
	}

	receiveMessage := string(dt)
	utils.LogInfo("解密内容: ", receiveMessage)

	// 解析xml
	xp := &core.XMLParse{}
	data, err := xp.Parse(receiveMessage)
	if err != nil {
		utils.LogError("解析解密数据失败:", err)
		c.ResponseRaw(nilData)
	}

	infoType := data["InfoType"]
	// fromWXOffice := data["AppId"] == WX_OFFICE_TEST_APPID

	switch infoType {
	case INFOTYPE_TICKET:
		// 微信测试推送 component_verify_ticket, 只需要返回 success
		// if fromWXOffice {
		// 	c.ResponseRaw(nilData)
		// }

		// 更新ticket
		c.compConf.Ticket = data["ComponentVerifyTicket"]
		err := c.wechatServ.UpdateComponentTicket(c.compConf)
		if err != nil {
			utils.LogError("修改ticket失败:", err)
		}
		utils.RefreshComponentTicket(data["ComponentVerifyTicket"])
		break

	case INFOTYPE_AUTHORIZED:
		// 授权成功
		var cert = map[string]string{
			"appid":              data["AuthorizerAppid"],
			"authorization_code": data["AuthorizationCode"],
		}

		wxclient := utils.WechatInit(cert, c.wechatServ.UpdateToken)
		if wxclient == nil {
			utils.LogError("获取wxclient失败")
			c.ResponseRaw(nilData)
		}

		utils.AppendWxClient(wxclient)

		// 获取微信信息
		info, err := wxclient.GetWechatInfo()
		if err != nil {
			utils.LogError("获取微信信息失败")
			c.ResponseRaw(nilData)
		}

		authorizerInfo := (info["authorizer_info"]).(map[string]interface{})

		// 插入数据库
		mjson, _ := json.Marshal(data)
		HeadImg := ""
		if authorizerInfo["head_img"] != nil {
			HeadImg = authorizerInfo["head_img"].(string)
		}
		var conf = model.SysWechatConf{
			Appid:             data["AuthorizerAppid"],
			AuthorizationCode: data["AuthorizationCode"],
			AuthorizationInfo: string(mjson),
			WxNikeName:        authorizerInfo["nick_name"].(string),
			HeadImg:           HeadImg,
			UserName:          authorizerInfo["user_name"].(string),
			PrincipalName:     authorizerInfo["principal_name"].(string),
			QrcodeUrl:         authorizerInfo["qrcode_url"].(string),
		}

		err = c.wechatServ.SaveWechatConf(conf)
		if err != nil {
			utils.LogError("保存微信授权信息失败:", err)
			c.ResponseRaw(nilData)
		}
		break
	case INFOTYPE_UPDATEAUTHORIZED:
		// 授权更新
		break
	case INFOTYPE_UNAUTHORIZED:
		// 取消授权
		// 删除wechatConf信息,同时将org解绑
		appid := data["AuthorizerAppid"]
		c.wechatServ.UnAuthorized(appid)
		// 删除第三方中的微信信息
		utils.Component.DelWxClient(appid)
		break
	}

	c.ResponseRaw(nilData)
}

// WechatInfo 微信接入
func (c *WechatController) WechatInfo() {
	method := c.Ctx.Input.Method()
	if method == http.MethodGet {
		echostr := c.GetString("echostr")
		c.ResponseRaw([]byte(echostr))
	} else {
		c.WxReceive()
	}
}

// WxReceive 微信消息接收
func (c *WechatController) WxReceive() {
	utils.LogInfo("接收微信事件, URI: ", c.Ctx.Input.URI())
	c.preCheck(nilData)

	// 初始化微信客户端
	appid := c.GetString(":appid")
	wechat, err := utils.Component.GetWxClient(appid)
	if err != nil {
		utils.LogError("获取微信失败: " + err.Error())
		c.ResponseRaw(nilData)
	}

	// 校验接收数据
	receiveData, err := c.validReceiveData(c.Ctx.Input.RequestBody, c.compConf.Aeskey, wechat)
	if err != nil {
		c.ResponseRaw(nilData)
	}

	// 获取返回内容
	replyMessage, err := c.getReplayMessage(receiveData, wechat)
	if err != nil && err.Error() == RESPONSE_CUSTOM_MESSAGE {
		// 微信测试用例会跑到这边
		// 模拟粉丝发送消息, 并立即调用客户接口, 反馈固定的字串
		go wechat.SendCustomText(receiveData["FromUserName"], string(replyMessage))

		c.ResponseRaw(nil)
	}

	if err != nil || replyMessage == nil || len(replyMessage) == 0 {
		c.ResponseRaw(nilData)
	}

	utils.LogInfo("反馈微信内容: ", string(replyMessage))

	// 加密内容
	sendData, err := c.encryptMessage(replyMessage, c.compConf.Aeskey, c.compConf.OriginToken, c.compConf.Appid, wechat)
	if err != nil {
		c.ResponseRaw(nilData)
	}

	sendMessage := string(sendData)

	// 去掉多余的回车
	sendMessage = strings.Replace(sendMessage, "\n\n", "\n", -1)
	sendMessage = strings.Trim(sendMessage, "\n")
	utils.LogInfo("反馈加密内容: ", sendMessage)

	c.Ctx.Output.Header("Content-Type", "text/xml; charset=utf-8")
	c.ResponseRaw([]byte(sendMessage))
}

// validReceiveData 校验接收信息
// 没有通过 signature 校验,默认是正确的
func (c *WechatController) validReceiveData(receiveData []byte, aesKey string, wechat *component.WxClient) (data map[string]string, err error) {
	receiveMessage := string(receiveData)
	utils.LogInfo("事件内容: ", receiveMessage)

	var msg map[string]string
	msg, err = wechat.TransformMessage(receiveMessage)
	if err != nil {
		utils.LogError("读取微信服务发送内容失败: " + err.Error())
		return
	}

	var key []byte
	var encryptData []byte
	var decryptData []byte

	key, err = base64.StdEncoding.DecodeString(aesKey + "=")
	if err != nil {
		utils.LogError("Base64 decode aes-key 失败:", err)
		return
	}

	encryptData, err = base64.StdEncoding.DecodeString(msg["Encrypt"])
	if err != nil {
		utils.LogError("密文base64解析", err)
		return
	}

	decryptData, err = utils.AesDecrypt(encryptData, key)
	if err != nil {
		utils.LogError("解密失败:", err)
		return
	}

	utils.LogInfo("内容解密: ", string(decryptData))

	// 解析xml
	decmsg := string(decryptData)
	xmlStart := strings.Index(decmsg, "<xml>")
	if xmlStart < 0 {
		utils.LogError("xml解析失败:格式不正确,没有xml节点")
		return
	}

	decmsg = decmsg[xmlStart:]
	xp := &core.XMLParse{}
	data, err = xp.Parse(decmsg)
	if err != nil {
		utils.LogError("xml解析失败:", err)
		return
	}

	return
}

// encryptReceiveMessage 加密需要发送的信息
func (c *WechatController) encryptMessage(message []byte, aesKey, token, appID string, wechat *component.WxClient) ([]byte, error) {
	cipher, err := core.NewCipher(token, aesKey, appID)
	if err != nil {
		utils.LogError("NewCipher 失败: " + err.Error())
		return nil, err
	}

	dt := bytes.NewBuffer([]byte{})

	nonce := c.GetString("nonce")
	timestamp := c.GetString("timestamp")

	cipher.Encrypt(dt, message, nonce, timestamp)

	return dt.Bytes(), nil
}

func (c *WechatController) getReplayMessage(receviceData map[string]string, wechat *component.WxClient) (message []byte, err error) {
	var replay *model.TaAutoReply

	msgType := receviceData["MsgType"]
	from := receviceData["ToUserName"]
	openID := receviceData["FromUserName"]
	appID := wechat.GetAppID()

	// 暂时支持 文本以及订阅事件消息
	switch msgType {
	case "text":
		content := receviceData["Content"]

		// 微信文本消息测试
		// 普通文本测试, 返回 TESTCOMPONENT_MSG_TYPE_TEXT_callback
		if content == "TESTCOMPONENT_MSG_TYPE_TEXT" {
			replay = &model.TaAutoReply{
				MessageType:      models.MESSAGE_TYPE_PARAGRAPH,
				MessageParagraph: "TESTCOMPONENT_MSG_TYPE_TEXT_callback",
				IsUse:            models.AUTOREPLY_IS_USE_ON,
			}

			// 发送 query_auth_code , 返回空, 并立即调用客服接口
		} else if strings.Index(content, "QUERY_AUTH_CODE") > -1 {
			rplTxt := strings.TrimSpace(strings.Split(content, ":")[1])

			return []byte(rplTxt + "_from_api"), errors.New(RESPONSE_CUSTOM_MESSAGE)
		} else {
			if replay, err = c.serv.GetAutoReplayByAppID(appID, content); err != nil {
				utils.LogError("获取微信自动回复信息失败: " + err.Error())
				return
			}
		}
		break
	case "event":
		beego.Info("_______________这里是event————————————————")
		beego.Info(receviceData["Event"])
		switch receviceData["Event"] {
		case "subscribe":
			if replay, err = c.serv.GetSubscribeByAppID(appID); err != nil {
				beego.Info("_______________这里是关注回复————————————————")
				beego.Info(replay)
				utils.LogError("获取微信自动回复信息失败: " + err.Error())
				return
			}

			break
		case "CLICK", "LICK":
			beego.Info("_______________这里是CLICK————————————————")
			target := receviceData["EventKey"]
			if target != "" {
				var replyText string
				replyText, err = c.wechatServ.GetMenuReplyText(target)
				if err != nil {
					utils.LogError("获取菜单回复信息失败: " + err.Error())
					return
				}

				if replyText != "" {
					replay = &model.TaAutoReply{
						MessageType:      models.MESSAGE_TYPE_PARAGRAPH,
						MessageParagraph: replyText,
						IsUse:            models.AUTOREPLY_IS_USE_ON,
					}
				}
			}
			break
		}

		break
	}

	if replay == nil {
		return
	}

	switch replay.MessageType {
	case models.MESSAGE_TYPE_PARAGRAPH:
		return wechat.ResponseMessageText(from, openID, replay.MessageParagraph)
	case models.MESSAGE_TYPE_IMG:
		return wechat.ResponseMessageImage(from, openID, replay.MessageImg)
	}
	return
}

// GetPreAuthCode 获取预授权码
func (c *WechatController) GetPreAuthCode() {
	code, err := utils.Component.GetPreAuthCode()
	if err != nil {
		utils.LogError("获取预授权码错误: " + err.Error())
		c.ResponseError(err)
	}
	appid := utils.Component.GetCertificate()["appid"]
	c.ResponseJSON(map[string]string{
		"appid": appid,
		"code":  code,
	})
}

// GetWechatMenuByAppID 根据appid获取微信详情
func (c *WechatController) GetWechatInfoByAppID() {
	appid := c.GetString(":appid")
	wxclient, err := utils.Component.GetWxClient(appid)
	if err != nil {
		utils.LogError("获取微信信息失败: " + err.Error())
		c.ResponseError(err)
	}
	info, err := wxclient.GetWechatInfo()
	if err != nil {
		utils.LogError("获取微信详情失败: " + err.Error())
		c.ResponseError(err)
	}
	c.ResponseJSON(info)
}

// GetWechatMenuByAppID 获取菜单
func (c *WechatController) GetMaterialByAppID() {
	appid := c.GetString(":appid")
	wxclient, err := utils.Component.GetWxClient(appid)
	if err != nil {
		utils.LogError("获取微信信息失败: " + err.Error())
		c.ResponseError(err)
	}
	var data = map[string]string{}
	data["offset"] = "0"
	data["type"] = "image"
	data["count"] = "10"

	menus, err := wxclient.GetMaterialList(data)
	if err != nil {
		utils.LogError("获取微信详情失败: " + err.Error())
		c.ResponseError(err)
	}
	c.ResponseJSON(menus)
}

// GetWechatByCode 根据code获取微信信息
func (c *WechatController) GetWechatByCode() {
	code := c.GetString(":code")
	beego.Error(code)
	conf, err := c.wechatServ.GetWechatByCode(code)
	if err != nil {
		utils.LogError("获取微信详情失败: " + err.Error())
		c.ResponseError(err)
	}
	beego.Error(conf)
	if conf == nil || conf.ConfId == "" || conf.Status == models.STATUS_NORMAL {
		c.ResponseJSON("")
	}

	c.ResponseJSON(map[string]string{
		"ConfId":   conf.ConfId,
		"NickName": conf.WxNikeName,
		"HeadImg":  conf.HeadImg,
	})
}