"use client";

import type React from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  ChevronDown,
  ChevronUp,
  ExternalLink,
  MessageSquareText,
  Pause,
  Play,
  Radio,
  RotateCcw,
  Settings2,
  ShieldCheck,
  SkipForward,
  Trash2,
  UserPlus,
  Ticket
} from "lucide-react";
import { io, type Socket } from "socket.io-client";
import { addRoomMember, createRoomInvite, deleteRoom, removeRoomMember, resetPersonalRoom, revokeRoomInvite, updateRoomSettings } from "@/lib/room-actions";
import { Avatar } from "./avatar";
import { StatusBadge } from "./status-badge";
import { EmptyState, Panel, StatusDot } from "./ui";

declare global {
  interface Window {
    YT?: {
      Player: new (
        element: HTMLElement,
        options: {
          videoId: string;
          width: string;
          height: string;
          playerVars: Record<string, string | number>;
          events: {
            onReady?: () => void;
            onError?: () => void;
          };
        }
      ) => YouTubePlayer;
    };
    onYouTubeIframeAPIReady?: () => void;
    Twitch?: {
      Player: new (
        element: HTMLElement,
        options: {
          width: string;
          height: string;
          parent: string[];
          autoplay?: boolean;
          video?: string;
          channel?: string;
        }
      ) => TwitchPlayer;
    };
  }
}

type YouTubePlayer = {
  playVideo: () => void;
  pauseVideo: () => void;
  seekTo: (seconds: number, allowSeekAhead: boolean) => void;
  getCurrentTime: () => number;
  destroy: () => void;
};

type TwitchPlayer = {
  play: () => void;
  pause: () => void;
  seek?: (seconds: number) => void;
  getCurrentTime?: () => number;
  destroy?: () => void;
};

type QueueItem = {
  id: string;
  title: string;
  provider: string;
  originalUrl: string;
  playbackUrl: string;
  thumbnailUrl?: string | null;
  by: string;
  createdAt: string;
};

type RoomMessage = {
  id: string;
  body: string;
  createdAt: string;
  user: string;
  avatarUrl?: string | null;
};

type PlaybackState = {
  mediaSourceId: string;
  media: QueueItem;
  status: "PLAYING" | "PAUSED";
  position: number;
  rate: number;
  updatedAt: number;
  updatedBy?: string;
};

type RoomSnapshot = {
  queue: QueueItem[];
  playback: PlaybackState | null;
  messages: RoomMessage[];
};

type RoomAck = {
  ok: boolean;
  at?: number;
  message?: string;
};

type Participant = {
  id: string;
  name: string;
  avatarUrl?: string | null;
  role: string;
  status: string;
};

type RoomInvite = {
  id: string;
  code: string;
  status: string;
  expiresAt: string | null;
  createdAt: string;
  creator: string;
};

