/**
 * WebSocket 客户端工具类
 * 基于 STOMP over WebSocket 协议
 * 用于接收后端推送的播放方案和播报状态
 */

import { ref } from "vue"

// WebSocket 连接状态
export const ConnectionState = {
  DISCONNECTED: "disconnected",
  CONNECTING: "connecting",
  CONNECTED: "connected",
  RECONNECTING: "reconnecting",
  ERROR: "error"
}

// 单例模式
let stompClient = null
let socket = null

// 连接配置
const defaultConfig = {
  brokerURL: getWebSocketURL(),
  reconnectDelay: 3000,
  maxReconnectAttempts: 5,
  heartbeatIncoming: 10000,
  heartbeatOutgoing: 10000,
  debug: false
}

let config = { ...defaultConfig }

// 连接状态
const connectionState = ref(ConnectionState.DISCONNECTED)

// 重连计数器
let reconnectAttempts = 0
let reconnectTimer = null
let isFirstConnection = true

// 订阅列表
const subscriptions = new Map()

function getWebSocketURL() {
  if (import.meta.env.VITE_WS_URL) {
    return import.meta.env.VITE_WS_URL
  }
  // SockJS 需要 http/https 协议，不是 ws/wss
  const protocol = window.location.protocol === "https:" ? "https:" : "http:"
  const host = window.location.host
  return `${protocol}//${host}/ws/robot`
}

export function configure(options) {
  config = { ...defaultConfig, ...options }
}

export function connect(onConnected) {
  if (connectionState.value === ConnectionState.CONNECTED) {
    console.log("[WebSocket] Already connected")
    return
  }

  if (connectionState.value === ConnectionState.CONNECTING) {
    console.log("[WebSocket] Connecting...")
    return
  }

  connectionState.value = ConnectionState.CONNECTING
  console.log("[WebSocket] Connecting...", config.brokerURL)

  Promise.all([
    import("sockjs-client"),
    import("@stomp/stompjs")
  ]).then(([SockJS, Stomp]) => {
    // v6 API: Stomp is exported as default or Stomp property
    const StompJS = Stomp.default || Stomp.Stomp
    socket = new SockJS.default(config.brokerURL)
    stompClient = StompJS.over(socket, {
      heartbeatIncoming: config.heartbeatIncoming,
      heartbeatOutgoing: config.heartbeatOutgoing,
      debug: config.debug
    })

    stompClient.connect(
      {},
      (frame) => {
        console.log("[WebSocket] Connected successfully")
        connectionState.value = ConnectionState.CONNECTED
        reconnectAttempts = 0
        if (onConnected) {
          onConnected()
        }
        // Only resubscribe on actual reconnect (not first connect)
        // On first connect, subscriptions are created by subscribeChannels() called from onConnected callback
        if (!isFirstConnection && subscriptions.size > 0) {
          resubscribeAll()
        }
        isFirstConnection = false
      },
      (error) => {
        console.warn("[WebSocket] Connection disconnected:", error)
        handleDisconnect(error)
      }
    )

    stompClient.onWebSocketClose = (event) => {
      console.warn("[WebSocket] WebSocket closed", event)
      handleDisconnect(event)
    }

    stompClient.onStompError = (event) => {
      console.error("[WebSocket] STOMP error:", event)
      handleDisconnect(event)
    }
  }).catch((error) => {
    console.error("[WebSocket] Failed to load SockJS/STOMP:", error)
    connectionState.value = ConnectionState.ERROR
    scheduleReconnect()
  })
}

export function disconnect() {
  console.log("[WebSocket] Disconnecting")
  if (reconnectTimer) {
    clearTimeout(reconnectTimer)
    reconnectTimer = null
  }
  reconnectAttempts = 0
  isFirstConnection = true  // Reset for next connection attempt
  unsubscribeAll()
  if (stompClient) {
    try {
      stompClient.disconnect()
    } catch (e) {
      console.warn("[WebSocket] Error on disconnect:", e)
    }
    stompClient = null
  }
  if (socket) {
    socket.close()
    socket = null
  }
  connectionState.value = ConnectionState.DISCONNECTED
}

