import { computed, onScopeDispose } from 'vue'
import { useWebSocket } from '@vueuse/core'

type Handler = (body: any) => void

/**
 * 客服聊天 - WebSocket 连接封装（基于 VueUse useWebSocket）
 *
 * 直连 ws://host/ws/chat?token=xxx&type=user|admin（vite 已代理 /ws 到后端）
 * JSON 文本协议：
 *   发送  {type:'send'|'ack'|'close'|'accept'|'ping'}
 *   接收  {type:'message'|'event'|'admin_event'|'error'|'pong'}
 * 断线自动重连，按 type 分发消息
 */
export function useChatSocket(token: string, type: 'user' | 'admin') {
  const handlers = new Map<string, Set<Handler>>()
  let pingTimer: number | undefined

  const { status, send, open, close } = useWebSocket(
    `/ws/chat?token=${encodeURIComponent(token)}&type=${type}`,
    {
      autoReconnect: { retries: 30, delay: 3000 },
      onMessage: (_ws, event) => {
        let body: any = null
        try {
          body = JSON.parse(String(event.data))
        } catch {
          return
        }
        if (!body || !body.type || body.type === 'pong') return
        // 会话消息解包：{type:'message', message: ChatMessageVO}
        if (body.type === 'message') {
          handlers.get('message')?.forEach((h) => h(body.message))
          return
        }
        handlers.get(body.type)?.forEach((h) => h(body))
      }
    }
  )

  const connected = computed(() => status.value === 'OPEN')

  function on(event: string, handler: Handler) {
    let set = handlers.get(event)
    if (!set) {
      set = new Set()
      handlers.set(event, set)
    }
    set.add(handler)
  }

  function off(event: string, handler: Handler) {
    handlers.get(event)?.delete(handler)
  }

  function publish(payload: unknown): boolean {
    if (status.value !== 'OPEN') return false
    send(JSON.stringify(payload))
    return true
  }

  function connect() {
    if (status.value !== 'OPEN') open()
  }

  function disconnect() {
    if (pingTimer) window.clearInterval(pingTimer)
    pingTimer = undefined
    close()
  }

  // 应用层心跳（30s），保持代理/服务端连接活性
  if (token) {
    pingTimer = window.setInterval(() => {
      if (status.value === 'OPEN') send(JSON.stringify({ type: 'ping' }))
    }, 30000)
  }

  onScopeDispose(() => disconnect())

  return { connected, on, off, publish, connect, disconnect, status }
}
