package wechat

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"hongze/hongze_yb/global"
	"hongze/hongze_yb/models/tables/user_record"
	"hongze/hongze_yb/models/tables/user_template_record"
	"hongze/hongze_yb/models/tables/wx_user"
	"hongze/hongze_yb/services/alarm_msg"
	"hongze/hongze_yb/utils"
	"io/ioutil"
	"net/http"
	"time"
)

var (
	TemplateMsgSendUrl       = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s"
	TemplateMsgClearQuotaUrl = "https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s"
)

type TemplateMsgSendClient struct {
	AccessToken string
	Data        []byte
}

type SendTemplateResponse struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
	MsgID   int    `json:"msgid"`
}

type ClearQuotaResponse struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
}

type OpenIdList struct {
	OpenId string
	UserId int
}

// TemplateMsgSendClient.SendMsg 推送消息
func (c *TemplateMsgSendClient) SendMsg() (sendRes *SendTemplateResponse, err error) {
	// 请求接口
	sendUrl := fmt.Sprintf(TemplateMsgSendUrl, c.AccessToken)
	client := http.Client{}
	resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(c.Data))
	if err != nil {
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	body, _ := ioutil.ReadAll(resp.Body)
	if err = json.Unmarshal(body, &sendRes); err != nil {
		return
	}
	// 模板消息发送超过当日10万次限制错误处理
	if sendRes.Errcode == 45009 {
		// 发送提示邮件
		go alarm_msg.SendAlarmMsg("模板消息发送超过当日10万次限制, SendTemplateResponse: "+string(body), 3)
		// 清理限制
		clearRes, e := c.ClearQuota()
		if e != nil {
			err = e
			return
		}
		if clearRes.Errcode != 0 {
			clearJson, _ := json.Marshal(clearRes)
			go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: "+string(clearJson), 3)
			return
		}
		// 发送成功邮件
		go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用成功", 1)
		// 重新推送
		go func() {
			reSend, e := c.SendMsg()
			if e != nil {
				reSendJson, _ := json.Marshal(reSend)
				alarm_msg.SendAlarmMsg("重新推送模板消息失败, SendTemplateResponse: "+string(reSendJson), 3)
			}
		}()
	}
	if sendRes.Errcode != 0 {
		err = errors.New("推送模板消息失败, SendTemplateResponse: " + string(body))
	}
	return
}

// TemplateMsgSendClient.ClearQuota 清除发送超过当日10万次限制
func (c *TemplateMsgSendClient) ClearQuota() (result *ClearQuotaResponse, err error) {
	key := "CACHE_SendTemplateMsg_ERR"
	exists, _ := global.Redis.Exists(context.TODO(), key).Result()
	if exists == 1 {
		return
	}
	_ = global.Redis.SetEX(context.TODO(), key, 1, 6*time.Minute)

	sendUrl := fmt.Sprintf(TemplateMsgClearQuotaUrl, c.AccessToken)
	client := http.Client{}
	clearData := make(map[string]interface{})
	clearData["appid"] = WxAppId
	clearJson, _ := json.Marshal(clearData)
	resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(clearJson))
	if err != nil {
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	clearBody, err := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(clearBody, &result)
	return
}

// AddUserTemplateRecord 新增模板消息推送记录
func AddUserTemplateRecord(userId, sendStatus, sendType int, openid, resource, sendData, result string) (err error) {
	item := &user_template_record.UserTemplateRecord{
		UserID:     userId,
		OpenID:     openid,
		Resource:   resource,
		SendData:   sendData,
		Result:     result,
		CreateDate: time.Now().Format(utils.FormatDate),
		CreateTime: time.Now().Format(utils.FormatDateTime),
		SendStatus: sendStatus,
		SendType:   sendType,
	}
	err = item.Create()
	return
}

// SendMultiTemplateMsg 推送模板消息至多个用户
func SendMultiTemplateMsg(sendMap map[string]interface{}, items []*OpenIdList, resource string, sendType int) (err error) {
	ws := GetWxChat()
	accessToken, err := ws.GetAccessToken()
	if err != nil {
		return
	}
	for _, item := range items {
		sendMap["touser"] = item.OpenId
		data, e := json.Marshal(sendMap)
		if e != nil {
			err = e
			return
		}
		ts := &TemplateMsgSendClient{
			AccessToken: accessToken,
			Data:        data,
		}
		result, e := ts.SendMsg()
		if result == nil {
			return
		}
		// 推送消息记录
		{
			go func(v *OpenIdList) {
				sendStatus := 1
				if e != nil {
					sendStatus = 0
				}
				resultJson, _ := json.Marshal(result)
				_ = AddUserTemplateRecord(v.UserId, sendStatus, sendType, v.OpenId, resource, string(data), string(resultJson))
			}(item)
		}
		if e != nil {
			err = e
			return
		}
	}
	return
}

