"use client";

import Hls from "hls.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";

export type Rendition = { id: number; label: string; height: number; bitrateKbps: number; url: string };
export type CaptionTrack = {
  lang: string;
  label: string;
  cues: { start: number; end: number; text: string }[];
};

export type PlayerVideo = {
  id: string;
  title: string;
  mp4Url: string | null;
  hlsUrl: string | null;
  thumbnailUrl: string | null;
  durationSeconds: number;
  chapters: { time: number; title: string }[];
  captions: CaptionTrack[];
  renditions: Rendition[];
  progressSeconds: number | null;
};

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

function fmt(seconds: number) {
  if (!Number.isFinite(seconds)) return "0:00";
  const s = Math.max(0, Math.floor(seconds));
  const h = Math.floor(s / 3600);
  const m = Math.floor((s % 3600) / 60);
  const sec = s % 60;
  return h ? `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}` : `${m}:${String(sec).padStart(2, "0")}`;
}

export function VideoPlayer({
  video,
  theater,
  onToggleTheater,
  onNext,
  autoplayNext = true,
  onProgress,
  isLive = false,
}: {
  video: PlayerVideo;
  theater: boolean;
  onToggleTheater: () => void;
  onNext?: () => void;
  autoplayNext?: boolean;
  onProgress?: (seconds: number) => void;
  isLive?: boolean;
}) {
  const wrapRef = useRef<HTMLDivElement | null>(null);
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const hlsRef = useRef<Hls | null>(null);
  const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const lastSaved = useRef(0);

  const [playing, setPlaying] = useState(false);
  const [current, setCurrent] = useState(0);
  const [duration, setDuration] = useState(video.durationSeconds || 0);
  const [buffered, setBuffered] = useState(0);
  const [volume, setVolume] = useState(1);
  const [muted, setMuted] = useState(false);
  const [speed, setSpeed] = useState(1);
  const [quality, setQuality] = useState("auto");
  const [levels, setLevels] = useState<{ index: number; label: string }[]>([]);
  const [showCaptions, setShowCaptions] = useState(false);
  const [captionTrack, setCaptionTrack] = useState(0);
  const [captionText, setCaptionText] = useState("");
  const [menu, setMenu] = useState<null | "quality" | "speed" | "captions" | "settings">(null);
  const [docked, setDocked] = useState(false);
  const [ready, setReady] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [hoverTime, setHoverTime] = useState<{ x: number; time: number } | null>(null);
  const [autoplay, setAutoplay] = useState(autoplayNext);

  const sources = useMemo(() => {
    const list = video.renditions?.length
      ? video.renditions.map((r) => ({ label: r.label, url: r.url, height: r.height }))
      : [];
    if (video.mp4Url && !list.some((l) => l.url === video.mp4Url)) {
      list.push({ label: `${video.durationSeconds ? "Source" : "Auto"}`, url: video.mp4Url, height: 1080 });
    }
    return list;
  }, [video.renditions, video.mp4Url, video.durationSeconds]);

  /* ---------------------------------------------------------- attach media */
  const attach = useCallback(
    (sourceUrl: string) => {
      const el = videoRef.current;
      if (!el) return;
      setError(null);
      if (hlsRef.current) {
        hlsRef.current.destroy();
        hlsRef.current = null;
      }
      const isHls = sourceUrl.includes(".m3u8");
      if (isHls && Hls.isSupported()) {
        const hls = new Hls({ capLevelToPlayerSize: true, startLevel: -1, maxBufferLength: 30 });
        hls.loadSource(sourceUrl);
        hls.attachMedia(el);
        hls.on(Hls.Events.MANIFEST_PARSED, (_e, data) => {
          setLevels(data.levels.map((level, index) => ({ index, label: `${level.height}p` })));
          setReady(true);
        });
        hls.on(Hls.Events.LEVEL_SWITCHED, (_e, data) => {
          const level = hls.levels[data.level];
          if (level?.height) setQuality((q) => (q === "auto" ? "auto" : q));
        });
        hls.on(Hls.Events.ERROR, (_e, data) => {
          if (data.fatal) setError("This stream could not be loaded. Try another quality.");
        });
        hlsRef.current = hls;
      } else {
        el.src = sourceUrl;
        setLevels([]);
        setReady(true);
      }
    },
    [],
  );

  useEffect(() => {
    const primary = video.hlsUrl ?? sources[sources.length - 1]?.url ?? video.mp4Url;
    if (!primary) {
      setError("No playable source available yet.");
      return;
    }
    attach(primary);
    return () => {
      if (hlsRef.current) {
        hlsRef.current.destroy();
        hlsRef.current = null;
      }
    };
  }, [video.id, video.hlsUrl, video.mp4Url, attach, sources]);

  /* ------------------------------------------------------ resume position */
  useEffect(() => {
    const el = videoRef.current;
    if (!el || !ready) return;
    if (video.progressSeconds && video.progressSeconds > 5 && video.progressSeconds < (duration || Infinity) - 15) {
      el.currentTime = video.progressSeconds;
    }
  }, [ready, video.progressSeconds, duration]);

  /* ------------------------------------------------------------- controls */
  const togglePlay = useCallback(() => {
    const el = videoRef.current;
    if (!el) return;
    if (el.paused) void el.play().catch(() => setError("Tap the video to start playback."));
    else el.pause();
  }, []);

  const seek = useCallback((seconds: number) => {
    const el = videoRef.current;
    if (!el) return;
    el.currentTime = Math.max(0, Math.min(el.duration || 1e9, seconds));
  }, []);

  const changeVolume = useCallback((value: number) => {
    const el = videoRef.current;
    if (!el) return;
    el.volume = value;
    el.muted = value === 0;
    setVolume(value);
    setMuted(value === 0);
  }, []);

  const changeQuality = useCallback(
    (label: string) => {
      setQuality(label);
      setMenu(null);
      const hls = hlsRef.current;
      if (hls) {
        if (label === "auto") {
          hls.currentLevel = -1;
          return;
        }
        const index = hls.levels.findIndex((level) => `${level.height}p` === label);
        if (index >= 0) {
          hls.currentLevel = index;
          return;
        }
      }
      const source = sources.find((s) => s.label === label);
      if (source) {
        const time = videoRef.current?.currentTime ?? 0;
        const wasPlaying = !videoRef.current?.paused;
        attach(source.url);
        requestAnimationFrame(() => {
          if (videoRef.current) {
            videoRef.current.currentTime = time;
            if (wasPlaying) void videoRef.current.play().catch(() => undefined);
          }
        });
      }
    },
    [sources, attach],
  );

  const toggleFullscreen = useCallback(() => {
    const node = wrapRef.current;
    if (!node) return;
    if (document.fullscreenElement) void document.exitFullscreen();
    else void node.requestFullscreen?.().catch(() => undefined);
  }, []);

  const togglePip = useCallback(async () => {
    const el = videoRef.current;
    if (!el) return;
    try {
      if (document.pictureInPictureElement) await document.exitPictureInPicture();
      else await el.requestPictureInPicture();
    } catch {
      setError("Picture-in-picture is not supported on this device.");
    }
  }, []);

  /* ------------------------------------------------------ progress saving */
  useEffect(() => {
    if (!onProgress) return;
    const interval = setInterval(() => {
      const el = videoRef.current;
      if (!el || el.paused) return;
      if (el.currentTime - lastSaved.current > 8) {
        lastSaved.current = el.currentTime;
        onProgress(el.currentTime);
      }
    }, 5000);
    return () => clearInterval(interval);
  }, [onProgress]);

  useEffect(() => {
    const el = videoRef.current;
    if (!el) return;
    const handler = () => onProgress?.(el.currentTime);
    el.addEventListener("pause", handler);
    window.addEventListener("pagehide", handler);
    return () => {
      el.removeEventListener("pause", handler);
      window.removeEventListener("pagehide", handler);
    };
  }, [onProgress]);

  /* ------------------------------------------------------------ listeners */
  useEffect(() => {
    const el = videoRef.current;
    if (!el) return;
    const onTime = () => {
      setCurrent(el.currentTime);
      if (el.buffered.length) setBuffered(el.buffered.end(el.buffered.length - 1));
      const track = video.captions[captionTrack];
      if (track) {
        const cue = track.cues.find((c) => el.currentTime >= c.start && el.currentTime <= c.end);
        setCaptionText(cue?.text ?? "");
      }
    };
    const onLoaded = () => {
      setDuration(el.duration || video.durationSeconds);
      setReady(true);
    };
    const onPlay = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    const onEnded = () => {
      setPlaying(false);
      if (autoplay && onNext) onNext();
    };
    el.addEventListener("timeupdate", onTime);
    el.addEventListener("loadedmetadata", onLoaded);
    el.addEventListener("play", onPlay);
    el.addEventListener("pause", onPause);
    el.addEventListener("ended", onEnded);
    return () => {
      el.removeEventListener("timeupdate", onTime);
      el.removeEventListener("loadedmetadata", onLoaded);
      el.removeEventListener("play", onPlay);
      el.removeEventListener("pause", onPause);
      el.removeEventListener("ended", onEnded);
    };
  }, [autoplay, onNext, video.captions, video.durationSeconds, captionTrack]);

  /* --------------------------------------------------- keyboard shortcuts */
  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      const target = event.target as HTMLElement | null;
      if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable)) return;
      const el = videoRef.current;
      if (!el) return;
      switch (event.key.toLowerCase()) {
        case " ":
        case "k":
          event.preventDefault();
          togglePlay();
          break;
        case "j":
          seek(el.currentTime - 10);
          break;
        case "l":
          seek(el.currentTime + 10);
          break;
        case "arrowleft":
          event.preventDefault();
          seek(el.currentTime - 5);
          break;
        case "arrowright":
          event.preventDefault();
          seek(el.currentTime + 5);
          break;
        case "arrowup":
          event.preventDefault();
          changeVolume(Math.min(1, el.volume + 0.1));
          break;
        case "arrowdown":
          event.preventDefault();
          changeVolume(Math.max(0, el.volume - 0.1));
          break;
        case "m":
          el.muted = !el.muted;
          setMuted(el.muted);
          break;
        case "f":
          toggleFullscreen();
          break;
        case "t":
          onToggleTheater();
          break;
        case "c":
          setShowCaptions((v) => !v);
          break;
        case "p":
          void togglePip();
          break;
        case "i":
          setDocked((v) => !v);
          break;
        case ">":
          setSpeed((s) => SPEEDS[Math.min(SPEEDS.indexOf(s) + 1, SPEEDS.length - 1)]);
          break;
        case "<":
          setSpeed((s) => SPEEDS[Math.max(SPEEDS.indexOf(s) - 1, 0)]);
          break;
        default:
          if (/^[0-9]$/.test(event.key)) seek(((duration || 0) * Number(event.key)) / 10);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [changeVolume, duration, onToggleTheater, seek, togglePip, togglePlay, toggleFullscreen]);

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

  /* -------------------------------------------------------- mini (docked) */
  useEffect(() => {
    const node = wrapRef.current;
    if (!node) return;
    const observer = new IntersectionObserver(
      (entries) => {
        const entry = entries[0];
        if (entry.boundingClientRect.top < 0) setDocked(true);
      },
      { threshold: 0 },
    );
    observer.observe(node);
    return () => observer.disconnect();
  }, []);

  /* ------------------------------------------------------------- UI state */
  const showControls = useCallback(() => {
    const node = wrapRef.current;
    if (!node) return;
    node.dataset.controls = "1";
    if (hideTimer.current) clearTimeout(hideTimer.current);
    hideTimer.current = setTimeout(() => {
      if (!videoRef.current?.paused) node.dataset.controls = "0";
    }, 2600);
  }, []);

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

  const activeChapterIndex = useMemo(() => {
    if (!video.chapters?.length) return -1;
    let index = -1;
    video.chapters.forEach((chapter, i) => {
      if (current >= chapter.time) index = i;
    });
    return index;
  }, [current, video.chapters]);

  const progressPercent = duration ? (current / duration) * 100 : 0;
  const qualityLabels = [...new Set([...levels.map((l) => l.label), ...sources.map((s) => s.label)])];

  return (
    <>
      <div
        ref={wrapRef}
        data-controls="1"
        onMouseMove={showControls}
        onTouchStart={showControls}
        className={`group relative overflow-hidden bg-black transition-all duration-300 ${
          docked
            ? "fixed bottom-16 right-2 z-40 w-64 rounded-xl border border-line shadow-2xl sm:bottom-4 sm:w-80"
            : theater
              ? "aspect-[21/9] max-h-[85vh] w-full"
              : "aspect-video w-full"
        } rounded-none sm:rounded-xl`}
      >
        <video
          ref={videoRef}
          poster={video.thumbnailUrl ?? undefined}
          playsInline
          preload="metadata"
          className="h-full w-full bg-black object-contain"
          onClick={togglePlay}
          onDoubleClick={toggleFullscreen}
          crossOrigin="anonymous"
        />

        {!playing && ready ? (
          <button
            onClick={togglePlay}
            aria-label="Play"
            className="absolute inset-0 grid place-items-center bg-black/25 transition"
          >
            <span className="grid h-16 w-16 place-items-center rounded-full bg-brand/95 shadow-2xl transition hover:scale-105">
              <svg viewBox="0 0 24 24" className="h-8 w-8 text-white" fill="currentColor" aria-hidden>
                <path d="M8 5v14l11-7z" />
              </svg>
            </span>
          </button>
        ) : null}

        {!ready && !error ? (
          <div className="absolute inset-0 grid place-items-center bg-black/60">
            <span className="h-10 w-10 animate-spin rounded-full border-[3px] border-white/30 border-t-white" />
          </div>
        ) : null}

        {error ? (
          <div className="absolute inset-0 grid place-items-center bg-black/80 p-6 text-center text-sm text-white">
            <div>
              <p className="mb-2 font-semibold">Playback problem</p>
              <p className="text-white/70">{error}</p>
              <button className="mt-3 rounded-full bg-white/15 px-4 py-1.5 text-white" onClick={() => attach(video.hlsUrl ?? video.mp4Url ?? "")}>
                Retry
              </button>
            </div>
          </div>
        ) : null}

        {showCaptions && captionText ? (
          <p className="pointer-events-none absolute bottom-20 left-1/2 max-w-[85%] -translate-x-1/2 rounded bg-black/80 px-3 py-1 text-center text-sm text-white sm:text-base">
            {captionText}
          </p>
        ) : null}

        {isLive ? (
          <span className="absolute left-3 top-3 flex items-center gap-1.5 rounded bg-brand px-2 py-0.5 text-[11px] font-bold uppercase text-white">
            <span className="h-1.5 w-1.5 rounded-full bg-white live-dot" /> Live
          </span>
        ) : null}

        {/* progress bar */}
        <div
          className="absolute bottom-11 left-0 right-0 z-10 px-3 transition-opacity duration-200 group-data-[controls=0]:opacity-0"
          onMouseMove={(event) => {
            const rect = event.currentTarget.getBoundingClientRect();
            const ratio = (event.clientX - rect.left) / rect.width;
            setHoverTime({ x: ratio * rect.width, time: ratio * duration });
          }}
          onMouseLeave={() => setHoverTime(null)}
        >
            {/* progress bar */}
        <div
          className="absolute bottom-11 left-0 right-0 z-10 px-3 transition-opacity duration-200 group-data-[controls=0]:opacity-0"
          onMouseMove={(event) => {
            const rect = event.currentTarget.getBoundingClientRect();
            const ratio = (event.clientX - rect.left) / rect.width;
            setHoverTime({ x: ratio * rect.width, time: ratio * duration });
          }}
          onMouseLeave={() => setHoverTime(null)}
        >
          {video.chapters?.length ? (
            <div className="mb-1 flex gap-0.5">
              {video.chapters.map((chapter, index) => {
                const start = chapter.time;
                const end = video.chapters[index + 1]?.time ?? duration;
                const width = duration ? ((end - start) / duration) * 100 : 0;
                const filled = current >= start;
                return (
                  <div key={chapter.title} className="relative h-1" style={{ width: `${width}%` }}>
                    <div className={`h-full w-full rounded-sm ${filled ? "bg-brand" : "bg-white/30"}`} />
                  </div>
                );
              })}
            </div>
          ) : null}
          <div className="relative h-1 w-full rounded bg-white/30">
            <div className="absolute inset-y-0 left-0 rounded bg-white/50" style={{ width: `${duration ? (buffered / duration) * 100 : 0}%` }} />
            <div className="absolute inset-y-0 left-0 rounded bg-brand" style={{ width: `${progressPercent}%` }} />
            <span
              className="absolute top-1/2 h-3.5 w-3.5 -translate-y-1/2 rounded-full bg-brand transition-transform group-hover:scale-100"
              style={{ left: `calc(${progressPercent}% - 7px)` }}
            />
            {hoverTime ? (
              <span className="absolute -top-8 -translate-x-1/2 rounded bg-black/90 px-1.5 py-0.5 text-[11px] text-white" style={{ left: hoverTime.x }}>
                {fmt(hoverTime.time)}
              </span>
            ) : null}
          </div>
        </div>

        {/* controls */}
        <div className="absolute bottom-0 left-0 right-0 z-10 flex items-center gap-1 bg-gradient-to-t from-black/85 to-transparent px-2 pb-2 pt-8 text-white transition-opacity duration-200 group-data-[controls=0]:opacity-0">
          <button onClick={togglePlay} aria-label={playing ? "Pause" : "Play"} className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
            {playing ? (
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M6 5h4v14H6zm8 0h4v14h-4z" /></svg>
            ) : (
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M8 5v14l11-7z" /></svg>
            )}
          </button>

          <button onClick={() => seek(current + 10)} aria-label="Forward 10 seconds" className="grid h-9 w-9 place-items-center rounded-full text-xs font-bold hover:bg-white/15">
            10s
          </button>

          <div className="group/vol flex items-center">
            <button onClick={() => changeVolume(muted ? 0.8 : 0)} aria-label={muted ? "Unmute" : "Mute"} className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
              {muted || volume === 0 ? (
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M16.5 12A4.5 4.5 0 0 0 14 8v2.2l2.5 2.5zM19 12a7.9 7.9 0 0 1-1.2 4.2l1.5 1.5A9.9 9.9 0 0 0 21 12c0-4.3-3-8-7-9v2.1c2.9.9 5 3.5 5 6.9zM4.3 3 3 4.3 7.7 9H3v6h4l5 5v-6.7l4.3 4.3c-.7.5-1.4.9-2.3 1.2v2.1c1.4-.3 2.6-.9 3.7-1.7l2 2 1.3-1.3zM12 4 9.9 6.1 12 8.2z" /></svg>
              ) : (
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M3 9v6h4l5 5V4L7 9zm13.5 3A4.5 4.5 0 0 0 14 8v8a4.5 4.5 0 0 0 2.5-4zM14 3.2v2.1c2.9.9 5 3.5 5 6.7s-2.1 5.8-5 6.7v2.1c4-.9 7-4.6 7-8.8s-3-7.9-7-8.8z" /></svg>
              )}
            </button>
            <input
              type="range"
              min={0}
              max={1}
              step={0.05}
              value={muted ? 0 : volume}
              onChange={(event) => changeVolume(Number(event.target.value))}
              aria-label="Volume"
              className="pt-range w-0 opacity-0 transition-all group-hover/vol:w-16 group-hover/vol:opacity-100 sm:w-16 sm:opacity-100"
            />
          </div>

          <span className="ml-1 select-none text-xs tabular-nums text-white/90">
            {fmt(current)} / {isLive ? "live" : fmt(duration)}
          </span>

          <div className="ml-auto flex items-center gap-1">
            {video.captions?.length ? (
              <button
                onClick={() => setShowCaptions((v) => !v)}
                aria-label="Toggle captions"
                className={`grid h-9 min-w-9 place-items-center rounded px-1 text-[11px] font-bold hover:bg-white/15 ${showCaptions ? "text-brand" : ""}`}
              >
                CC
              </button>
            ) : null}

            <div className="relative">
              <button onClick={() => setMenu(menu === "settings" ? null : "settings")} aria-label="Settings" className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M12 15.5A3.5 3.5 0 1 1 15.5 12 3.5 3.5 0 0 1 12 15.5zm7.4-2.6.1-.9-.1-.9 2-1.5-2-3.4-2.3 1a7.4 7.4 0 0 0-1.6-.9L15 3H9l-.5 2.3c-.6.2-1.1.5-1.6.9l-2.3-1-2 3.4 2 1.5-.1.9.1.9-2 1.5 2 3.4 2.3-1c.5.4 1 .7 1.6.9L9 21h6l.5-2.3c.6-.2 1.1-.5 1.6-.9l2.3 1 2-3.4z" /></svg>
              </button>
              {menu === "settings" ? (
                <div className="absolute bottom-11 right-0 w-56 overflow-hidden rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("quality")}>
                    <span>Quality</span>
                    <span className="text-white/60">{quality}</span>
                  </button>
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("speed")}>
                    <span>Playback speed</span>
                    <span className="text-white/60">{speed}×</span>
                  </button>
                  {video.captions?.length ? (
                    <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("captions")}>
                      <span>Subtitles</span>
                      <span className="text-white/60">{showCaptions ? video.captions[captionTrack]?.label ?? "On" : "Off"}</span>
                    </button>
                  ) : null}
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => { setAutoplay((v) => !v); setMenu(null); }}>
                    <span>Autoplay</span>
                    <span className="text-white/60">{autoplay ? "On" : "Off"}</span>
                  </button>
                </div>
              ) : null}
              {menu === "quality" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <p className="px-3 pb-1 text-xs uppercase text-white/50">Quality</p>
                  <button className={`w-full px-3 py-2 text-left hover:bg-white/10 ${quality === "auto" ? "text-brand" : ""}`} onClick={() => changeQuality("auto")}>
                    Auto {hlsRef.current ? "(adaptive)" : ""}
                  </button>
                  {qualityLabels.map((label) => (
                    <button key={label} className={`w-full px-3 py-2 text-left hover:bg-white/10 ${quality === label ? "text-brand" : ""}`} onClick={() => changeQuality(label)}>
                      {label}
                      {label === "2160p" ? " · 4K" : ""}
                    </button>
                  ))}
                </div>
              ) : null}
              {menu === "speed" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <p className="px-3 pb-1 text-xs uppercase text-white/50">Playback speed</p>
                  {SPEEDS.map((value) => (
                    <button key={value} className={`w-full px-3 py-2 text-left hover:bg-white/10 ${speed === value ? "text-brand" : ""}`} onClick={() => { setSpeed(value); setMenu(null); }}>
                      {value === 1 ? "Normal" : `${value}×`}
                    </button>
                  ))}
                </div>
              ) : null}
              {menu === "captions" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <button className="w-full px-3 py-2 text-left hover:bg-white/10" onClick={() => { setShowCaptions(false); setMenu(null); }}>
                    Off
                  </button>
                  {video.captions.map((track, index) => (
                    <button
                      key={track.lang}
                      className={`w-full px-3 py-2 text-left hover:bg-white/10 ${showCaptions && captionTrack === index ? "text-brand" : ""}`}
                      onClick={() => { setCaptionTrack(index); setShowCaptions(true); setMenu(null); }}
                    >
                      {track.label}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>

            <button onClick={() => setDocked((v) => !v)} aria-label="Mini player" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 sm:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M21 3H3v18h18zM5 19V7h14v12z" /></svg>
            </button>
            <button onClick={onToggleTheater} aria-label="Theater mode" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 lg:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M19 6H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2zm0 10H5V8h14z" /></svg>
            </button>
            <button onClick={togglePip} aria-label="Picture in picture" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 sm:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M19 11h-8v6h8zm4-8H1v18h22zm-2 16H3V5h18z" /></svg>
            </button>
            <button onClick={toggleFullscreen} aria-label="Fullscreen" className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M7 14H5v5h5v-2H7zm-2-4h2V7h3V5H5zm12 7h-3v2h5v-5h-2zM14 5v2h3v3h2V5z" /></svg>
            </button>
          </div>
        </div>
      </div>

      {video.chapters?.length ? (
            <div className="mb-1 flex gap-0.5">
              {video.chapters.map((chapter, index) => {
                const start = chapter.time;
                const end = video.chapters[index + 1]?.time ?? duration;
                const width = duration ? ((end - start) / duration) * 100 : 0;
                const filled = current >= start;
                return (
                  <div key={chapter.title} className="relative h-1" style={{ width: `${width}%` }}>
                    <div className={`h-full w-full rounded-sm ${filled ? "bg-brand" : "bg-white/30"}`} />
                  </div>
                );
              })}
            </div>
          ) : null}
          <div className="relative h-1 w-full rounded bg-white/30">
            <div className="absolute inset-y-0 left-0 rounded bg-white/50" style={{ width: `${duration ? (buffered / duration) * 100 : 0}%` }} />
            <div className="absolute inset-y-0 left-0 rounded bg-brand" style={{ width: `${progressPercent}%` }} />
            <span
              className="absolute top-1/2 h-3.5 w-3.5 -translate-y-1/2 rounded-full bg-brand transition-transform group-hover:scale-100"
              style={{ left: `calc(${progressPercent}% - 7px)` }}
            />
            {hoverTime ? (
              <span className="absolute -top-8 -translate-x-1/2 rounded bg-black/90 px-1.5 py-0.5 text-[11px] text-white" style={{ left: hoverTime.x }}>
                {fmt(hoverTime.time)}
              </span>
            ) : null}
          </div>
        </div>

        {/* controls */}
        <div className="absolute bottom-0 left-0 right-0 z-10 flex items-center gap-1 bg-gradient-to-t from-black/85 to-transparent px-2 pb-2 pt-8 text-white transition-opacity duration-200 group-data-[controls=0]:opacity-0">
          <button onClick={togglePlay} aria-label={playing ? "Pause" : "Play"} className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
            {playing ? (
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M6 5h4v14H6zm8 0h4v14h-4z" /></svg>
            ) : (
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M8 5v14l11-7z" /></svg>
            )}
          </button>

          <button onClick={() => seek(current + 10)} aria-label="Forward 10 seconds" className="grid h-9 w-9 place-items-center rounded-full text-xs font-bold hover:bg-white/15">
            10s
          </button>

          <div className="group/vol flex items-center">
            <button onClick={() => changeVolume(muted ? 0.8 : 0)} aria-label={muted ? "Unmute" : "Mute"} className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
              {muted || volume === 0 ? (
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M16.5 12A4.5 4.5 0 0 0 14 8v2.2l2.5 2.5zM19 12a7.9 7.9 0 0 1-1.2 4.2l1.5 1.5A9.9 9.9 0 0 0 21 12c0-4.3-3-8-7-9v2.1c2.9.9 5 3.5 5 6.9zM4.3 3 3 4.3 7.7 9H3v6h4l5 5v-6.7l4.3 4.3c-.7.5-1.4.9-2.3 1.2v2.1c1.4-.3 2.6-.9 3.7-1.7l2 2 1.3-1.3zM12 4 9.9 6.1 12 8.2z" /></svg>
              ) : (
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M3 9v6h4l5 5V4L7 9zm13.5 3A4.5 4.5 0 0 0 14 8v8a4.5 4.5 0 0 0 2.5-4zM14 3.2v2.1c2.9.9 5 3.5 5 6.7s-2.1 5.8-5 6.7v2.1c4-.9 7-4.6 7-8.8s-3-7.9-7-8.8z" /></svg>
              )}
            </button>
            <input
              type="range"
              min={0}
              max={1}
              step={0.05}
              value={muted ? 0 : volume}
              onChange={(event) => changeVolume(Number(event.target.value))}
              aria-label="Volume"
              className="pt-range w-0 opacity-0 transition-all group-hover/vol:w-16 group-hover/vol:opacity-100 sm:w-16 sm:opacity-100"
            />
          </div>

          <span className="ml-1 select-none text-xs tabular-nums text-white/90">
            {fmt(current)} / {isLive ? "live" : fmt(duration)}
          </span>

          <div className="ml-auto flex items-center gap-1">
            {video.captions?.length ? (
              <button
                onClick={() => setShowCaptions((v) => !v)}
                aria-label="Toggle captions"
                className={`grid h-9 min-w-9 place-items-center rounded px-1 text-[11px] font-bold hover:bg-white/15 ${showCaptions ? "text-brand" : ""}`}
              >
                CC
              </button>
            ) : null}

            <div className="relative">
              <button onClick={() => setMenu(menu === "settings" ? null : "settings")} aria-label="Settings" className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
                <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M12 15.5A3.5 3.5 0 1 1 15.5 12 3.5 3.5 0 0 1 12 15.5zm7.4-2.6.1-.9-.1-.9 2-1.5-2-3.4-2.3 1a7.4 7.4 0 0 0-1.6-.9L15 3H9l-.5 2.3c-.6.2-1.1.5-1.6.9l-2.3-1-2 3.4 2 1.5-.1.9.1.9-2 1.5 2 3.4 2.3-1c.5.4 1 .7 1.6.9L9 21h6l.5-2.3c.6-.2 1.1-.5 1.6-.9l2.3 1 2-3.4z" /></svg>
              </button>
              {menu === "settings" ? (
                <div className="absolute bottom-11 right-0 w-56 overflow-hidden rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("quality")}>
                    <span>Quality</span>
                    <span className="text-white/60">{quality}</span>
                  </button>
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("speed")}>
                    <span>Playback speed</span>
                    <span className="text-white/60">{speed}×</span>
                  </button>
                  {video.captions?.length ? (
                    <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => setMenu("captions")}>
                      <span>Subtitles</span>
                      <span className="text-white/60">{showCaptions ? video.captions[captionTrack]?.label ?? "On" : "Off"}</span>
                    </button>
                  ) : null}
                  <button className="flex w-full items-center justify-between px-3 py-2 hover:bg-white/10" onClick={() => { setAutoplay((v) => !v); setMenu(null); }}>
                    <span>Autoplay</span>
                    <span className="text-white/60">{autoplay ? "On" : "Off"}</span>
                  </button>
                </div>
              ) : null}
              {menu === "quality" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <p className="px-3 pb-1 text-xs uppercase text-white/50">Quality</p>
                  <button className={`w-full px-3 py-2 text-left hover:bg-white/10 ${quality === "auto" ? "text-brand" : ""}`} onClick={() => changeQuality("auto")}>
                    Auto {hlsRef.current ? "(adaptive)" : ""}
                  </button>
                  {qualityLabels.map((label) => (
                    <button key={label} className={`w-full px-3 py-2 text-left hover:bg-white/10 ${quality === label ? "text-brand" : ""}`} onClick={() => changeQuality(label)}>
                      {label}
                      {label === "2160p" ? " · 4K" : ""}
                    </button>
                  ))}
                </div>
              ) : null}
              {menu === "speed" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <p className="px-3 pb-1 text-xs uppercase text-white/50">Playback speed</p>
                  {SPEEDS.map((value) => (
                    <button key={value} className={`w-full px-3 py-2 text-left hover:bg-white/10 ${speed === value ? "text-brand" : ""}`} onClick={() => { setSpeed(value); setMenu(null); }}>
                      {value === 1 ? "Normal" : `${value}×`}
                    </button>
                  ))}
                </div>
              ) : null}
              {menu === "captions" ? (
                <div className="absolute bottom-11 right-0 max-h-72 w-56 overflow-y-auto rounded-lg bg-black/95 py-2 text-sm text-white shadow-2xl">
                  <button className="w-full px-3 py-2 text-left hover:bg-white/10" onClick={() => { setShowCaptions(false); setMenu(null); }}>
                    Off
                  </button>
                  {video.captions.map((track, index) => (
                    <button
                      key={track.lang}
                      className={`w-full px-3 py-2 text-left hover:bg-white/10 ${showCaptions && captionTrack === index ? "text-brand" : ""}`}
                      onClick={() => { setCaptionTrack(index); setShowCaptions(true); setMenu(null); }}
                    >
                      {track.label}
                    </button>
                  ))}
                </div>
              ) : null}
            </div>

            <button onClick={() => setDocked((v) => !v)} aria-label="Mini player" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 sm:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M21 3H3v18h18zM5 19V7h14v12z" /></svg>
            </button>
            <button onClick={onToggleTheater} aria-label="Theater mode" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 lg:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M19 6H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2zm0 10H5V8h14z" /></svg>
            </button>
            <button onClick={togglePip} aria-label="Picture in picture" className="hidden h-9 w-9 place-items-center rounded-full hover:bg-white/15 sm:grid">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M19 11h-8v6h8zm4-8H1v18h22zm-2 16H3V5h18z" /></svg>
            </button>
            <button onClick={toggleFullscreen} aria-label="Fullscreen" className="grid h-9 w-9 place-items-center rounded-full hover:bg-white/15">
              <svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor" aria-hidden><path d="M7 14H5v5h5v-2H7zm-2-4h2V7h3V5H5zm12 7h-3v2h5v-5h-2zM14 5v2h3v3h2V5z" /></svg>
            </button>
          </div>
        </div>


      {video.chapters?.length ? (
        <div className="mt-2 flex flex-wrap gap-2">
          {video.chapters.map((chapter, index) => (
            <button
              key={chapter.title}
              onClick={() => seek(chapter.time)}
              className={`rounded-full px-3 py-1 text-xs font-medium transition ${
                index === activeChapterIndex ? "bg-brand text-white" : "bg-chip text-fg hover:bg-surface-hover"
              }`}
            >
              {fmt(chapter.time)} · {chapter.title}
            </button>
          ))}
        </div>
      ) : null}
    </>
  );
}
