package wechat

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"hongze/hongze_yb/controller/response"
	"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/services/wx_app"
	"hongze/hongze_yb/utils"
	"io/ioutil"
	"net/http"
	"strings"
	"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
}

type SendWxTemplate struct {
	WxAppId        string   `description:"公众号appId"`
	First          string   `description:"模板消息first字段"`
	Keyword1       string   `description:"模板消息keyword1字段"`
	Keyword2       string   `description:"模板消息keyword2字段"`
	Keyword3       string   `description:"模板消息keyword3字段"`
	Keyword4       string   `description:"模板消息keyword4字段"`
	Remark         string   `description:"模板消息remark字段"`
	TemplateId     string   `description:"模板id"`
	RedirectUrl    string   `description:"跳转地址"`
	RedirectTarget int      `description:"小程序跳转目标:1:弘则研报小程序,2:随手办公小程序"`
	Resource       string   `description:"资源唯一标识"`
	SendType       int      `description:"发送的消息类型:1:报告,2:指标更新提醒,3:审批通知,4:销售领取客户通知,5:活动取消通知,6活动更改时间通知,7:关注的作者发布报告通知,8:发送日报（周报、双周报、月报）模板消息,9:活动预约/报名时间通知"`
	OpenIdArr      []string `description:"消息接收者openid"`
}

// 推送模板消息
func SendTemplateMsg(sendInfo *SendWxTemplate) (err error) {
	postData, err := json.Marshal(sendInfo)
	if err != nil {
		alarm_msg.SendAlarmMsg("SendTemplateMsg json.Marshal Err:"+err.Error(), 1)
		return err
	}
	body := ioutil.NopCloser(strings.NewReader(string(postData)))
	client := &http.Client{}
	req, err := http.NewRequest("POST", wx_app.SendWxTemplateMsgUrl, body)
	if err != nil {
		alarm_msg.SendAlarmMsg("SendTemplateMsg http.NewRequest Err:"+err.Error(), 1)
		return err
	}
	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Authorization", utils.SendTemplateMsgAuthorization)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("http client.Do Err:" + err.Error())
		return err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	result := new(response.BaseResponse)
	err = json.Unmarshal(b, &result)
	if err != nil {
		return err
	}
	if result.Ret != 200 {
		err = errors.New(string(b))
		return err
	}
	return
}

// 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)

	openIdArr := make([]string, len(openIdList))
	for i, v := range openIdList {
		openIdArr[i] = v.OpenId
	}
	// 获取小程序配置
	appConf, err := wx_app.GetWxAppConf()
	if err != nil {
		return
	}

	sendInfo := new(SendWxTemplate)
	sendInfo.WxAppId = WxAppId
	sendInfo.First = first
	sendInfo.Keyword1 = keyword1
	sendInfo.Keyword2 = keyword2
	sendInfo.Remark = remark
	sendInfo.RedirectUrl = wxAppPath
	sendInfo.TemplateId = TemplateIdWithCommunityQuestion
	sendInfo.RedirectTarget = appConf.MsgRedirectTarget
	sendInfo.Resource = wxAppPath
	sendInfo.SendType = utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION
	sendInfo.OpenIdArr = openIdArr

	err = SendTemplateMsg(sendInfo)
	return
}

