/**
 * composables/useWebSocket.ts
 *
 * WebSocket client for Nexus alert/recovery/system push.
 * Backend protocol: ws://host/ws/alerts?token=<JWT>
 *
 * Message types:
 *   alert       → { type: "alert", server_id, server_name, alert_type, alert_value }
 *   recovery    → { type: "recovery", server_id, server_name, metric, value }
 *   system      → { type: "system", event_type, message }
 *   sync_progress → { type: "sync_progress", batch_id, server_id, status, completed, failed, total }
 *   ping        → { type: "ping" } → client must respond with text "pong"
 */
import { ref, computed } from 'vue'
import { useAuthStore } from '@/stores/auth'

const ws = ref<WebSocket | null>(null)
const connected = ref(false)
const reconnectAttempts = ref(0)
const alerts = ref<WsAlert[]>([])
let reconnectTimer: ReturnType<typeof setTimeout> | null = null

const MAX_RECONNECT_DELAY = 30000
const MAX_ALERTS = 50

function getWsUrl(): string {
  const base = import.meta.env.VITE_API_BASE || ''
  if (base) {
    // Replace http(s) with ws(s)
    const wsBase = base.replace(/^http/, 'ws')
    return `${wsBase}/ws/alerts`
  }
  // Default: same origin
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'
  return `${proto}//${location.host}/ws/alerts`
}

export function useWebSocket() {
  const auth = useAuthStore()

  const connectionState = computed(() => {
    if (!ws.value) return 'disconnected'
    switch (ws.value.readyState) {
      case WebSocket.CONNECTING: return 'connecting'
      case WebSocket.OPEN: return 'connected'
      case WebSocket.CLOSING: return 'closing'
      default: return 'disconnected'
    }
  })

  function connect() {
    if (!auth.token) return
    if (ws.value?.readyState === WebSocket.OPEN) return
    // Close stale connection if still CONNECTING/CLOSING
    if (ws.value) {
      ws.value.onclose = null // prevent reconnect loop
      ws.value.close()
      ws.value = null
    }

    const url = `${getWsUrl()}?token=${auth.token}`
    const socket = new WebSocket(url)

    socket.onopen = () => {
      connected.value = true
      reconnectAttempts.value = 0
    }

    socket.onmessage = (event) => {
      // Handle pong (plain text)
      if (event.data === 'pong') return

      try {
        const msg = JSON.parse(event.data)

        // Handle ping
        if (msg.type === 'ping') {
          socket.send('pong')
          return
        }

        // Handle alert/recovery/system
        if (msg.type === 'alert' || msg.type === 'recovery') {
          const alert: WsAlert = {
            type: msg.type,
            serverId: msg.server_id,
            serverName: msg.server_name,
            alertType: msg.alert_type || msg.metric,
            value: msg.alert_value || msg.value,
            time: new Date().toLocaleTimeString(),
          }
          alerts.value.unshift(alert)
          if (alerts.value.length > MAX_ALERTS) alerts.value.pop()

          // Show snackbar
          const fn = window.$snackbar
          if (fn) {
            const color = msg.type === 'alert' ? 'error' : 'success'
            const text = msg.type === 'alert'
              ? `告警: ${msg.server_name} ${msg.alert_type} ${msg.alert_value}%`
              : `恢复: ${msg.server_name} ${msg.metric} 恢复正常`
            fn(text, color)
          }
        }
      } catch {
        // Ignore malformed messages
      }
    }

    socket.onclose = (event) => {
      connected.value = false
      ws.value = null
      // Don't reconnect on auth failure — token expired
      if (event.code === 4001 || event.code === 4401) {
        auth.forceLogout()
        return
      }
      scheduleReconnect()
    }

    socket.onerror = () => {
      // onclose will fire after this
    }

    ws.value = socket
  }

  function disconnect() {
    if (reconnectTimer) {
      clearTimeout(reconnectTimer)
      reconnectTimer = null
    }
    if (ws.value) {
      ws.value.close()
      ws.value = null
    }
    connected.value = false
    reconnectAttempts.value = 0
  }

  function scheduleReconnect() {
    if (!auth.isLoggedIn) return

    reconnectAttempts.value++
    // Exponential backoff with jitter
    const delay = Math.min(
      1000 * Math.pow(2, reconnectAttempts.value - 1) + Math.random() * 1000,
      MAX_RECONNECT_DELAY,
    )

    reconnectTimer = setTimeout(() => {
      if (auth.isLoggedIn) connect()
    }, delay)
  }

  function clearAlerts() {
    alerts.value = []
  }

  return {
    connected,
    connectionState,
    alerts,
    connect,
    disconnect,
    clearAlerts,
  }
}

interface WsAlert {
  type: 'alert' | 'recovery'
  serverId: number
  serverName: string
  alertType: string
  value: number
  time: string
}
