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";

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" },
};


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

  const [isQueueVisible, setQueueVisible] = useState(false);
  // 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 [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);

  // DJ controls state
  const [requestInput, setRequestInput] = useState("");
  const [typeaheadOptions, setTypeaheadOptions] = useState([]);
  const [requestInputArtist, setRequestInputArtist] = useState("");
  const [requestInputSong, setRequestInputSong] = useState("");
  const [requestInputUuid, setRequestInputUuid] = useState("");

  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 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}`;
  };

  // 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) => {
    import('hls.js').then(({ default: Hls }) => {
      const audio = audioElement.current;
      if (!audio) return;
      const streamUrl = `https://stream.codey.lol/hls/${station}/${station}.m3u8`;

      // Clean up previous HLS
      if (hlsInstance.current) {
        hlsInstance.current.destroy();
        hlsInstance.current = null;
      }
      audio.pause();
      audio.removeAttribute("src");
      audio.load();

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

      if (audio.canPlayType("application/vnd.apple.mpegurl")) {
        audio.src = streamUrl;
        audio.load();
        audio.play().then(() => setIsPlaying(true)).catch(() => {
          setTrackTitle("Offline");
          setIsPlaying(false);
        });
        return;
      }

      if (!Hls.isSupported()) {
        console.error("HLS not supported");
        return;
      }

      const hls = new Hls({
        lowLatencyMode: false,
        // abrEnabled: false, // ABR not in current HLS.js types
        liveSyncDuration: 0.6,               // seconds behind live edge target
        liveMaxLatencyDuration: 3.0,        // max allowed latency before catchup
        // liveCatchUpPlaybackRate: 1.02, // Not in current HLS.js types
        // maxBufferLength: 30,
        // maxMaxBufferLength: 60,
        // maxBufferHole: 2.0,
        // manifestLoadingTimeOut: 5000,
        // fragLoadingTimeOut: 10000,     // playback speed when catching up
      } 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, () => {
        audio.play().then(() => setIsPlaying(true)).catch(() => {
          setIsPlaying(false);
        });
      });
      hls.on(Hls.Events.ERROR, (event, data) => {
        console.warn("HLS error:", data);
        if (data.fatal) {
          hls.destroy();
          hlsInstance.current = null;
          setTrackTitle("Offline");
          setIsPlaying(false);
        }
      });
    });
  };

  // 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(() => {
    setTimeout(() => {
      const activeElement = document.querySelector('.lrc-line.active');
      const lyricsContainer = document.querySelector('.lrc-text');
      if (activeElement && lyricsContainer) {
        (lyricsContainer as HTMLElement).style.maxHeight = '220px';
        (lyricsContainer as HTMLElement).style.overflowY = 'auto';
        activeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }, 0);
  }, [currentLyricIndex, lyrics]);

  // Handle station changes: reset and start new stream
  useEffect(() => {
    // Reset metadata and state when switching stations
    setTrackTitle("");
    setTrackArtist("");
    setTrackGenre("");
    setTrackAlbum("");
    setCoverArt("/images/radio_art_default.jpg");
    setLyrics([]);
    setCurrentLyricIndex(0);
    setElapsedTime(0);
    setTrackDuration(0);

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

    initializeStream(activeStation);

    // Update page title to reflect the new station
    document.title = `${metaData.title} - Radio [${activeStation}]`;
  }, [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;

    // Only set "No track playing" if we have clear indication of no track
    // and not just missing song field (to avoid flickering during transitions)
    if ((!trackData.song || trackData.song === 'N/A') && (!trackData.artist || trackData.artist === 'N/A')) {
      setTrackTitle('No track playing');
      setLyrics([]);
      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 when track changes
      fetchQueue();
    } 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
    if (wsInstance.current) {
      wsInstance.current.onclose = null; // Prevent triggering reconnection logic
      wsInstance.current.close();
      wsInstance.current = null;
    }

    const connectWebSocket = (retryCount = 0) => {
      const baseDelay = 1000; // 1 second
      const maxDelay = 30000; // 30 seconds

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

      ws.onopen = function () {
        // Reset retry count on successful connection
      };

      ws.onmessage = function (event) {
        try {
          const data = JSON.parse(event.data);

          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 {
            // Handle initial now playing data
            handleTrackData(data);
          }
        } catch (error) {
          console.error('Error parsing WebSocket message:', error);
        }
      };

      ws.onclose = function (event) {
        // Don't retry if it was a clean close (code 1000)
        if (event.code === 1000) return;

        // Attempt reconnection with exponential backoff
        const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);

        setTimeout(() => {
          connectWebSocket(retryCount + 1);
        }, 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();
  }, [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;

    // Clean up the existing WebSocket connection before initializing a new one
    const cleanupWebSocket = async () => {
      if (wsInstance.current) {
        wsInstance.current.onclose = null; // Prevent triggering reconnection logic
        wsInstance.current.close();
        wsInstance.current = null;
      }
    };

    cleanupWebSocket().then(() => {
      // Initialize WebSocket connection for metadata
      initializeWebSocket(activeStation);
    });

    return () => {
      // Clean up WebSocket on station change or component unmount
      if (wsInstance.current) {
        wsInstance.current.onclose = null; // Prevent triggering reconnection logic
        wsInstance.current.close();
        wsInstance.current = null;
      }
    };
  }, [activeStation, initializeWebSocket]);

  // Cleanup WebSocket on component unmount
  useEffect(() => {
    return () => {
      if (wsInstance.current) {
        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("OK!");
        fetchQueue();
      } else {
        toast.error("Remove from queue failed.");
      }
    } catch (error) {
      console.error("Error removing from queue:", error);
      toast.error("Remove from queue failed.");
    }
  };

  const [queueSearch, setQueueSearch] = useState("");
  const fetchQueue = async (page = queuePage, rows = queueRows, search = queueSearch) => {
    const start = page * rows;
    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: rows,
          search,
        }),
      });
      const data = await response.json();
      setQueueData(data.items || []);
      setQueueTotalRecords(
        typeof search === 'string' && search.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);
    }
  }, [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">
          <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>

          <section className="music-player">
            <h1 className="music-player__header text-2xl font-bold">serious.FM</h1>
            <h1 className="music-player__title text-xl font-semibold">{trackTitle}</h1>
            <h2 className="music-player__author text-lg font-medium">{trackArtist}</h2>
            {trackGenre && <h2 className="music-player__genre text-md italic">{trackGenre}</h2>}

            <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>

            <div className={`lrc-text mt-4 p-4 rounded-lg bg-neutral-100 dark:bg-neutral-800 ${lyrics.length === 0 ? "empty" : ""}`}>
              {lyrics.map((lyricObj, index) => (
                <p
                  key={index}
                  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={() => {
                      setTimeout(() => {
                        const panel = document.querySelector('.p-autocomplete-panel');
                        const items = panel?.querySelector('.p-autocomplete-items');
                        if (items) {
                          (items as HTMLElement).style.maxHeight = '200px';
                          (items as HTMLElement).style.overflowY = 'auto';
                          (items as HTMLElement).style.overscrollBehavior = 'contain';
                          const wheelHandler: EventListener = (e) => {
                            const wheelEvent = e as WheelEvent;
                            const delta = wheelEvent.deltaY;
                            const atTop = items.scrollTop === 0;
                            const atBottom = items.scrollTop + items.clientHeight >= items.scrollHeight;
                            if ((delta < 0 && atTop) || (delta > 0 && atBottom)) {
                              e.preventDefault();
                            } else {
                              e.stopPropagation();
                            }
                          };
                          items.removeEventListener('wheel', wheelHandler);
                          items.addEventListener('wheel', wheelHandler, { passive: false });
                        }
                      }, 0);
                    }}
                    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={() => {
                  const audio = audioElement.current;
                  if (!audio) return;
                  if (isPlaying) {
                    audio.pause();
                    setIsPlaying(false);
                  } else {
                    audio.play().then(() => setIsPlaying(true));
                  }
                }}
                role="button"
                tabIndex={0}
                aria-pressed={isPlaying}
              >
                {isPlaying ? <Pause /> : <Play />}
              </div>
            </div>
          </section>
        </div>

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

        <Dialog
          header={`Queue - ${activeStation}`}
          visible={isQueueVisible}
          style={{ width: "80vw", maxWidth: "1200px", height: "auto", maxHeight: "90vh" }}
          footer={queueFooter}
          onHide={() => setQueueVisible(false)}
          // Use the same dark class used by other dialog styles (CSS escapes the colon)
          className={theme === "dark" ? "dark:bg-neutral-900" : "light-theme"}
          dismissableMask={true}
        >
          <div style={{ maxHeight: "calc(90vh - 100px)", overflow: "visible" }}>
            <div className="mb-2 flex justify-end">
              <input
                type="text"
                value={queueSearch}
                onChange={e => setQueueSearch(e.target.value)}
                placeholder="Search..."
                className="px-2 py-1 rounded border border-neutral-300 dark:border-neutral-700 text-sm"
                style={{ minWidth: 180 }}
              />
            </div>
            <DataTable
              value={queueData}
              paginator
              alwaysShowPaginator={true}
              rows={queueRows}
              first={queuePage * queueRows}
              totalRecords={queueTotalRecords}
              onPage={(e) => {
                setQueuePage(e.page ?? 0);
                fetchQueue(e.page ?? 0, queueRows, queueSearch);
              }}
              paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport"
              currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries"
              paginatorClassName="queue-paginator !bg-neutral-50 dark:!bg-neutral-800 !text-neutral-900 dark:!text-neutral-100 !border-neutral-200 dark:!border-neutral-700"
              className="p-datatable-gridlines rounded-lg shadow-md border-t border-neutral-300 dark:border-neutral-700"
              style={{ minHeight: 'auto', height: 'auto', tableLayout: 'fixed', width: '100%' }}
              lazy
            >
              <Column
                field="pos"
                header="Position"
                body={(rowData) => rowData.pos + 1}
                sortable
                headerClassName="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 font-bold font-sans"
                style={{ width: '50px', textAlign: 'center' }}
              ></Column>
              <Column
                field="artistsong"
                header="Track"
                sortable
                headerClassName="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 font-bold font-sans"
                style={{ width: '300px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
                body={(rowData) => <span title={rowData.artistsong} style={{ display: 'block', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '300px' }}>{rowData.artistsong}</span>}
              ></Column>
              <Column
                field="album"
                header="Album"
                sortable
                headerClassName="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 font-bold font-sans"
                style={{ width: '220px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
                body={(rowData) => <span title={rowData.album} style={{ display: 'block', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '220px' }}>{rowData.album}</span>}
              ></Column>
              <Column
                field="genre"
                header="Genre"
                sortable
                headerClassName="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 font-bold font-sans"
                style={{ width: '120px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
                body={(rowData) => <span title={rowData.genre} style={{ display: 'block', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '120px' }}>{rowData.genre}</span>}
              ></Column>
              {isDJ && (
                <Column
                  field="actions"
                  header="Actions"
                  body={(rowData) => (
                    <div className="flex gap-1 flex-nowrap" style={{ minWidth: '220px', justifyContent: 'center' }}>
                      <button
                        className="px-2 py-1 rounded bg-blue-400 text-neutral-900 hover:bg-blue-500 text-xs shadow-md cursor-pointer font-bold font-sans"
                        onClick={() => handleSkip(rowData.uuid)}
                        title="Skip To"
                      >
                        Skip To
                      </button>
                      <button
                        className="px-2 py-1 rounded bg-yellow-400 text-neutral-900 hover:bg-yellow-500 text-xs shadow-md cursor-pointer font-bold font-sans"
                        onClick={() => handleQueueShift(rowData.uuid, false)}
                        title="Play"
                      >
                        Play
                      </button>
                      <button
                        className="px-2 py-1 rounded bg-green-400 text-neutral-900 hover:bg-green-500 text-xs shadow-md cursor-pointer font-bold font-sans"
                        onClick={() => handleQueueShift(rowData.uuid, true)}
                        title="Play Next"
                      >
                        Play Next
                      </button>
                      <button
                        className="px-2 py-1 rounded bg-red-400 text-neutral-900 hover:bg-red-500 text-xs shadow-md cursor-pointer font-bold font-sans"
                        onClick={() => {
                          handleRemoveFromQueue(rowData.uuid);
                        }}
                        title="Remove"
                      >
                        Remove
                      </button>
                    </div>
                  )}
                  headerClassName="bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 font-bold font-sans"
                  style={{ width: '220px', textAlign: 'center' }}
                ></Column>
              )}
            </DataTable>
          </div>
        </Dialog>
      </div>
    </>
  );
}