// SendQuestionReplyWxMsg 推送研报小程序模板消息-问答社区回复
func SendQuestionReplyWxMsg(questionId, userId int, questionTitle string) (err error) {
	if userId == 0 {
		return
	}
	var errMsg string
	defer func() {
		if err != nil {
			alarmMsg := fmt.Sprintf("SendQuestionReplyWxMsg-推送问答社区回复模版消息失败; QuestionId: %d; Err: %s; Msg: %s", questionId, err.Error(), errMsg)
			go alarm_msg.SendAlarmMsg(alarmMsg, 3)
		}
	}()
	// 校验用户是否取消关注公众号
	userRecord, e := user_record.GetByUserId(userId, utils.USER_RECORD_PLATFORM_RDDP)
	if e != nil {
		// 用户无公众号信息
		if e == utils.ErrNoRow {
			return
		}
		err = errors.New("获取用户Record信息失败, Err:" + e.Error())
		return
	}
	// 取消关注则不推送
	if userRecord.Subscribe == 0 {
		return
	}

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: userRecord.OpenID,
		UserId: userId,
	})

	sendMap := make(map[string]interface{})
	sendData := make(map[string]interface{})

	first := "您好，您的提问已被回复"
	keyword1 := questionTitle
	keyword2 := "待查看"
	remark := "请点击详情查看回复"

	sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
	sendData["keyword1"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
	sendData["keyword2"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
	sendData["remark"] = map[string]interface{}{"value": remark, "color": "#173177"}

	sendMap["template_id"] = TemplateIdWithCommunityQuestion
	sendMap["data"] = sendData

	wxAppPath := fmt.Sprintf("pages-question/answerDetail?id=%d", questionId)
	if global.CONFIG.Serve.RunMode == "debug" {
		// 仅测试环境测试用
		wxAppPath = "pages-report/reportDetail?reportId=3800"
	}
	if wxAppPath != "" {
		sendMap["miniprogram"] = map[string]interface{}{"appid": WxYbAppId, "pagepath": wxAppPath}
	}
	err = SendMultiTemplateMsg(sendMap, openIdList, wxAppPath, utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION)
	return
}

// SendVoiceBroadcastWxMsg 推送研报小程序模板消息-语音播报
func SendVoiceBroadcastWxMsg(broadcastId int, sectionName, broadcastName string) (err error) {
	var errMsg string
	defer func() {
		if err != nil {
			alarmMsg := fmt.Sprintf("SendVoiceBroadcastWxMsg-推送语音播报模版消息失败; broadcastId: %d; Err: %s; Msg: %s", broadcastId, err.Error(), errMsg)
			go alarm_msg.SendAlarmMsg(alarmMsg, 3)
		}
	}()

	List, err := wx_user.GetOpenIdList()
	if err != nil {
		return
	}
	openIdList := make([]*OpenIdList, 0)
	for _, item := range List {
		openIdList = append(openIdList, &OpenIdList{
			OpenId: item.OpenID,
			UserId: item.UserID,
		})
	}
	//openIdList = append(openIdList, &OpenIdList{
	//			OpenId: "oN0jD1cuiBLxV1IRpu74_oHnoOjk",
	//			UserId: 52709,
	//		})
	sendMap := make(map[string]interface{})
	sendData := make(map[string]interface{})

	first := "您好，有新的语音播报待查看"
	keyword1 := sectionName + "：" + broadcastName
	keyword2 := "待查看"
	remark := "请点击详情查看"

	sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
	sendData["keyword1"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
	sendData["keyword2"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
	sendData["remark"] = map[string]interface{}{"value": remark, "color": "#173177"}

	sendMap["template_id"] = TemplateIdWithCommunityQuestion
	sendMap["data"] = sendData

	wxAppPath := fmt.Sprintf("pages-voice/voiceDetail?voiceId=%d", broadcastId)
	if global.CONFIG.Serve.RunMode == "debug" {
		// 仅测试环境测试用
		wxAppPath = "pages-report/reportDetail?reportId=3800"
	}
	if wxAppPath != "" {
		sendMap["miniprogram"] = map[string]interface{}{"appid": WxYbAppId, "pagepath": wxAppPath}
	}
	err = SendMultiTemplateMsgNoReturn(sendMap, openIdList, wxAppPath, utils.TEMPLATE_MSG_YB_VOICE_BROADCAST)
	return
}

// SendMultiTemplateMsg 推送模板消息至多个用户中间出错不返回
func SendMultiTemplateMsgNoReturn(sendMap map[string]interface{}, items []*OpenIdList, resource string, sendType int) (err error) {
	ws := GetWxChat()
	accessToken, err := ws.GetAccessToken()
	if err != nil {
		return
	}
	for _, item := range items {
		sendMap["touser"] = item.OpenId
		data, e := json.Marshal(sendMap)
		if e != nil {
			err = e
			return
		}
		ts := &TemplateMsgSendClient{
			AccessToken: accessToken,
			Data:        data,
		}
		result, e := ts.SendMsg()
		if result == nil {
			return
		}
		// 推送消息记录
		{
			go func(v *OpenIdList) {
				sendStatus := 1
				if e != nil {
					sendStatus = 0
				}
				resultJson, _ := json.Marshal(result)
				_ = AddUserTemplateRecord(v.UserId, sendStatus, sendType, v.OpenId, resource, string(data), string(resultJson))
			}(item)
		}
		if e != nil {
			err = e
		}
	}
	return
}

// SendQuestionToAdmin 推送研报小程序模板消息-用户提问时，通知到管理员
func SendQuestionToAdmin(questionId, userId int, questionTitle string) (err error) {
	if userId == 0 {
		return
	}
	var errMsg string
	defer func() {
		if err != nil {
			alarmMsg := fmt.Sprintf("SendQuestionToAdmin-推送研报小程序模板消息-用户提问时，通知到管理员; QuestionId: %d; Err: %s; Msg: %s", questionId, err.Error(), errMsg)
			go alarm_msg.SendAlarmMsg(alarmMsg, 3)
		}
	}()
	// 校验用户是否取消关注公众号
	userRecord, e := user_record.GetByUserId(userId, utils.USER_RECORD_PLATFORM_RDDP)
	if e != nil {
		// 用户无公众号信息
		if e == utils.ErrNoRow {
			return
		}
		err = errors.New("获取用户Record信息失败, Err:" + e.Error())
		return
	}
	// 取消关注则不推送
	if userRecord.Subscribe == 0 {
		return
	}

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: userRecord.OpenID,
		UserId: userId,
	})

	sendMap := make(map[string]interface{})
	sendData := make(map[string]interface{})

	first := "您好，有新的提问待分配"
	keyword1 := questionTitle
	keyword2 := "待查看"
	remark := "请点击详情查看回复"

	sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
	sendData["keyword1"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
	sendData["keyword2"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
	sendData["remark"] = map[string]interface{}{"value": remark, "color": "#173177"}

	sendMap["template_id"] = TemplateIdWithCommunityQuestion
	sendMap["data"] = sendData

	wxAppPath := fmt.Sprintf("/pages-question/detail/index?type=question&id=%d", questionId)
	if global.CONFIG.Serve.RunMode == "debug" {
		// 仅测试环境测试用
		wxAppPath = "pages-approve/seal/list"
	}
	if wxAppPath != "" {
		sendMap["miniprogram"] = map[string]interface{}{"appid": WxMobileCrmAppId, "pagepath": wxAppPath}
	}
	err = SendMultiTemplateMsg(sendMap, openIdList, wxAppPath, utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION)
	return
}

// SendQuestionCommentToAdmin 推送研报小程序模板消息-用户评论问答时，通知到管理员
func SendQuestionCommentToAdmin(commentId, userId int, commentContent string) (err error) {
	if userId == 0 {
		return
	}
	var errMsg string
	defer func() {
		if err != nil {
			alarmMsg := fmt.Sprintf("SendQuestionCommentToAdmin-推送研报小程序模板消息-用户评论问答时，通知到管理员; CommentId: %d; Err: %s; Msg: %s", commentId, err.Error(), errMsg)
			go alarm_msg.SendAlarmMsg(alarmMsg, 3)
		}
	}()
	// 校验用户是否取消关注公众号
	userRecord, e := user_record.GetByUserId(userId, utils.USER_RECORD_PLATFORM_RDDP)
	if e != nil {
		// 用户无公众号信息
		if e == utils.ErrNoRow {
			return
		}
		err = errors.New("获取用户Record信息失败, Err:" + e.Error())
		return
	}
	// 取消关注则不推送
	if userRecord.Subscribe == 0 {
		return
	}

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: userRecord.OpenID,
		UserId: userId,
	})

	sendMap := make(map[string]interface{})
	sendData := make(map[string]interface{})

	first := "您好，有新的评论待查看"
	keyword1 := commentContent
	keyword2 := "待查看"
	remark := "请点击详情查看回复"

	sendData["first"] = map[string]interface{}{"value": first, "color": "#173177"}
	sendData["keyword1"] = map[string]interface{}{"value": keyword1, "color": "#173177"}
	sendData["keyword2"] = map[string]interface{}{"value": keyword2, "color": "#173177"}
	sendData["remark"] = map[string]interface{}{"value": remark, "color": "#173177"}

	sendMap["template_id"] = TemplateIdWithCommunityQuestion
	sendMap["data"] = sendData

	wxAppPath := fmt.Sprintf("/pages-question/detail/index?type=comment&id=%d", commentId)
	if global.CONFIG.Serve.RunMode == "debug" {
		// 仅测试环境测试用
		wxAppPath = "pages-approve/seal/list"
	}
	if wxAppPath != "" {
		sendMap["miniprogram"] = map[string]interface{}{"appid": WxMobileCrmAppId, "pagepath": wxAppPath}
	}
	err = SendMultiTemplateMsg(sendMap, openIdList, wxAppPath, utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION)
	return
}
