package bitgetservice

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"go-admin/services/proxy"
	"strings"
	"sync"
	"time"

	"github.com/bytedance/sonic"
	log "github.com/go-admin-team/go-admin-core/logger"
	"github.com/gorilla/websocket"
)

// BitgetMarketClient Bitget公开行情订阅客户端
// 基于官方SDK设计，专门用于公开市场数据订阅
type BitgetMarketClient struct {
	config         *BitgetMarketConfig
	conn           *websocket.Conn
	subscriptions  map[SubscribeReq]OnReceive
	allSubscribes  *Set
	listener       OnReceive
	errorListener  OnReceive
	sendMutex      sync.Mutex
	reconnectMutex sync.Mutex
	ctx            context.Context
	cancel         context.CancelFunc
	connected      bool
	lastPongTime   time.Time
	proxyType      string
	proxyAddress   string
	reconnectCount int
}

// NewBitgetMarketClient 创建新的Bitget市场数据客户端
func NewBitgetMarketClient(config *BitgetMarketConfig) *BitgetMarketClient {
	if config == nil {
		config = DefaultMarketConfig()
	}

	ctx, cancel := context.WithCancel(context.Background())

	return &BitgetMarketClient{
		config:        config,
		subscriptions: make(map[SubscribeReq]OnReceive),
		allSubscribes: NewSet(),
		ctx:           ctx,
		cancel:        cancel,
		connected:     false,
		lastPongTime:  time.Now(),
	}
}

// SetProxy 设置代理
func (c *BitgetMarketClient) SetProxy(proxyType, proxyAddress string) {
	c.proxyType = proxyType
	c.proxyAddress = proxyAddress
}

// SetListeners 设置消息监听器
func (c *BitgetMarketClient) SetListeners(msgListener, errorListener OnReceive) {
	c.listener = msgListener
	c.errorListener = errorListener
}

// Connect 连接WebSocket
func (c *BitgetMarketClient) Connect() error {
	c.reconnectMutex.Lock()
	defer c.reconnectMutex.Unlock()

	if c.connected {
		return nil
	}

	// 创建WebSocket连接
	dialer := websocket.DefaultDialer

	// 设置代理
	if c.proxyAddress != "" {
		var err error
		dialer, err = proxy.GetDialer(c.proxyType, c.proxyAddress)
		if err != nil {
			return fmt.Errorf("设置代理失败: %v", err)
		}
	}

	dialer.HandshakeTimeout = 10 * time.Second
	log.Infof("正在连接Bitget市场数据WebSocket: %s", c.config.WsUrl)
	conn, _, err := dialer.Dial(c.config.WsUrl, nil)
	if err != nil {
		return fmt.Errorf("webSocket连接失败: %v", err)
	}

	c.conn = conn
	c.connected = true
	c.lastPongTime = time.Now()
	c.reconnectCount = 0

	log.Info("Bitget市场数据WebSocket连接成功")

	// 启动消息处理协程
	go c.readLoop()
	go c.pingLoop()
	go c.monitorConnection()

	return nil
}

// Subscribe 订阅市场数据
func (c *BitgetMarketClient) Subscribe(subscribeReqs []SubscribeReq, listener OnReceive) error {
	if !c.connected {
		if err := c.Connect(); err != nil {
			return err
		}
	}

	var args []interface{}
	for _, req := range subscribeReqs {
		// 标准化订阅请求
		normalizedReq := c.normalizeSubscribeReq(req)
		args = append(args, normalizedReq)

		// 保存订阅信息
		c.subscriptions[normalizedReq] = listener
		c.allSubscribes.Add(normalizedReq)
	}

	// 发送订阅消息
	wsReq := WsBaseReq{
		Op:   "subscribe",
		Args: args,
	}

	return c.sendMessage(wsReq)
}

// Unsubscribe 取消订阅
func (c *BitgetMarketClient) Unsubscribe(subscribeReqs []SubscribeReq) error {
	if !c.connected {
		return errors.New("WebSocket未连接")
	}

	var args []interface{}
	for _, req := range subscribeReqs {
		normalizedReq := c.normalizeSubscribeReq(req)
		args = append(args, normalizedReq)

		// 移除订阅信息
		delete(c.subscriptions, normalizedReq)
		c.allSubscribes.Remove(normalizedReq)
	}

	// 发送取消订阅消息
	wsReq := WsBaseReq{
		Op:   "unsubscribe",
		Args: args,
	}

	return c.sendMessage(wsReq)
}

// normalizeSubscribeReq 标准化订阅请求
func (c *BitgetMarketClient) normalizeSubscribeReq(req SubscribeReq) SubscribeReq {
	return SubscribeReq{
		InstType: strings.ToUpper(req.InstType),
		Channel:  strings.ToLower(req.Channel),
		InstId:   strings.ToUpper(req.InstId),
	}
}

// sendMessage 发送消息
func (c *BitgetMarketClient) sendMessage(msg interface{}) error {
	if c.conn == nil {
		return errors.New("WebSocket连接不可用")
	}

	c.sendMutex.Lock()
	defer c.sendMutex.Unlock()

	data, err := sonic.Marshal(msg)
	if err != nil {
		return fmt.Errorf("序列化消息失败: %v", err)
	}

	log.Info("发送消息: %s", string(data))

	c.conn.SetWriteDeadline(time.Now().Add(c.config.WriteTimeout))
	return c.conn.WriteMessage(websocket.TextMessage, data)
}