// SendVoiceBroadcastWxMsg 推送研报小程序模板消息-语音播报
func SendVoiceBroadcastWxMsg(broadcastId, varietyId 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)
		}
	}()

	openIdArr, err := wx_user.GetOpenIdArrByVarietyTag(varietyId)

	//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 := "待查看"
	keyword1 := broadcastName
	keyword2 := fmt.Sprintf("【%s】 待查看", sectionName)
	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)

	//openIdArr := make([]string, len(openIdList))
	//for i, v := range openIdList {
	//	openIdArr[i] = v.OpenId
	//}

	// 获取小程序配置
	appConf, err := wx_app.GetWxAppConf()
	if err != nil {
		return
	}

	//openIdArr := make([]string, len(openIdList))
	//for i, v := range openIdList {
	//	openIdArr[i] = v.OpenId
	//}
	sendInfo := new(SendWxTemplate)
	sendInfo.WxAppId = WxAppId
	sendInfo.First = first
	sendInfo.Keyword1 = keyword1
	sendInfo.Keyword2 = keyword2
	sendInfo.Remark = remark
	sendInfo.RedirectUrl = wxAppPath
	sendInfo.TemplateId = TemplateIdWithCommunityQuestion
	sendInfo.RedirectTarget = appConf.MsgRedirectTarget
	sendInfo.Resource = wxAppPath
	sendInfo.SendType = utils.TEMPLATE_MSG_YB_VOICE_BROADCAST
	sendInfo.OpenIdArr = openIdArr

	err = SendTemplateMsg(sendInfo)
	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, adminId int, adminOpenId string, questionTitle string) (err error) {
	if adminId == 0 || adminOpenId == "" {
		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)
		}
	}()

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: adminOpenId,
		UserId: 0,
	})

	//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)

	openIdArr := make([]string, len(openIdList))
	for i, v := range openIdList {
		openIdArr[i] = v.OpenId
	}
	sendInfo := new(SendWxTemplate)
	sendInfo.WxAppId = AdminWxAppId
	sendInfo.First = first
	sendInfo.Keyword1 = keyword1
	sendInfo.Keyword2 = keyword2
	sendInfo.Remark = remark
	sendInfo.RedirectUrl = wxAppPath
	sendInfo.TemplateId = TemplateIdWithCommunityQuestionNotifyAdmin
	sendInfo.RedirectTarget = 2
	sendInfo.Resource = wxAppPath
	sendInfo.SendType = utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION
	sendInfo.OpenIdArr = openIdArr

	err = SendTemplateMsg(sendInfo)
	return
}

// SendQuestionCommentToAdmin 推送研报小程序模板消息-用户评论问答时，通知到管理员
func SendQuestionCommentToAdmin(commentId, adminId int, adminOpenId string, commentContent string) (err error) {
	if adminId == 0 || adminOpenId == "" {
		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)
		}
	}()

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: adminOpenId,
		UserId: 0,
	})

	//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)

	openIdArr := make([]string, len(openIdList))
	for i, v := range openIdList {
		openIdArr[i] = v.OpenId
	}
	sendInfo := new(SendWxTemplate)
	sendInfo.WxAppId = AdminWxAppId
	sendInfo.First = first
	sendInfo.Keyword1 = keyword1
	sendInfo.Keyword2 = keyword2
	sendInfo.Remark = remark
	sendInfo.RedirectUrl = wxAppPath
	sendInfo.TemplateId = TemplateIdWithCommunityQuestionNotifyAdmin
	sendInfo.RedirectTarget = 2
	sendInfo.Resource = wxAppPath
	sendInfo.SendType = utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION
	sendInfo.OpenIdArr = openIdArr

	err = SendTemplateMsg(sendInfo)
	return
}

// SendQuestionToResearcher 推送研报小程序模板消息-用户提问时，通知到研究员
func SendQuestionToResearcher(questionId int, adminOpenId, questionTitle, remark string) (err error) {
	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)
		}
	}()
	if adminOpenId == "" {
		err = errors.New("研究员openid为空")
		errMsg = "研究员openid为空"
		return
	}

	openIdList := make([]*OpenIdList, 0)
	openIdList = append(openIdList, &OpenIdList{
		OpenId: adminOpenId,
		UserId: 0,
	})

	first := "您好，有新的提问待回复"
	keyword1 := questionTitle
	keyword2 := "待回复"
	if remark == "" {
		remark = "请点击详情尽快处理"
		keyword2 = remark
	}

	wxAppPath := fmt.Sprintf("pages-question/answerDetail?id=%d", questionId)
	if global.CONFIG.Serve.RunMode == "debug" {
		// 仅测试环境测试用
		wxAppPath = "pages-report/reportDetail?reportId=3800"
	}

	openIdArr := make([]string, len(openIdList))
	for i, v := range openIdList {
		openIdArr[i] = v.OpenId
	}
	sendInfo := new(SendWxTemplate)
	sendInfo.WxAppId = AdminWxAppId
	sendInfo.First = first
	sendInfo.Keyword1 = keyword1
	sendInfo.Keyword2 = keyword2
	sendInfo.Remark = remark
	sendInfo.RedirectUrl = wxAppPath
	sendInfo.TemplateId = TemplateIdWithCommunityQuestionNotifyAdmin
	sendInfo.RedirectTarget = 1
	sendInfo.Resource = wxAppPath
	sendInfo.SendType = utils.TEMPLATE_MSG_YB_COMMUNITY_QUESTION
	sendInfo.OpenIdArr = openIdArr

	err = SendTemplateMsg(sendInfo)
	return
}
