package service

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
	"youngee_b_api/consts"
	"youngee_b_api/model/http_model"
	"youngee_b_api/model/system_model"
	"youngee_b_api/redis"
)

const (
	accessTokenUrlFormat = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
	qrCodeUrlFormat      = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
	wxAccesssTokenKey    = "wx_access_token"
)

var QrCode *qrcode

type qrcode struct {
	sessionTTL time.Duration
}

func QrCodeInit(config *system_model.Session) {
	qrCode := new(qrcode)
	qrCode.sessionTTL = 5 * time.Minute
	QrCode = qrCode
}

// getAndCacheWxAccessToken 获取并缓存微信的access token
func getAndCacheWxAccessToken(ctx context.Context) (string, error) {
	appId := "wxac396a3be7a16844"
	secret := "c82ae9e75b4ed7d8022db5bda5371892"
	url := fmt.Sprintf(accessTokenUrlFormat, appId, secret)

	resp, err := http.Get(url)
	if err != nil {
		return "", errors.New("request access token failed")
	}
	defer resp.Body.Close()

	// 解析微信服务端返回的信息
	var accessTokenRes http_model.WxAccessTokenResponse
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(&accessTokenRes); err != nil {
		return "", errors.New("decode wx response failed")
	}

	if accessTokenRes.Errcode != 0 {
		return "", errors.New("request access token failed")
	}

	// 缓存获取的access token,比微信返回的有效时间短5分钟失效
	err = redis.Set(ctx, wxAccesssTokenKey, accessTokenRes.AccessToken, QrCode.sessionTTL)
	if err != nil {
		return "", err
	}
	return accessTokenRes.AccessToken, nil
}

func (q *qrcode) GetWxQrCode(ctx context.Context, Scene string, Page string) (*http_model.GetWxQRCodeData, error) {
	// 获取access_token
	accessToken, err := redis.Get(ctx, wxAccesssTokenKey)
	if err != nil && err != consts.RedisNil {
		fmt.Printf("err: %+v\n", err)
		return nil, err
	}
	if accessToken == "" {
		// 如果没有缓存的access token 则获取并缓存
		accessToken, err = getAndCacheWxAccessToken(ctx)
		if err != nil {
			fmt.Printf("err: %+v\n", err)
			return nil, err
		}
	}

	qrRequest := http_model.WxQrCodeRequest{
		Scene:      Scene,
		Page:       Page,
		Width:      430,
		CheckPath:  false,
		EnvVersion: "release",
	}
	jsonBody, err := json.Marshal(qrRequest)
	if err != nil {
		fmt.Printf("err: %+v\n", err)
		return nil, err
	}

	qrCodeUrl := fmt.Sprintf(qrCodeUrlFormat, accessToken)
	resp, err := http.Post(qrCodeUrl, "application/json; charset=utf8", bytes.NewReader(jsonBody))
	if err != nil {
		fmt.Printf("err: %+v\n", err)
		return nil, err
	}
	defer resp.Body.Close()

	var qrcodeBytes []byte

	switch header := resp.Header.Get("content-Type"); {
	case strings.HasPrefix(header, "application/json"):
		// 如果返回的是json结构，说明发生错误
		var qrResponse *http_model.WxQrCodeResponse
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(&qrResponse); err != nil {
			fmt.Printf("decoder: %+v\nerr: %+v", decoder, err)
			return nil, err
		}
		fmt.Printf("qrResponse: %+v\ndecoder: %+v\n", qrResponse, decoder)
		return nil, nil
	case strings.HasPrefix(header, "image"):
		qrcodeBytes, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("qrcodeBytes: %+v\nerr: %+v", qrcodeBytes, err)
			return nil, err
		}
	}
	data := http_model.GetWxQRCodeData{
		QrCodeBytes: qrcodeBytes,
	}
	//fmt.Printf("data: %+v\n", data)
	return &data, nil
}