export function RoomConsole({
  roomId,
  roomSlug,
  roomName,
  roomVisibility,
  isPersonal,
  ownerName,
  canManageRoom,
  initialRail = "Activity",
  savedState,
  errorState,
  currentUser,
  queue = [],
  participants = [],
  invites = []
}: {
  roomId: string;
  roomSlug: string;
  roomName: string;
  roomVisibility: string;
  isPersonal: boolean;
  ownerName: string;
  canManageRoom: boolean;
  initialRail?: string;
  savedState?: string;
  errorState?: string;
  currentUser: string;
  queue?: QueueItem[];
  participants?: Participant[];
  invites?: RoomInvite[];
}) {
  const [connected, setConnected] = useState(false);
  const [source, setSource] = useState("");
  const [rail, setRail] = useState(["Activity", "Chat", "Invite", "Settings"].includes(initialRail) ? initialRail : "Activity");
  const [queueState, setQueueState] = useState(queue);
  const [messages, setMessages] = useState<RoomMessage[]>([]);
  const [presence, setPresence] = useState<Participant[]>(participants);
  const [playback, setPlayback] = useState<PlaybackState | null>(queue[0] ? initialPlayback(queue[0]) : null);
  const [socketError, setSocketError] = useState("");
  const [syncHealth, setSyncHealth] = useState<"idle" | "pending" | "confirmed" | "failed">("idle");
  const [lastAckAt, setLastAckAt] = useState<number | null>(null);
  const [syncBlocked, setSyncBlocked] = useState(false);
  const [playerReady, setPlayerReady] = useState(false);
  const [providerIssue, setProviderIssue] = useState("");
  const socketRef = useRef<Socket | null>(null);
  const youtubePlayerRef = useRef<YouTubePlayer | null>(null);
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const twitchPlayerRef = useRef<TwitchPlayer | null>(null);
  const userActivatedRef = useRef(false);
  const lastAppliedRef = useRef("");

  const activeMedia = playback?.media || queueState[0] || null;
  const participantRows = useMemo(() => mergePresence(participants, presence), [participants, presence]);
  const handlePlayerReady = useCallback(() => setPlayerReady(true), []);
  const handleProviderIssue = useCallback((message: string) => setProviderIssue(message), []);

  const emit = useCallback((event: string, payload: Record<string, unknown> = {}) => {
    setSocketError("");
    setSyncHealth("pending");
    socketRef.current?.timeout(5000).emit(event, payload, (error: Error | null, response?: RoomAck) => {
      if (error || !response?.ok) {
        setSyncHealth("failed");
        setSocketError(response?.message || "Realtime action was not acknowledged.");
        return;
      }
      setLastAckAt(response.at || Date.now());
      setSyncHealth("confirmed");
    });
  }, []);

  const currentPosition = useCallback(() => {
    if (activeMedia?.provider === "YOUTUBE") return youtubePlayerRef.current?.getCurrentTime?.() || playback?.position || 0;
    if (activeMedia?.provider === "TWITCH") return twitchPlayerRef.current?.getCurrentTime?.() || playback?.position || 0;
    if (activeMedia?.provider === "DIRECT") return videoRef.current?.currentTime || playback?.position || 0;
    return playback?.position || 0;
  }, [activeMedia?.provider, playback?.position]);

  const applyPlayback = useCallback(
    async (state: PlaybackState | null, force = false) => {
      if (!state?.media || !playerReady) return;
      const key = `${state.mediaSourceId}:${state.status}:${Math.floor(state.position)}:${state.updatedAt}`;
      if (!force && lastAppliedRef.current === key) return;
      lastAppliedRef.current = key;
      const target = Math.max(0, state.position);
      setProviderIssue("");
      if (state.status === "PLAYING" && !userActivatedRef.current) {
        setSyncBlocked(true);
        return;
      }

      try {
        if (state.media.provider === "YOUTUBE" && youtubePlayerRef.current) {
          youtubePlayerRef.current.seekTo(target, true);
          if (state.status === "PLAYING") youtubePlayerRef.current.playVideo();
          if (state.status === "PAUSED") youtubePlayerRef.current.pauseVideo();
        }

        if (state.media.provider === "TWITCH" && twitchPlayerRef.current) {
          if (state.status === "PLAYING") twitchPlayerRef.current.play();
          if (state.status === "PAUSED") twitchPlayerRef.current.pause();
          if (isTwitchVod(state.media.playbackUrl) && twitchPlayerRef.current.seek) {
            twitchPlayerRef.current.seek(target);
          }
        }

        if (state.media.provider === "DIRECT" && videoRef.current) {
          if (Math.abs(videoRef.current.currentTime - target) > 1.5) videoRef.current.currentTime = target;
          if (state.status === "PLAYING") await videoRef.current.play();
          if (state.status === "PAUSED") videoRef.current.pause();
        }
        setSyncBlocked(false);
      } catch {
        setSyncBlocked(state.status === "PLAYING");
      }
    },
    [playerReady]
  );

  useEffect(() => {
    const socket = io({ path: "/api/socket", autoConnect: false, withCredentials: true });
    socketRef.current = socket;
    socket.on("connect", () => {
      setConnected(true);
      setSyncHealth("pending");
      socket.timeout(5000).emit("room:join", { roomSlug }, (error: Error | null, response?: RoomAck) => {
        if (error || !response?.ok) {
          setSyncHealth("failed");
          setSocketError(response?.message || "Realtime join was not acknowledged.");
          return;
        }
        setLastAckAt(response.at || Date.now());
        setSyncHealth("confirmed");
      });
    });
    socket.on("disconnect", () => {
      setConnected(false);
      setSyncHealth("idle");
    });
    socket.on("room:error", ({ message }: { message: string }) => {
      setSocketError(message);
      setSyncHealth("failed");
    });
    socket.on("presence:list", (rows: Participant[]) => setPresence(rows));
    socket.on("room:state", (snapshot: RoomSnapshot | null) => {
      if (!snapshot) return;
      setQueueState(snapshot.queue || []);
      setMessages(snapshot.messages || []);
      setPlayback(snapshot.playback);
    });
    socket.connect();
    return () => {
      socket.disconnect();
      socketRef.current = null;
    };
  }, [roomSlug]);

  useEffect(() => {
    setPlayerReady(false);
    setProviderIssue("");
    lastAppliedRef.current = "";
  }, [activeMedia?.id]);

  useEffect(() => {
    void applyPlayback(playback);
  }, [applyPlayback, playback]);

  useEffect(() => {
    const timer = window.setInterval(() => {
      if (!playback || playback.status !== "PLAYING" || !activeMedia) return;
      const drift = Math.abs(currentPosition() - playback.position);
      if (drift > 3) void applyPlayback(playback, true);
    }, 5000);
    return () => window.clearInterval(timer);
  }, [activeMedia, applyPlayback, currentPosition, playback]);

  function activateSync() {
    userActivatedRef.current = true;
    setSyncBlocked(false);
    void applyPlayback(playback, true);
  }

  function handleTransport(event: "playback:play" | "playback:pause" | "playback:seek") {
    userActivatedRef.current = true;
    if (!activeMedia) return;
    const position = event === "playback:seek" ? currentPosition() + 30 : currentPosition();
    emit(event, { mediaSourceId: activeMedia.id, position });
  }

  function addSource(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    userActivatedRef.current = true;
    const sourceUrl = source.trim();
    if (!sourceUrl) return;
    emit("queue:add", { sourceUrl });
    setSource("");
  }

  function sendMessage(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const form = event.currentTarget;
    const input = form.elements.namedItem("body") as HTMLInputElement | null;
    const body = input?.value.trim() || "";
    if (!body) return;
    emit("chat:message", { body });
    form.reset();
  }

  return (
    <section className="watch-console">
      <div className="player-column">
        <Panel
          className="player-panel"
          title={roomName}
          eyebrow={`/${roomSlug}`}
          actions={
            <div className="status-row">
              <StatusDot tone={connected ? "good" : "warn"} label={connected ? "connected" : "connecting"} />
              <StatusDot tone={syncStatusTone(syncHealth)} label={syncStatusLabel(syncHealth, lastAckAt)} />
              <StatusBadge>{formatVisibility(roomVisibility)}</StatusBadge>
            </div>
          }
        >
          {socketError ? <p className="form-error">{socketError}</p> : null}
          <div className="video-frame">
            {activeMedia ? (
              <>
                <MediaPreview
                  provider={activeMedia.provider}
                  playbackUrl={activeMedia.playbackUrl}
                  originalUrl={activeMedia.originalUrl}
                  title={activeMedia.title || activeMedia.originalUrl}
                  youtubePlayerRef={youtubePlayerRef}
                  videoRef={videoRef}
                  twitchPlayerRef={twitchPlayerRef}
                  onReady={handlePlayerReady}
                  onIssue={handleProviderIssue}
                />
                {syncBlocked ? (
                  <div className="player-overlay">
                    <StatusBadge tone="warn">Action needed</StatusBadge>
                    <h2>Enable sync in this browser</h2>
                    <p>Browsers can block remote autoplay until you interact with the page.</p>
                    <button className="button primary animated-button" type="button" onClick={activateSync}>
                      <Play size={16} /> Enable sync
                    </button>
                  </div>
                ) : null}
                {providerIssue ? (
                  <div className="player-overlay compact-overlay">
                    <StatusBadge tone="warn">Provider</StatusBadge>
                    <p>{providerIssue}</p>
                    {activeMedia.provider === "YOUTUBE" ? (
                      <a className="button compact-button" href={activeMedia.originalUrl} target="_blank" rel="noreferrer">
                        <ExternalLink size={14} /> Open on YouTube
                      </a>
                    ) : null}
                  </div>
                ) : null}
              </>
            ) : (
              <div className="video-state">
                <StatusBadge>Idle</StatusBadge>
                <h2>No media queued</h2>
                <p>Add a YouTube, Twitch, or direct video URL to start this room.</p>
              </div>
            )}
          </div>

          <form className="transport-bar" onSubmit={addSource}>
            <button className="icon-button animated-button" type="button" onClick={() => handleTransport("playback:play")} title="Play" disabled={!activeMedia}>
              <Play size={16} />
            </button>
            <button className="icon-button animated-button" type="button" onClick={() => handleTransport("playback:pause")} title="Pause" disabled={!activeMedia}>
              <Pause size={16} />
            </button>
            <button
              className="icon-button animated-button"
              type="button"
              onClick={() => handleTransport("playback:seek")}
              title="Seek 30 seconds"
              disabled={!activeMedia || (activeMedia.provider === "TWITCH" && !isTwitchVod(activeMedia.playbackUrl))}
            >
              <SkipForward size={16} />
            </button>
            <input
              className="input"
              aria-label="Source URL"
              name="sourceUrl"
              value={source}
              onChange={(event) => setSource(event.target.value)}
              placeholder="Paste YouTube, Twitch, or direct video URL"
            />
            <button className="button primary animated-button" type="submit" disabled={!source.trim()}>
              <Radio size={16} /> Add source
            </button>
          </form>
          {activeMedia?.provider === "YOUTUBE" ? (
            <div className="provider-note">
              <span>YouTube may require sign-in in embeds.</span>
              <a href={activeMedia.originalUrl} target="_blank" rel="noreferrer">
                <ExternalLink size={13} /> Open on YouTube
              </a>
            </div>
          ) : null}
        </Panel>

        <div className="console-grid">
          <Panel title="Queue" eyebrow={`${queueState.length} sources`}>
            {queueState.length === 0 ? (
              <EmptyState title="Queue is empty" description="Sources added by room participants will appear here with moderation actions." />
            ) : (
              <div className="queue-list">
                {queueState.map((item, index) => (
                  <div className={`queue-row ${playback?.mediaSourceId === item.id ? "active-row" : ""}`} key={item.id}>
                    <span className="queue-index">{index + 1}</span>
                    <div className="queue-thumbnail">
                      {item.thumbnailUrl ? <img src={item.thumbnailUrl} alt="" /> : <span>{item.provider.slice(0, 2)}</span>}
                    </div>
                    <div className="row-title">
                      <strong>{item.title}</strong>
                      <span>{item.provider} by {item.by} - {item.createdAt}</span>
                    </div>
                    <div className="row-actions">
                      <button className="icon-button compact animated-button" type="button" title="Play now" onClick={() => emit("queue:play", { mediaSourceId: item.id })}>
                        <Play size={14} />
                      </button>
                      <button
                        className="icon-button compact animated-button"
                        type="button"
                        title="Move up"
                        disabled={index === 0}
                        onClick={() => emit("queue:move", { mediaSourceId: item.id, direction: "up" })}
                      >
                        <ChevronUp size={14} />
                      </button>
                      <button
                        className="icon-button compact animated-button"
                        type="button"
                        title="Move down"
                        disabled={index === queueState.length - 1}
                        onClick={() => emit("queue:move", { mediaSourceId: item.id, direction: "down" })}
                      >
                        <ChevronDown size={14} />
                      </button>
                      <button className="icon-button compact danger animated-button" type="button" title="Remove" onClick={() => emit("queue:remove", { mediaSourceId: item.id })}>
                        <Trash2 size={14} />
                      </button>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </Panel>

          <Panel title="Participants" eyebrow={`${participantRows.length} present or allowed`}>
            {participantRows.length === 0 ? (
              <EmptyState title="No participants listed" description="Room presence appears when users open the room." />
            ) : (
              participantRows.map((item) => (
                <div className="row" key={item.id}>
                  <div className="row-main">
                    <Avatar name={item.name} src={item.avatarUrl} />
                    <div className="row-title">
                      <strong>{item.name}</strong>
                      <span>{item.role}</span>
                    </div>
                  </div>
                  <StatusBadge tone={item.status === "Online" ? "good" : undefined}>{item.status}</StatusBadge>
                </div>
              ))
            )}
          </Panel>
        </div>
      </div>

      <aside className="right-rail">
        <Panel title="Room Rail" eyebrow="Activity and settings">
          <div className="rail-tabs" role="tablist" aria-label="Room rail">
            {["Activity", "Chat", "Invite", "Settings"].map((item) => (
              <button key={item} className={rail === item ? "active" : ""} type="button" onClick={() => setRail(item)}>
                {item}
              </button>
            ))}
          </div>

          {rail === "Activity" ? (
            <div className="timeline-list">
              {queueState.length === 0 ? (
                <EmptyState title="No activity yet" description="Playback and queue events will stream here." />
              ) : (
                queueState.slice(0, 8).map((item) => (
                  <div className="timeline-item" key={`activity-${item.id}`}>
                    <Radio size={15} />
                    <div className="row-title">
                      <strong>{item.by} added {item.provider.toLowerCase()} media</strong>
                      <span>{item.createdAt}</span>
                    </div>
                  </div>
                ))
              )}
            </div>
          ) : null}

          {rail === "Chat" ? (
            <div className="chat-panel">
              <div className="chat-messages">
                {messages.length === 0 ? (
                  <EmptyState title="No chat messages" description="Messages in this room will persist here." />
                ) : (
                  messages.map((message) => (
                    <div className="timeline-item" key={message.id}>
                      <Avatar name={message.user} src={message.avatarUrl} size={26} />
                      <div className="row-title">
                        <strong>{message.user}</strong>
                        <span>{message.body}</span>
                      </div>
                      {canManageRoom ? (
                        <button className="icon-button compact danger animated-button" type="button" title="Delete message" onClick={() => emit("chat:delete", { messageId: message.id })}>
                          <Trash2 size={14} />
                        </button>
                      ) : null}
                    </div>
                  ))
                )}
              </div>
              <form className="chat-input" onSubmit={sendMessage}>
                <MessageSquareText size={15} />
                <input name="body" placeholder="Message this room" maxLength={1000} />
              </form>
            </div>
          ) : null}

          {rail === "Invite" ? (
            <div className="settings-list" id="room-invite">
              {savedState ? <p className="form-success">Room invite settings saved.</p> : null}
              {errorState === "user" ? <p className="form-error">No matching user was found, or the user already owns the room.</p> : null}
              {canManageRoom ? (
                <>
                  <form className="form compact-form" action={createRoomInvite}>
                    <input type="hidden" name="roomId" value={roomId} />
                    <label>
                      Invite expires after days
                      <input className="input" name="expiresDays" type="number" min="0" max="365" defaultValue="7" />
                    </label>
                    <button className="button primary animated-button" type="submit">
                      <Ticket size={16} /> Create invite code
                    </button>
                  </form>
                  {invites.length > 0 ? (
                    <div className="settings-list">
                      {invites.map((invite) => (
                        <div className="setting-row" key={invite.id}>
                          <Ticket size={16} />
                          <span>
                            <strong>{invite.code}</strong>
                            <small>
                              {invite.status.toLowerCase()} by {invite.creator} - {invite.createdAt}
                              {invite.expiresAt ? ` - expires ${formatDateTime(invite.expiresAt)}` : ""}
                            </small>
                          </span>
                          <StatusBadge tone={invite.status === "ACTIVE" && !isExpired(invite.expiresAt) ? "good" : "warn"}>
                            {isExpired(invite.expiresAt) ? "expired" : invite.status.toLowerCase()}
                          </StatusBadge>
                          {invite.status === "ACTIVE" && !isExpired(invite.expiresAt) ? (
                            <form action={revokeRoomInvite}>
                              <input type="hidden" name="inviteId" value={invite.id} />
                              <button className="icon-button compact danger animated-button" type="submit" title="Revoke invite">
                                <Trash2 size={14} />
                              </button>
                            </form>
                          ) : null}
                        </div>
                      ))}
                    </div>
                  ) : null}
                  <form className="form compact-form" action={addRoomMember}>
                    <input type="hidden" name="roomId" value={roomId} />
                    <label>
                      Username
                      <input className="input" name="username" placeholder="username" autoComplete="off" required />
                    </label>
                    <label className="inline-check">
                      <input name="canManage" type="checkbox" />
                      Can manage room
                    </label>
                    <button className="button primary animated-button" type="submit">
                      <UserPlus size={16} /> Add member
                    </button>
                  </form>
                </>
              ) : (
                <p className="disabled-note">Only room managers can create invite codes or change room members.</p>
              )}
              {participants.filter((participant) => participant.role !== "Owner").length > 0 ? (
                <div className="settings-list">
                  {participants
                    .filter((participant) => participant.role !== "Owner")
                    .map((participant) => (
                      <div className="setting-row" key={`member-${participant.id}`}>
                        <Avatar name={participant.name} src={participant.avatarUrl} size={26} />
                        <span>
                          <strong>{participant.name}</strong>
                          <small>{participant.role}</small>
                        </span>
                        {canManageRoom ? (
                          <form action={removeRoomMember}>
                            <input type="hidden" name="roomId" value={roomId} />
                            <input type="hidden" name="userId" value={participant.id} />
                            <button className="icon-button compact danger animated-button" type="submit" title="Remove member">
                              <Trash2 size={14} />
                            </button>
                          </form>
                        ) : null}
                      </div>
                    ))}
                </div>
              ) : null}
            </div>
          ) : null}

          {rail === "Settings" ? (
            <div className="settings-list" id="room-settings">
              {savedState ? <p className="form-success">Room settings saved.</p> : null}
              <form className="form compact-form" action={updateRoomSettings}>
                <input type="hidden" name="roomId" value={roomId} />
                <label>
                  Room name
                  <input className="input" name="name" defaultValue={roomName} maxLength={80} required />
                </label>
                <label>
                  Visibility
                  <select className="input" name="visibility" defaultValue={roomVisibility}>
                    <option value="FRIENDS">Friends-only</option>
                    <option value="PUBLIC">Public</option>
                    <option value="EXPLICIT">Explicit members</option>
                    <option value="ROLE_RESTRICTED">Role restricted</option>
                  </select>
                </label>
                <button className="button primary animated-button" type="submit">
                  <Settings2 size={16} /> Save room settings
                </button>
              </form>
              <button className="button animated-button" type="button" onClick={() => emit("queue:remove", { mediaSourceId: playback?.mediaSourceId })} disabled={!playback?.mediaSourceId}>
                <RotateCcw size={16} /> Remove current source
              </button>
              {isPersonal ? (
                <form action={resetPersonalRoom}>
                  <input type="hidden" name="roomId" value={roomId} />
                  <button className="button danger animated-button" type="submit">
                    <RotateCcw size={16} /> Reset personal room
                  </button>
                </form>
              ) : (
                <form action={deleteRoom}>
                  <input type="hidden" name="roomId" value={roomId} />
                  <button className="button danger animated-button" type="submit">
                    <Trash2 size={16} /> Delete room
                  </button>
                </form>
              )}
              <SettingRow icon={<ShieldCheck size={16} />} label="Current visibility" value={formatVisibility(roomVisibility)} />
              <SettingRow icon={<Play size={16} />} label="Playback control" value="All authorized participants" />
              <SettingRow icon={<Settings2 size={16} />} label="Owner" value={ownerName} />
              <SettingRow icon={<Radio size={16} />} label="Sync mode" value="Persistent Socket.IO state" />
            </div>
          ) : null}
        </Panel>
      </aside>
    </section>
  );
}

function MediaPreview({
  provider,
  playbackUrl,
  originalUrl,
  title,
  youtubePlayerRef,
  videoRef,
  twitchPlayerRef,
  onReady,
  onIssue
}: {
  provider: string;
  playbackUrl: string;
  originalUrl: string;
  title: string;
  youtubePlayerRef: React.MutableRefObject<YouTubePlayer | null>;
  videoRef: React.RefObject<HTMLVideoElement | null>;
  twitchPlayerRef: React.MutableRefObject<TwitchPlayer | null>;
  onReady: () => void;
  onIssue: (message: string) => void;
}) {
  if (provider === "YOUTUBE") {
    return <YouTubePreview playbackUrl={playbackUrl} originalUrl={originalUrl} title={title} playerRef={youtubePlayerRef} onReady={onReady} onIssue={onIssue} />;
  }
  if (provider === "TWITCH") {
    return <TwitchPreview playbackUrl={playbackUrl} title={title} twitchPlayerRef={twitchPlayerRef} onReady={onReady} onIssue={onIssue} />;
  }
  if (provider === "DIRECT") {
    return <video ref={videoRef} className="media-embed" src={playbackUrl} controls playsInline onCanPlay={onReady} />;
  }
  return (
    <div className="video-state">
      <StatusBadge tone="warn">Unsupported</StatusBadge>
      <h2>{title}</h2>
      <p>This URL is stored in the queue, but it cannot be embedded directly.</p>
      <p className="eyebrow">{playbackUrl}</p>
    </div>
  );
}

function YouTubePreview({
  playbackUrl,
  originalUrl,
  title,
  playerRef,
  onReady,
  onIssue
}: {
  playbackUrl: string;
  originalUrl: string;
  title: string;
  playerRef: React.MutableRefObject<YouTubePlayer | null>;
  onReady: () => void;
  onIssue: (message: string) => void;
}) {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    let disposed = false;
    const timeout = window.setTimeout(() => {
      if (!disposed && !playerRef.current) {
        onIssue("YouTube did not expose a controllable player. If YouTube asks for sign-in, open the video directly.");
      }
    }, 6000);

    async function mount() {
      await loadYouTubeApi();
      if (disposed || !containerRef.current || !window.YT) return;
      playerRef.current?.destroy();
      const videoId = getYouTubeId(playbackUrl) || getYouTubeId(originalUrl);
      if (!videoId) {
        onIssue("This YouTube URL does not contain a usable video id.");
        return;
      }
      playerRef.current = new window.YT.Player(containerRef.current, {
        videoId,
        width: "100%",
        height: "100%",
        playerVars: {
          origin: window.location.origin,
          playsinline: 1,
          rel: 0,
          modestbranding: 1
        },
        events: {
          onReady: () => {
            window.clearTimeout(timeout);
            onReady();
          },
          onError: () => onIssue("YouTube refused this embed. Open the video on YouTube or choose another source.")
        }
      });
    }

    void mount();
    return () => {
      disposed = true;
      window.clearTimeout(timeout);
      playerRef.current?.destroy();
      playerRef.current = null;
    };
  }, [originalUrl, playbackUrl, playerRef, onIssue, onReady]);

  return <div ref={containerRef} className="media-embed youtube-embed" role="region" aria-label={title} />;
}

function TwitchPreview({
  playbackUrl,
  title,
  twitchPlayerRef,
  onReady,
  onIssue
}: {
  playbackUrl: string;
  title: string;
  twitchPlayerRef: React.MutableRefObject<TwitchPlayer | null>;
  onReady: () => void;
  onIssue: (message: string) => void;
}) {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    let disposed = false;
    const container = containerRef.current;
    if (!container) return;
    async function mount() {
      await loadTwitchApi();
      if (disposed || !container || !window.Twitch) return;
      container.replaceChildren();
      twitchPlayerRef.current?.destroy?.();
      const options = getTwitchOptions(playbackUrl);
      twitchPlayerRef.current = new window.Twitch.Player(container, {
        width: "100%",
        height: "100%",
        parent: [window.location.hostname || "localhost"],
        autoplay: false,
        ...options
      });
      onReady();
      if (!isTwitchVod(playbackUrl)) onIssue("Twitch live streams support source and play/pause sync; seeking is unavailable.");
    }
    void mount();
    return () => {
      disposed = true;
      twitchPlayerRef.current?.destroy?.();
      twitchPlayerRef.current = null;
    };
  }, [playbackUrl, twitchPlayerRef, onIssue, onReady]);

  return <div ref={containerRef} className="media-embed twitch-embed" role="region" aria-label={title} />;
}

function loadYouTubeApi() {
  if (window.YT?.Player) return Promise.resolve();
  const existing = document.querySelector<HTMLScriptElement>('script[src="https://www.youtube.com/iframe_api"]');
  if (existing) {
    return new Promise<void>((resolve) => {
      const previous = window.onYouTubeIframeAPIReady;
      window.onYouTubeIframeAPIReady = () => {
        previous?.();
        resolve();
      };
    });
  }
  return new Promise<void>((resolve) => {
    const previous = window.onYouTubeIframeAPIReady;
    window.onYouTubeIframeAPIReady = () => {
      previous?.();
      resolve();
    };
    const script = document.createElement("script");
    script.src = "https://www.youtube.com/iframe_api";
    script.async = true;
    document.head.appendChild(script);
  });
}

function loadTwitchApi() {
  if (window.Twitch) return Promise.resolve();
  const existing = document.querySelector<HTMLScriptElement>('script[src="https://player.twitch.tv/js/embed/v1.js"]');
  if (existing) {
    return new Promise<void>((resolve, reject) => {
      existing.addEventListener("load", () => resolve(), { once: true });
      existing.addEventListener("error", () => reject(new Error("Failed to load Twitch player API.")), { once: true });
    });
  }
  return new Promise<void>((resolve, reject) => {
    const script = document.createElement("script");
    script.src = "https://player.twitch.tv/js/embed/v1.js";
    script.async = true;
    script.onload = () => resolve();
    script.onerror = () => reject(new Error("Failed to load Twitch player API."));
    document.head.appendChild(script);
  });
}

function getTwitchOptions(playbackUrl: string) {
  try {
    const url = new URL(playbackUrl);
    const video = url.searchParams.get("video");
    const channel = url.searchParams.get("channel");
    return video ? { video } : { channel: channel || "" };
  } catch {
    return { channel: "" };
  }
}

function getYouTubeId(value: string) {
  try {
    const url = new URL(value);
    if (url.hostname.includes("youtu.be")) return url.pathname.slice(1);
    if (url.searchParams.get("v")) return url.searchParams.get("v");
    const embed = url.pathname.match(/\/embed\/([^/?]+)/);
    return embed?.[1] || null;
  } catch {
    return null;
  }
}

function isTwitchVod(playbackUrl: string) {
  try {
    return new URL(playbackUrl).searchParams.has("video");
  } catch {
    return false;
  }
}

function initialPlayback(item: QueueItem): PlaybackState {
  return {
    mediaSourceId: item.id,
    media: item,
    status: "PAUSED",
    position: 0,
    rate: 1,
    updatedAt: Date.now()
  };
}

function mergePresence(allowed: Participant[], online: Participant[]) {
  const rows = new Map(allowed.map((item) => [item.id, item]));
  for (const item of online) rows.set(item.id, { ...rows.get(item.id), ...item, role: rows.get(item.id)?.role || "Participant" });
  return [...rows.values()];
}

function SettingRow({ icon, label, value }: { icon: React.ReactNode; label: string; value: string }) {
  return (
    <div className="setting-row">
      {icon}
      <span>
        <strong>{label}</strong>
        <small>{value}</small>
      </span>
    </div>
  );
}

function formatVisibility(value: string) {
  return value.toLowerCase().replace("_", " ");
}

function syncStatusTone(value: "idle" | "pending" | "confirmed" | "failed") {
  if (value === "confirmed") return "good";
  if (value === "pending") return "info";
  if (value === "failed") return "danger";
  return "neutral";
}

function syncStatusLabel(value: "idle" | "pending" | "confirmed" | "failed", lastAckAt: number | null) {
  if (value === "confirmed" && lastAckAt) {
    return `sync ack ${new Intl.DateTimeFormat("en", { hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date(lastAckAt))}`;
  }
  if (value === "pending") return "sync pending";
  if (value === "failed") return "sync unconfirmed";
  return "sync idle";
}

function formatDateTime(value: string) {
  return new Intl.DateTimeFormat("en", { month: "short", day: "numeric", year: "numeric" }).format(new Date(value));
}

function isExpired(value: string | null) {
  return Boolean(value && new Date(value).getTime() < Date.now());
}
