/**
 * 现货用户推送 WS（与 App spot_ws_client：spot_asset / spot_order 一致）
 * 首次数据仍用 REST；此处仅增量更新资产与当前委托。
 */
const WS_BASE = import.meta.env.VITE_WS_SPOT_URL as string

export interface SpotUserPushHandlers {
  onAsset: (msg: Record<string, unknown>) => void
  onOrder: (msg: Record<string, unknown>) => void
  /** WS 断线重连成功后调用，用于重新拉取全量数据 */
  onReconnect?: () => void
}

export function useSpotUserPush(handlers: SpotUserPushHandlers) {
  let ws: WebSocket | null = null
  let destroyed = false
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null
  let reconnectAttempt = 0
  let curUid = ''

  function buildUrl(): string {
    const q = new URLSearchParams({ clientType: 'web' })
    if (curUid) q.set('uid', curUid)
    const sep = WS_BASE.includes('?') ? '&' : '?'
    return `${WS_BASE}${sep}${q.toString()}`
  }

  function send(obj: object) {
    if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(obj))
  }

  function backoffMs(): number {
    const base = Math.min(30_000, 1000 * Math.pow(2, reconnectAttempt))
    return reconnectAttempt >= 50 ? 120_000 : base
  }

  function subUserChannels() {
    send({ event: 'sub', params: { channel: 'spot_asset' } })
    send({ event: 'sub', params: { channel: 'spot_order' } })
  }

  function routeMsg(raw: string) {
    let msg: Record<string, unknown>
    try {
      msg = JSON.parse(raw) as Record<string, unknown>
    } catch {
      return
    }
    if (msg.ping !== undefined) {
      send({ pong: msg.ping })
      return
    }
    const pushType = msg.type != null ? String(msg.type) : ''
    if (pushType === 'asset') {
      handlers.onAsset(msg)
      return
    }
    if (pushType === 'order') {
      handlers.onOrder(msg)
    }
  }

  function doConnect() {
    if (destroyed || !WS_BASE || !curUid) return
    ws = new WebSocket(buildUrl())
    ws.onopen = () => {
      const isReconnect = reconnectAttempt > 0
      reconnectAttempt = 0
      subUserChannels()
      if (isReconnect) handlers.onReconnect?.()
    }
    ws.onmessage = e => routeMsg(String(e.data))
    ws.onerror = () => {}
    ws.onclose = () => {
      if (destroyed) return
      reconnectAttempt++
      reconnectTimer = setTimeout(doConnect, backoffMs())
    }
  }

  function connect(uid: string) {
    const next = String(uid ?? '').trim()
    curUid = next
    destroyed = false
    if (reconnectTimer !== null) {
      clearTimeout(reconnectTimer)
      reconnectTimer = null
    }
    if (ws) {
      ws.onclose = null
      ws.close()
      ws = null
    }
    if (!next) return
    doConnect()
  }

  function disconnect() {
    destroyed = true
    reconnectAttempt = 0
    curUid = ''
    if (reconnectTimer !== null) {
      clearTimeout(reconnectTimer)
      reconnectTimer = null
    }
    if (ws) {
      try {
        send({ event: 'unsub', params: { channel: 'spot_asset' } })
        send({ event: 'unsub', params: { channel: 'spot_order' } })
      } catch {
        /* ignore */
      }
      ws.onclose = null
      ws.close()
      ws = null
    }
  }

  return { connect, disconnect }
}
