package svc

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"time"

	"git.i2edu.net/i2/i2-bill-api/internal/config"
	"git.i2edu.net/i2/i2-bill-api/internal/utils"
	"git.i2edu.net/i2/i2-bill-api/model"
)

// Wechat defined
type Wechat struct {
	RdCli Cache
	c     config.Config
}

// WechatPhonedecrypt defined TODO
type WechatPhonedecrypt struct {
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Appid     string `json:"appid"`
		Timestamp int32  `json:"timestamp"`
	} `json:"watermark"`
}

// GetAccessToken defined TODO
func (wc *Wechat) GetAccessToken() (string, error) {
	token := ""
	err := wc.RdCli.GetCache(cacheWechatTokenPrefix, &token)
	if err == model.ErrRdsNotFound || token == "" {
		req := utils.Get("https://api.weixin.qq.com/cgi-bin/token")
		req.Param("grant_type", "client_credential")
		req.Param("secret", wc.c.Weixin.Secret)
		req.Param("appid", wc.c.Weixin.Appid)
		var res = struct {
			AccessToken string `json:"access_token"`
			ExpiresIn   int    `json:"expires_in"`
		}{}
		err = req.ToJSON(&res)
		if err != nil {
			return "", err
		}
		token = res.AccessToken
		err := wc.RdCli.SetWithExpire(cacheWechatTokenPrefix, res.AccessToken, time.Duration((res.ExpiresIn-60*2))*time.Second)
		if err != nil {
			return "", err
		}
	}
	return token, nil
}

// GenQrId defined TODO
func (wc *Wechat) GenQrId(prefix string, params interface{}) (string, error) {
	ube, err := json.Marshal(params)
	hashCode := utils.HashCode(string(ube))
	if err != nil {
		return "", err
	}
	err = wc.RdCli.SetWithExpire(fmt.Sprintf(prefix, hashCode), string(ube), time.Duration(1)*time.Hour)
	return fmt.Sprintf("%v", hashCode), err
}

// GenUserQrId defined TODO
func (wc *Wechat) GenUserQrId(prefix string, userId int64, params interface{}) (string, error) {
	ube, err := json.Marshal(params)
	hashCode := utils.HashCode(string(ube))
	if err != nil {
		return "", err
	}
	qrId := fmt.Sprintf("u%v-%v", userId, hashCode)
	err = wc.RdCli.SetWithExpire(fmt.Sprintf(prefix, qrId), string(ube), time.Duration(1)*time.Hour)
	return qrId, err
}

// GetQrParams defined TODO
func (wc *Wechat) GetQrParams(prefix string, id string, params interface{}) error {
	ube := ""
	err := wc.RdCli.GetCache(fmt.Sprintf(prefix, id), &ube)
	if err != nil {
		return err
	}
	err = json.Unmarshal([]byte(ube), params)
	if err != nil {
		return err
	}
	return err
}

// GetQrParams defined TODO
func (wc *Wechat) DecryptMobile(sessionKey, encryptedData, iv string) (string, error) {
	key, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return "", err
	}
	bIv, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return "", err
	}
	bEncryptedData, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return "", err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	var decrypt = bEncryptedData
	blockMode := cipher.NewCBCDecrypter(block, bIv)
	blockMode.CryptBlocks(decrypt, bEncryptedData)
	length := len(decrypt)
	unpadding := int(decrypt[length-1])
	decData := decrypt[:(length - unpadding)]

	decryptData := WechatPhonedecrypt{}
	err = json.Unmarshal(decData, &decryptData)
	if err != nil {
		return "", err
	}
	return decryptData.PhoneNumber, err
}

// GenQrCode defined TODO
func (wc *Wechat) GenQrCode(scene, page string) (*model.I2billSysAttachment, error) {
	token, err := wc.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := utils.Post("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + token)
	req.JSONBody(map[string]interface{}{"scene": scene, "page": page, "is_hyaline": true})
	resp, err := req.Response()
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	oss, err := GetAliYunOssInstance()
	if err != nil {
		return nil, err
	}
	return oss.UploadRead(resp.Body, ".png")

}
