import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
import { onBeforeUnmount, onMounted } from 'vue'

interface NotificationSocketMessage<T> {
  type: string
  data?: T
}

interface UseNotificationSocketOptions<T> {
  onNotification: (data: T) => void
  onReconnectAfter?: () => void | Promise<void>
}

const MAX_RECONNECT_ATTEMPTS = 8
const MAX_RECONNECT_DELAY = 30_000
const RECONNECT_STEP_DELAY = 1_000

function buildNotificationSocketUrl() {
  const token = SafeStorage.get<string>(AppStorageKey.TOKEN)
  if (!token) return null

  const baseUrl = import.meta.env.VITE_API_BASE_URL || window.location.origin
  const url = new URL(baseUrl, window.location.origin)
  const basePath = url.pathname.replace(/\/$/, '')

  url.pathname = `${basePath}/Alert/v1/message/ws`
  url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'

  // WebSocket 无法直接设置 Authorization header；如后端仍依赖 header 认证，
  // 需要后端适配 query token 或签发一次性 ws ticket。
  url.searchParams.set('token', token)

  return url.toString()
}

export default function useNotificationSocket<T>(options: UseNotificationSocketOptions<T>) {
  let socket: WebSocket | null = null
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null
  let reconnectAttempt = 0
  let isManualClose = false
  let isReconnecting = false

  const clearReconnectTimer = () => {
    if (!reconnectTimer) return
    clearTimeout(reconnectTimer)
    reconnectTimer = null
  }

  const closeSocket = () => {
    if (!socket) return
    socket.onopen = null
    socket.onmessage = null
    socket.onerror = null
    socket.onclose = null
    socket.close()
    socket = null
  }

  const scheduleReconnect = () => {
    if (isManualClose || reconnectTimer || reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) return

    reconnectAttempt += 1
    isReconnecting = true

    const delay = Math.min(reconnectAttempt * RECONNECT_STEP_DELAY, MAX_RECONNECT_DELAY)
    reconnectTimer = setTimeout(() => {
      reconnectTimer = null
      if (isManualClose) return
      connect()
    }, delay)
  }

  const handleMessage = (event: MessageEvent<string>) => {
    try {
      const message = JSON.parse(event.data) as NotificationSocketMessage<T>
      if (message.type === 'notification' && message.data) {
        options.onNotification(message.data)
      }
    } catch (error) {
      // 忽略无法识别的消息，避免单条异常数据中断后续通知接收。
    }
  }

  function connect() {
    if (isManualClose) return

    const socketUrl = buildNotificationSocketUrl()
    if (!socketUrl) return

    closeSocket()
    socket = new WebSocket(socketUrl)

    socket.onopen = () => {
      reconnectAttempt = 0
      if (isReconnecting) {
        isReconnecting = false
        options.onReconnectAfter?.()
      }
    }
    socket.onmessage = handleMessage
    socket.onerror = () => {
      socket?.close()
    }
    socket.onclose = () => {
      socket = null
      scheduleReconnect()
    }
  }

  const cleanup = () => {
    isManualClose = true
    clearReconnectTimer()
    closeSocket()
  }

  onMounted(() => {
    isManualClose = false
    connect()
  })

  onBeforeUnmount(cleanup)

  return {
    cleanup,
  }
}
