package service

import (
	"errors"
	"fmt"
	"go-admin/app/admin/models"
	"go-admin/common/const/rediskey"
	"go-admin/common/global"
	"go-admin/common/helper"
	ext "go-admin/config"
	"go-admin/pkg/utility"
	"net/http"
	"time"

	"github.com/bytedance/sonic"
	"github.com/go-admin-team/go-admin-core/logger"
	"github.com/go-admin-team/go-admin-core/sdk/service"
	"github.com/shopspring/decimal"
)

const ProxyType = "socks5"

type BinanceAccount struct {
	service.Service
}

// GetFundingAsset 查询资金账户
func (e *BinanceAccount) GetFundingAsset(userId int) (resp []models.FundingAsset, err error) {
	var apiUser models.LineApiUser
	err = e.Orm.Where(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser).Error
	if err != nil {
		e.Log.Errorf("Service LineApiUser error:%s \r\n", err)
		return []models.FundingAsset{}, err
	}
	if apiUser.Id <= 0 {
		e.Log.Errorf("Service LineApiUser error:%s \r\n", "用户未找到")
		return []models.FundingAsset{}, err
	}
	var proxyUrl, proxyType string
	if ext.ExtConfig.ProxyUrl != "" {
		proxyUrl = "127.0.0.1:7890"
		proxyType = "http"
	}

	if apiUser.IpAddress != "" && apiUser.UserPass != "" {
		proxyUrl = fmt.Sprintf("%s:%s", apiUser.IpAddress, apiUser.UserPass)
		proxyType = ProxyType
	}
	clinet, err := helper.NewBinanceClient(apiUser.ApiKey, apiUser.ApiSecret, proxyType, proxyUrl)
	if err != nil {
		e.Log.Errorf("Service NewBinanceClient error:%s \r\n", err)
		return []models.FundingAsset{}, err
	}
	req := map[string]int64{
		"recvWindow": 10000,
	}

	httpResp, statusCode, err := clinet.SendSpotAuth("/sapi/v1/asset/get-funding-asset", "POST", req)
	if err != nil {
		e.Log.Errorf("Service SendSpotAuth error:%s \r\n", err)
		return []models.FundingAsset{}, err
	}

	if statusCode != http.StatusOK {
		e.Log.Errorf("Service 请求失败 error:%s \r\n", err)
		return []models.FundingAsset{}, errors.New("请求失败")
	}
	sonic.Unmarshal(httpResp, &resp)

	return resp, nil
}

/*
获取钱包余额

  - @param userId 用户id
  - @param quoteAsset 币种 USDT, ETH, USDC, BNB等。 默认 BTC
  - @return []models.FundingAsset 钱包余额
*/
func (e *BinanceAccount) GetWalletBalance(userId int, quoteAsset string) (resp []models.BinanceWalletBalance, err error) {
	if quoteAsset == "" {
		quoteAsset = "BTC"
	}

	var proxyUrl, proxyType string
	var apiUser models.LineApiUser
	url := "/sapi/v1/asset/wallet/balance"
	err = e.Orm.Where(&models.LineApiUser{}).Where("user_id = ?", userId).Find(&apiUser).Error
	if err != nil {
		e.Log.Errorf("Service LineApiUser error:%s \r\n", err)
		return []models.BinanceWalletBalance{}, err
	}
	if apiUser.Id <= 0 {
		e.Log.Errorf("Service LineApiUser error:%s \r\n", "用户未找到")
		return []models.BinanceWalletBalance{}, err
	}

	if ext.ExtConfig.ProxyUrl != "" {
		proxyUrl = "127.0.0.1:7890"
		proxyType = "http"
	}

	if apiUser.IpAddress != "" && apiUser.UserPass != "" {
		proxyUrl = fmt.Sprintf("%s:%s", apiUser.IpAddress, apiUser.UserPass)
		proxyType = ProxyType
	}
	clinet, err := helper.NewBinanceClient(apiUser.ApiKey, apiUser.ApiSecret, proxyType, proxyUrl)
	if err != nil {
		e.Log.Errorf("Service NewBinanceClient error:%s \r\n", err)
		return []models.BinanceWalletBalance{}, err
	}
	req := map[string]string{
		"quoteAsset": quoteAsset,
		"recvWindow": "10000",
	}

	httpResp, statusCode, err := clinet.SendSpotAuth(url, "GET", req)
	if err != nil {
		e.Log.Errorf("Service SendSpotAuth error:%s \r\n", err)
		return []models.BinanceWalletBalance{}, err
	}

	if statusCode != http.StatusOK {
		e.Log.Errorf("Service 请求失败 error:%s \r\n", err)
		return []models.BinanceWalletBalance{}, errors.New("请求失败")
	}
	sonic.Unmarshal(httpResp, &resp)

	return resp, nil
}

/*
获取用户总资产
*/
func (e *BinanceAccount) GetTotalAsset(userId int) (decimal.Decimal, error) {
	key := fmt.Sprintf(rediskey.User_Total_Asset, global.EXCHANGE_BINANCE, userId)
	totalStr, _ := helper.DefaultRedis.GetString(key)
	result := decimal.Zero
	if totalStr != "" {
		result = utility.StrToDecimal(totalStr)
	} else {
		assets, err := e.GetWalletBalance(userId, "USDT")

		if err != nil {
			logger.Errorf("GetTotalAsset error:%s", err)

			return decimal.Decimal{}, err
		}

		for _, asset := range assets {
			result = result.Add(utility.StrToDecimal(asset.Balance))
		}

		helper.DefaultRedis.SetStringExpire(key, result.String(), 30*time.Second)
	}

	return result, nil
}
