package quick_recharge

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
	"net/http"
	"net/url"
	"strings"
	"unicode"
)

type API struct {
	platformapi.APICookie
	appKey string
	userID string
	client *http.Client
	config *platformapi.APIConfig
}

func New(appKey, userId string, config ...*platformapi.APIConfig) *API {
	curConfig := platformapi.DefAPIConfig
	if len(config) > 0 {
		curConfig = *config[0]
	}
	return &API{
		appKey: appKey,
		userID: userId,
		client: &http.Client{Timeout: curConfig.ClientTimeout},
		config: &curConfig,
	}
}

// AccessInfo 发送请求
func (a *API) AccessInfo(baseUrl, url, requestMethods string, resultKey string, param map[string]interface{}) (retVal map[string]interface{}, err error) {
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if requestMethods == http.MethodPost {
				fullURL := utils.GenerateGetURL(baseUrl, url, nil)
				// 获取json结构体的参数体
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(param).Encode()))
				request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
			} else {
				request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(baseUrl, url, param), nil)
			}

			return request
		},
		a.config,
		func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
			if jsonResult1 == nil {
				return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
			}
			retVal = jsonResult1
			if jsonResult1["code"] == nil {
				return platformapi.ErrLevelGeneralFail, fmt.Errorf("返回结果格式不正常")
			}
			code := int(utils.MustInterface2Int64(jsonResult1["code"]))
			if code == ResponseCodeSuccess {
				if resultKey == "" {
					retVal = jsonResult1
				} else {
					retVal, _ = jsonResult1[resultKey].(map[string]interface{})
				}
				return platformapi.ErrLevelSuccess, nil
			}
			newErr := utils.NewErrorIntCode(jsonResult1["msg"].(string), code)
			return platformapi.ErrLevelCodeIsNotOK, newErr
		})
	return retVal, err
}

// AESEncrypt 加密函数
func AESEncrypt(plaintext, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	paddedPlaintext := PKCS7Padding(plaintext, blockSize)
	ciphertext := make([]byte, len(paddedPlaintext))

	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
	blockMode.CryptBlocks(ciphertext, paddedPlaintext)

	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// AESDecrypt 解密函数
func AESDecrypt(ciphertext, key []byte) (string, error) {
	decodedCiphertext, err := base64.StdEncoding.DecodeString(string(ciphertext))
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	plaintext := make([]byte, len(decodedCiphertext))
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
	blockMode.CryptBlocks(plaintext, decodedCiphertext)

	unpaddedPlaintext, err := PKCS7UnPadding(plaintext)
	if err != nil {
		return "", err
	}

	return string(unpaddedPlaintext), nil
}

// Md5Sign 签名
func (a *API) Md5Sign(signKey string, param ...string) string {
	md5Data := signKey

	for _, v := range param {
		if ContainsChinese(v) {
			md5Data += url.QueryEscape(v)
		} else {
			md5Data += v
		}
	}

	lowerStr := strings.ToLower(md5Data)
	harsher := md5.New()
	harsher.Write([]byte(lowerStr))
	hash := harsher.Sum(nil)

	return hex.EncodeToString(hash)
}

// ContainsChinese 参数化是否包含中文
func ContainsChinese(s string) bool {
	for _, v := range s {
		if unicode.Is(unicode.Han, v) {
			return true
		}
	}
	return false
}

// PKCS7Padding 填充方法
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

// PKCS7UnPadding 移除填充
func PKCS7UnPadding(origData []byte) ([]byte, error) {
	length := len(origData)
	if length == 0 {
		return nil, fmt.Errorf("invalid encrypted data")
	}
	unpadding := int(origData[length-1])
	if unpadding > length || unpadding <= 0 {
		return nil, fmt.Errorf("invalid encrypted data")
	}
	return origData[:(length - unpadding)], nil
}
