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

/**
 * 客服 WebSocket 客户端（封装 SockJS + STOMP）
 *
 * 握手协议（见后端 WebSocketConfig + ChatHandshakeInterceptor）：
 *   ws(s)://host/ws/chat?type=user|admin&token=xxx
 *   - 走 SockJS（兼容老浏览器与代理穿透）
 *   - token 通过 query 传递（浏览器原生 WebSocket 没法设自定义 header）
 *   - type=user 表示用户端，type=admin 表示管理端
 *
 * 订阅：
 *   /user/queue/chat/{sessionId}  收消息
 *   /user/queue/chat/event       收系统事件
 *
 * 发送：
 *   /app/chat/send    { sessionId, type, content, extra, clientMsgId }
 *   /app/chat/ack     { sessionId, lastSeq }
 *   /app/chat/close   { sessionId }
 *   /app/chat/accept  { sessionId }（仅管理端）
 *
 * 用法：
 *   const ws = new ChatSocket({ type: 'user', token: 'xxx' })
 *   ws.onMessage = (msg) => { ... }
 *   ws.connect().then(() => ws.subscribeSession(sessionId))
 *   ws.sendMessage({ sessionId, type: 'text', content: 'hi' })
 */

export type ChatClientType = 'user' | 'admin'

export interface ChatSendPayload {
  sessionId: number
  type?: string
  content: string
  extra?: string
  clientMsgId?: string
}

export interface ChatAckPayload {
  sessionId: number
  lastSeq: number
}

export interface ChatSessionPayload {
  sessionId: number
}

export interface ChatSocketOptions {
  type: ChatClientType
  token: string
  /** 完整 ws(s)://host 前缀；开发态空字符串 = 走 Vite /ws 代理 */
  baseURL?: string
}

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

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

  constructor(private readonly opts: ChatSocketOptions) {}

  /** 连接到 /ws/chat */
  connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      const base = (this.opts.baseURL ?? '').replace(/\/+$/, '')
      const sockJsUrl = `${base}/ws/chat`
      const socket = new SockJS(sockJsUrl)

      this.client = new Client({
        // SockJS 模式：webSocketFactory 用现成的 socket
        webSocketFactory: () => socket as any,
        // 心跳
        heartbeatIncoming: 10000,
        heartbeatOutgoing: 10000,
        // 调试时可开
        debug: () => null,
        reconnectDelay: 5000
      })

      // 客户端拿不到自定义 header，所以 token 放 SockJS url 的 query
      // ChatHandshakeInterceptor 会从 query 取 token 与 type
      // 由于 sockjs-client 的 url 在 new SockJS 时已固定，我们需要让 url 携带 token
      // 重新创建 socket：
      const tokenQS = `token=${encodeURIComponent(this.opts.token)}&type=${this.opts.type}`
      const urlWithToken = `${base}/ws/chat?${tokenQS}`
      const socket2 = new SockJS(urlWithToken)
      this.client.webSocketFactory = () => socket2 as any

      this.client.onConnect = () => {
        this.connected = true
        this.onStatus?.('connected')
        // 触发待订阅
        this.pendingSubscribes.splice(0).forEach(fn => fn())
        resolve()
      }
      this.client.onWebSocketClose = (evt: any) => {
        this.connected = false
        this.onStatus?.('disconnected', evt)
      }
      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 doSubscribe = () => {
      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) doSubscribe()
    else this.pendingSubscribes.push(doSubscribe)
  }

  /** 发送消息 */
  sendMessage(payload: ChatSendPayload) {
    this.client?.publish({
      destination: '/app/chat/send',
      body: JSON.stringify({
        type: 'text',
        ...payload
      })
    })
  }

  /** 已读确认 */
  ack(payload: ChatAckPayload) {
    this.client?.publish({
      destination: '/app/chat/ack',
      body: JSON.stringify(payload)
    })
  }

  /** 关闭会话 */
  close(payload: ChatSessionPayload) {
    this.client?.publish({
      destination: '/app/chat/close',
      body: JSON.stringify(payload)
    })
  }

  /** 客服接管（仅管理端） */
  accept(payload: ChatSessionPayload) {
    this.client?.publish({
      destination: '/app/chat/accept',
      body: JSON.stringify(payload)
    })
  }
}