// readLoop 消息读取循环
func (c *BitgetMarketClient) readLoop() {
	defer func() {
		if r := recover(); r != nil {
			log.Error("readLoop panic: %v", r)
		}
	}()

	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			if c.conn == nil {
				time.Sleep(time.Second)
				continue
			}

			c.conn.SetReadDeadline(time.Now().Add(c.config.ReadTimeout))
			_, message, err := c.conn.ReadMessage()
			if err != nil {
				log.Error("读取消息失败: %v", err)
				c.handleDisconnect()
				continue
			}

			c.handleMessage(string(message))
		}
	}
}

// handleMessage 处理接收到的消息
func (c *BitgetMarketClient) handleMessage(message string) {
	log.Info("收到消息: %s", message)

	// 处理pong消息
	if message == "pong" {
		c.lastPongTime = time.Now()
		log.Debug("收到pong消息")
		return
	}

	// 解析JSON消息
	var jsonMap map[string]interface{}
	if err := json.Unmarshal([]byte(message), &jsonMap); err != nil {
		log.Error("解析JSON消息失败: %v", err)
		if c.errorListener != nil {
			c.errorListener(message)
		}
		return
	}

	// 检查错误码
	if code, exists := jsonMap["code"]; exists {
		if codeNum, ok := code.(float64); ok && int(codeNum) != 0 {
			log.Error("收到错误消息: %s", message)
			if c.errorListener != nil {
				c.errorListener(message)
			}
			return
		}
	}

	// 处理订阅确认消息
	if event, exists := jsonMap["event"]; exists && event == "subscribe" {
		log.Info("订阅确认: %s", message)
		return
	}

	// 处理数据推送
	if data, exists := jsonMap["data"]; exists && data != nil {
		if arg, argExists := jsonMap["arg"]; argExists {
			listener := c.getListener(arg)
			if listener != nil {
				listener(message)
			}
		}
		return
	}

	// 默认处理
	if c.listener != nil {
		c.listener(message)
	}
}

// getListener 获取对应的监听器
func (c *BitgetMarketClient) getListener(argJson interface{}) OnReceive {
	argMap, ok := argJson.(map[string]interface{})
	if !ok {
		return c.listener
	}

	subscribeReq := SubscribeReq{
		InstType: fmt.Sprintf("%v", argMap["instType"]),
		Channel:  fmt.Sprintf("%v", argMap["channel"]),
		InstId:   fmt.Sprintf("%v", argMap["instId"]),
	}

	if listener, exists := c.subscriptions[subscribeReq]; exists {
		return listener
	}

	return c.listener
}

// pingLoop Ping循环
func (c *BitgetMarketClient) pingLoop() {
	ticker := time.NewTicker(c.config.PingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-c.ctx.Done():
			return
		case <-ticker.C:
			if c.connected && c.conn != nil {
				c.sendMutex.Lock()
				c.conn.SetWriteDeadline(time.Now().Add(c.config.WriteTimeout))
				err := c.conn.WriteMessage(websocket.TextMessage, []byte("ping"))
				c.sendMutex.Unlock()

				if err != nil {
					log.Error("发送ping失败: %v", err)
					c.handleDisconnect()
				}
			}
		}
	}
}

// monitorConnection 监控连接状态
func (c *BitgetMarketClient) monitorConnection() {
	ticker := time.NewTicker(30 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-c.ctx.Done():
			return
		case <-ticker.C:
			if c.connected && time.Since(c.lastPongTime) > 60*time.Second {
				log.Warn("长时间未收到pong消息，触发重连")
				c.handleDisconnect()
			}
		}
	}
}

// handleDisconnect 处理断开连接
func (c *BitgetMarketClient) handleDisconnect() {
	c.reconnectMutex.Lock()
	defer c.reconnectMutex.Unlock()

	if !c.connected {
		return
	}

	log.Warn("WebSocket连接断开，准备重连")
	c.connected = false

	if c.conn != nil {
		c.conn.Close()
		c.conn = nil
	}

	// 重连逻辑
	go c.reconnect()
}

// reconnect 重连
func (c *BitgetMarketClient) reconnect() {
	for c.reconnectCount < c.config.MaxReconnectTimes {
		select {
		case <-c.ctx.Done():
			return
		default:
			c.reconnectCount++
			log.Info("尝试重连 (%d/%d)", c.reconnectCount, c.config.MaxReconnectTimes)

			time.Sleep(c.config.ReconnectInterval)

			if err := c.Connect(); err != nil {
				log.Error("重连失败: %v", err)
				continue
			}

			// 重新订阅
			if err := c.resubscribe(); err != nil {
				log.Error("重新订阅失败: %v", err)
				c.handleDisconnect()
				continue
			}

			log.Info("重连成功")
			return
		}
	}

	log.Error("达到最大重连次数，停止重连")
}

// resubscribe 重新订阅
func (c *BitgetMarketClient) resubscribe() error {
	if len(c.subscriptions) == 0 {
		return nil
	}

	var args []interface{}
	for req := range c.subscriptions {
		args = append(args, req)
	}

	wsReq := WsBaseReq{
		Op:   "subscribe",
		Args: args,
	}

	return c.sendMessage(wsReq)
}

// Close 关闭客户端
func (c *BitgetMarketClient) Close() error {
	c.cancel()
	c.connected = false

	if c.conn != nil {
		return c.conn.Close()
	}

	return nil
}

// IsConnected 检查连接状态
func (c *BitgetMarketClient) IsConnected() bool {
	return c.connected
}
