/**
 * Composable for managing notifications state across the application
 *
 * This composable provides a shared reactive state for the unread notifications count
 * that can be used across multiple components (dashboard navbar, notifications page, etc.)
 *
 * Features:
 * - Real-time updates via Pusher (WebSocket)
 * - Smart polling fallback (30 seconds when tab is active)
 * - Automatically pauses when tab is inactive (saves battery/bandwidth)
 * - Resumes when tab becomes visible again
 */
import { useApiClient } from './useApiClient'

// Global flag to ensure Pusher listeners are only set up once
let pusherListenersSetup = false
let currentPusherChannel: Awaited<ReturnType<ReturnType<typeof usePusher>['subscribeToUserChannel']>> | null = null
const boundEvents = new Set<string>()

export const useNotifications = () => {
  // Shared state for unread count - persists across component instances
  const unreadCount = useState<number>('notifications:unread-count', () => 0)
  const log = useLogger('Notifications')

  // Set up Pusher listeners for real-time updates (only once globally)
  if (import.meta.client && !pusherListenersSetup) {
    pusherListenersSetup = true

    onMounted(() => {
      const { user } = useAuth()
      const { subscribeToUserChannel, isConnected } = usePusher()
      let pollingStarted = false
      // Track the current subscription attempt to prevent race conditions
      let currentSubscriptionUserId: string | null = null

      watch(user, async (currentUser) => {
        // Unbind old channel listeners if channel changed
        if (currentPusherChannel && currentPusherChannel.name !== `private-user-${currentUser?.id}`) {
          currentPusherChannel.unbind_all()
          currentPusherChannel = null
          boundEvents.clear()
        }

        // Clear subscription tracking if user logged out
        if (!currentUser?.id) {
          currentSubscriptionUserId = null
          return
        }

        const channelName = `private-user-${currentUser.id}`
        // Only subscribe if we don't already have this channel or listeners aren't bound
        if (!currentPusherChannel || currentPusherChannel.name !== channelName || boundEvents.size === 0) {
          // Abort previous subscription attempt if user changed
          currentSubscriptionUserId = currentUser.id

          // Unbind all existing listeners before rebinding (prevent duplicates)
          if (currentPusherChannel && currentPusherChannel.name === channelName) {
            currentPusherChannel.unbind_all()
            boundEvents.clear()
          }

          try {
            const channel = await subscribeToUserChannel(currentUser.id)

            // Check if user changed during async operation (race condition prevention)
            if (currentSubscriptionUserId !== currentUser.id) {
              // User changed, abort this subscription
              if (channel) {
                channel.unbind_all()
              }
              return
            }

            currentPusherChannel = channel
            if (!currentPusherChannel) {
              // Start polling if Pusher is not available
              if (!pollingStarted) {
                startPolling()
                pollingStarted = true
              }
              return
            }

            // Double-check channel name matches current user (prevent stale channel binding)
            if (currentPusherChannel.name !== channelName) {
              currentPusherChannel.unbind_all()
              currentPusherChannel = null
              boundEvents.clear()
              return
            }

            if (!boundEvents.has('pusher:subscription_succeeded')) {
              currentPusherChannel.bind('pusher:subscription_succeeded', () => {
                // Stop polling if Pusher is working
                if (pollingStarted) {
                  pollingStarted = false
                }
              })
              boundEvents.add('pusher:subscription_succeeded')
            }

            if (!boundEvents.has('pusher:subscription_error')) {
              currentPusherChannel.bind('pusher:subscription_error', () => {
                // Start polling as fallback
                if (!pollingStarted) {
                  startPolling()
                  pollingStarted = true
                }
              })
              boundEvents.add('pusher:subscription_error')
            }

            if (!boundEvents.has('notification:unread-count')) {
              currentPusherChannel.bind('notification:unread-count', (data: { count: number }) => {
                unreadCount.value = data.count
              })
              boundEvents.add('notification:unread-count')
            }

            if (!boundEvents.has('notification:new')) {
              currentPusherChannel.bind('notification:new', () => {
                fetchUnreadCount()
              })
              boundEvents.add('notification:new')
            }

            if (!boundEvents.has('notification:read')) {
              currentPusherChannel.bind('notification:read', () => {
                fetchUnreadCount()
              })
              boundEvents.add('notification:read')
            }
          } catch (error) {
            log.error('Failed to subscribe to Pusher channel:', error)
            // Start polling as fallback
            if (!pollingStarted) {
              startPolling()
              pollingStarted = true
            }
          }
        }
      }, { immediate: true })

      // Also check Pusher connection status
      watch(isConnected, (connected) => {
        if (!connected && user.value?.id && !pollingStarted) {
          startPolling()
          pollingStarted = true
        }
      })
    })
  }

  /**
   * Fetch the current unread count from the API
   */
  const fetchUnreadCount = async () => {
    try {
      const { apiCall, extractData } = useApiClient()
      const response = await apiCall<{ count: number }>('/api/user/notifications/unread-count')
      const data = extractData(response)
      unreadCount.value = data.count
      return data.count
    } catch (error) {
      log.error('Failed to fetch unread count:', error)
      return 0
    }
  }

  /**
   * Decrement the unread count (when a notification is marked as read)
   */
  const decrementUnreadCount = () => {
    if (unreadCount.value > 0) {
      unreadCount.value--
    }
  }

  /**
   * Reset the unread count to 0 (when all notifications are marked as read)
   */
  const resetUnreadCount = () => {
    unreadCount.value = 0
  }

  /**
   * Set the unread count to a specific value
   */
  const setUnreadCount = (count: number) => {
    unreadCount.value = Math.max(0, count)
  }

  /**
   * Start smart polling for notification updates
   * - Polls every 30 seconds when tab is active
   * - Automatically pauses when tab is hidden
   * - Resumes when tab becomes visible again
   *
   * @param intervalMs - Polling interval in milliseconds (default: 30000 = 30 seconds)
   */
  const startPolling = (intervalMs: number = 30000) => {
    if (import.meta.client) {
      const { pause, resume } = useIntervalFn(
        () => {
          fetchUnreadCount()
        },
        intervalMs,
        { immediate: false }
      )

      // Use Page Visibility API to pause/resume polling
      useEventListener(document, 'visibilitychange', () => {
        if (document.hidden) {
          pause()
        } else {
          resume()
          // Fetch immediately when tab becomes visible again
          fetchUnreadCount()
        }
      })

      // Start polling if tab is visible
      if (!document.hidden) {
        resume()
        fetchUnreadCount()
      }
    }
  }

  /**
   * Send a notification to a user
   *
   * @param targetUserId - The user ID to send the notification to
   * @param body - The notification message
   * @param url - The URL to navigate to when notification is clicked
   * @param senderName - Optional sender name (defaults to current user)
   * @param senderEmail - Optional sender email (defaults to current user)
   * @param senderAvatar - Optional sender avatar (defaults to current user)
   */
  const sendNotification = async (params: {
    targetUserId: string
    body: string
    url: string
    senderName?: string
    senderEmail?: string
    senderAvatar?: string
  }) => {
    try {
      const { apiCall } = useApiClient()
      await apiCall('/api/user/notifications/send', {
        method: 'POST',
        body: {
          targetUserId: params.targetUserId,
          notificationBody: params.body,
          url: params.url,
          senderName: params.senderName,
          senderEmail: params.senderEmail,
          senderAvatar: params.senderAvatar
        }
      })

      // Increment unread count if sending to self
      // (In real scenarios, this would only happen for testing)
      const { user } = useAuth()
      if (user.value?.id === params.targetUserId) {
        unreadCount.value++
      }

      return { success: true }
    } catch (error) {
      log.error('Failed to send notification:', error)
      throw error
    }
  }

  return {
    unreadCount,
    fetchUnreadCount,
    decrementUnreadCount,
    resetUnreadCount,
    setUnreadCount,
    startPolling,
    sendNotification
  }
}
