import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Check, ListPlus, Plus, Youtube } from "lucide-react";
import { api, type Playlist } from "../lib/api";

// A small popover (portaled to body, so the feed grid can't clip it) for toggling a
// video's membership in the user's playlists, with inline "new playlist" creation.
export default function AddToPlaylist({
  videoId,
  className,
}: {
  videoId: string;
  className?: string;
}) {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const triggerRef = useRef<HTMLButtonElement | null>(null);
  const panelRef = useRef<HTMLDivElement | null>(null);
  const [open, setOpen] = useState(false);
  const [coords, setCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
  const [newName, setNewName] = useState("");
  const [busy, setBusy] = useState(false);

  const membership = useQuery({
    queryKey: ["playlist-membership", videoId],
    queryFn: () => api.playlists(videoId),
    enabled: open,
  });

  // Re-place once the panel is actually rendered (so we know its real height) and whenever
  // its content — hence height — changes, so the flip-above decision uses the true size.
  useLayoutEffect(() => {
    if (open) place();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, membership.data]);

  function place() {
    const r = triggerRef.current?.getBoundingClientRect();
    if (!r) return;
    const width = 240;
    const margin = 8;
    // Use the panel's real height once it's mounted; estimate on the first open.
    const h = panelRef.current?.offsetHeight ?? 300;
    let top = r.bottom + 6;
    if (top + h > window.innerHeight - margin) {
      // Not enough room below — open upward, clamped to the viewport.
      top = Math.max(margin, r.top - h - 6);
    }
    const left = Math.min(Math.max(margin, r.left), window.innerWidth - width - margin);
    setCoords({ top: Math.round(top), left: Math.round(left) });
  }

  function toggleOpen(e: React.MouseEvent) {
    e.preventDefault();
    e.stopPropagation();
    if (!open) place();
    setOpen((o) => !o);
  }

  useEffect(() => {
    if (!open) return;
    function onDoc(e: MouseEvent) {
      if (
        !panelRef.current?.contains(e.target as Node) &&
        !triggerRef.current?.contains(e.target as Node)
      )
        setOpen(false);
    }
    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") setOpen(false);
    }
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    window.addEventListener("resize", place);
    window.addEventListener("scroll", place, true);
    return () => {
      document.removeEventListener("mousedown", onDoc);
      document.removeEventListener("keydown", onKey);
      window.removeEventListener("resize", place);
      window.removeEventListener("scroll", place, true);
    };
  }, [open]);

  function refresh() {
    qc.invalidateQueries({ queryKey: ["playlist-membership", videoId] });
    qc.invalidateQueries({ queryKey: ["playlists"] });
    // Also invalidate every open/cached playlist detail (["playlist", id]) so the
    // Playlists page reflects the add/remove when navigated to, not only after F5.
    qc.invalidateQueries({ queryKey: ["playlist"] });
  }

  async function toggle(pl: Playlist) {
    if (busy) return;
    setBusy(true);
    try {
      if (pl.has_video) await api.removeFromPlaylist(pl.id, videoId);
      else await api.addToPlaylist(pl.id, videoId);
      refresh();
    } finally {
      setBusy(false);
    }
  }

  async function createAndAdd(e: React.FormEvent) {
    e.preventDefault();
    const name = newName.trim();
    if (!name || busy) return;
    setBusy(true);
    try {
      const pl = await api.createPlaylist(name);
      await api.addToPlaylist(pl.id, videoId);
      setNewName("");
      refresh();
    } finally {
      setBusy(false);
    }
  }

  // All of the user's playlists are add targets, including YouTube-linked ones — adding
  // marks them dirty, and a later "Sync to YouTube" pushes the change back.
  const lists = membership.data ?? [];

  return (
    <>
      <button
        ref={triggerRef}
        onClick={toggleOpen}
        title={t("playlists.addToPlaylist")}
        aria-label={t("playlists.addToPlaylist")}
        className={className ?? "p-1.5 rounded-md hover:bg-surface text-muted hover:text-fg"}
      >
        <ListPlus className="w-4 h-4" />
      </button>

      {open &&
        createPortal(
          <div
            ref={panelRef}
            style={{ position: "fixed", top: coords.top, left: coords.left, width: 240 }}
            className="z-50 glass rounded-xl p-2 shadow-2xl"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="text-xs text-muted px-1.5 pb-1.5">
              {t("playlists.addToPlaylist")}
            </div>
            <div className="max-h-56 overflow-y-auto">
              {lists.length === 0 && !membership.isLoading && (
                <div className="text-xs text-muted px-1.5 py-2">
                  {t("playlists.noneYet")}
                </div>
              )}
              {lists.map((pl) => (
                <button
                  key={pl.id}
                  onClick={() => toggle(pl)}
                  disabled={busy}
                  className="w-full flex items-center gap-2 text-sm px-1.5 py-1.5 rounded-lg text-fg hover:bg-card/60 transition disabled:opacity-50"
                >
                  <span
                    className={`grid place-items-center w-4 h-4 rounded border ${
                      pl.has_video
                        ? "bg-accent border-accent text-accent-fg"
                        : "border-border"
                    }`}
                  >
                    {pl.has_video && <Check className="w-3 h-3" />}
                  </span>
                  <span className="truncate flex-1 text-left">
                    {pl.kind === "watch_later" ? t("playlists.watchLater") : pl.name}
                  </span>
                  {(pl.source === "youtube" || pl.yt_playlist_id) && (
                    <Youtube className="w-3 h-3 shrink-0 text-muted" />
                  )}
                </button>
              ))}
            </div>
            <form
              onSubmit={createAndAdd}
              className="flex items-center gap-1.5 border-t border-border mt-1.5 pt-1.5"
            >
              <Plus className="w-3.5 h-3.5 text-muted shrink-0" />
              <input
                value={newName}
                onChange={(e) => setNewName(e.target.value)}
                placeholder={t("playlists.newPlaylist")}
                className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1 text-xs outline-none focus:border-accent"
              />
            </form>
          </div>,
          document.body
        )}
    </>
  );
}
