import SockJS from 'sockjs-client'
import { Client, IMessage, IFrame } from '@stomp/stompjs'

/**
 * 管理端客服 WebSocket 客户端
 *
 * 同 web-snack 端：握手走 /ws/chat?type=admin&token=xxx
 * 订阅 /user/queue/chat/{sessionId} 与 /user/queue/chat/event
 */

export type ChatClientType = 'user' | 'admin'

export interface ChatSocketOptions {
  type: ChatClientType
  token: string
  baseURL?: string
}

export class ChatSocket {
  private client: Client | null = null
  private connected = false
  private pending: Array<() => void> = []

  onMessage: ((msg: any) => void) | null = null
  onEvent: ((evt: any) => void) | null = null
  onStatus: ((status: 'connecting' | 'connected' | 'disconnected' | 'error', err?: any) => void) | null = null

  constructor(private readonly opts: ChatSocketOptions) {}

  connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      const base = (this.opts.baseURL ?? '').replace(/\/+$/, '')
      const tokenQS = `token=${encodeURIComponent(this.opts.token)}&type=${this.opts.type}`
      const url = `${base}/ws/chat?${tokenQS}`
      const socket = new SockJS(url)

      this.client = new Client({
        webSocketFactory: () => socket as any,
        heartbeatIncoming: 10000,
        heartbeatOutgoing: 10000,
        debug: () => null,
        reconnectDelay: 5000
      })

      this.client.onConnect = () => {
        this.connected = true
        this.onStatus?.('connected')
        this.pending.splice(0).forEach(fn => fn())
        resolve()
      }
      this.client.onWebSocketClose = () => {
        this.connected = false
        this.onStatus?.('disconnected')
      }
      this.client.onStompError = (frame: IFrame) => {
        this.onStatus?.('error', frame)
        reject(new Error(frame.headers['message'] || 'STOMP 错误'))
      }

      this.onStatus?.('connecting')
      this.client.activate()
    })
  }

  disconnect() {
    this.client?.deactivate()
    this.connected = false
  }

  subscribeSession(sessionId: number) {
    const fn = () => {
      this.client?.subscribe(`/user/queue/chat/${sessionId}`, (frame: IMessage) => {
        try { this.onMessage?.(JSON.parse(frame.body)) } catch { /* ignore */ }
      })
      this.client?.subscribe('/user/queue/chat/event', (frame: IMessage) => {
        try { this.onEvent?.(JSON.parse(frame.body)) } catch { /* ignore */ }
      })
    }
    if (this.connected) fn()
    else this.pending.push(fn)
  }

  sendMessage(payload: { sessionId: number; type?: string; content: string; extra?: string; clientMsgId?: string }) {
    this.client?.publish({ destination: '/app/chat/send', body: JSON.stringify({ type: 'text', ...payload }) })
  }

  ack(payload: { sessionId: number; lastSeq: number }) {
    this.client?.publish({ destination: '/app/chat/ack', body: JSON.stringify(payload) })
  }

  close(payload: { sessionId: number }) {
    this.client?.publish({ destination: '/app/chat/close', body: JSON.stringify(payload) })
  }

  accept(payload: { sessionId: number }) {
    this.client?.publish({ destination: '/app/chat/accept', body: JSON.stringify(payload) })
  }
}