function handleDisconnect(error) {
  if (connectionState.value === ConnectionState.DISCONNECTED) {
    return
  }
  connectionState.value = ConnectionState.ERROR
  scheduleReconnect()
}

function scheduleReconnect() {
  if (reconnectAttempts >= config.maxReconnectAttempts) {
    console.error(`[WebSocket] Max reconnect attempts (${config.maxReconnectAttempts}) reached`)
    connectionState.value = ConnectionState.ERROR
    return
  }

  reconnectAttempts++
  connectionState.value = ConnectionState.RECONNECTING
  const delay = config.reconnectDelay * reconnectAttempts
  console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${reconnectAttempts})...`)

  reconnectTimer = setTimeout(() => {
    connect()
  }, delay)
}

export function subscribe(destination, callback, id = null) {
  if (!stompClient || connectionState.value !== ConnectionState.CONNECTED) {
    console.warn("[WebSocket] Not connected, cannot subscribe:", destination)
    return null
  }

  const subscriptionId = id || `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  console.log("[WebSocket] Subscribing:", destination, subscriptionId)

  const subscription = stompClient.subscribe(destination, (message) => {
    try {
      const body = message.body ? JSON.parse(message.body) : null
      if (config.debug) {
        console.log("[WebSocket] Received:", destination, body)
      }
      callback(body, message)
    } catch (e) {
      console.error("[WebSocket] Parse error:", e)
      callback(message.body, message)
    }
  }, { id: subscriptionId })

  subscriptions.set(subscriptionId, { subscription, destination })
  return subscriptionId
}

export function unsubscribe(subscriptionId) {
  const sub = subscriptions.get(subscriptionId)
  if (sub) {
    try {
      sub.subscription.unsubscribe()
      console.log("[WebSocket] Unsubscribed:", sub.destination, subscriptionId)
    } catch (e) {
      console.warn("[WebSocket] Unsubscribe failed:", e)
    }
    subscriptions.delete(subscriptionId)
  }
}

function unsubscribeAll() {
  subscriptions.forEach((sub, id) => {
    try {
      sub.subscription.unsubscribe()
    } catch (e) {
      // Ignore
    }
  })
  subscriptions.clear()
}

function resubscribeAll() {
  subscriptions.forEach((sub, id) => {
    const newSub = stompClient.subscribe(sub.destination, (message) => {
      try {
        const body = message.body ? JSON.parse(message.body) : null
        if (config.debug) {
          console.log("[WebSocket] Received:", sub.destination, body)
        }
      } catch (e) {
        console.error("[WebSocket] Parse error:", e)
      }
    }, { id })
    sub.subscription = newSub
  })
}

export function send(destination, body = {}) {
  if (!stompClient || connectionState.value !== ConnectionState.CONNECTED) {
    console.warn("[WebSocket] Not connected, cannot send:", destination)
    return false
  }
  try {
    stompClient.send(destination, {}, JSON.stringify(body))
    if (config.debug) {
      console.log("[WebSocket] Sent:", destination, body)
    }
    return true
  } catch (e) {
    console.error("[WebSocket] Send failed:", e)
    return false
  }
}

export function getConnectionState() {
  return connectionState.value
}

export function isConnected() {
  return connectionState.value === ConnectionState.CONNECTED
}

export function sendHeartbeat() {
  if (isConnected()) {
    stompClient.send("/app/heartbeat", {}, JSON.stringify({
      timestamp: Date.now(),
      client: "screen"
    }))
  }
}

// 屏幕端专用订阅
export function subscribePlayPlan(callback) {
  return subscribe("/topic/screen/play-plan", callback, "play-plan")
}

export function subscribeBroadcast(callback) {
  return subscribe("/topic/screen/broadcast", callback, "broadcast")
}

export function subscribeConnect(callback) {
  return subscribe("/topic/screen/connect", callback, "connect")
}

export default {
  connect,
  disconnect,
  subscribe,
  unsubscribe,
  send,
  subscribePlayPlan,
  subscribeBroadcast,
  subscribeConnect,
  sendHeartbeat,
  getConnectionState,
  isConnected,
  configure,
  ConnectionState
}