import React, { useState, useEffect, useRef, Suspense, lazy, useMemo, useCallback } from "react";
import type Hls from "hls.js";
import "@styles/player.css";
import "@/components/TRip/RequestManagement.css";
import { metaData } from "../config";
import Play from "@mui/icons-material/PlayArrow";
import Pause from "@mui/icons-material/Pause";
import { Dialog } from "primereact/dialog";
import { AutoComplete } from "primereact/autocomplete";
import { DataTable } from "primereact/datatable";
import { Column } from "primereact/column";
import { toast } from "react-toastify";
import { ENVIRONMENT } from "../config";
import { API_URL } from "@/config";
import { authFetch } from "@/utils/authFetch";
import { requireAuthHook } from "@/hooks/requireAuthHook";
import { useHtmlThemeAttr } from "@/hooks/useHtmlThemeAttr";
import { useAutoCompleteScrollFix } from "@/hooks/useAutoCompleteScrollFix";

interface LyricLine {
  timestamp: number;
  line: string;
}

interface PlayerProps {
  user?: {
    id?: string;
    username?: string;
    roles?: string[];
    [key: string]: unknown;
  };
}


const STATIONS = {
  main: { label: "Main" },
  rock: { label: "Rock" },
  rap: { label: "Rap" },
  electronic: { label: "Electronic" },
  pop: { label: "Pop" },
};

// Marquee component for long text that scrolls on mobile
function MarqueeText({ text, className = "" }: { text: string; className?: string }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const textRef = useRef<HTMLSpanElement>(null);
  const [shouldScroll, setShouldScroll] = useState(false);

  useEffect(() => {
    const checkOverflow = () => {
      if (containerRef.current && textRef.current) {
        const containerWidth = containerRef.current.offsetWidth;
        const textWidth = textRef.current.scrollWidth;
        setShouldScroll(textWidth > containerWidth);
      }
    };

    checkOverflow();
    window.addEventListener('resize', checkOverflow);
    return () => window.removeEventListener('resize', checkOverflow);
  }, [text]);

  return (
    <div ref={containerRef} className={`marquee-wrapper ${className}`}>
      <span
        ref={textRef}
        className={`marquee-content ${shouldScroll ? 'scrolling' : ''}`}
        data-text={shouldScroll ? text : ''}
      >
        {text}
      </span>
    </div>
  );
}


