import { useEffect, useRef, useState, useCallback } from "react";
import { useParams, useNavigate, Link } from "react-router-dom";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  getVideoByYtId, updateProgress, createDownload, getDownload, deleteDownload,
  followChannelByUrl, toggleQueue, toggleLike, getChannelVideos, getChannel,
  getSettings, updateSettings, getRelatedVideos, getDownloads, rateVideo,
  getBookmarks, createBookmark, updateBookmark, deleteBookmark, importChapters, clearChapters,
  getCollections, addToCollection, getQueue,
  getVideoComments, refreshVideoComments, getAvailableSubs,
} from "../api";
import VideoCard from "../components/VideoCard";

const SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];

// ── Helpers ────────────────────────────────────────────────────────────────

function formatDuration(s) {
  if (!s) return "0:00";
  const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60), sec = s % 60;
  if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
  return `${m}:${String(sec).padStart(2, "0")}`;
}

function formatDate(s) {
  if (!s) return null;
  return new Date(s).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
}

function formatViews(n) {
  if (!n) return null;
  if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B views`;
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n >= 100_000_000 ? 0 : 1)}M views`;
  if (n >= 1_000) return `${Math.round(n / 1_000)}K views`;
  return `${n} views`;
}

function formatSubs(n) {
  if (!n) return null;
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n >= 10_000_000 ? 0 : 1)}M`;
  if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
  return String(n);
}

function avatarColor(name) {
  if (!name) return "#52525b";
  let h = 0;
  for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) % 360;
  return `hsl(${h}, 55%, 42%)`;
}

function linkify(text) {
  const parts = text.split(/(https?:\/\/[^\s)"'\]]+)/g);
  return parts.map((part, i) =>
    /^https?:\/\//.test(part) ? (
      <a key={i} href={part} target="_blank" rel="noopener noreferrer"
        className="text-accent hover:underline break-all"
        onClick={(e) => e.stopPropagation()}
      >{part}</a>
    ) : part
  );
}

// ── Description ─────────────────────────────────────────────────────────────

function DescriptionBox({ text }) {
  const [expanded, setExpanded] = useState(false);
  const lines = text.split("\n");
  const hasMore = lines.length > 2 || text.length > 200;
  const displayed = expanded ? text : lines.slice(0, 2).join("\n") + (hasMore ? "…" : "");

  return (
    <div
      className="bg-zinc-900 rounded-xl p-3 sm:p-4 cursor-pointer select-none"
      onClick={() => hasMore && setExpanded(v => !v)}
    >
      <p className="text-[13px] text-zinc-300 whitespace-pre-line leading-relaxed">
        {linkify(displayed)}
      </p>
      {hasMore && (
        <p className="text-xs font-semibold text-zinc-400 mt-2 hover:text-white">
          {expanded ? "Show less" : "Show more"}
        </p>
      )}
    </div>
  );
}

// ── Action button ────────────────────────────────────────────────────────────

function Chip({ onClick, active, disabled, children }) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={[
        "flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium transition-colors",
        active ? "bg-zinc-100 text-zinc-900 hover:bg-white" : "bg-zinc-800 text-zinc-300 hover:bg-zinc-700",
        disabled && "opacity-40 cursor-not-allowed",
      ].filter(Boolean).join(" ")}
    >
      {children}
    </button>
  );
}

// ── Sidebar mode toggle ──────────────────────────────────────────────────────

function SidebarModeToggle({ mode, onToggle }) {
  const isRandom = mode === "random";
  return (
    <button
      onClick={onToggle}
      title={isRandom ? "Switch to weighted (by score)" : "Switch to random"}
      className={`flex items-center gap-1 text-xs transition-colors ${isRandom ? "text-accent" : "text-zinc-600 hover:text-zinc-400"}`}
    >
      <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
          d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
      </svg>
      {isRandom ? "Random" : "Weighted"}
    </button>
  );
}

// ── Autoplay toggle ──────────────────────────────────────────────────────────

function AutoplayToggle({ value, onChange }) {
  return (
    <button
      onClick={() => onChange(!value)}
      className="flex items-center gap-1.5 group"
    >
      <div className={`relative w-7 h-3.5 rounded-full transition-colors ${value ? "bg-accent" : "bg-zinc-700 group-hover:bg-zinc-600"}`}>
        <div className={`absolute top-0.5 w-2.5 h-2.5 rounded-full bg-white shadow transition-transform ${value ? "translate-x-3.5" : "translate-x-0.5"}`} />
      </div>
      <span className={`text-xs ${value ? "text-accent" : "text-zinc-500 group-hover:text-zinc-400"}`}>Autoplay</span>
    </button>
  );
}

// ── Sidebar video row ────────────────────────────────────────────────────────

function VideoRow({ video: v, showChannel = false }) {
  return (
    <Link
      to={`/watch/${v.youtube_video_id}`}
      className="flex gap-3 p-2 rounded-xl hover:bg-zinc-900 transition-colors group"
    >
      <div className="relative shrink-0 w-36 aspect-video rounded-lg overflow-hidden bg-zinc-800">
        {v.thumbnail_url && (
          <img src={v.thumbnail_url} alt={v.title}
            className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
        )}
        {v.duration_seconds && (
          <span className="absolute bottom-1 right-1 bg-black/80 text-white text-[10px] font-mono px-1 py-0.5 rounded">
            {formatDuration(v.duration_seconds)}
          </span>
        )}
        {v.is_watched && (
          <span className="absolute top-1 left-1 bg-black/70 text-accent text-[10px] px-1 py-0.5 rounded">✓</span>
        )}
        {v.watch_progress_seconds > 0 && v.duration_seconds > 0 && (
          <div className="absolute bottom-0 inset-x-0 h-0.5 bg-white/20">
            <div
              className="h-full bg-accent"
              style={{ width: `${Math.min(v.watch_progress_seconds / v.duration_seconds, 1) * 100}%` }}
            />
          </div>
        )}
      </div>
      <div className="flex-1 min-w-0 pt-0.5">
        <p className="text-xs font-medium text-zinc-200 line-clamp-2 leading-snug group-hover:text-white">
          {v.title}
        </p>
        {showChannel && v.channel_name && (
          <p className="text-xs text-zinc-500 mt-0.5 truncate">{v.channel_name}</p>
        )}
        {v.published_at && (
          <p className="text-xs text-zinc-600 mt-0.5">
            {new Date(v.published_at).toLocaleDateString("en-US", { year: "numeric", month: "short" })}
          </p>
        )}
      </div>
    </Link>
  );
}

// ── Player placeholder ───────────────────────────────────────────────────────

function Placeholder({ video, dlStatus, onPlay, onDownloadAndPlay, isDownloading }) {
  const active = isDownloading || dlStatus?.status === "pending" || dlStatus?.status === "downloading";
  const pct = dlStatus?.progress_percent ?? 0;
  const polling = !active && !onPlay && !onDownloadAndPlay && dlStatus?.status !== "failed";

  return (
    <div className="relative w-full h-full bg-zinc-950 flex items-center justify-center">
      {video?.thumbnail_url && (
        <img src={video.thumbnail_url} alt="" className="absolute inset-0 w-full h-full object-cover opacity-30" />
      )}
      <div className="relative flex flex-col items-center gap-3">
        {dlStatus?.status === "failed" ? (
          <p className="text-zinc-400 text-sm">Download failed</p>
        ) : active ? (
          <>
            <svg className="w-14 h-14 -rotate-90" viewBox="0 0 56 56">
              <circle cx="28" cy="28" r="24" fill="none" stroke="#3f3f46" strokeWidth="4" />
              <circle cx="28" cy="28" r="24" fill="none" stroke="#facc15" strokeWidth="4"
                strokeDasharray={`${2 * Math.PI * 24}`}
                strokeDashoffset={`${2 * Math.PI * 24 * (1 - pct / 100)}`}
                strokeLinecap="round" style={{ transition: "stroke-dashoffset 0.4s" }} />
            </svg>
            <p className="text-zinc-300 text-sm font-mono">{pct.toFixed(0)}%</p>
            <p className="text-zinc-500 text-xs">Preparing…</p>
          </>
        ) : polling ? (
          <div className="w-6 h-6 border-2 border-accent border-t-transparent rounded-full animate-spin" />
        ) : onPlay ? (
          <button
            onClick={onPlay}
            className="group/play flex flex-col items-center gap-2"
          >
            <div className="w-20 h-20 rounded-full bg-black/50 backdrop-blur-sm border border-white/20 flex items-center justify-center group-hover/play:bg-black/70 transition-colors">
              <svg className="w-10 h-10 text-white ml-1.5" fill="currentColor" viewBox="0 0 24 24">
                <path d="M8 5v14l11-7z" />
              </svg>
            </div>
            <span className="text-white text-sm font-medium drop-shadow">Play</span>
          </button>
        ) : onDownloadAndPlay ? (
          <button
            onClick={onDownloadAndPlay}
            className="flex items-center gap-2 px-6 py-3 rounded-full bg-accent text-black font-bold text-sm hover:bg-yellow-300 transition-colors"
          >
            <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
                d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
            </svg>
            Download &amp; Watch
          </button>
        ) : null}
      </div>
    </div>
  );
}

// ── Keyboard shortcut hook ───────────────────────────────────────────────────

function useVideoKeys(videoRef, { onSpeedUp, onSpeedDown, onTheater } = {}) {
  useEffect(() => {
    const handler = (e) => {
      const tag = document.activeElement?.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;

      if (e.key === "t") { e.preventDefault(); onTheater?.(); return; }

      const v = videoRef.current;
      if (!v) return;

      switch (e.key) {
        case " ": case "k": e.preventDefault(); v.paused ? v.play() : v.pause(); break;
        case "f": e.preventDefault(); document.fullscreenElement ? document.exitFullscreen() : v.requestFullscreen(); break;
        case "m": e.preventDefault(); v.muted = !v.muted; break;
        case "ArrowLeft": case "j": e.preventDefault(); v.currentTime = Math.max(0, v.currentTime - 5); break;
        case "ArrowRight": case "l": e.preventDefault(); v.currentTime = Math.min(v.duration, v.currentTime + 5); break;
        case "ArrowUp": e.preventDefault(); v.volume = Math.min(1, v.volume + 0.1); break;
        case "ArrowDown": e.preventDefault(); v.volume = Math.max(0, v.volume - 0.1); break;
        case ">": case ".": e.preventDefault(); onSpeedUp?.(); break;
        case "<": case ",": e.preventDefault(); onSpeedDown?.(); break;
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [videoRef, onSpeedUp, onSpeedDown, onTheater]);
}

// ── Collections ──────────────────────────────────────────────────────────────

function AddToCollectionSection({ videoId }) {
  const qc = useQueryClient();
  const [open, setOpen] = useState(false);

  const { data: collections = [] } = useQuery({
    queryKey: ["collections"],
    queryFn: () => getCollections().then(r => r.data),
    staleTime: 60_000,
  });

  const addMut = useMutation({
    mutationFn: (colId) => addToCollection(colId, videoId),
    onSuccess: (_, colId) => {
      qc.invalidateQueries({ queryKey: ["collection-videos", colId] });
      setOpen(false);
    },
  });

  return (
    <div className="flex flex-col gap-2">
      <div className="flex items-center justify-between">
        <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Collections</h2>
        <button
          onClick={() => setOpen(o => !o)}
          className="flex items-center gap-1 text-xs text-zinc-500 hover:text-zinc-200 transition-colors"
        >
          <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
          </svg>
          Add
        </button>
      </div>
      {open && (
        <div className="flex flex-col gap-1 bg-zinc-800/60 rounded-xl p-2">
          {collections.length === 0 ? (
            <p className="text-xs text-zinc-600 px-2 py-1">No collections yet — create one on the Collections page.</p>
          ) : collections.map(col => (
            <button
              key={col.id}
              onClick={() => addMut.mutate(col.id)}
              disabled={addMut.isPending}
              className="flex items-center justify-between px-3 py-2 rounded-lg hover:bg-zinc-700 transition-colors text-left disabled:opacity-50"
            >
              <span className="text-sm text-zinc-200">{col.name}</span>
              <span className="text-[11px] text-zinc-600">{col.video_count} videos</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}


// ── Bookmarks ────────────────────────────────────────────────────────────────

function BookmarksSection({ videoId, videoRef }) {
  const qc = useQueryClient();
  const [editingId, setEditingId] = useState(null);
  const [editNote, setEditNote] = useState("");

  const { data: bookmarks = [] } = useQuery({
    queryKey: ["bookmarks", videoId],
    queryFn: () => getBookmarks(videoId).then(r => r.data),
    enabled: !!videoId,
    staleTime: 30_000,
  });

  // Auto-import chapters once on mount
  const importMut = useMutation({
    mutationFn: () => importChapters(videoId),
    onSuccess: (res) => {
      if (res.data.length > 0) qc.invalidateQueries({ queryKey: ["bookmarks", videoId] });
    },
  });
  useEffect(() => {
    if (videoId) importMut.mutate();
  }, [videoId]); // eslint-disable-line react-hooks/exhaustive-deps

  const clearMut = useMutation({
    mutationFn: () => clearChapters(videoId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["bookmarks", videoId] }),
  });

  const addMut = useMutation({
    mutationFn: () => {
      const secs = videoRef.current ? Math.floor(videoRef.current.currentTime) : 0;
      return createBookmark(videoId, { timestamp_seconds: secs, note: "" });
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ["bookmarks", videoId] }),
  });

  const updateMut = useMutation({
    mutationFn: ({ bookmarkId, note }) => updateBookmark(videoId, bookmarkId, { note }),
    onSuccess: () => {
      setEditingId(null);
      qc.invalidateQueries({ queryKey: ["bookmarks", videoId] });
    },
  });

  const deleteMut = useMutation({
    mutationFn: (bookmarkId) => deleteBookmark(videoId, bookmarkId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["bookmarks", videoId] }),
  });

  const chapters = bookmarks.filter(b => b.source === "auto");
  const manual = bookmarks.filter(b => b.source !== "auto");
  const currentTimeSecs = videoRef.current ? Math.floor(videoRef.current.currentTime) : 0;

  const BookmarkRow = ({ bm, isChapter }) => (
    <div className="flex items-center gap-2 group">
      <button
        onClick={() => { if (videoRef.current) videoRef.current.currentTime = bm.timestamp_seconds; }}
        className="shrink-0 px-2 py-0.5 rounded-md bg-zinc-800 text-accent text-xs font-mono hover:bg-zinc-700 transition-colors tabular-nums"
      >
        {formatDuration(bm.timestamp_seconds)}
      </button>
      {!isChapter && editingId === bm.id ? (
        <input
          autoFocus
          value={editNote}
          onChange={e => setEditNote(e.target.value)}
          onKeyDown={e => {
            if (e.key === "Enter") updateMut.mutate({ bookmarkId: bm.id, note: editNote });
            if (e.key === "Escape") setEditingId(null);
          }}
          onBlur={() => updateMut.mutate({ bookmarkId: bm.id, note: editNote })}
          className="flex-1 bg-zinc-800 text-zinc-100 text-xs rounded-md px-2 py-1 focus:outline-none border border-zinc-600"
        />
      ) : (
        <span
          onClick={() => { if (!isChapter) { setEditingId(bm.id); setEditNote(bm.note || ""); } }}
          className={`flex-1 text-xs py-0.5 truncate ${isChapter ? "text-zinc-400" : "text-zinc-400 cursor-pointer hover:text-zinc-200 transition-colors"}`}
        >
          {bm.note || (!isChapter && <span className="text-zinc-600 italic">Add note…</span>)}
        </span>
      )}
      <button
        onClick={() => deleteMut.mutate(bm.id)}
        disabled={deleteMut.isPending}
        className="shrink-0 p-1 rounded text-zinc-700 hover:text-red-400 transition-colors opacity-0 group-hover:opacity-100"
      >
        <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
        </svg>
      </button>
    </div>
  );

  return (
    <div className="flex flex-col gap-4">
      {/* Chapters (auto) */}
      {chapters.length > 0 && (
        <div className="flex flex-col gap-2">
          <div className="flex items-center justify-between">
            <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Chapters</h2>
            <button
              onClick={() => clearMut.mutate()}
              disabled={clearMut.isPending}
              className="text-[11px] text-zinc-600 hover:text-red-400 transition-colors disabled:opacity-50"
            >
              Clear all
            </button>
          </div>
          <div className="flex flex-col gap-1.5">
            {chapters.map(bm => <BookmarkRow key={bm.id} bm={bm} isChapter />)}
          </div>
        </div>
      )}

      {/* Manual bookmarks */}
      <div className="flex flex-col gap-2">
        <div className="flex items-center justify-between">
          <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Bookmarks</h2>
          <button
            onClick={() => addMut.mutate()}
            disabled={addMut.isPending}
            className="flex items-center gap-1 text-xs text-zinc-500 hover:text-zinc-200 transition-colors disabled:opacity-50"
          >
            <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
            </svg>
            {formatDuration(currentTimeSecs)}
          </button>
        </div>
        {manual.length === 0 ? (
          <p className="text-xs text-zinc-700">No bookmarks yet.</p>
        ) : (
          <div className="flex flex-col gap-1.5">
            {manual.map(bm => <BookmarkRow key={bm.id} bm={bm} isChapter={false} />)}
          </div>
        )}
      </div>
    </div>
  );
}


function CommentsSection({ youtubeVideoId }) {
  const qc = useQueryClient();

  const { data: comments = [], isLoading } = useQuery({
    queryKey: ["comments", youtubeVideoId],
    queryFn: () => getVideoComments(youtubeVideoId).then(r => r.data),
    staleTime: Infinity,
  });

  const refresh = useMutation({
    mutationFn: () => refreshVideoComments(youtubeVideoId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["comments", youtubeVideoId] }),
  });

  if (isLoading) return null;

  if (!comments.length) {
    return (
      <div className="flex items-center justify-between py-2">
        <p className="text-sm text-zinc-500">No comments loaded yet</p>
        <button
          onClick={() => refresh.mutate()}
          disabled={refresh.isPending}
          className="text-xs px-3 py-1.5 rounded-lg bg-zinc-800 text-zinc-400 hover:bg-zinc-700 transition-colors disabled:opacity-50 flex items-center gap-1.5"
        >
          {refresh.isPending ? (
            <>
              <span className="w-3 h-3 border border-zinc-500 border-t-transparent rounded-full animate-spin inline-block" />
              Fetching…
            </>
          ) : "Load comments"}
        </button>
      </div>
    );
  }

  return (
    <div className="flex flex-col gap-3">
      <div className="flex items-center justify-between">
        <p className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">
          Top comments
        </p>
        <button
          onClick={() => refresh.mutate()}
          disabled={refresh.isPending}
          className="text-xs text-zinc-600 hover:text-zinc-400 transition-colors"
        >
          {refresh.isPending ? "Refreshing…" : "Refresh"}
        </button>
      </div>
      <div className="flex flex-col gap-3">
        {comments.map((c, i) => (
          <div key={i} className="flex flex-col gap-1">
            <div className="flex items-center gap-2">
              <span className="text-xs font-medium text-zinc-300">{c.author}</span>
              {c.is_pinned && (
                <span className="text-[10px] text-accent font-medium">Pinned</span>
              )}
              {c.likes > 0 && (
                <span className="text-[10px] text-zinc-600 ml-auto">
                  👍 {c.likes >= 1000 ? `${(c.likes / 1000).toFixed(c.likes >= 10000 ? 0 : 1)}K` : c.likes}
                </span>
              )}
            </div>
            <p className="text-xs text-zinc-400 leading-relaxed">{c.text}</p>
          </div>
        ))}
      </div>
    </div>
  );
}


// ── Main component ───────────────────────────────────────────────────────────

export default function Watch() {
  const { youtubeVideoId } = useParams();
  const navigate = useNavigate();
  const qc = useQueryClient();

  const [downloadId, setDownloadId] = useState(null);
  const [fileReady, setFileReady] = useState(false);
  const [confirmedFileUrl, setConfirmedFileUrl] = useState(null);
  const [playRequested, setPlayRequested] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [queued, setQueued] = useState(null);
  const [liked, setLiked] = useState(null);
  const [disliked, setDisliked] = useState(null);
  const [isRedownloading, setIsRedownloading] = useState(false);
  const [selectedQuality, setSelectedQuality] = useState(null);
  const [selectedSubLang, setSelectedSubLang] = useState("");
  const [speed, setSpeed] = useState(1);
  const [autoplay, setAutoplay] = useState(false);
  const [theater, setTheater] = useState(false);
  const [sidebarMode, setSidebarMode] = useState(() => localStorage.getItem("sidebar-related-mode") ?? "weighted");
  const [theaterInitialized, setTheaterInitialized] = useState(false);
  const videoRef = useRef(null);
  const lastSaveRef = useRef(0);
  const pollTimerRef = useRef(null);

  const onSpeedUp = useCallback(() => {
    setSpeed(s => {
      const next = SPEEDS[Math.min(SPEEDS.indexOf(s) + 1, SPEEDS.length - 1)];
      if (videoRef.current) videoRef.current.playbackRate = next;
      return next;
    });
  }, []);

  const onSpeedDown = useCallback(() => {
    setSpeed(s => {
      const next = SPEEDS[Math.max(SPEEDS.indexOf(s) - 1, 0)];
      if (videoRef.current) videoRef.current.playbackRate = next;
      return next;
    });
  }, []);

  const onTheater = useCallback(() => {
    setTheater(t => {
      const next = !t;
      updateSettings({ theater_mode: next });
      qc.setQueryData(["settings"], (old) => old ? { ...old, theater_mode: next } : old);
      return next;
    });
  }, [qc]);
  useVideoKeys(videoRef, { onSpeedUp, onSpeedDown, onTheater });

  useEffect(() => {
    if (videoRef.current) videoRef.current.playbackRate = speed;
  }, [speed]);

  const { data: video, refetch: refetchVideo } = useQuery({
    queryKey: ["video-play", youtubeVideoId],
    queryFn: () => getVideoByYtId(youtubeVideoId)
      .then(r => r.data)
      .catch(err => err.response?.status === 404 ? null : Promise.reject(err)),
    enabled: !!youtubeVideoId,
    staleTime: 5 * 60_000,
    refetchInterval: (query) => {
      const v = query.state.data;
      return v && v.description == null ? 3000 : false;
    },
  });

  const { data: userSettings } = useQuery({
    queryKey: ["settings"],
    queryFn: () => getSettings().then(r => r.data),
    staleTime: 60_000,
  });

  useEffect(() => {
    if (userSettings?.preferred_quality && selectedQuality === null)
      setSelectedQuality(userSettings.preferred_quality);
    if (!theaterInitialized && userSettings) {
      setTheater(userSettings.theater_mode ?? false);
      setAutoplay(userSettings.autoplay_enabled ?? false);
      setTheaterInitialized(true);
    }
  }, [userSettings]);

  const { data: channel } = useQuery({
    queryKey: ["channel", video?.channel_id],
    queryFn: () => getChannel(video.channel_id).then(r => r.data),
    enabled: !!video?.channel_id,
    staleTime: 5 * 60_000,
  });

  const { data: channelVideos } = useQuery({
    queryKey: ["channel-videos", video?.channel_id],
    queryFn: () => getChannelVideos(video.channel_id).then(r => r.data),
    enabled: !!video?.channel_id,
    staleTime: 60_000,
  });

  const { data: queueVideos = [] } = useQuery({
    queryKey: ["queue"],
    queryFn: () => getQueue().then(r => r.data),
    staleTime: 60_000,
  });

  const { data: discoveryVideos = [] } = useQuery({
    queryKey: ["video-related", video?.id, sidebarMode],
    queryFn: () => getRelatedVideos(video.id, sidebarMode).then(r => r.data),
    enabled: !!video?.id,
    staleTime: sidebarMode === "random" ? 0 : 5 * 60_000,
  });

  const { data: availableSubs, isLoading: subsLoading } = useQuery({
    queryKey: ["available-subs", youtubeVideoId],
    queryFn: () => getAvailableSubs(youtubeVideoId).then(r => r.data),
    enabled: !!youtubeVideoId,
    staleTime: 10 * 60_000,
  });

  const { data: dlStatus } = useQuery({
    queryKey: ["download-status", downloadId],
    queryFn: () => getDownload(downloadId).then(r => r.data),
    enabled: !!downloadId,
    refetchInterval: (query) => {
      const s = query.state.data?.status;
      return s === "complete" || s === "failed" ? false : 1500;
    },
  });

  // Detect a download that was started elsewhere (e.g. from the VideoCard)
  const { data: allDownloads = [] } = useQuery({
    queryKey: ["downloads"],
    queryFn: () => getDownloads().then(r => r.data),
    staleTime: 5_000,
    enabled: !downloadId,
  });
  useEffect(() => {
    if (downloadId || fileReady) return;
    const active = allDownloads.find(
      d => d.youtube_video_id === youtubeVideoId &&
           (d.status === "pending" || d.status === "downloading"),
    );
    if (active) {
      setDownloadId(active.id);
      setPlayRequested(true);
    }
  }, [allDownloads, youtubeVideoId, downloadId, fileReady]);

  const pollForFile = useCallback((url) => {
    if (!url || fileReady) return;
    fetch(url, { method: "HEAD" })
      .then(res => {
        if (res.ok) { setConfirmedFileUrl(url); setFileReady(true); }
        else pollTimerRef.current = setTimeout(() => pollForFile(url), 1000);
      })
      .catch(() => { pollTimerRef.current = setTimeout(() => pollForFile(url), 1500); });
  }, [fileReady]);

  // Only poll once the backend confirms the download is fully written.
  // Polling before status==="complete" risks playing a partial file.
  useEffect(() => {
    if (fileReady || !playRequested) return;
    const backendDone = dlStatus?.status === "complete" || !!video?.is_downloaded;
    const fileUrl = dlStatus?.file_url ?? (video?.is_downloaded ? `/files/${youtubeVideoId}.mp4` : null);
    if (!backendDone || !fileUrl) return;
    pollForFile(fileUrl);
    return () => clearTimeout(pollTimerRef.current);
  }, [playRequested, fileReady, dlStatus?.status, dlStatus?.file_url, video?.is_downloaded, youtubeVideoId, pollForFile]); // eslint-disable-line react-hooks/exhaustive-deps

  const downloadMut = useMutation({
    mutationFn: ({ quality, subLang } = {}) =>
      createDownload(youtubeVideoId, quality ?? selectedQuality, subLang ?? selectedSubLang),
    onSuccess: (res) => {
      setDownloadId(res.data.id);
      refetchVideo();
    },
  });

  const handlePlay = useCallback(() => setPlayRequested(true), []);
  const handleDownloadAndPlay = useCallback(() => {
    setPlayRequested(true);
    downloadMut.mutate({});
  }, [downloadMut]);
  const handleRedownload = useCallback(async (quality) => {
    const dlId = downloadId ?? allDownloads.find(
      d => d.youtube_video_id === youtubeVideoId && d.status === "complete"
    )?.id;
    if (!dlId) return;
    setIsRedownloading(true);
    try { await deleteDownload(dlId); } catch (_) {}
    setFileReady(false);
    setConfirmedFileUrl(null);
    setDownloadId(null);
    setPlayRequested(false);
    setIsRedownloading(false);
    qc.invalidateQueries({ queryKey: ["downloads"] });
    refetchVideo();
    downloadMut.mutate({ quality });
  }, [downloadId, allDownloads, youtubeVideoId, downloadMut, refetchVideo, qc]);

  const saveProgress = useCallback((secs) => {
    if (!video?.id) return;
    lastSaveRef.current = Date.now();
    const duration = video.duration_seconds ?? 0;
    const threshold = (userSettings?.mark_watched_at_percent ?? 90) / 100;
    const watched = duration > 0 && secs >= duration * threshold;
    updateProgress(video.id, { watch_progress_seconds: secs, watched });
  }, [video, userSettings]);

  const handleTimeUpdate = useCallback(() => {
    if (!videoRef.current) return;
    const secs = Math.floor(videoRef.current.currentTime);
    setCurrentTime(secs);
    if (Date.now() - lastSaveRef.current > 30_000) saveProgress(secs);
  }, [saveProgress]);

  const handlePause = useCallback(() => {
    if (videoRef.current) saveProgress(Math.floor(videoRef.current.currentTime));
  }, [saveProgress]);

  useEffect(() => () => {
    clearTimeout(pollTimerRef.current);
  }, []);

  const followMut = useMutation({
    mutationFn: () => followChannelByUrl({ youtube_channel_id: video?.channel_youtube_id }),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["channels"] }),
  });

  const deleteMut = useMutation({
    mutationFn: () => deleteDownload(downloadId),
    onSuccess: () => {
      setFileReady(false); setConfirmedFileUrl(null); setDownloadId(null);
      qc.invalidateQueries({ queryKey: ["downloads"] });
      refetchVideo();
    },
  });

  const queueMut = useMutation({
    mutationFn: () => toggleQueue(video.id),
    onSuccess: (res) => setQueued(res.data.queued),
  });

  const likeMut = useMutation({
    mutationFn: () => toggleLike(video.id),
    onSuccess: (res) => { setLiked(res.data.liked); qc.invalidateQueries({ queryKey: ["liked-videos"] }); },
  });

  const dislikeMut = useMutation({
    mutationFn: () => rateVideo(video.id, isDisliked ? 0 : -1),
    onSuccess: (res) => setDisliked(res.data.rating === -1),
  });

  const handlePiP = useCallback(async () => {
    if (!videoRef.current) return;
    try {
      if (document.pictureInPictureElement) await document.exitPictureInPicture();
      else await videoRef.current.requestPictureInPicture();
    } catch (_) {}
  }, []);

  const isDownloading = dlStatus && (dlStatus.status === "pending" || dlStatus.status === "downloading");
  const title = video?.title ?? youtubeVideoId;
  const channelName = video?.channel_name ?? channel?.name;
  const date = formatDate(video?.published_at);
  const startAt = video?.watch_progress_seconds ?? 0;
  const isQueued = queued ?? video?.queued ?? false;
  const isLiked = liked ?? video?.liked ?? false;
  const isDisliked = disliked ?? (video?.rating === -1) ?? false;
  const dlComplete = dlStatus?.status === "complete" || video?.is_downloaded;
  const downloadedResolution = dlStatus?.resolution ?? video?.download_resolution;
  const isFollowed = followMut.isSuccess || video?.channel_followed;
  const subs = formatSubs(channel?.subscriber_count);

  const channelOtherVideos = channelVideos?.filter(v => v.youtube_video_id !== youtubeVideoId) ?? [];
  const nextFromQueue = queueVideos.find(v => v.youtube_video_id !== youtubeVideoId) ?? null;
  const nextFromChannel = channelOtherVideos.find(v => !v.is_watched) ?? channelOtherVideos[0] ?? null;
  const nextFromRelated = discoveryVideos[0] ?? null;
  const nextVideo = nextFromQueue ?? nextFromChannel ?? nextFromRelated ?? null;
  const nextVideoSource = nextFromQueue ? "queue" : nextFromChannel ? "channel" : nextFromRelated ? "related" : null;
  const channelSidebarVideos = channelOtherVideos.filter(v => v !== nextVideo).slice(0, 4);
  const tags = video?.tags ? video.tags.split(",").map(t => t.trim()).filter(Boolean).slice(0, 12) : [];
  const hasSidebar = nextVideo || channelSidebarVideos.length > 0 || discoveryVideos.length > 0;

  const handleAutoplayChange = useCallback((next) => {
    setAutoplay(next);
    updateSettings({ autoplay_enabled: next });
    qc.setQueryData(["settings"], (old) => old ? { ...old, autoplay_enabled: next } : old);
  }, [qc]);

  const handleSidebarModeToggle = useCallback(() => {
    setSidebarMode(m => {
      const next = m === "weighted" ? "random" : "weighted";
      localStorage.setItem("sidebar-related-mode", next);
      return next;
    });
  }, []);

  const handleEnded = useCallback(() => {
    if (videoRef.current) saveProgress(Math.floor(videoRef.current.currentTime));
    if (autoplay && nextVideo) navigate(`/watch/${nextVideo.youtube_video_id}`);
  }, [saveProgress, autoplay, nextVideo, navigate]);

  return (
    <div className={theater ? "flex flex-col gap-0 pb-16" : "max-w-screen-xl mx-auto flex flex-col gap-0 pb-16"}>

      {/* Back */}
      <button
        onClick={() => navigate(-1)}
        className={`flex items-center gap-1 text-zinc-500 hover:text-zinc-300 transition-colors text-sm mb-4 self-start ${theater ? "px-4" : ""}`}
      >
        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
        </svg>
        Back
      </button>

      <div className={theater ? "flex flex-col gap-6" : "flex flex-col xl:flex-row gap-6"}>

        {/* ── Left: video + info ───────────────────────────────────────────── */}
        <div className={theater ? "w-full flex flex-col gap-4 sm:gap-5" : "flex-1 min-w-0 flex flex-col gap-4 sm:gap-5"}>

          {/* Player */}
          <div className={theater ? "w-full aspect-video bg-black overflow-hidden shadow-2xl" : "w-full aspect-video bg-black rounded-2xl overflow-hidden shadow-2xl"}>
            {fileReady && playRequested ? (
              <video
                ref={videoRef}
                src={confirmedFileUrl}
                controls
                className="w-full h-full"
                onTimeUpdate={handleTimeUpdate}
                onPause={handlePause}
                onEnded={handleEnded}
                onLoadedMetadata={() => {
                  const v = videoRef.current;
                  if (!v) return;
                  v.playbackRate = speed;
                  if (video?.watch_progress_seconds > 10) v.currentTime = video.watch_progress_seconds;
                  v.play().catch(() => {});
                }}
              />
            ) : (
              <Placeholder
                video={video}
                dlStatus={dlStatus}
                isDownloading={downloadMut.isPending || isDownloading}
                onPlay={dlComplete && !playRequested ? handlePlay : null}
                onDownloadAndPlay={!dlComplete && !isDownloading && !downloadMut.isPending && !playRequested ? handleDownloadAndPlay : null}
              />
            )}
          </div>

          {/* Download progress */}
          {isDownloading && (
            <div className="h-1 w-full bg-zinc-800 rounded-full overflow-hidden -mt-2">
              <div
                className="h-full bg-accent rounded-full transition-all duration-700"
                style={{ width: `${dlStatus.progress_percent ?? 0}%` }}
              />
            </div>
          )}

          {/* Title */}
          <h1 className="text-base sm:text-xl font-bold text-white leading-snug">{title}</h1>

          {/* Meta + actions row */}
          <div className="flex items-center justify-between flex-wrap gap-3">
            <div className="flex items-center gap-2 text-xs text-zinc-500 flex-wrap">
              {date && <span>{date}</span>}
              {video?.view_count > 0 && <><span>·</span><span>{formatViews(video.view_count)}</span></>}
              {video?.like_count > 0 && <><span>·</span><span>{formatViews(video.like_count).replace(" views", "")} likes</span></>}
              {video?.category && <><span>·</span><span>{video.category}</span></>}
              {video?.duration_seconds && (
                <><span>·</span>
                <span className="tabular-nums">
                  {formatDuration(currentTime || startAt)}
                  <span className="text-zinc-700 mx-1">/</span>
                  <span className="text-zinc-600">{formatDuration(video.duration_seconds)}</span>
                </span></>
              )}
              {fileReady && isDownloading && (
                <span className="text-accent flex items-center gap-1">
                  <span className="w-1.5 h-1.5 rounded-full bg-accent inline-block animate-pulse" />
                  saving
                </span>
              )}
            </div>


            {/* Actions */}
            <div className="flex items-center gap-1.5 flex-wrap">
              {!isDownloading && !downloadMut.isPending && !isRedownloading && (
                <select
                  value={selectedQuality ?? "best"}
                  onChange={(e) => {
                    const q = e.target.value;
                    setSelectedQuality(q);
                    if (dlComplete) handleRedownload(q);
                  }}

                  className="bg-zinc-800 text-zinc-300 text-xs rounded-full px-3 py-2 border border-zinc-700 focus:outline-none focus:border-accent"
                >
                  {[
                    ["best",  "Highest available"],
                    ["2160p", "4K — 2160p"],
                    ["1440p", "2K — 1440p"],
                    ["1080p", "1080p — Full HD"],
                    ["720p",  "720p — HD"],
                    ["480p",  "480p — SD"],
                    ["360p",  "360p"],
                    ["240p",  "240p"],
                    ["144p",  "144p"],
                  ].map(([v, label]) => (
                    <option key={v} value={v}>{label}</option>
                  ))}
                </select>
              )}

              {!dlComplete && (() => {
                const manual = new Set(availableSubs?.manual ?? []);
                const auto   = (availableSubs?.auto ?? []).filter(l => !manual.has(l));
                const allLangs = [...manual, ...auto];
                if (subsLoading) return (
                  <span className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-zinc-800 text-zinc-500 text-xs">
                    <span className="w-3 h-3 border border-zinc-600 border-t-transparent rounded-full animate-spin inline-block" />
                    CC
                  </span>
                );
                if (!allLangs.length) return null;
                return (
                  <select
                    value={selectedSubLang}
                    onChange={(e) => setSelectedSubLang(e.target.value)}
                    className="bg-zinc-800 text-zinc-300 text-xs rounded-full px-3 py-2 border border-zinc-700 focus:outline-none focus:border-accent"
                    title="Subtitle language"
                  >
                    <option value="">No subtitles</option>
                    {[...manual].map(l => <option key={l} value={l}>{l}</option>)}
                    {auto.map(l => <option key={l} value={l}>{l} (auto)</option>)}
                  </select>
                );
              })()}

              {fileReady && (
                <select
                  value={speed}
                  onChange={(e) => setSpeed(Number(e.target.value))}
                  className="bg-zinc-800 text-zinc-300 text-xs rounded-full px-3 py-2 border border-zinc-700 focus:outline-none focus:border-accent"
                >
                  {SPEEDS.map(s => (
                    <option key={s} value={s}>{s === 1 ? "1× Speed" : `${s}×`}</option>
                  ))}
                </select>
              )}

              <Chip
                active={dlComplete}
                disabled={dlComplete || isDownloading || downloadMut.isPending}
                onClick={() => !dlComplete && !isDownloading && downloadMut.mutate({})}
              >
                {dlComplete ? (
                  <><svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M9 16.2l-3.5-3.5-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>Saved{downloadedResolution ? ` · ${downloadedResolution}` : ""}</>
                ) : isDownloading || downloadMut.isPending ? (
                  <><svg className="w-3.5 h-3.5 animate-spin" fill="none" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"/></svg>Downloading</>
                ) : (
                  <><svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>Download</>
                )}
              </Chip>

              {dlComplete && downloadId && (
                <Chip onClick={() => deleteMut.mutate()} disabled={deleteMut.isPending}>
                  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
                  </svg>
                  Delete
                </Chip>
              )}


              {video?.id && (
                <>
                  <Chip active={isLiked} onClick={() => likeMut.mutate()} disabled={likeMut.isPending}>
                    <svg className="w-4 h-4" fill={isLiked ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
                    </svg>
                    {isLiked ? "Liked" : "Like"}
                  </Chip>
                  <Chip active={isDisliked} onClick={() => dislikeMut.mutate()} disabled={dislikeMut.isPending} title="Not for me">
                    <svg className="w-4 h-4" fill={isDisliked ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z"/>
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 2h2.67A2.31 2.31 0 0122 4v7a2.31 2.31 0 01-2.33 2H17"/>
                    </svg>
                  </Chip>
                </>
              )}


              {video?.id && (
                <Chip active={isQueued} onClick={() => queueMut.mutate()} disabled={queueMut.isPending}>
                  <svg className="w-4 h-4" fill={isQueued ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
                  </svg>
                  {isQueued ? "Queued" : "Watch Later"}
                </Chip>
              )}

              {fileReady && document.pictureInPictureEnabled && (
                <Chip onClick={handlePiP} title="Picture in picture">
                  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <rect x="2" y="4" width="20" height="16" rx="2" strokeWidth="2"/>
                    <rect x="12" y="12" width="8" height="6" rx="1.5" strokeWidth="2"/>
                  </svg>
                </Chip>
              )}

              <Chip active={theater} onClick={() => setTheater(t => !t)} title={theater ? "Exit theater" : "Theater mode"}>
                <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  {theater ? (
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 9L4 4m0 5V4h5M15 9l5-5m0 5V4h-5M9 15l-5 5m0-5v5h5M15 15l5 5m0-5v5h-5" />
                  ) : (
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 8V5a1 1 0 011-1h3M4 16v3a1 1 0 001 1h3m10-4v3a1 1 0 01-1 1h-3M20 8V5a1 1 0 00-1-1h-3" />
                  )}
                </svg>
              </Chip>
            </div>
          </div>

          {/* Divider */}
          <div className="h-px bg-zinc-800" />

          {/* Channel row */}
          <div className="flex items-center gap-3">
            <Link to={`/channels/${video?.channel_id}`} className="shrink-0">
              {channel?.thumbnail_url ? (
                <img src={channel.thumbnail_url} alt={channelName}
                  className="w-9 h-9 sm:w-11 sm:h-11 rounded-full object-cover" />
              ) : (
                <div className="w-9 h-9 sm:w-11 sm:h-11 rounded-full flex items-center justify-center font-bold text-white text-base shrink-0"
                  style={{ backgroundColor: avatarColor(channelName) }}>
                  {channelName?.[0]?.toUpperCase()}
                </div>
              )}
            </Link>
            <div className="flex-1 min-w-0">
              <Link to={`/channels/${video?.channel_id}`}
                className="font-semibold text-white text-sm hover:text-zinc-300 transition-colors block truncate">
                {channelName ?? "Unknown channel"}
              </Link>
              {subs && !(userSettings?.hide_subscriber_counts) && <p className="text-xs text-zinc-500">{subs} subscribers</p>}
            </div>
            {video?.channel_youtube_id && !isFollowed && (
              <button
                onClick={() => followMut.mutate()}
                disabled={followMut.isPending}
                className="shrink-0 px-4 py-1.5 rounded-full bg-zinc-100 text-zinc-900 text-sm font-semibold hover:bg-white transition-colors disabled:opacity-60"
              >
                Follow
              </button>
            )}
            {isFollowed && (
              <span className="shrink-0 text-xs text-zinc-500 font-medium">Following ✓</span>
            )}
          </div>

          {/* Description */}
          {video?.description && (
            <DescriptionBox text={video.description} />
          )}

          {/* Tags */}
          {tags.length > 0 && (
            <div className="hidden sm:flex flex-wrap gap-1.5">
              {tags.map(tag => (
                <span key={tag} className="px-2.5 py-1 rounded-full bg-zinc-800/80 text-zinc-500 text-xs">
                  {tag}
                </span>
              ))}
            </div>
          )}

          {/* Collections */}
          {video?.id && <AddToCollectionSection videoId={video.id} />}

          {/* Bookmarks */}
          {video?.id && (
            <BookmarksSection videoId={video.id} videoRef={videoRef} />
          )}

          {/* Comments */}
          {video?.youtube_video_id && (
            <CommentsSection youtubeVideoId={video.youtube_video_id} />
          )}

          {/* Keyboard shortcuts hint */}
          <p className={`hidden sm:block text-xs text-zinc-700 text-center ${theater ? "max-w-4xl mx-auto w-full" : ""}`}>
            Space/K · pause &nbsp;·&nbsp; F · fullscreen &nbsp;·&nbsp; M · mute &nbsp;·&nbsp; ←/→ seek 5s &nbsp;·&nbsp; ↑/↓ volume &nbsp;·&nbsp; ,/. speed &nbsp;·&nbsp; T · theater
          </p>

          {/* ── Theater mode sidebar (grid below info) ───────────────────── */}
          {theater && hasSidebar && (
            <div className="flex flex-col gap-8 pt-6 border-t border-zinc-800">
              {nextVideo && (
                <div>
                  <div className="flex items-center justify-between mb-3">
                    <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Up Next {nextVideoSource === "queue" ? <span className="text-accent normal-case font-normal">· from queue</span> : nextVideoSource === "related" ? <span className="normal-case font-normal">· suggested</span> : null}</h2>
                    <AutoplayToggle value={autoplay} onChange={handleAutoplayChange} />
                  </div>
                  <div className="max-w-[240px]">
                    <VideoCard video={nextVideo} />
                  </div>
                </div>
              )}
              {channelSidebarVideos.length > 0 && (
                <div>
                  <div className="flex items-center justify-between mb-3">
                    <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">From {channelName}</h2>
                    {video?.channel_id && (
                      <Link to={`/channels/${video.channel_id}`} className="text-xs text-zinc-600 hover:text-zinc-400 transition-colors">
                        See all →
                      </Link>
                    )}
                  </div>
                  <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
                    {channelSidebarVideos.map(v => <VideoCard key={v.youtube_video_id} video={v} />)}
                  </div>
                </div>
              )}
              {discoveryVideos.length > 0 && (
                <div>
                  <div className="flex items-center justify-between mb-3">
                    <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Recommended for you</h2>
                    <SidebarModeToggle mode={sidebarMode} onToggle={handleSidebarModeToggle} />
                  </div>
                  <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
                    {discoveryVideos.map(v => <VideoCard key={v.youtube_video_id} video={v} />)}
                  </div>
                </div>
              )}
            </div>
          )}
        </div>

        {/* ── Normal sidebar (right column) ────────────────────────────────── */}
        {!theater && hasSidebar && (
          <div className="xl:w-[360px] shrink-0 flex flex-col gap-6">

            {/* Up Next */}
            {nextVideo && (
              <div className="flex flex-col gap-2">
                <div className="flex items-center justify-between">
                  <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">Up Next {nextVideoSource === "queue" ? <span className="text-accent normal-case font-normal">· from queue</span> : nextVideoSource === "related" ? <span className="normal-case font-normal">· suggested</span> : null}</h2>
                  <AutoplayToggle value={autoplay} onChange={handleAutoplayChange} />
                </div>
                <VideoRow video={nextVideo} />
              </div>
            )}

            {/* More from channel */}
            {channelSidebarVideos.length > 0 && (
              <div className="flex flex-col gap-2">
                <div className="flex items-center justify-between">
                  <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">
                    From {channelName}
                  </h2>
                  {video?.channel_id && (
                    <Link to={`/channels/${video.channel_id}`}
                      className="text-xs text-zinc-600 hover:text-zinc-400 transition-colors">
                      See all →
                    </Link>
                  )}
                </div>
                <div className="flex flex-col gap-1">
                  {channelSidebarVideos.map(v => (
                    <VideoRow key={v.youtube_video_id} video={v} />
                  ))}
                </div>
              </div>
            )}

            {/* Recommended from discovery */}
            {discoveryVideos.length > 0 && (
              <div className="flex flex-col gap-2">
                <div className="flex items-center justify-between">
                  <h2 className="text-xs font-semibold text-zinc-500 uppercase tracking-wider">
                    Recommended for you
                  </h2>
                  <SidebarModeToggle mode={sidebarMode} onToggle={handleSidebarModeToggle} />
                </div>
                <div className="flex flex-col gap-1">
                  {discoveryVideos.map(v => (
                    <VideoRow key={v.youtube_video_id} video={v} showChannel />
                  ))}
                </div>
              </div>
            )}

          </div>
        )}
      </div>
    </div>
  );
}
