package wechat

import (
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"encoding/json"
	"fmt"
	"github.com/go-resty/resty/v2"
	"strconv"
	"time"
)

var (
	client *resty.Client
)

const (
	GRANT_TYPE          = "client_credential"
	ACCESS_TOKEN_PATH   = "/cgi-bin/token"
	QR_CODE_PATH        = "/cgi-bin/qrcode/create"
	GET_MATERIAL_PATH   = "/cgi-bin/material/batchget_material"
	SEND_TEMPLATE_PATH  = "/cgi-bin/message/template/send"
	ACTION_QR_STR_SCENE = "QR_STR_SCENE"

	ERR_CODE_ACCESSTOKEN_EXPIRE  = 42001
	ERR_CODE_ACCESSTOKEN_INVALID = 40001
)

type AccessTokenResponse struct {
	Access_token string
	Expires_in   int
}

type QRCodeResponse struct {
	Ticket         string `json:"ticket"`
	Expire_Seconds int    `json:"expire_seconds"`
	Url            string `json:"url"`
}

type QRCodeRequest struct {
	Action_name    string     `json:"action_name"`
	Action_info    ActionInfo `json:"action_info"`
	Expire_seconds int        `json:"expire_seconds"`
}

type MaterialRequest struct {
	Type   string `json:"type"`
	Offset int    `json:"offset"`
	Count  int    `json:"count"`
}

type TemplateMsgRequest struct {
	ToUser      string      `json:"touser"`
	TemplateId  string      `json:"template_id"`
	Url         string      `json:"url"`
	ClientMsgId string      `json:"client_msg_id"`
	Data        interface{} `json:"data"`
}
type TemplateValue struct {
	Value string `json:"value"`
	Color string `json:"color"`
}

type DefaultWechatTemplate struct {
	First    TemplateValue `json:"first"`
	Keyword1 TemplateValue `json:"keyword1"`
	Keyword2 TemplateValue `json:"keyword2"`
	Keyword3 TemplateValue `json:"keyword3"`
	Keyword4 TemplateValue `json:"keyword4"`
	Remark   TemplateValue `json:"remark"`
}

type ActionInfo struct {
	Scene Scene `json:"scene"`
}

type Scene struct {
	Scene_str string `json:"scene_str"`
}

type ErrorResponse struct {
	Errcode int
	Errmsg  string
}

func getWechatRestyClient() *resty.Client {
	if client == nil {
		client = resty.New()
		client.SetTimeout(time.Duration(setting.WechatApiTimeoutSeconds) * time.Second)
	}
	return client
}

// api doc:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
func callAccessToken() *AccessTokenResponse {
	client := getWechatRestyClient()

	log.Info("start to get wechat access token")
	var result AccessTokenResponse
	_, err := client.R().
		SetQueryParam("grant_type", GRANT_TYPE).
		SetQueryParam("appid", setting.WechatAppId).
		SetQueryParam("secret", setting.WechatAppSecret).
		SetResult(&result).
		Get(setting.WechatApiHost + ACCESS_TOKEN_PATH)
	if err != nil {
		log.Error("get wechat access token failed,e=%v", err)
		return nil
	}
	log.Info("get wechat access token result=%v", result)
	return &result
}

//callQRCodeCreate  call the wechat api to create qr-code,
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
	client := getWechatRestyClient()

	body := &QRCodeRequest{
		Action_name:    ACTION_QR_STR_SCENE,
		Action_info:    ActionInfo{Scene: Scene{Scene_str: sceneStr}},
		Expire_seconds: setting.WechatQRCodeExpireSeconds,
	}
	bodyJson, _ := json.Marshal(body)
	var result QRCodeResponse
	r, err := client.R().
		SetHeader("Content-Type", "application/json").
		SetQueryParam("access_token", GetWechatAccessToken()).
		SetBody(bodyJson).
		SetResult(&result).
		Post(setting.WechatApiHost + QR_CODE_PATH)
	if err != nil {
		log.Error("create QR code failed,e=%v", err)
		return nil, false
	}
	errCode := getErrorCodeFromResponse(r)
	if errCode == ERR_CODE_ACCESSTOKEN_EXPIRE || errCode == ERR_CODE_ACCESSTOKEN_INVALID {
		return nil, true
	}
	if result.Url == "" {
		return nil, false
	}
	log.Info("%v", r)
	return &result, false
}

//getMaterial
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
func getMaterial(mType string, offset, count int) (interface{}, bool) {
	client := getWechatRestyClient()

	body := &MaterialRequest{
		Type:   mType,
		Offset: offset,
		Count:  count,
	}
	bodyJson, _ := json.Marshal(body)
	r, err := client.R().
		SetHeader("Content-Type", "application/json").
		SetQueryParam("access_token", GetWechatAccessToken()).
		SetBody(bodyJson).
		Post(setting.WechatApiHost + GET_MATERIAL_PATH)
	if err != nil {
		log.Error("create QR code failed,e=%v", err)
		return nil, false
	}
	a := r.Body()
	resultMap := make(map[string]interface{}, 0)
	json.Unmarshal(a, &resultMap)
	errcode := resultMap["errcode"]
	if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
		return nil, true
	}
	log.Info("%v", r)
	return &resultMap, false
}

func getErrorCodeFromResponse(r *resty.Response) int {
	a := r.Body()
	resultMap := make(map[string]interface{}, 0)
	json.Unmarshal(a, &resultMap)
	code := resultMap["errcode"]
	if code == nil {
		return -1
	}
	c, _ := strconv.Atoi(fmt.Sprint(code))
	return c
}

func sendTemplateMsg(req TemplateMsgRequest) (error, bool) {
	client := getWechatRestyClient()

	bodyJson, _ := json.Marshal(req)
	r, err := client.R().
		SetHeader("Content-Type", "application/json").
		SetQueryParam("access_token", GetWechatAccessToken()).
		SetBody(bodyJson).
		Post(setting.WechatApiHost + SEND_TEMPLATE_PATH)
	if err != nil {
		log.Error("sendTemplateMsg,e=%v", err)
		return nil, false
	}
	a := r.Body()
	resultMap := make(map[string]interface{}, 0)
	json.Unmarshal(a, &resultMap)
	errcode := resultMap["errcode"]
	log.Info("sendTemplateMsg,%v", r)
	if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
		return nil, true
	}
	return nil, false
}
