package helper

import (
	"crypto"
	"crypto/hmac"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

// BitgetClient Bitget HTTP客户端
type BitgetClient struct {
	APIKey     string
	APISecret  string
	Passphrase string
	HTTPClient *http.Client
	BaseURL    string
	SignType   string
}

// NewBitgetClient 创建新的Bitget客户端
func NewBitgetClient(apiKey, apiSecret, passphrase string, proxyUrl string) (*BitgetClient, error) {
	var proxyType, proxyAddr string
	if proxyUrl != "" {
		if strings.HasPrefix(proxyUrl, "http://") {
			proxyType = "http"
			proxyAddr = proxyUrl
		} else if strings.HasPrefix(proxyUrl, "https://") {
			proxyType = "https"
			proxyAddr = proxyUrl
		} else if strings.HasPrefix(proxyUrl, "socks5://") {
			proxyType = "socks5"
			proxyAddr = proxyUrl
		} else {
			// 默认为http代理
			proxyType = "http"
			proxyAddr = "http://" + proxyUrl
		}
	}
	// 创建HTTP客户端
	client := &http.Client{
		Transport: &http.Transport{
			MaxIdleConns:    1000,
			IdleConnTimeout: 10 * time.Second,
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}

	// 设置代理
	if proxyAddr != "" {
		err := CreateHtppProxy(proxyType, proxyAddr, client)
		if err != nil {
			return nil, err
		}
	}

	return &BitgetClient{
		APIKey:     apiKey,
		APISecret:  apiSecret,
		Passphrase: passphrase,
		HTTPClient: client,
		BaseURL:    "https://api.bitget.com",
		SignType:   "SHA256",
	}, nil
}

// DoGet 执行GET请求
func (bc *BitgetClient) DoGet(uri string, params map[string]string) ([]byte, error) {
	timestamp := bc.getTimestamp()
	body := bc.buildGetParams(params)

	sign := bc.sign("GET", uri, body, timestamp)
	requestURL := bc.BaseURL + uri + body

	request, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, err
	}

	bc.setHeaders(request, timestamp, sign)

	response, err := bc.HTTPClient.Do(request)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	return ioutil.ReadAll(response.Body)
}

// DoPost 执行POST请求
func (bc *BitgetClient) DoPost(uri string, params interface{}) ([]byte, error) {
	timestamp := bc.getTimestamp()

	var body string
	if params != nil {
		bodyBytes, err := json.Marshal(params)
		if err != nil {
			return nil, err
		}
		body = string(bodyBytes)
	}

	sign := bc.sign("POST", uri, body, timestamp)
	requestURL := bc.BaseURL + uri

	request, err := http.NewRequest("POST", requestURL, strings.NewReader(body))
	if err != nil {
		return nil, err
	}

	bc.setHeaders(request, timestamp, sign)

	response, err := bc.HTTPClient.Do(request)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	return ioutil.ReadAll(response.Body)
}

// getTimestamp 获取时间戳
func (bc *BitgetClient) getTimestamp() string {
	return strconv.FormatInt(time.Now().Unix()*1000, 10)
}

// setHeaders 设置请求头
func (bc *BitgetClient) setHeaders(request *http.Request, timestamp, sign string) {
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("ACCESS-KEY", bc.APIKey)
	request.Header.Add("ACCESS-SIGN", sign)
	request.Header.Add("ACCESS-TIMESTAMP", timestamp)
	request.Header.Add("ACCESS-PASSPHRASE", bc.Passphrase)
}

// sign 生成签名
func (bc *BitgetClient) sign(method, requestPath, body, timestamp string) string {
	var payload strings.Builder
	payload.WriteString(timestamp)
	payload.WriteString(method)
	payload.WriteString(requestPath)
	if body != "" && body != "?" {
		payload.WriteString(body)
	}

	if bc.SignType == "RSA" {
		return bc.signByRSA(payload.String())
	}

	return bc.signByHMAC(payload.String())
}

// signByHMAC HMAC签名
func (bc *BitgetClient) signByHMAC(payload string) string {
	hash := hmac.New(sha256.New, []byte(bc.APISecret))
	hash.Write([]byte(payload))
	return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}

// signByRSA RSA签名
func (bc *BitgetClient) signByRSA(payload string) string {
	sign, _ := bc.rsaSign([]byte(payload), []byte(bc.APISecret), crypto.SHA256)
	return base64.StdEncoding.EncodeToString(sign)
}

// rsaSign RSA签名实现
func (bc *BitgetClient) rsaSign(src []byte, priKey []byte, hash crypto.Hash) ([]byte, error) {
	block, _ := pem.Decode(priKey)
	if block == nil {
		return nil, errors.New("key is invalid format")
	}

	var pkixPrivateKey interface{}
	var err error
	if block.Type == "RSA PRIVATE KEY" {
		pkixPrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	} else if block.Type == "PRIVATE KEY" {
		pkixPrivateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
	}

	if err != nil {
		return nil, err
	}

	h := hash.New()
	_, err = h.Write(src)
	if err != nil {
		return nil, err
	}

	bytes := h.Sum(nil)
	sign, err := rsa.SignPKCS1v15(rand.Reader, pkixPrivateKey.(*rsa.PrivateKey), hash, bytes)
	if err != nil {
		return nil, err
	}

	return sign, nil
}

// buildGetParams 构建GET请求参数
func (bc *BitgetClient) buildGetParams(params map[string]string) string {
	if len(params) == 0 {
		return ""
	}
	return "?" + bc.sortParams(params)
}

// sortParams 参数排序
func (bc *BitgetClient) sortParams(params map[string]string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	sorted := make([]string, 0, len(params))
	for _, k := range keys {
		sorted = append(sorted, k+"="+params[k])
	}
	return strings.Join(sorted, "&")
}

// SendSpotRequestAuth 发送现货认证请求
func (bc *BitgetClient) SendSpotRequestAuth(endpoint, method string, params map[string]interface{}) ([]byte, *http.Response, error) {
	if method == "GET" {
		paramStr := make(map[string]string)
		for k, v := range params {
			paramStr[k] = fmt.Sprintf("%v", v)
		}
		resp, err := bc.DoGet(endpoint, paramStr)
		return resp, nil, err
	} else {
		resp, err := bc.DoPost(endpoint, params)
		return resp, nil, err
	}
}

// SendFuturesRequestAuth 发送合约认证请求
func (bc *BitgetClient) SendFuturesRequestAuth(endpoint, method string, params map[string]interface{}) ([]byte, *http.Response, error) {
	return bc.SendSpotRequestAuth(endpoint, method, params)
}
