package excservice

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	"go-admin/models"
	"go-admin/pkg/timehelper"
	"go-admin/pkg/utility"

	"github.com/bytedance/sonic"
)

type BinanceWs struct {
	baseURL         string
	combinedBaseURL string
	proxyUrl        string
	WorkType        string
	wsConns         []*WsConn
	mu              sync.Mutex // 新增互斥锁
	tickerCallback  func(models.Ticker24, string, string)
	forceCallback   func(models.ForceOrder, string, string)
	depthCallback   func(models.DepthBin, string, string)
	tradeCallback   func(models.NewDealPush, string, string)
	klineCallback   func(models.Kline, int, string, string)
	allBack         func(msg []byte)
	allBackKline    func(msg []byte, tradeSet models.TradeSet)
}

func NewBinanceWs(wsbaseURL, proxyUrl string) *BinanceWs {
	return &BinanceWs{
		baseURL:         wsbaseURL,
		combinedBaseURL: "wss://stream.binance.com:9443/stream?streams=",
		proxyUrl:        proxyUrl,
	}
}

func (bnWs *BinanceWs) SetProxyUrl(proxyUrl string) {
	bnWs.proxyUrl = proxyUrl
}

func (bnWs *BinanceWs) SetBaseUrl(baseURL string) {
	bnWs.baseURL = baseURL
}

func (bnWs *BinanceWs) SetCombinedBaseURL(combinedBaseURL string) {
	bnWs.combinedBaseURL = combinedBaseURL
}

func (bnWs *BinanceWs) SetAllCallbacks(allBack func(msg []byte), allBackKline func(msg []byte, tradeSet models.TradeSet)) {
	if bnWs.allBack == nil {
		bnWs.allBack = allBack
	}

	if bnWs.allBackKline == nil {
		bnWs.allBackKline = allBackKline
	}
}

// 订阅通用函数
func (bnWs *BinanceWs) subscribe(endpoint string, handle func(msg []byte) error) {
	wsConn := NewWsBuilder().
		WsUrl(endpoint).
		AutoReconnect().
		ProtoHandleFunc(handle).
		ProxyUrl(bnWs.proxyUrl).
		ReconnectInterval(time.Millisecond * 5).
		Build()
	if wsConn == nil {
		return
	}
	bnWs.mu.Lock()
	bnWs.wsConns = append(bnWs.wsConns, wsConn)
	bnWs.mu.Unlock()
	go bnWs.exitHandler(wsConn)
}

func (bnWs *BinanceWs) Close() {
	bnWs.mu.Lock()
	defer bnWs.mu.Unlock()
	for _, con := range bnWs.wsConns {
		con.CloseWs()
	}
}

func (bnWs *BinanceWs) Subscribe(streamName string, tradeSet models.TradeSet, callback func(msg []byte, tradeSet models.TradeSet)) error {
	endpoint := bnWs.baseURL + streamName
	handle := func(msg []byte) error {
		callback(msg, tradeSet)
		return nil
	}
	bnWs.subscribe(endpoint, handle)
	return nil
}

func (bnWs *BinanceWs) exitHandler(c *WsConn) {
    // exitHandler 维护对底层连接的 Ping/Pong 心跳发送
    // 修复点：监听 c.close 通道，当连接关闭时退出循环，防止 goroutine 长期驻留导致内存泄漏
    pingTicker := time.NewTicker(1 * time.Minute)
    pongTicker := time.NewTicker(30 * time.Second)
    defer func() {
        pingTicker.Stop()
        pongTicker.Stop()
        c.CloseWs()

		if err := recover(); err != nil {
			fmt.Printf("CloseWs, panic: %s\r\n", err)
		}
	}()

    for {
        select {
        case <-c.close:
            // 连接关闭，退出心跳协程
            return
        case t := <-pingTicker.C:
            c.SendPingMessage([]byte(strconv.Itoa(int(t.UnixNano() / int64(time.Millisecond)))))
        case t := <-pongTicker.C:
            c.SendPongMessage([]byte(strconv.Itoa(int(t.UnixNano() / int64(time.Millisecond)))))
        }
    }
}

func parseJsonToMap(msg []byte) (map[string]interface{}, error) {
	datamap := make(map[string]interface{})
	err := sonic.Unmarshal(msg, &datamap)
	return datamap, err
}

func handleForceOrder(msg []byte, tradeSet models.TradeSet, callback func(models.ForceOrder, string, string)) error {
	datamap, err := parseJsonToMap(msg)
	if err != nil {
		return fmt.Errorf("json unmarshal error: %v", err)
	}

	msgType, ok := datamap["e"].(string)
	if !ok || msgType != "forceOrder" {
		return errors.New("unknown message type")
	}

	datamapo := datamap["o"].(map[string]interface{})
	order := models.ForceOrder{
		Side:        datamapo["S"].(string),
		Symbol:      datamapo["s"].(string),
		Ordertype:   datamapo["o"].(string),
		TimeInForce: datamapo["f"].(string),
		Num:         utility.ToFloat64(datamapo["q"]),
		Price:       utility.ToFloat64(datamapo["p"]),
		AvgPrice:    utility.ToFloat64(datamapo["ap"]),
		State:       datamapo["X"].(string),
		CreateTime:  timehelper.IntToTime(utility.ToInt64(datamapo["T"])),
	}
	callback(order, tradeSet.Coin, tradeSet.Currency)
	return nil
}

// SubscribeAll 订阅 组合streams的URL格式为 /stream?streams=<streamName1>/<streamName2>/<streamName3>
// 订阅组合streams时，事件payload会以这样的格式封装: {"stream":"<streamName>","data":<rawPayload>}
// 单一原始 streams 格式为 /ws/<streamName>
func (bnWs *BinanceWs) SubscribeAll(streamName string) error {
	endpoint := bnWs.baseURL + streamName

	handle := func(msg []byte) error {
		bnWs.allBack(msg)
		return nil
	}

	bnWs.subscribe(endpoint, handle)
	return nil
}

// SubscribeAllKline 订阅kline推送 组合streams的URL格式为 /stream?streams=<streamName1>/<streamName2>/<streamName3>
func (bnWs *BinanceWs) SubscribeAllKline(streamName string, tradeSet models.TradeSet) error {
	endpoint := bnWs.baseURL + streamName

	handle := func(msg []byte) error {
		bnWs.allBackKline(msg, tradeSet)
		return nil
	}

	bnWs.subscribe(endpoint, handle)
	return nil
}
