import React, {
  useState,
  useEffect,
  useRef,
  createContext,
  useContext,
  useMemo,
  useCallback,
} from 'react';
import { notifications } from '@mantine/notifications';
import useChannelsStore from './store/channels';
import useLogosStore from './store/logos';
import usePlaylistsStore from './store/playlists';
import useEPGsStore from './store/epgs';
import { Box, Button, Stack, Alert, Group } from '@mantine/core';
import API from './api';
import useSettingsStore from './store/settings';
import useAuthStore from './store/auth';

export const WebsocketContext = createContext([false, () => {}, null]);

export const WebsocketProvider = ({ children }) => {
  const [isReady, setIsReady] = useState(false);
  const [val, setVal] = useState(null);
  const ws = useRef(null);
  const reconnectTimerRef = useRef(null);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);
  const [connectionError, setConnectionError] = useState(null);
  const maxReconnectAttempts = 5;
  const initialBackoffDelay = 1000; // 1 second initial delay
  const env_mode = useSettingsStore((s) => s.environment.env_mode);
  const accessToken = useAuthStore((s) => s.accessToken);
  const isAuthenticated = useAuthStore((s) => s.isAuthenticated);

  const epgs = useEPGsStore((s) => s.epgs);
  const updateEPG = useEPGsStore((s) => s.updateEPG);
  const updateEPGProgress = useEPGsStore((s) => s.updateEPGProgress);

  const updatePlaylist = usePlaylistsStore((s) => s.updatePlaylist);

  // Calculate reconnection delay with exponential backoff
  const getReconnectDelay = useCallback(() => {
    return Math.min(
      initialBackoffDelay * Math.pow(1.5, reconnectAttempts),
      30000
    ); // max 30 seconds
  }, [reconnectAttempts]);

  // Clear any existing reconnect timers
  const clearReconnectTimer = useCallback(() => {
    if (reconnectTimerRef.current) {
      clearTimeout(reconnectTimerRef.current);
      reconnectTimerRef.current = null;
    }
  }, []);

  // Function to get WebSocket URL that works with both HTTP and HTTPS
  const getWebSocketUrl = useCallback(() => {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const host = window.location.hostname;
    const appPort = window.location.port;

    // In development mode, connect directly to the WebSocket server on port 8001
    if (env_mode === 'dev') {
      return `${protocol}//${host}:8001/ws/?token=${accessToken}`;
    } else {
      // In production mode, use the same port as the main application
      // This allows nginx to handle the WebSocket forwarding
      return appPort
        ? `${protocol}//${host}:${appPort}/ws/?token=${accessToken}`
        : `${protocol}//${host}/ws/?token=${accessToken}`;
    }
  }, [env_mode, accessToken]);

  // Function to handle websocket connection
  const connectWebSocket = useCallback(() => {
    // Clear any existing timers to avoid multiple reconnection attempts
    clearReconnectTimer();

    // Clear old websocket if exists
    if (ws.current) {
      // Remove event handlers to prevent duplicate events
      ws.current.onclose = null;
      ws.current.onerror = null;
      ws.current.onopen = null;
      ws.current.onmessage = null;

      try {
        ws.current.close();
      } catch (e) {
        console.warn('Error closing existing WebSocket:', e);
      }
    }

    try {
      console.log(
        `Attempting WebSocket connection (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})...`
      );

      // Use the function to get the correct WebSocket URL
      const wsUrl = getWebSocketUrl();
      console.log(`Connecting to WebSocket at: ${wsUrl}`);

      // Create new WebSocket connection
      const socket = new WebSocket(wsUrl);

      socket.onopen = () => {
        console.log('WebSocket connected successfully');
        setIsReady(true);
        setConnectionError(null);
        setReconnectAttempts(0);
      };

      socket.onerror = (error) => {
        console.error('WebSocket connection error:', error);

        // Don't show error notification on initial page load,
        // only show it after a connection was established then lost
        if (reconnectAttempts > 0 || isReady) {
          setConnectionError('Failed to connect to WebSocket server.');
        } else {
          console.log('Initial connection attempt failed, will retry...');
        }
      };

      socket.onclose = (event) => {
        console.warn('WebSocket connection closed', event);
        setIsReady(false);

        // Only attempt reconnect if we haven't reached max attempts
        if (reconnectAttempts < maxReconnectAttempts) {
          const delay = getReconnectDelay();
          setConnectionError(
            `Connection lost. Reconnecting in ${Math.ceil(delay / 1000)} seconds...`
          );
          console.log(
            `Scheduling reconnect in ${delay}ms (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})...`
          );

          // Store timer reference so we can cancel it if needed
          reconnectTimerRef.current = setTimeout(() => {
            setReconnectAttempts((prev) => prev + 1);
            connectWebSocket();
          }, delay);
        } else {
          setConnectionError(
            'Maximum reconnection attempts reached. Please reload the page.'
          );
          console.error(
            'Maximum reconnection attempts reached. WebSocket connection failed.'
          );
        }
      };

      // Message handler
      socket.onmessage = async (event) => {
        try {
          const parsedEvent = JSON.parse(event.data);

          // Handle connection_established event
          if (parsedEvent.type === 'connection_established') {
            console.log(
              'WebSocket connection established:',
              parsedEvent.data?.message
            );
            // Don't need to do anything else for this event type
            return;
          }

          // Handle standard message format for other event types
          switch (parsedEvent.data?.type) {
            case 'comskip_status': {
              const rid = parsedEvent.data.recording_id;
              const id = `comskip-${rid}`;
              const status = parsedEvent.data.status;
              const title = parsedEvent.data.title || 'Recording';
              if (status === 'started') {
                notifications.show({
                  id,
                  title: 'Removing commercials',
                  message: `Processing ${title}...`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (status === 'completed') {
                notifications.update({
                  id,
                  title: 'Commercials removed',
                  message: `${title} — kept ${parsedEvent.data.segments_kept} segments`,
                  color: 'green.5',
                  loading: false,
                  autoClose: 4000,
                });
                try {
                  await useChannelsStore.getState().fetchRecordings();
                } catch {}
              } else if (status === 'skipped') {
                notifications.update({
                  id,
                  title: 'No commercials to remove',
                  message: parsedEvent.data.reason || '',
                  color: 'teal',
                  loading: false,
                  autoClose: 3000,
                });
                try {
                  await useChannelsStore.getState().fetchRecordings();
                } catch {}
              } else if (status === 'error') {
                notifications.update({
                  id,
                  title: 'Comskip failed',
                  message: parsedEvent.data.reason || 'Unknown error',
                  color: 'red',
                  loading: false,
                  autoClose: 6000,
                });
                try {
                  await useChannelsStore.getState().fetchRecordings();
                } catch {}
              }
              break;
            }
            case 'epg_file':
              fetchEPGs();
              notifications.show({
                title: 'EPG File Detected',
                message: `Processing ${parsedEvent.data.filename}`,
              });
              break;

            case 'm3u_file':
              fetchPlaylists();
              notifications.show({
                title: 'M3U File Detected',
                message: `Processing ${parsedEvent.data.filename}`,
              });
              break;

            case 'm3u_refresh':
              // Update the store with progress information
              setRefreshProgress(parsedEvent.data);

              // Update the playlist status whenever we receive a status update
              // Not just when progress is 100% or status is pending_setup
              if (parsedEvent.data.status && parsedEvent.data.account) {
                // Get fresh playlists from store to avoid stale state from React render cycle
                const currentPlaylists = usePlaylistsStore.getState().playlists;
                const isArray = Array.isArray(currentPlaylists);
                const playlist = isArray
                  ? currentPlaylists.find(
                      (p) => p.id === parsedEvent.data.account
                    )
                  : currentPlaylists[parsedEvent.data.account];

                if (playlist) {
                  // When we receive a "success" status with 100% progress, this is a completed refresh
                  // So we should also update the updated_at timestamp
                  const updateData = {
                    ...playlist,
                    status: parsedEvent.data.status,
                    last_message:
                      parsedEvent.data.message || playlist.last_message,
                  };

                  // Update the timestamp when we complete a successful refresh
                  if (
                    parsedEvent.data.status === 'success' &&
                    parsedEvent.data.progress === 100
                  ) {
                    updateData.updated_at = new Date().toISOString();
                    // Log successful completion for debugging
                    console.log(
                      'M3U refresh completed successfully:',
                      updateData
                    );
                    fetchPlaylists(); // Refresh playlists to ensure UI is up-to-date
                    fetchChannelProfiles(); // Ensure channel profiles are updated
                  }

                  updatePlaylist(updateData);
                } else {
                  // Playlist not in store yet - this happens when backend sends websocket
                  // updates immediately after creating the playlist, before the API response
                  // returns. The frontend will receive a 'playlist_created' event shortly
                  // which will trigger a fetchPlaylists() to sync the store.
                  console.log(
                    `Received update for playlist ID ${parsedEvent.data.account} not yet in store. ` +
                      `Waiting for playlist_created event to sync...`
                  );
                }
              }
              break;

            case 'channel_stats':
              setChannelStats(JSON.parse(parsedEvent.data.stats));
              break;

            case 'epg_channels':
              notifications.show({
                message: 'EPG channels updated!',
                color: 'green.5',
              });

              // If source_id is provided, update that specific EPG's status
              if (parsedEvent.data.source_id) {
                const epg = epgs[parsedEvent.data.source_id];
                if (epg) {
                  updateEPG({
                    ...epg,
                    status: 'success',
                  });
                }
              }

              fetchEPGData();
              break;

            case 'epg_match':
              notifications.show({
                message: parsedEvent.data.message || 'EPG match is complete!',
                color: 'green.5',
              });

              // Check if we have associations data and use the more efficient batch API
              if (
                parsedEvent.data.associations &&
                parsedEvent.data.associations.length > 0
              ) {
                API.batchSetEPG(parsedEvent.data.associations);
              }
              break;

            case 'epg_matching_progress': {
              const progress = parsedEvent.data;
              const id = 'epg-matching-progress';

              if (progress.stage === 'starting') {
                notifications.show({
                  id,
                  title: 'EPG Matching in Progress',
                  message: `Starting to match ${progress.total} channels...`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.stage === 'matching') {
                let message = `Matched ${progress.matched} of ${progress.total} channels`;
                if (progress.remaining > 0) {
                  message += ` (${progress.remaining} remaining)`;
                }
                if (progress.current_channel) {
                  message += `\nCurrently processing: ${progress.current_channel}`;
                }

                notifications.update({
                  id,
                  title: 'EPG Matching in Progress',
                  message,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.stage === 'completed') {
                notifications.update({
                  id,
                  title: 'EPG Matching Complete',
                  message: `Successfully matched ${progress.matched} of ${progress.total} channels (${progress.progress_percent}%)`,
                  color: progress.matched > 0 ? 'green.5' : 'orange',
                  loading: false,
                  autoClose: 6000,
                });
              }
              break;
            }

            case 'epg_logo_setting_progress': {
              const progress = parsedEvent.data;
              const id = 'epg-logo-setting-progress';

              if (progress.status === 'running' && progress.progress === 0) {
                // Initial message
                notifications.show({
                  id,
                  title: 'Setting Logos from EPG',
                  message: `Processing ${progress.total} channels...`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.status === 'running') {
                // Progress update
                let message = `Processed ${progress.progress} of ${progress.total} channels`;
                if (progress.updated_count !== undefined) {
                  message += ` (${progress.updated_count} updated)`;
                }
                if (progress.created_logos_count !== undefined) {
                  message += `, created ${progress.created_logos_count} logos`;
                }

                notifications.update({
                  id,
                  title: 'Setting Logos from EPG',
                  message,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.status === 'completed') {
                notifications.update({
                  id,
                  title: 'Logo Setting Complete',
                  message: `Successfully updated ${progress.updated_count || 0} channel logos${progress.created_logos_count ? `, created ${progress.created_logos_count} new logos` : ''}`,
                  color: progress.updated_count > 0 ? 'green.5' : 'orange',
                  loading: false,
                  autoClose: 6000,
                });
                // Refresh channels data and logos
                try {
                  await API.requeryChannels();
                  await useChannelsStore.getState().fetchChannels();

                  // Get updated channel data and extract logo IDs to load
                  const channels = useChannelsStore.getState().channels;
                  const logoIds = Object.values(channels)
                    .filter((channel) => channel.logo_id)
                    .map((channel) => channel.logo_id);

                  // Fetch the specific logos that were just assigned
                  if (logoIds.length > 0) {
                    await useLogosStore.getState().fetchLogosByIds(logoIds);
                  }
                } catch (e) {
                  console.warn(
                    'Failed to refresh channels after logo setting:',
                    e
                  );
                }
              }
              break;
            }

            case 'epg_name_setting_progress': {
              const progress = parsedEvent.data;
              const id = 'epg-name-setting-progress';

              if (progress.status === 'running' && progress.progress === 0) {
                // Initial message
                notifications.show({
                  id,
                  title: 'Setting Names from EPG',
                  message: `Processing ${progress.total} channels...`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.status === 'running') {
                // Progress update
                let message = `Processed ${progress.progress} of ${progress.total} channels`;
                if (progress.updated_count !== undefined) {
                  message += ` (${progress.updated_count} updated)`;
                }

                notifications.update({
                  id,
                  title: 'Setting Names from EPG',
                  message,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (progress.status === 'completed') {
                notifications.update({
                  id,
                  title: 'Name Setting Complete',
                  message: `Successfully updated ${progress.updated_count || 0} channel names from EPG data`,
                  color: progress.updated_count > 0 ? 'green.5' : 'orange',
                  loading: false,
                  autoClose: 6000,
                });
                // Refresh channels data
                try {
                  await API.requeryChannels();
                  await useChannelsStore.getState().fetchChannels();
                } catch (e) {
                  console.warn(
                    'Failed to refresh channels after name setting:',
                    e
                  );
                }
              }
              break;
            }

            case 'm3u_profile_test':
              setProfilePreview(
                parsedEvent.data.search_preview,
                parsedEvent.data.result
              );
              break;

            case 'recording_updated':
              try {
                await useChannelsStore.getState().fetchRecordings();
              } catch (e) {
                console.warn('Failed to refresh recordings on update:', e);
              }
              break;

            case 'recordings_refreshed':
              try {
                await useChannelsStore.getState().fetchRecordings();
              } catch (e) {
                console.warn('Failed to refresh recordings on refreshed:', e);
              }
              break;

            case 'recording_started':
              notifications.show({
                title: 'Recording started!',
                message: `Started recording channel ${parsedEvent.data.channel}`,
              });
              try {
                await useChannelsStore.getState().fetchRecordings();
              } catch (e) {
                console.warn('Failed to refresh recordings on start:', e);
              }
              break;

            case 'recording_ended':
              notifications.show({
                title: 'Recording finished!',
                message: `Stopped recording channel ${parsedEvent.data.channel}`,
              });
              try {
                await useChannelsStore.getState().fetchRecordings();
              } catch (e) {
                console.warn('Failed to refresh recordings on end:', e);
              }
              break;

            case 'epg_fetch_error':
              notifications.show({
                title: 'EPG Source Error',
                message: parsedEvent.data.message,
                color: 'orange.5',
                autoClose: 8000,
              });

              // Update EPG status in store
              if (parsedEvent.data.source_id) {
                const epg = epgs[parsedEvent.data.source_id];
                if (epg) {
                  updateEPG({
                    ...epg,
                    status: 'error',
                    last_message: parsedEvent.data.message,
                  });
                }
              }
              break;

            case 'epg_refresh':
              // If we have source/account info, check if EPG exists before processing
              if (parsedEvent.data.source || parsedEvent.data.account) {
                const sourceId =
                  parsedEvent.data.source || parsedEvent.data.account;
                const epg = epgs[sourceId];

                // Only update progress if the EPG still exists in the store
                // This prevents crashes when receiving updates for deleted EPGs
                if (epg) {
                  // Update the store with progress information
                  updateEPGProgress(parsedEvent.data);
                } else {
                  // EPG was deleted, ignore this update
                  console.debug(
                    `Ignoring EPG refresh update for deleted EPG ${sourceId}`
                  );
                  break;
                }

                if (epg) {
                  // Check for any indication of an error (either via status or error field)
                  const hasError =
                    parsedEvent.data.status === 'error' ||
                    !!parsedEvent.data.error ||
                    (parsedEvent.data.message &&
                      parsedEvent.data.message.toLowerCase().includes('error'));

                  if (hasError) {
                    // Handle error state
                    const errorMessage =
                      parsedEvent.data.error ||
                      parsedEvent.data.message ||
                      'Unknown error occurred';

                    updateEPG({
                      ...epg,
                      status: 'error',
                      last_message: errorMessage,
                    });

                    // Show notification for the error
                    notifications.show({
                      title: 'EPG Refresh Error',
                      message: errorMessage,
                      color: 'red.5',
                    });
                  }
                  // Update status on completion only if no errors
                  else if (parsedEvent.data.progress === 100) {
                    updateEPG({
                      ...epg,
                      status: parsedEvent.data.status || 'success',
                      last_message:
                        parsedEvent.data.message || epg.last_message,
                      // Use the timestamp from the backend if provided
                      ...(parsedEvent.data.updated_at && {
                        updated_at: parsedEvent.data.updated_at,
                      }),
                    });

                    // Only show success notification if we've finished parsing programs and had no errors
                    if (parsedEvent.data.action === 'parsing_programs') {
                      notifications.show({
                        title: 'EPG Processing Complete',
                        message: 'EPG data has been updated successfully',
                        color: 'green.5',
                      });

                      fetchEPGData();
                    }
                  }
                }
              }
              break;

            case 'epg_sources_changed':
              // A plugin or backend process signaled that the EPG sources changed
              try {
                await fetchEPGs();
              } catch (e) {
                console.warn(
                  'Failed to refresh EPG sources after change notification:',
                  e
                );
              }
              break;

            case 'epg_data_created':
              // A new EPG data entry was created (e.g., for a dummy EPG)
              // Fetch EPG data so the channel form can immediately assign it
              try {
                await fetchEPGData();
              } catch (e) {
                console.warn('Failed to refresh EPG data after creation:', e);
              }
              break;

            case 'stream_rehash':
              // Handle stream rehash progress updates
              if (parsedEvent.data.action === 'starting') {
                notifications.show({
                  id: 'stream-rehash-progress', // Persistent ID
                  title: 'Stream Rehash Started',
                  message: parsedEvent.data.message,
                  color: 'blue.5',
                  autoClose: false, // Don't auto-close
                  withCloseButton: false, // No close button during processing
                  loading: true, // Show loading indicator
                });
              } else if (parsedEvent.data.action === 'processing') {
                // Update the existing notification
                notifications.update({
                  id: 'stream-rehash-progress',
                  title: 'Stream Rehash in Progress',
                  message: `${parsedEvent.data.progress}% complete - ${parsedEvent.data.processed} streams processed, ${parsedEvent.data.duplicates_merged} duplicates merged`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (parsedEvent.data.action === 'completed') {
                // Update to completion state
                notifications.update({
                  id: 'stream-rehash-progress',
                  title: 'Stream Rehash Complete',
                  message: `Processed ${parsedEvent.data.total_processed} streams, merged ${parsedEvent.data.duplicates_merged} duplicates. Final count: ${parsedEvent.data.final_count}`,
                  color: 'green.5',
                  autoClose: 8000, // Auto-close after completion
                  withCloseButton: true, // Allow manual close
                  loading: false, // Remove loading indicator
                });
                // Requery streams and channels after rehash completes
                try {
                  await API.requeryChannels();
                  await API.requeryStreams();
                  await useChannelsStore.getState().fetchChannels();
                } catch (error) {
                  console.error(
                    'Error refreshing channels/streams after rehash:',
                    error
                  );
                }
              } else if (parsedEvent.data.action === 'blocked') {
                // Handle blocked rehash attempt
                notifications.show({
                  title: 'Stream Rehash Blocked',
                  message: parsedEvent.data.message,
                  color: 'orange.5',
                  autoClose: 8000,
                });
              }
              break;

            case 'logo_processing_summary':
              notifications.show({
                title: 'Logo Processing Summary',
                message: `${parsedEvent.data.message}`,
                color: 'blue',
                autoClose: 5000,
              });
              fetchLogos();
              break;

            case 'account_info_refresh_success':
              notifications.show({
                title: 'Account Info Refreshed',
                message: `Successfully updated account information for ${parsedEvent.data.profile_name}`,
                color: 'green',
                autoClose: 4000,
              });
              // Trigger refresh of playlists to update the UI
              fetchPlaylists();
              break;

            case 'account_info_refresh_error':
              notifications.show({
                title: 'Account Info Refresh Failed',
                message:
                  parsedEvent.data.error ||
                  'Failed to refresh account information',
                color: 'red',
                autoClose: 8000,
              });
              break;

            case 'channels_created':
              // General notification for channel creation
              notifications.show({
                title: 'Channels Created',
                message: `Successfully created ${parsedEvent.data.count || 'multiple'} channel(s)`,
                color: 'green',
                autoClose: 4000,
              });

              // Refresh the channels table to show new channels
              try {
                await API.requeryChannels();
                await API.requeryStreams();
                await useChannelsStore.getState().fetchChannels();
                await fetchChannelProfiles();
                console.log('Channels refreshed after bulk creation');
              } catch (error) {
                console.error(
                  'Error refreshing channels after creation:',
                  error
                );
              }

              break;

            case 'playlist_created':
              // Backend signals that a new playlist has been created and we should refresh
              console.log(
                'Playlist created event received, refreshing playlists...'
              );
              fetchPlaylists();
              break;

            case 'bulk_channel_creation_progress': {
              // Handle progress updates with persistent notifications like stream rehash
              const data = parsedEvent.data;

              if (data.status === 'starting') {
                notifications.show({
                  id: 'bulk-channel-creation-progress', // Persistent ID
                  title: 'Bulk Channel Creation Started',
                  message: data.message || 'Starting bulk channel creation...',
                  color: 'blue.5',
                  autoClose: false, // Don't auto-close
                  withCloseButton: false, // No close button during processing
                  loading: true, // Show loading indicator
                });
              } else if (
                data.status === 'processing' ||
                data.status === 'creating_logos' ||
                data.status === 'creating_channels'
              ) {
                // Calculate progress percentage
                const progressPercent =
                  data.total > 0
                    ? Math.round((data.progress / data.total) * 100)
                    : 0;

                // Update the existing notification with progress
                notifications.update({
                  id: 'bulk-channel-creation-progress',
                  title: 'Bulk Channel Creation in Progress',
                  message: `${progressPercent}% complete - ${data.message}`,
                  color: 'blue.5',
                  autoClose: false,
                  withCloseButton: false,
                  loading: true,
                });
              } else if (data.status === 'completed') {
                // Hide the progress notification since channels_created will show success
                notifications.hide('bulk-channel-creation-progress');
              } else if (data.status === 'failed') {
                // Update to error state
                notifications.update({
                  id: 'bulk-channel-creation-progress',
                  title: 'Bulk Channel Creation Failed',
                  message:
                    data.error ||
                    'An error occurred during bulk channel creation',
                  color: 'red.5',
                  autoClose: 12000, // Auto-close after longer delay for errors
                  withCloseButton: true, // Allow manual close
                  loading: false, // Remove loading indicator
                });
              }

              // Pass through to individual components for any additional handling
              setVal(parsedEvent);
              break;
            }

            case 'system_notification': {
              // Handle real-time system notifications (version updates, setting recommendations, etc.)
              const notificationData = parsedEvent.data.notification;
              if (notificationData) {
                // Import and update the notifications store
                const { default: useNotificationsStore } =
                  await import('./store/notifications');
                useNotificationsStore
                  .getState()
                  .addNotification(notificationData);

                // Show a toast notification for high priority items
                if (
                  notificationData.priority === 'high' ||
                  notificationData.priority === 'critical'
                ) {
                  const color =
                    notificationData.notification_type === 'version_update'
                      ? 'green'
                      : notificationData.notification_type === 'warning'
                        ? 'orange'
                        : 'blue';

                  notifications.show({
                    title: notificationData.title,
                    message: notificationData.message,
                    color,
                    autoClose: 10000,
                  });
                }
              }
              break;
            }

            case 'notification_dismissed': {
              // Handle notification dismissed from another session
              const { notification_key } = parsedEvent.data;
              if (notification_key) {
                const { default: useNotificationsStore } =
                  await import('./store/notifications');
                useNotificationsStore
                  .getState()
                  .dismissNotification(notification_key);
              }
              break;
            }

            case 'notifications_cleared': {
              // Handle bulk notification clearing (e.g., when version is updated)
              API.getNotifications();
              break;
            }

            default:
              console.error(
                `Unknown websocket event type: ${parsedEvent.data?.type}`
              );
              break;
          }
        } catch (error) {
          console.error(
            'Error processing WebSocket message:',
            error,
            event.data
          );
        }
      };

      ws.current = socket;
    } catch (error) {
      console.error('Error creating WebSocket connection:', error);
      setConnectionError(`WebSocket error: ${error.message}`);

      // Schedule a reconnect if we haven't reached max attempts
      if (reconnectAttempts < maxReconnectAttempts) {
        const delay = getReconnectDelay();
        reconnectTimerRef.current = setTimeout(() => {
          setReconnectAttempts((prev) => prev + 1);
          connectWebSocket();
        }, delay);
      }
    }
  }, [
    reconnectAttempts,
    clearReconnectTimer,
    getReconnectDelay,
    getWebSocketUrl,
    isReady,
  ]);

  // Initial connection and cleanup
  useEffect(() => {
    // Only attempt to connect if the user is authenticated
    if (isAuthenticated && accessToken) {
      connectWebSocket();
    } else if (ws.current) {
      // Close the connection if the user logs out
      clearReconnectTimer();
      console.log('Closing WebSocket connection due to logout');
      ws.current.onclose = null;
      ws.current.close();
      ws.current = null;
      setIsReady(false);
    }

    return () => {
      clearReconnectTimer(); // Clear any pending reconnect timers

      if (ws.current) {
        console.log('Closing WebSocket connection due to component unmount');
        ws.current.onclose = null; // Remove handlers to avoid reconnection
        ws.current.close();
        ws.current = null;
      }
    };
  }, [connectWebSocket, clearReconnectTimer, isAuthenticated, accessToken]);

  const setChannelStats = useChannelsStore((s) => s.setChannelStats);
  const fetchPlaylists = usePlaylistsStore((s) => s.fetchPlaylists);
  const setRefreshProgress = usePlaylistsStore((s) => s.setRefreshProgress);
  const setProfilePreview = usePlaylistsStore((s) => s.setProfilePreview);
  const fetchEPGData = useEPGsStore((s) => s.fetchEPGData);
  const fetchEPGs = useEPGsStore((s) => s.fetchEPGs);
  const fetchLogos = useLogosStore((s) => s.fetchAllLogos);
  const fetchChannelProfiles = useChannelsStore((s) => s.fetchChannelProfiles);

  const ret = useMemo(() => {
    return [isReady, ws.current?.send.bind(ws.current), val];
  }, [isReady, val]);

  return (
    <WebsocketContext.Provider value={ret}>
      {connectionError &&
        !isReady &&
        reconnectAttempts >= maxReconnectAttempts && (
          <Alert
            color="red"
            title="WebSocket Connection Failed"
            style={{
              position: 'fixed',
              bottom: 10,
              right: 10,
              zIndex: 1000,
              maxWidth: 350,
            }}
          >
            {connectionError}
            <Button
              size="xs"
              mt={10}
              onClick={() => {
                setReconnectAttempts(0);
                connectWebSocket();
              }}
            >
              Try Again
            </Button>
          </Alert>
        )}
      {connectionError &&
        !isReady &&
        reconnectAttempts < maxReconnectAttempts &&
        reconnectAttempts > 0 && (
          <Alert
            color="orange"
            title="WebSocket Reconnecting"
            style={{
              position: 'fixed',
              bottom: 10,
              right: 10,
              zIndex: 1000,
              maxWidth: 350,
            }}
          >
            {connectionError}
          </Alert>
        )}
      {children}
    </WebsocketContext.Provider>
  );
};

export const useWebSocket = () => {
  const socket = useContext(WebsocketContext);
  return socket;
};
