import { ref, computed, watch, onUnmounted } from 'vue'
import { useAuthStore } from '@/stores/auth'

export interface WebSocketMessage {
  type: 'connected' | 'event' | 'error'
  license_id?: string
  subscribed_to?: string
  event?: string
  subject?: string
  data?: any
  raw?: string
  message?: string
}

type MessageHandler = (message: WebSocketMessage) => void

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

  const ws = ref<WebSocket | null>(null)
  const isConnected = ref(false)
  const isConnecting = ref(false)
  const error = ref<string | null>(null)
  const reconnectAttempts = ref(0)
  const maxReconnectAttempts = 10
  const baseReconnectDelay = 1000 // 1 second

  const messageHandlers = new Set<MessageHandler>()
  let reconnectTimeout: number | null = null

  const status = computed(() => {
    if (isConnected.value) return 'connected'
    if (isConnecting.value) return 'connecting'
    if (error.value) return 'error'
    return 'disconnected'
  })

  function getWebSocketUrl(): string {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
    const host = import.meta.env.PROD
      ? window.location.host
      : 'localhost:3000'
    const token = authStore.accessToken

    if (!token) {
      throw new Error('No access token available for WebSocket authentication')
    }

    return `${protocol}//${host}/api/ws?token=${encodeURIComponent(token)}`
  }

  function connect() {
    if (!authStore.isAuthenticated) {
      console.log('[WebSocket] Not authenticated, skipping connection')
      return
    }

    if (isConnecting.value || isConnected.value) {
      console.log('[WebSocket] Already connecting or connected')
      return
    }

    try {
      isConnecting.value = true
      error.value = null

      const url = getWebSocketUrl()
      console.log('[WebSocket] Connecting to', url.replace(/token=[^&]+/, 'token=***'))

      ws.value = new WebSocket(url)

      ws.value.onopen = () => {
        console.log('[WebSocket] Connected')
        isConnected.value = true
        isConnecting.value = false
        reconnectAttempts.value = 0
        error.value = null
      }

      ws.value.onmessage = (event) => {
        try {
          const message: WebSocketMessage = JSON.parse(event.data)
          console.log('[WebSocket] Message received:', message)

          // Broadcast to all handlers
          messageHandlers.forEach(handler => {
            try {
              handler(message)
            } catch (err) {
              console.error('[WebSocket] Handler error:', err)
            }
          })
        } catch (err) {
          console.error('[WebSocket] Failed to parse message:', err)
        }
      }

      ws.value.onerror = (event) => {
        console.error('[WebSocket] Error:', event)
        error.value = 'WebSocket connection error'
        isConnecting.value = false
      }

      ws.value.onclose = (event) => {
        console.log('[WebSocket] Closed:', event.code, event.reason)
        isConnected.value = false
        isConnecting.value = false

        // Attempt reconnect if not a clean close or max attempts not reached
        if (!event.wasClean && reconnectAttempts.value < maxReconnectAttempts) {
          scheduleReconnect()
        } else if (reconnectAttempts.value >= maxReconnectAttempts) {
          error.value = 'Max reconnection attempts reached'
        }
      }
    } catch (err) {
      console.error('[WebSocket] Connection failed:', err)
      error.value = err instanceof Error ? err.message : 'Unknown connection error'
      isConnecting.value = false
      scheduleReconnect()
    }
  }

  function scheduleReconnect() {
    if (reconnectTimeout) {
      clearTimeout(reconnectTimeout)
    }

    reconnectAttempts.value++
    const delay = Math.min(
      baseReconnectDelay * Math.pow(2, reconnectAttempts.value - 1),
      30000 // Max 30 seconds
    )

    console.log(
      `[WebSocket] Reconnecting in ${delay}ms (attempt ${reconnectAttempts.value}/${maxReconnectAttempts})`
    )

    reconnectTimeout = window.setTimeout(() => {
      connect()
    }, delay)
  }

  function disconnect() {
    if (reconnectTimeout) {
      clearTimeout(reconnectTimeout)
      reconnectTimeout = null
    }

    if (ws.value) {
      console.log('[WebSocket] Disconnecting')
      ws.value.close(1000, 'Client disconnect')
      ws.value = null
    }

    isConnected.value = false
    isConnecting.value = false
    reconnectAttempts.value = 0
  }

  function send(data: any) {
    if (!ws.value || !isConnected.value) {
      console.warn('[WebSocket] Cannot send, not connected')
      return false
    }

    try {
      ws.value.send(JSON.stringify(data))
      return true
    } catch (err) {
      console.error('[WebSocket] Send failed:', err)
      return false
    }
  }

  function subscribe(handler: MessageHandler) {
    messageHandlers.add(handler)

    // Return unsubscribe function
    return () => {
      messageHandlers.delete(handler)
    }
  }

  // Auto-connect when authenticated
  watch(
    () => authStore.isAuthenticated,
    (authenticated) => {
      if (authenticated) {
        connect()
      } else {
        disconnect()
      }
    },
    { immediate: true }
  )

  // Cleanup on unmount
  onUnmounted(() => {
    disconnect()
  })

  return {
    isConnected,
    isConnecting,
    status,
    error,
    connect,
    disconnect,
    send,
    subscribe,
  }
}