export default function Player({ user }: PlayerProps) {
  // Global CSS now contains the paginator / dialog datatable dark rules.

  const [isQueueVisible, setQueueVisible] = useState(false);
  const lrcContainerRef = useRef<HTMLDivElement | null>(null);
  const lrcWheelHandlerRef = useRef<((e: WheelEvent) => void) | null>(null);
  const lrcScrollTimeout = useRef<number | null>(null);
  const lrcScrollRaf = useRef<number | null>(null);
  const lrcActiveRef = useRef<HTMLElement | null>(null);
  // Mouse wheel scroll fix for queue modal
  useEffect(() => {
    if (!isQueueVisible) return;

    const setupScrollHandlers = () => {
      // Target multiple possible scroll containers
      const selectors = [
        '.p-dialog .p-dialog-content',
        '.p-dialog .p-datatable-wrapper',
        '.p-dialog .p-datatable-scrollable-body',
        '.p-dialog .p-datatable-tbody'
      ];

      const elements: Element[] = selectors
        .map(selector => document.querySelector(selector))
        .filter((el): el is Element => el !== null);

      // Also allow scrolling on the entire dialog
      const dialog = document.querySelector('.p-dialog');
      if (dialog) elements.push(dialog);

      elements.forEach(element => {
        (element as HTMLElement).style.overflowY = 'auto';
        (element as HTMLElement).style.overscrollBehavior = 'contain';

        const wheelHandler: EventListener = (e) => {
          e.stopPropagation();
          // Allow normal scrolling within the modal
        };

        element.removeEventListener('wheel', wheelHandler);
        element.addEventListener('wheel', wheelHandler, { passive: true });
      });
    };

    // Use multiple timeouts to ensure elements are rendered
    setTimeout(setupScrollHandlers, 0);
    setTimeout(setupScrollHandlers, 100);
    setTimeout(setupScrollHandlers, 300);
  }, [isQueueVisible]);
  // Debugging output
  // Autocomplete for requests
  const fetchTypeahead = async (query) => {
    if (!query || query.length < 2) {
      setTypeaheadOptions([]);
      return;
    }
    try {
      const response = await authFetch(`${API_URL}/radio/typeahead`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ station: activeStation, query }),
      });
      const data = await response.json();
      // Accept bare array or { options: [...] }
      setTypeaheadOptions(Array.isArray(data) ? data : data.options || []);
    } catch (error) {
      console.error('Typeahead: error', error);
      setTypeaheadOptions([]);
    }
  };
  const theme = useHtmlThemeAttr();

  const [activeStation, setActiveStation] = useState("main");
  const [isPlaying, setIsPlaying] = useState(false);
  const [trackTitle, setTrackTitle] = useState("");
  const [trackArtist, setTrackArtist] = useState("");
  const [trackGenre, setTrackGenre] = useState("");
  const [trackAlbum, setTrackAlbum] = useState("");
  const [nextTrack, setNextTrack] = useState<{ artist: string; song: string } | null>(null);
  const [coverArt, setCoverArt] = useState("/images/radio_art_default.jpg");
  const [lyrics, setLyrics] = useState<LyricLine[]>([]);
  const [currentLyricIndex, setCurrentLyricIndex] = useState(0);
  const [elapsedTime, setElapsedTime] = useState(0);
  // Update currentLyricIndex as song progresses
  useEffect(() => {
    if (!lyrics || lyrics.length === 0) return;
    // Find the last lyric whose timestamp is <= elapsedTime
    let idx = 0;
    for (let i = 0; i < lyrics.length; i++) {
      if (lyrics[i].timestamp <= elapsedTime) {
        idx = i;
      } else {
        break;
      }
    }
    setCurrentLyricIndex(idx);
  }, [elapsedTime, lyrics]);
  const [trackDuration, setTrackDuration] = useState(0);
  const [queueData, setQueueData] = useState([]);
  const [queueTotalRecords, setQueueTotalRecords] = useState(0);
  const [queuePage, setQueuePage] = useState(0);
  const [queueRows, setQueueRows] = useState(20);
  
  // Refs to avoid stale closure issues in callbacks
  const queuePageRef = useRef(queuePage);
  const queueRowsRef = useRef(queueRows);

  // DJ controls state
  const [requestInput, setRequestInput] = useState("");
  const [typeaheadOptions, setTypeaheadOptions] = useState([]);
  const [requestInputArtist, setRequestInputArtist] = useState("");
  const [requestInputSong, setRequestInputSong] = useState("");
  const [requestInputUuid, setRequestInputUuid] = useState("");
  const { attachScrollFix: handleTypeaheadShow, cleanupScrollFix: handleTypeaheadHide } = useAutoCompleteScrollFix();

  const audioElement = useRef<HTMLAudioElement | null>(null);
  const hlsInstance = useRef<Hls | null>(null);
  const currentTrackUuid = useRef<string | null>(null);
  const baseTrackElapsed = useRef<number>(0);
  const lastUpdateTimestamp = useRef<number>(Date.now());
  const activeStationRef = useRef<string>(activeStation);
  const wsInstance = useRef<WebSocket | null>(null);
  const wsReconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const wsConnectionCheckTimer = useRef<ReturnType<typeof setInterval> | null>(null);
  const wsLastMessageTime = useRef<number>(Date.now());
  const [isStreamReady, setIsStreamReady] = useState(false);
  
  // Quality selection state
  const [qualityLevels, setQualityLevels] = useState<{index: number, bitrate: number, name: string, isLossless?: boolean}[]>([]);
  const [selectedQuality, setSelectedQuality] = useState<number>(-1); // -1 = auto
  const [currentBitrate, setCurrentBitrate] = useState<number | null>(null); // Stream's declared bitrate
  const [liveDownloadRate, setLiveDownloadRate] = useState<number | null>(null); // Actual download speed
  const [actualPlayingLevel, setActualPlayingLevel] = useState<number>(-1); // Actual HLS.js level being played

  const formatTime = (seconds: number): string => {
    if (!seconds || isNaN(seconds) || seconds < 0) return "00:00";
    const mins = String(Math.floor(seconds / 60)).padStart(2, "0");
    const secs = String(Math.floor(seconds % 60)).padStart(2, "0");
    return `${mins}:${secs}`;
  };

  // Change stream quality
  const handleQualityChange = (levelIndex: number) => {
    setSelectedQuality(levelIndex);
    localStorage.setItem('radioQuality', levelIndex.toString());
    if (hlsInstance.current) {
      hlsInstance.current.currentLevel = levelIndex; // -1 = auto
    }
  };

  // Format live bitrate for display (shows actual download rate)
  const formatBitrate = (rawBitrate: number): string => {
    const kbps = rawBitrate / 1000;
    if (kbps >= 1000) {
      return `${(kbps / 1000).toFixed(1)} Mbps`;
    }
    return `${Math.round(kbps)} kbps`;
  };

  // Set page title based on current track and station
  const setPageTitle = (artist, song) => {
    document.title = `${metaData.title} - Radio - ${artist} - ${song} [${activeStationRef.current}]`;
  };

  // Initialize or switch HLS stream
  const initializeStream = (station) => {
    setIsStreamReady(false);
    
    const audio = audioElement.current;
    if (!audio) return;
    
    // Clean up previous HLS instance first (synchronous but quick)
    if (hlsInstance.current) {
      hlsInstance.current.destroy();
      hlsInstance.current = null;
    }
    
    // Pause and reset audio element
    audio.pause();
    audio.removeAttribute("src");
    
    // Defer the blocking audio.load() call
    setTimeout(() => {
      audio.load();
    }, 0);

    // Handle audio load errors
    audio.onerror = () => {
      setIsPlaying(false);
      setIsStreamReady(false);
    };

    const streamUrl = `https://stream.codey.horse/hls/${station}/${station}.m3u8`;

    // Check for native HLS support (Safari)
    if (audio.canPlayType("application/vnd.apple.mpegurl")) {
      // Use setTimeout to defer the blocking operations
      setTimeout(() => {
        audio.src = streamUrl;
        audio.load();
        setIsStreamReady(true);
        audio.play().then(() => setIsPlaying(true)).catch(() => {
          setTrackTitle("Offline");
          setIsPlaying(false);
        });
      }, 0);
      return;
    }

    // Dynamic import HLS.js for non-Safari browsers
    import('hls.js').then(({ default: Hls }) => {
      // Double-check audio element still exists after async import
      if (!audioElement.current) return;
      
      if (!Hls.isSupported()) {
        console.error("HLS not supported");
        return;
      }

      const hls = new Hls({
        lowLatencyMode: false,
        liveSyncDuration: 0.6,
        liveMaxLatencyDuration: 3.0,
      } as Partial<typeof Hls.DefaultConfig>);

      hlsInstance.current = hls;
      hls.attachMedia(audio);
      hls.on(Hls.Events.MEDIA_ATTACHED, () => hls.loadSource(streamUrl));
      hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {
        // Capture available quality levels (use friendly bitrate names)
        const standards = [64, 96, 128, 160, 192, 256, 320];
        const levels = data.levels.map((level, index) => {
          const kbps = (level.bitrate || 0) / 1000;
          const codec = level.audioCodec || level.codecs || '';
          
          // Check if this is a lossless codec
          const isLossless = codec.toLowerCase().includes('flac') || 
                             codec.toLowerCase().includes('alac') ||
                             kbps > 500; // Likely lossless if >500kbps
          
          let name: string;
          if (isLossless) {
            name = 'Lossless';
          } else if (level.bitrate) {
            const friendly = standards.reduce((prev, curr) => 
              Math.abs(curr - kbps) < Math.abs(prev - kbps) ? curr : prev
            );
            name = `${friendly} kbps`;
          } else {
            name = `Level ${index + 1}`;
          }
          
          return {
            index,
            bitrate: level.bitrate || 0,
            name,
            isLossless,
          };
        });
        
        // Sort: lossless first, then by bitrate descending
        levels.sort((a, b) => {
          if (a.isLossless && !b.isLossless) return -1;
          if (!a.isLossless && b.isLossless) return 1;
          return b.bitrate - a.bitrate;
        });
        
        setQualityLevels(levels);
        console.log('HLS quality levels:', levels, data.levels);  // Debug log
        
        // Apply saved quality preference, or default to Lossless if network supports it
        const savedQuality = localStorage.getItem('radioQuality');
        if (savedQuality !== null) {
          const qualityIndex = parseInt(savedQuality, 10);
          hls.currentLevel = qualityIndex;
          setSelectedQuality(qualityIndex);
        } else {
          // Check if network can support lossless (~2 Mbps needed)
          const connection = (navigator as any).connection;
          const downlink = connection?.downlink; // Mbps
          const effectiveType = connection?.effectiveType; // '4g', '3g', etc.
          
          // Default to Lossless only if:
          // - downlink >= 2 Mbps, OR
          // - effectiveType is '4g' and no downlink info, OR
          // - no connection info available (assume good connection)
          const canSupportLossless = !connection || 
            (downlink && downlink >= 2) || 
            (!downlink && effectiveType === '4g');
          
          const losslessLevel = levels.find(l => l.isLossless);
          if (losslessLevel && canSupportLossless) {
            hls.currentLevel = losslessLevel.index;
            setSelectedQuality(losslessLevel.index);
          } else {
            setSelectedQuality(-1); // Auto - let HLS.js pick based on bandwidth
          }
        }
        
        setIsStreamReady(true);
        audio.play().then(() => setIsPlaying(true)).catch(() => {
          setIsPlaying(false);
        });
      });
      
      // Track level switches - update current bitrate and actual playing level
      hls.on(Hls.Events.LEVEL_SWITCHED, (_event, data) => {
        const level = hls.levels[data.level];
        if (level?.bitrate) {
          setCurrentBitrate(level.bitrate);
        }
        setActualPlayingLevel(data.level);
      });
      
      // Track actual live download bandwidth from fragment downloads
      hls.on(Hls.Events.FRAG_LOADED, (_event, data) => {
        const stats = data.frag.stats;
        if (stats && stats.total && stats.loading) {
          const downloadTime = stats.loading.end - stats.loading.start;
          if (downloadTime > 0) {
            const liveBps = (stats.total * 8 * 1000) / downloadTime;
            setLiveDownloadRate(liveBps);
          }
        }
      });
      
      let mediaRecoveryAttempts = 0;
      const MAX_RECOVERY_ATTEMPTS = 3;

      hls.on(Hls.Events.ERROR, (event, data) => {
        console.warn("HLS error:", data);
        if (data.fatal) {
          switch (data.type) {
            case Hls.ErrorTypes.MEDIA_ERROR:
              // bufferAppendError and other media errors are recoverable
              if (mediaRecoveryAttempts < MAX_RECOVERY_ATTEMPTS) {
                mediaRecoveryAttempts++;
                console.log(`Attempting media error recovery (attempt ${mediaRecoveryAttempts})`);
                if (mediaRecoveryAttempts === 1) {
                  hls.recoverMediaError();
                } else {
                  // On subsequent attempts, try swapping audio codec
                  hls.swapAudioCodec();
                  hls.recoverMediaError();
                }
              } else {
                console.error("Media error recovery failed after max attempts");
                hls.destroy();
                hlsInstance.current = null;
                setTrackTitle("Offline");
                setIsPlaying(false);
                setIsStreamReady(false);
              }
              break;
            case Hls.ErrorTypes.NETWORK_ERROR:
              // Network errors - try to recover by restarting the load
              console.log("Network error, attempting to recover...");
              hls.startLoad();
              break;
            default:
              // Unrecoverable error
              hls.destroy();
              hlsInstance.current = null;
              setTrackTitle("Offline");
              setIsPlaying(false);
              setIsStreamReady(false);
              break;
          }
        }
      });
    });
  };

  // Update elapsed time smoothly
  useEffect(() => {
    const intervalId = setInterval(() => {
      const now = Date.now();
      const deltaSec = (now - lastUpdateTimestamp.current) / 1000;
      let liveElapsed = (typeof baseTrackElapsed.current === 'number' && !isNaN(baseTrackElapsed.current) ? baseTrackElapsed.current : 0) + deltaSec;
      if (trackDuration && liveElapsed > trackDuration) liveElapsed = trackDuration;
      setElapsedTime(liveElapsed);
    }, 200);
    return () => clearInterval(intervalId);
  }, [isPlaying, trackDuration]);



  // Scroll active lyric into view
  useEffect(() => {
    const container = lrcContainerRef.current;
    if (!container || lyrics.length === 0) return;

    const scheduleScroll = () => {
      // Read ref/DOM inside the callback so we get the updated element after render
      const activeElement = lrcActiveRef.current || (container.querySelector('.lrc-line.active') as HTMLElement | null);
      if (!activeElement) return;
      
      // Use getBoundingClientRect for accurate positioning
      const containerRect = container.getBoundingClientRect();
      const activeRect = activeElement.getBoundingClientRect();
      
      // Calculate where the element is relative to the container's current scroll
      const elementTopInContainer = activeRect.top - containerRect.top + container.scrollTop;
      
      // Center the active line in the container
      const targetScrollTop = elementTopInContainer - (container.clientHeight / 2) + (activeElement.offsetHeight / 2);
      
      container.scrollTo({ top: Math.max(targetScrollTop, 0), behavior: 'smooth' });
    };

    // Debounce a tick then align to paint frame so ref is updated after render
    if (lrcScrollTimeout.current) window.clearTimeout(lrcScrollTimeout.current);
    lrcScrollTimeout.current = window.setTimeout(() => {
      if (lrcScrollRaf.current) cancelAnimationFrame(lrcScrollRaf.current);
      lrcScrollRaf.current = requestAnimationFrame(scheduleScroll);
    }, 16);

    const wheelHandler = (e: WheelEvent) => {
      const atTop = container.scrollTop === 0;
      const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight;
      if ((e.deltaY < 0 && atTop) || (e.deltaY > 0 && atBottom)) {
        e.preventDefault();
      } else {
        e.stopPropagation();
      }
    };

    if (lrcWheelHandlerRef.current) {
      container.removeEventListener('wheel', lrcWheelHandlerRef.current as EventListener);
    }
    lrcWheelHandlerRef.current = wheelHandler;
    container.addEventListener('wheel', wheelHandler, { passive: false });

    return () => {
      if (lrcScrollTimeout.current) window.clearTimeout(lrcScrollTimeout.current);
      if (lrcScrollRaf.current) cancelAnimationFrame(lrcScrollRaf.current);
      container.removeEventListener('wheel', wheelHandler as EventListener);
    };
  }, [currentLyricIndex, lyrics.length]);

  // Handle station changes: reset and start new stream
  useEffect(() => {
    // Batch all state resets to minimize re-renders
    // Use startTransition to mark this as a non-urgent update
    const resetState = () => {
      setTrackTitle("");
      setTrackArtist("");
      setTrackGenre("");
      setTrackAlbum("");
      setCoverArt("/images/radio_art_default.jpg");
      setLyrics([]);
      setCurrentLyricIndex(0);
      setElapsedTime(0);
      setTrackDuration(0);
    };

    // Reset refs
    currentTrackUuid.current = null;
    baseTrackElapsed.current = 0;
    lastUpdateTimestamp.current = Date.now();

    // Use requestAnimationFrame to defer state updates and stream init
    // This allows the UI to update the station button immediately
    requestAnimationFrame(() => {
      resetState();
      // Defer stream initialization to next frame to keep UI responsive
      requestAnimationFrame(() => {
        initializeStream(activeStation);
      });
    });

    // Update page title to reflect the new station
    document.title = `${metaData.title} - Radio [${activeStation}]`;
    
    // Cleanup on unmount or station change
    return () => {
      // Destroy HLS instance to stop network requests
      if (hlsInstance.current) {
        hlsInstance.current.destroy();
        hlsInstance.current = null;
      }
      // Stop and clear audio element
      if (audioElement.current) {
        audioElement.current.pause();
        audioElement.current.removeAttribute('src');
        audioElement.current.load();
      }
    };
  }, [activeStation]);

  const parseLrcString = useCallback((lrcString: string | undefined | null): LyricLine[] => {
    if (!lrcString || typeof lrcString !== 'string') return [];

    const lines = lrcString.split('\n').filter(line => line.trim());
    const parsedLyrics: LyricLine[] = [];

    for (const line of lines) {
      const match = line.match(/\[(\d{2}):(\d{2}\.\d{2})\]\s*(.+)/);
      if (match) {
        const [, mins, secs, text] = match;
        const timestamp = Number(mins) * 60 + parseFloat(secs);
        if (!isNaN(timestamp) && text.trim()) {
          parsedLyrics.push({ timestamp, line: text.trim() });
        }
      }
    }

    return parsedLyrics.sort((a, b) => a.timestamp - b.timestamp);
  }, []);

  const handleTrackData = useCallback((trackData) => {
    const requestStation = activeStationRef.current;

    // Guard: if trackData is null/undefined or empty object, ignore
    if (!trackData || (typeof trackData === 'object' && Object.keys(trackData).length === 0)) {
      return;
    }

    // Only set "No track playing" if we have clear indication of no track
    // AND we don't already have a valid track playing (to prevent race conditions)
    if ((!trackData.song || trackData.song === 'N/A') && (!trackData.artist || trackData.artist === 'N/A')) {
      // Only clear if we don't have a current track UUID (meaning we never had valid data)
      if (!currentTrackUuid.current) {
        setTrackTitle('No track playing');
        setLyrics([]);
      }
      // Otherwise ignore this empty data - keep showing current track
      return;
    }

    if (trackData.uuid !== currentTrackUuid.current) {
      currentTrackUuid.current = trackData.uuid;

      setTrackMetadata(trackData, requestStation);

      // Clear lyrics immediately when new track is detected
      setLyrics([]);
      setCurrentLyricIndex(0);

      // Refresh queue and next track when track changes
      fetchQueue();
      fetchNextTrack();
    } else {
      // Same track - update duration and elapsed time
      setTrackDuration(trackData.duration || 0);
      if (trackData.elapsed !== undefined && typeof trackData.elapsed === 'number' && !isNaN(trackData.elapsed)) {
        baseTrackElapsed.current = trackData.elapsed;
        lastUpdateTimestamp.current = Date.now();
        setElapsedTime(trackData.elapsed);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const initializeWebSocket = useCallback((station) => {
    // Clean up existing WebSocket and timers
    if (wsReconnectTimer.current) {
      clearTimeout(wsReconnectTimer.current);
      wsReconnectTimer.current = null;
    }
    if (wsConnectionCheckTimer.current) {
      clearInterval(wsConnectionCheckTimer.current);
      wsConnectionCheckTimer.current = null;
    }
    if (wsInstance.current) {
      wsInstance.current.onclose = null; // Prevent triggering reconnection logic
      wsInstance.current.onerror = null;
      wsInstance.current.close();
      wsInstance.current = null;
    }

    let retryCount = 0;
    const baseDelay = 1000; // 1 second
    const maxDelay = 30000; // 30 seconds
    let isIntentionallyClosed = false;

    const connectWebSocket = () => {
      if (isIntentionallyClosed) return;

      const wsUrl = `${API_URL.replace(/^https?:/, 'wss:')}/radio/ws/${station}`;
      const ws = new WebSocket(wsUrl);

      ws.onopen = function () {
        // Reset retry count on successful connection
        retryCount = 0;
        wsLastMessageTime.current = Date.now();
        
        // Start periodic connection check - if no messages received for 2 minutes,
        // the connection is likely dead (server sends track updates regularly)
        if (wsConnectionCheckTimer.current) {
          clearInterval(wsConnectionCheckTimer.current);
        }
        wsConnectionCheckTimer.current = setInterval(() => {
          const timeSinceLastMessage = Date.now() - wsLastMessageTime.current;
          const isStale = timeSinceLastMessage > 120000; // 2 minutes without any message
          
          // Check if connection appears dead
          if (ws.readyState !== WebSocket.OPEN || isStale) {
            console.warn('WebSocket connection appears stale, reconnecting...');
            // Force close and let onclose handle reconnection
            ws.close();
          }
        }, 30000); // Check every 30 seconds
      };

      ws.onmessage = function (event) {
        // Track last message time for connection health monitoring
        wsLastMessageTime.current = Date.now();
        
        try {
          const data = JSON.parse(event.data);
          
          // Ignore pong responses and other control messages
          if (data.type === 'pong' || data.type === 'ping' || data.type === 'error' || data.type === 'status') return;

          if (data.type === 'track_change') {
            // Handle track change
            setLyrics([]);
            setCurrentLyricIndex(0);
            handleTrackData(data.data);
          } else if (data.type === 'lrc') {
            // Handle LRC data
            const parsedLyrics = parseLrcString(data.data);
            setLyrics(parsedLyrics);
            setCurrentLyricIndex(0);
          } else if (data.type === 'now_playing' || data.type === 'initial') {
            // Explicit now playing message
            handleTrackData(data.data || data);
          } else if (!data.type && (data.song || data.artist || data.uuid)) {
            // Untyped message with track data fields - treat as now playing
            handleTrackData(data);
          }
          // Ignore any other message types that don't contain track data
        } catch (error) {
          console.error('Error parsing WebSocket message:', error);
        }
      };

      ws.onclose = function (event) {
        // Clear connection check timer
        if (wsConnectionCheckTimer.current) {
          clearInterval(wsConnectionCheckTimer.current);
          wsConnectionCheckTimer.current = null;
        }

        // Always attempt reconnection unless intentionally closed
        if (isIntentionallyClosed) return;

        // Exponential backoff for reconnection
        const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);
        retryCount++;

        wsReconnectTimer.current = setTimeout(() => {
          connectWebSocket();
        }, delay);
      };

      ws.onerror = function (error) {
        console.error('Radio WebSocket error:', error);
        // Don't set error state here - let onclose handle reconnection
      };

      wsInstance.current = ws;
    };

    connectWebSocket();

    // Return cleanup function
    return () => {
      isIntentionallyClosed = true;
      if (wsReconnectTimer.current) {
        clearTimeout(wsReconnectTimer.current);
        wsReconnectTimer.current = null;
      }
      if (wsConnectionCheckTimer.current) {
        clearInterval(wsConnectionCheckTimer.current);
        wsConnectionCheckTimer.current = null;
      }
      if (wsInstance.current) {
        wsInstance.current.onclose = null;
        wsInstance.current.onerror = null;
        wsInstance.current.close();
        wsInstance.current = null;
      }
    };
  }, [handleTrackData, parseLrcString]);

  const setTrackMetadata = useCallback((trackData, requestStation) => {
    setTrackTitle(trackData.song || 'Unknown Title');
    setTrackArtist(trackData.artist || 'Unknown Artist');
    setTrackGenre(trackData.genre || '');
    setTrackAlbum(trackData.album || 'Unknown Album');
    setCoverArt(`${API_URL}/radio/album_art?station=${requestStation}&_=${Date.now()}`);

    const elapsed = typeof trackData.elapsed === 'number' && !isNaN(trackData.elapsed) ? trackData.elapsed : 0;
    baseTrackElapsed.current = elapsed;
    lastUpdateTimestamp.current = Date.now();
    setElapsedTime(elapsed);

    setTrackDuration(trackData.duration || 0);
    setPageTitle(trackData.artist, trackData.song);
  }, []);

  useEffect(() => {
    // Ensure the ref points to the current activeStation for in-flight guards
    activeStationRef.current = activeStation;

    // Initialize WebSocket connection for metadata
    const cleanupWs = initializeWebSocket(activeStation);

    // Handle visibility change - reconnect when page becomes visible
    const handleVisibilityChange = () => {
      if (document.visibilityState === 'visible') {
        // Check if WebSocket is not connected or not open
        if (!wsInstance.current || wsInstance.current.readyState !== WebSocket.OPEN) {
          // Reinitialize WebSocket connection
          if (cleanupWs) cleanupWs();
          initializeWebSocket(activeStation);
        }
      }
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
      if (cleanupWs) cleanupWs();
    };
  }, [activeStation, initializeWebSocket]);

  // Cleanup WebSocket on component unmount
  useEffect(() => {
    return () => {
      if (wsReconnectTimer.current) {
        clearTimeout(wsReconnectTimer.current);
        wsReconnectTimer.current = null;
      }
      if (wsConnectionCheckTimer.current) {
        clearInterval(wsConnectionCheckTimer.current);
        wsConnectionCheckTimer.current = null;
      }
      if (wsInstance.current) {
        wsInstance.current.onclose = null;
        wsInstance.current.onerror = null;
        wsInstance.current.close();
        wsInstance.current = null;
      }
    };
  }, []);

  const progress = trackDuration > 0 ? (elapsedTime / trackDuration) * 100 : 0;
  const remaining = trackDuration - elapsedTime;

  const progressColorClass =
    progress >= 90
      ? "bg-red-500 dark:bg-red-400"
      : progress >= 75 || remaining <= 20
        ? "bg-yellow-400 dark:bg-yellow-300"
        : "bg-blue-500 dark:bg-blue-400";



  const handleSkip = async (uuid = null) => {
    try {
      const response = await authFetch(`${API_URL}/radio/skip`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          skipTo: uuid,
          station: activeStation,
        }),
      });
      const data = await response.json();
      if (data.success) {
        toast.success("OK!");
        fetchQueue();
      } else {
        toast.error("Skip failed.");
      }
    } catch (error) {
      console.error("Error skipping track:", error);
      toast.error("Skip failed.");
    }
  };

  const handleReshuffle = async () => {
    try {
      const response = await authFetch(`${API_URL}/radio/reshuffle`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          station: activeStation,
        }),
      });
      const data = await response.json();
      if (data.ok) {
        toast.success("OK!");
        fetchQueue();
      } else {
        toast.error("Reshuffle failed.");
      }
    } catch (error) {
      console.error("Error reshuffling queue:", error);
      toast.error("Reshuffle failed.");
    }
  };

  const handleQueueShift = async (uuid, next = false) => {
    try {
      const response = await authFetch(`${API_URL}/radio/queue_shift`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          uuid,
          next,
          station: activeStation,
        }),
      });
      const data = await response.json();
      if (!data.err) {
        toast.success("OK!");
        fetchQueue();
      } else {
        toast.error("Queue shift failed.");
      }
    } catch (error) {
      console.error("Error shifting queue:", error);
      toast.error("Queue shift failed.");
    }
  };

  const handlePlayNow = async (artistSong, next = false) => {
    const toastId = "playNowToast"; // Unique ID for this toast
    if (!toast.isActive(toastId)) {
      toast.info("Trying...", { toastId });
    }
    try {
      const response = await authFetch(`${API_URL}/radio/request`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          artistsong: artistSong,
          alsoSkip: !next,
          station: activeStation,
        }),
      });
      const data = await response.json();
      if (data.result) {
        setRequestInput(""); // Clear the input immediately
        toast.update(toastId, { render: "OK!", type: "success", autoClose: 2000 });
        fetchQueue();
      } else {
        toast.update(toastId, { render: "Play Now failed.", type: "error", autoClose: 3000 });
      }
    } catch (error) {
      console.error("Error playing song immediately:", error);
      toast.update(toastId, { render: "Play Now failed.", type: "error", autoClose: 3000 });
    }
  };

  const handleSongRequest = async (artistSong) => {
    const toastId = "songRequestToast"; // Unique ID for this toast
    if (!toast.isActive(toastId)) {
      toast.info("Trying...", { toastId });
    }
    try {
      const response = await authFetch(`${API_URL}/radio/request`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          artistsong: artistSong,
          alsoSkip: false, // Ensure alsoSkip is false for requests
          station: activeStation,
        }),
      });
      const data = await response.json();
      if (data.result) {
        setRequestInput(""); // Clear the input immediately
        toast.update(toastId, { render: "OK!", type: "success", autoClose: 2000 });
        fetchQueue();
      } else {
        toast.update(toastId, { render: "Song request failed.", type: "error", autoClose: 3000 });
      }
    } catch (error) {
      console.error("Error requesting song:", error);
      toast.update(toastId, { render: "Song request failed.", type: "error", autoClose: 3000 });
    }
  };

  const handleRemoveFromQueue = async (uuid) => {
    try {
      const response = await authFetch(`${API_URL}/radio/queue_remove`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          station: activeStation,
          uuid,
        }),
      });
      const data = await response.json();
      if (!data.err) {
        toast.success("Removed!");
        fetchQueue();
        fetchNextTrack();
      } else {
        toast.error("Remove from queue failed.");
      }
    } catch (error) {
      console.error("Error removing from queue:", error);
      toast.error("Remove from queue failed.");
    }
  };

  // Reorder a queue item to a new position
  const handleQueueReorder = async (uuid: string, newPosition: number) => {
    try {
      const response = await authFetch(`${API_URL}/radio/queue_reorder`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          station: activeStation,
          uuid,
          newPosition,
        }),
      });
      const data = await response.json();
      if (!data.err) {
        fetchQueue();
        fetchNextTrack();
      } else {
        toast.error("Reorder failed.");
        fetchQueue(); // Refresh to get correct state
      }
    } catch (error) {
      console.error("Error reordering queue:", error);
      toast.error("Reorder failed.");
      fetchQueue();
    }
  };

  // Move queue item up one position
  const handleMoveUp = (uuid: string, currentPos: number) => {
    if (currentPos <= 0) return;
    handleQueueReorder(uuid, currentPos - 1);
  };

  // Move queue item down one position
  const handleMoveDown = (uuid: string, currentPos: number) => {
    handleQueueReorder(uuid, currentPos + 1);
  };

  // Move queue item to top (position 0)
  const handleMoveToTop = (uuid: string) => {
    handleQueueReorder(uuid, 0);
  };

  // Drag and drop state
  const [draggedItem, setDraggedItem] = useState<{uuid: string, pos: number} | null>(null);
  const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);

  const handleDragStart = (e: React.DragEvent, uuid: string, pos: number) => {
    setDraggedItem({ uuid, pos });
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', uuid); // Required for Firefox
  };

  const handleDragOver = (e: React.DragEvent, index: number) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
    setDragOverIndex(index);
  };

  const handleDragLeave = () => {
    setDragOverIndex(null);
  };

  const handleDrop = (e: React.DragEvent, targetPos: number) => {
    e.preventDefault();
    if (draggedItem && draggedItem.pos !== targetPos) {
      handleQueueReorder(draggedItem.uuid, targetPos);
    }
    setDraggedItem(null);
    setDragOverIndex(null);
  };

  const handleDragEnd = () => {
    setDraggedItem(null);
    setDragOverIndex(null);
  };

  const [queueSearch, setQueueSearch] = useState("");
  const queueSearchRef = useRef(queueSearch);
  
  // Keep refs in sync with state
  useEffect(() => { queuePageRef.current = queuePage; }, [queuePage]);
  useEffect(() => { queueRowsRef.current = queueRows; }, [queueRows]);
  useEffect(() => { queueSearchRef.current = queueSearch; }, [queueSearch]);
  
  // Fetch just the next track for display (lightweight call)
  const fetchNextTrack = async () => {
    try {
      const response = await authFetch(`${API_URL}/radio/get_queue`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          station: activeStationRef.current,
          start: 0,
          length: 1,
          search: "",
        }),
      });
      const data = await response.json();
      if (data.items && data.items.length > 0) {
        setNextTrack({ artist: data.items[0].artist, song: data.items[0].song });
      } else {
        setNextTrack(null);
      }
    } catch (error) {
      console.error("Error fetching next track:", error);
      setNextTrack(null);
    }
  };

  const fetchQueue = async (page?: number, rows?: number, search?: string) => {
    // Use refs for default values to avoid stale closures
    const actualPage = page ?? queuePageRef.current;
    const actualRows = rows ?? queueRowsRef.current;
    const actualSearch = search ?? queueSearchRef.current;
    const start = actualPage * actualRows;
    // console.log("Fetching queue for station (ref):", activeStationRef.current);
    try {
      const response = await authFetch(`${API_URL}/radio/get_queue`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          station: activeStationRef.current, // Use ref to ensure latest value
          start,
          length: actualRows,
          search: actualSearch,
        }),
      });
      const data = await response.json();
      setQueueData(data.items || []);
      setQueueTotalRecords(
        typeof actualSearch === 'string' && actualSearch.length > 0
          ? data.recordsFiltered ?? data.recordsTotal ?? 0
          : data.recordsTotal ?? 0
      );
    } catch (error) {
      console.error("Error fetching queue:", error);
    }
  };

  useEffect(() => {
    if (isQueueVisible) {
      // console.log("Fetching queue for station:", activeStation);
      fetchQueue(queuePage, queueRows, queueSearch);
    }
  }, [isQueueVisible, queuePage, queueRows, queueSearch, activeStation]);

  useEffect(() => {
    // console.log("Active station changed to:", activeStation);
    if (isQueueVisible) {
      fetchQueue(queuePage, queueRows, queueSearch);
    }
    // Always fetch next track when station changes
    fetchNextTrack();
  }, [activeStation]);

  // Poll next track every 10 seconds (queue can change at any time)
  useEffect(() => {
    fetchNextTrack();
    const interval = setInterval(fetchNextTrack, 10000);
    return () => clearInterval(interval);
  }, [activeStation]);

  useEffect(() => {
    if (isQueueVisible) {
      // console.log("Track changed, refreshing queue for station:", activeStation);
      fetchQueue(queuePage, queueRows, queueSearch);
    }
  }, [currentTrackUuid]);

  // Always define queueFooter, fallback to Close button if user is not available
  // Normalize roles to lowercase for case-insensitive comparison
  const userRoles = Array.isArray(user?.roles)
    ? user.roles.map((r) => (typeof r === 'string' ? r.toLowerCase() : r)).filter(Boolean)
    : [];
  const isDJ = userRoles.includes('dj') || userRoles.includes('admin') || ENVIRONMENT === "Dev";
  const queueFooter = isDJ
    ? (
      <div className="flex gap-2 justify-end">
        <button
          className="px-3 py-1 rounded bg-green-400 text-white hover:bg-green-500 text-xs font-bold"
          onClick={() => handleReshuffle()}
        >
          Reshuffle
        </button>
        <button
          className="px-3 py-1 rounded bg-gray-400 text-white hover:bg-gray-500 text-xs font-bold"
          onClick={() => setQueueVisible(false)}
        >
          Close
        </button>
      </div>
    )
    : (
      <div className="flex gap-2 justify-end">
        <button
          className="px-3 py-1 rounded bg-gray-400 text-white hover:bg-gray-500 text-xs font-bold"
          onClick={() => setQueueVisible(false)}
        >
          Close
        </button>
      </div>
    );
  return (
    <>
      <div className="station-tabs flex gap-2 justify-center mb-4 flex-wrap z-10 relative">
        {Object.entries(STATIONS).map(([stationKey, { label }]) => (
          <button
            key={stationKey}
            onClick={() => setActiveStation(stationKey)}
            className={`px-3 py-1 rounded-full text-sm font-semibold transition-colors ${activeStation === stationKey
              ? "bg-neutral-800 text-white dark:bg-white dark:text-black"
              : "bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white"
              }`}
            aria-pressed={activeStation === stationKey}
          >
            {label}
          </button>
        ))}
      </div>

      <div className="c-container">
        {/* {user && <span className="text-lg font-semibold">Hello, {user.user}</span>} */}

        <div className="music-container mt-8">
          {/* Header - always first */}
          <h1 className="music-player__header text-2xl font-bold" title={trackGenre ? `Genre: ${trackGenre}` : undefined}>serious.FM</h1>
          
          {/* Album cover section */}
          <section className="album-cover">
            <div className="music-player__album" title="Album">
              {trackAlbum}
            </div>
            <img src={coverArt} className="cover rounded-lg shadow-md" alt="Cover Art" />
          </section>

          {/* Track info section */}
          <section className="music-player">
            <MarqueeText text={trackTitle} className="music-player__title text-xl font-semibold" />
            <MarqueeText text={trackArtist} className="music-player__author text-lg font-medium" />

            {/* Next track preview */}
            {nextTrack && (
              <div className="next-track text-xs text-neutral-500 dark:text-neutral-400 mt-1 px-2 py-1 bg-neutral-100 dark:bg-neutral-800 rounded-full inline-block">
                <span className="font-medium">Up next:</span> {nextTrack.artist} - {nextTrack.song}
              </div>
            )}

            <div className="music-time flex justify-between items-center mt-4">
              <p className="music-time__current text-sm">{formatTime(elapsedTime)}</p>
              <p className="music-time__last text-sm">- {formatTime(trackDuration - elapsedTime)}</p>
            </div>

            <div className="progress-bar-container w-full h-2 rounded bg-neutral-300 dark:bg-neutral-700 overflow-hidden">
              <div
                className={`h-full transition-all duration-200 ${progressColorClass}`}
                style={{ width: `${progress}%` }}
              ></div>
            </div>

            {/* Quality selector */}
            {qualityLevels.length > 0 && (
              <div className="quality-selector mt-2 flex justify-center items-center gap-3 text-xs">
                <div className="flex items-center gap-2">
                  <span className="text-neutral-500 dark:text-neutral-400">Quality:</span>
                  <select
                    value={selectedQuality}
                    onChange={(e) => handleQualityChange(parseInt(e.target.value, 10))}
                    className="px-2 py-1 rounded bg-neutral-200 dark:bg-neutral-700 border border-neutral-300 dark:border-neutral-600 text-neutral-800 dark:text-neutral-200 cursor-pointer"
                  >
                    <option value={-1}>Auto</option>
                    {qualityLevels.map((level) => (
                      <option key={level.index} value={level.index}>
                        {level.name}
                      </option>
                    ))}
                  </select>
                </div>
                <span className="text-green-600 dark:text-green-400 font-medium">
                  ⬇ {qualityLevels.find(l => l.index === actualPlayingLevel)?.name || '...'} 
                  {liveDownloadRate && <span className="text-neutral-500 dark:text-neutral-400 ml-1">@ {formatBitrate(liveDownloadRate)}</span>}
                </span>
              </div>
            )}

            <div
              ref={lrcContainerRef}
              className={`lrc-text mt-4 p-4 rounded-lg bg-neutral-100 dark:bg-neutral-800 ${lyrics.length === 0 ? "empty" : ""}`}
              tabIndex={lyrics.length > 0 ? 0 : -1}
              aria-label="Lyrics"
              onKeyDown={(e) => {
                if (!lrcContainerRef.current || lyrics.length === 0) return;
                const container = lrcContainerRef.current;
                const step = 24;
                switch (e.key) {
                  case 'ArrowDown':
                    container.scrollBy({ top: step, behavior: 'smooth' });
                    e.preventDefault();
                    break;
                  case 'ArrowUp':
                    container.scrollBy({ top: -step, behavior: 'smooth' });
                    e.preventDefault();
                    break;
                  case 'Home':
                    container.scrollTo({ top: 0, behavior: 'smooth' });
                    e.preventDefault();
                    break;
                  case 'End':
                    container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
                    e.preventDefault();
                    break;
                }
              }}
            >
              {lyrics.length === 0 && (
                <p className="lrc-line text-sm text-neutral-400 dark:text-neutral-500">No lyrics available.</p>
              )}
              {lyrics.map((lyricObj, index) => (
                <p
                  key={index}
                  ref={index === currentLyricIndex ? (lrcActiveRef as React.RefObject<HTMLParagraphElement>) : undefined}
                  className={`lrc-line text-sm ${index === currentLyricIndex ? "active font-bold" : ""}`}
                >
                  {lyricObj.line}
                </p>
              ))}
            </div>

            {isDJ && (
              <div className="dj-controls mt-6 flex flex-wrap justify-center items-center gap-2" style={{ minWidth: '24rem', maxWidth: '100%', alignItems: 'flex-start' }}>
                <div className="flex flex-col items-center gap-2 w-full" style={{ minWidth: '24rem' }}>
                  <AutoComplete
                    value={requestInput}
                    suggestions={typeaheadOptions}
                    completeMethod={(e) => {
                      fetchTypeahead(e.query);
                    }}
                    onChange={e => {
                      setRequestInput(e.target.value ?? '');
                    }}
                    onShow={handleTypeaheadShow}
                    onHide={handleTypeaheadHide}
                    placeholder="Request a song..."
                    inputStyle={{
                      width: '24rem',
                      minWidth: '24rem',
                      padding: '0.35rem 0.75rem',
                      borderRadius: '9999px',
                      border: '1px solid #d1d5db',
                      fontSize: '1rem',
                      fontWeight: 'bold',
                    }}
                    className="typeahead-input"
                    forceSelection={false}
                  />
                  <div className="flex flex-row flex-wrap justify-center gap-2 items-center" style={{ minWidth: '24rem' }}>
                    <button className="px-3 py-1 rounded-full bg-blue-400 text-white hover:bg-blue-500 text-xs font-bold"
                      onClick={() => handleSongRequest(requestInput)}>
                      Request
                    </button>
                    <button className="px-3 py-1 rounded-full bg-green-400 text-white hover:bg-green-500 text-xs font-bold"
                      onClick={() => handlePlayNow(requestInput)}>
                      Play Now
                    </button>
                    <button className="px-3 py-1 rounded-full bg-red-400 text-white hover:bg-red-500 text-xs font-bold" onClick={() => handleSkip()}>Skip</button>
                    <button
                      className="px-3 py-1 rounded-full bg-gray-400 text-white hover:bg-gray-500 text-xs font-bold"
                      onClick={() => setQueueVisible(true)}
                    >
                      Queue
                    </button>
                  </div>
                </div>
              </div>
            )}
            {/* Always show play/pause button */}
            <div className="music-control mt-4 flex justify-center">
              <div
                className="music-control__play"
                onClick={async () => {
                  const audio = audioElement.current;
                  if (!audio) return;
                  
                  if (isPlaying) {
                    audio.pause();
                    setIsPlaying(false);
                  } else {
                    // If stream is not ready, reinitialize it
                    if (!isStreamReady || !audio.src || audio.error) {
                      initializeStream(activeStation);
                      return;
                    }
                    try {
                      await audio.play();
                      setIsPlaying(true);
                    } catch (err) {
                      console.warn('Playback failed, reinitializing stream:', err);
                      // Reinitialize stream on playback failure
                      initializeStream(activeStation);
                    }
                  }
                }}
                role="button"
                tabIndex={0}
                aria-pressed={isPlaying}
              >
                {isPlaying ? <Pause /> : <Play />}
              </div>
            </div>
          </section>
        </div>

        <audio ref={audioElement} preload="none" />

        <Dialog
          header={
            <div className="flex items-center gap-3">
              <span>Queue - {STATIONS[activeStation]?.label || activeStation}</span>
              <span className="text-xs text-neutral-500 dark:text-neutral-400 font-normal">
                ({queueTotalRecords} tracks)
              </span>
            </div>
          }
          visible={isQueueVisible}
          style={{ width: "90vw", maxWidth: "1000px", height: "auto", maxHeight: "90vh" }}
          footer={queueFooter}
          onHide={() => setQueueVisible(false)}
          className={theme === "dark" ? "dark:bg-neutral-900" : "light-theme"}
          dismissableMask={true}
          contentStyle={{ overflow: 'auto', maxHeight: 'calc(90vh - 140px)' }}
        >
          <div>
            {/* Search bar */}
            <div className="mb-3 flex justify-between items-center gap-2 flex-wrap">
              <div className="text-xs text-neutral-500 dark:text-neutral-400">
                {isDJ ? "Drag to reorder or use arrows" : "View upcoming tracks"}
              </div>
              <input
                type="text"
                value={queueSearch}
                onChange={e => setQueueSearch(e.target.value)}
                placeholder="Search queue..."
                className="px-3 py-1.5 rounded-full border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400"
                style={{ minWidth: 200 }}
              />
            </div>

            {/* Queue list */}
            <div className="queue-list space-y-1">
              {queueData.map((item: any, index: number) => (
                <div
                  key={item.uuid}
                  draggable={isDJ}
                  onDragStart={(e) => isDJ && handleDragStart(e, item.uuid, item.pos)}
                  onDragOver={(e) => isDJ && handleDragOver(e, item.pos)}
                  onDragLeave={handleDragLeave}
                  onDrop={(e) => isDJ && handleDrop(e, item.pos)}
                  onDragEnd={handleDragEnd}
                  className={`queue-item flex items-center gap-2 p-2 rounded-lg transition-colors ${isDJ ? 'cursor-grab active:cursor-grabbing' : ''} ${
                    dragOverIndex === item.pos
                      ? 'bg-blue-100 dark:bg-blue-800/50 border-2 border-dashed border-blue-400'
                      : index === 0 
                        ? 'bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800' 
                        : 'bg-neutral-50 dark:bg-neutral-800/50 hover:bg-neutral-100 dark:hover:bg-neutral-800'
                  } ${draggedItem?.uuid === item.uuid ? 'opacity-50' : ''}`}
                >
                  {/* Drag Handle & Position */}
                  <div className="flex flex-col items-center gap-0.5 min-w-[50px]">
                    {isDJ && (
                      <div className="text-neutral-400 mb-1" title="Drag to reorder">
                        <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
                          <path d="M8 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm8-12a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0z" />
                        </svg>
                      </div>
                    )}
                    {isDJ && item.pos > 0 && (
                      <button
                        onClick={() => handleMoveUp(item.uuid, item.pos)}
                        className="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-200 p-0.5"
                        title="Move up"
                      >
                        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
                        </svg>
                      </button>
                    )}
                    <span className={`text-sm font-bold ${index === 0 ? 'text-blue-600 dark:text-blue-400' : 'text-neutral-500'}`}>
                      #{item.pos + 1}
                    </span>
                    {isDJ && (
                      <button
                        onClick={() => handleMoveDown(item.uuid, item.pos)}
                        className="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-200 p-0.5"
                        title="Move down"
                      >
                        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
                        </svg>
                      </button>
                    )}
                  </div>

                  {/* Track Info */}
                  <div className="flex-1 min-w-0">
                    <div className="font-semibold text-sm text-neutral-900 dark:text-neutral-100 truncate" title={`${item.artist} - ${item.song}`}>
                      {item.artist} - {item.song}
                    </div>
                    <div className="text-xs text-neutral-500 dark:text-neutral-400 truncate" title={item.album}>
                      {item.album}
                      {item.genre && item.genre !== 'N/A' && (
                        <span className="ml-2 px-1.5 py-0.5 bg-neutral-200 dark:bg-neutral-700 rounded text-[10px]">
                          {item.genre}
                        </span>
                      )}
                    </div>
                  </div>

                  {/* Actions */}
                  {isDJ && (
                    <div className="flex items-center gap-1.5 flex-shrink-0">
                      {item.pos > 0 && (
                        <button
                          onClick={() => handleMoveToTop(item.uuid)}
                          className="px-2.5 py-1.5 rounded-md bg-purple-500 text-white hover:bg-purple-600 text-xs font-bold shadow-sm flex items-center gap-1"
                          title="Move to top"
                        >
                          <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 11l7-7 7 7M5 19l7-7 7 7" />
                          </svg>
                          Top
                        </button>
                      )}
                      <button
                        onClick={() => handleQueueShift(item.uuid, true)}
                        className="px-2.5 py-1.5 rounded-md bg-green-500 text-white hover:bg-green-600 text-xs font-bold shadow-sm"
                        title="Play Next"
                      >
                        Play Next
                      </button>
                      <button
                        onClick={() => handleSkip(item.uuid)}
                        className="px-2.5 py-1.5 rounded-md bg-blue-500 text-white hover:bg-blue-600 text-xs font-bold shadow-sm"
                        title="Skip to this track"
                      >
                        Skip To
                      </button>
                      <button
                        onClick={() => handleRemoveFromQueue(item.uuid)}
                        className="px-2.5 py-1.5 rounded-md bg-red-500 text-white hover:bg-red-600 text-xs font-bold shadow-sm flex items-center gap-1"
                        title="Remove from queue"
                      >
                        <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M6 18L18 6M6 6l12 12" />
                        </svg>
                        Remove
                      </button>
                    </div>
                  )}
                </div>
              ))}
            </div>

            {/* Pagination */}
            {queueTotalRecords > queueRows && (
              <div className="mt-4 flex justify-center items-center gap-2">
                <button
                  onClick={() => { setQueuePage(0); fetchQueue(0, queueRows, queueSearch); }}
                  disabled={queuePage === 0}
                  className="px-2 py-1 rounded bg-neutral-200 dark:bg-neutral-700 disabled:opacity-50 text-sm"
                >
                  First
                </button>
                <button
                  onClick={() => { const p = Math.max(0, queuePage - 1); setQueuePage(p); fetchQueue(p, queueRows, queueSearch); }}
                  disabled={queuePage === 0}
                  className="px-2 py-1 rounded bg-neutral-200 dark:bg-neutral-700 disabled:opacity-50 text-sm"
                >
                  Prev
                </button>
                <span className="text-sm text-neutral-600 dark:text-neutral-400 px-2">
                  Page {queuePage + 1} of {Math.ceil(queueTotalRecords / queueRows)}
                </span>
                <button
                  onClick={() => { const p = queuePage + 1; setQueuePage(p); fetchQueue(p, queueRows, queueSearch); }}
                  disabled={(queuePage + 1) * queueRows >= queueTotalRecords}
                  className="px-2 py-1 rounded bg-neutral-200 dark:bg-neutral-700 disabled:opacity-50 text-sm"
                >
                  Next
                </button>
                <button
                  onClick={() => { const p = Math.ceil(queueTotalRecords / queueRows) - 1; setQueuePage(p); fetchQueue(p, queueRows, queueSearch); }}
                  disabled={(queuePage + 1) * queueRows >= queueTotalRecords}
                  className="px-2 py-1 rounded bg-neutral-200 dark:bg-neutral-700 disabled:opacity-50 text-sm"
                >
                  Last
                </button>
              </div>
            )}
          </div>
        </Dialog>
      </div>
    </>
  );
}
