import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { qk } from "../lib/queryKeys";
import { ArrowDown, ArrowUp, ListVideo, Pin, Plus, RefreshCw, Youtube } from "lucide-react";
import { api, type Playlist } from "../lib/api";
import { formatDuration } from "../lib/format";
import { notify } from "../lib/notifications";
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
import { LS, readAccountMerged, writeAccount } from "../lib/storage";
import { defaultLayout, type PanelLayout } from "../lib/panelLayout";
import SidePanel from "./SidePanel";
import PanelGroups, { type PanelItem } from "./PanelGroups";
import { usePlaylists, usePlaylistsActions } from "./PlaylistsProvider";
import { useMe } from "../lib/useMe";
import { playlistName } from "../lib/playlistName";

type PlSort = {
  key: "custom" | "name" | "count" | "duration";
  dir: "asc" | "desc";
  dirtyFirst: boolean;
};
const PL_SORT_DEFAULT: PlSort = { key: "custom", dir: "asc", dirtyFirst: false };

// The Playlists module's left rail — lifted to App level (like the filter rails) so it floats in the
// shared SidePanel with the same collapse-to-tab behavior. It owns the list query + its own rail
// controls (sort / new / sync); the selected playlist is App state so the module's detail pane
// (Playlists.tsx) reacts to it. Grouped into two islands: "manage" and the playlist list.
export default function PlaylistsRail({
  layout,
  setLayout,
  collapsed,
  onToggleCollapse,
}: {
  layout: PanelLayout;
  setLayout: (l: PanelLayout) => void;
  collapsed: boolean;
  onToggleCollapse: () => void;
}) {
  const canWrite = useMe().can_write;
  // The name filter + the selected playlist live in PlaylistsProvider (shared with the header search
  // box and the Playlists detail pane); this rail owns the list query + its own controls.
  const { search, selectedId } = usePlaylists();
  const { setSelectedId } = usePlaylistsActions();
  const { t } = useTranslation();
  const qc = useQueryClient();
  const [plSort, setPlSort] = useState<PlSort>(() => readAccountMerged(LS.plSort, PL_SORT_DEFAULT));
  useEffect(() => {
    writeAccount(LS.plSort, plSort);
  }, [plSort]);
  const [newName, setNewName] = useState("");
  const [syncing, setSyncing] = useState(false);
  const [editing, setEditing] = useState(false);
  const selectedRef = useRef<HTMLButtonElement | null>(null);
  const scrolledRef = useRef(false);

  const listQuery = useQuery({
    queryKey: qk.playlists(),
    queryFn: () => api.playlists(),
    refetchOnWindowFocus: true,
  });
  const playlists = listQuery.data ?? [];

  // Keep the selection valid: default to the first playlist, and if the selected one disappears
  // drop to the next available (or clear when none remain). Waits for the first load.
  useEffect(() => {
    if (!listQuery.data) return;
    if (!playlists.length) {
      if (selectedId != null) setSelectedId(null);
      return;
    }
    if (selectedId == null || !playlists.some((p) => p.id === selectedId)) {
      setSelectedId(playlists[0]!.id); // playlists is non-empty (the length guard above returns)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [playlists, selectedId, listQuery.data]);

  const plName = (p: { kind: string; name: string }) => playlistName(p, t);

  const sortedPlaylists = useMemo(() => {
    const sign = plSort.dir === "asc" ? 1 : -1;
    return [...playlists].sort((a, b) => {
      if (plSort.dirtyFirst && a.dirty !== b.dirty) return a.dirty ? -1 : 1;
      switch (plSort.key) {
        case "name":
          return sign * plName(a).localeCompare(plName(b));
        case "count":
          return sign * (a.item_count - b.item_count);
        case "duration":
          return sign * ((a.total_duration_seconds ?? 0) - (b.total_duration_seconds ?? 0));
        default:
          return 0;
      }
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [playlists, plSort]);

  const q = search.trim().toLowerCase();
  const visiblePlaylists = useMemo(
    () =>
      q ? sortedPlaylists.filter((p) => plName(p).toLowerCase().includes(q)) : sortedPlaylists,
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [sortedPlaylists, q],
  );

  useEffect(() => {
    if (!scrolledRef.current && selectedRef.current) {
      selectedRef.current.scrollIntoView({ block: "nearest" });
      scrolledRef.current = true;
    }
  }, [sortedPlaylists]);

  async function syncYoutube() {
    if (syncing) return;
    setSyncing(true);
    try {
      const r = await api.syncYoutubePlaylists();
      qc.invalidateQueries({ queryKey: qk.playlists() });
      qc.invalidateQueries({ queryKey: qk.playlist() });
      notify({ level: "success", message: t("playlists.syncedToast", { count: r.synced }) });
    } catch (e) {
      notifyYouTubeActionError(e, t("playlists.syncFailed"));
    } finally {
      setSyncing(false);
    }
  }

  const createMut = useMutation({
    mutationFn: (name: string) => api.createPlaylist(name),
    onSuccess: (pl: Playlist) => {
      setNewName("");
      qc.invalidateQueries({ queryKey: qk.playlists() });
      setSelectedId(pl.id);
    },
  });

  const items: PanelItem[] = [
    {
      id: "manage",
      title: t("playlists.manageGroup"),
      body: (
        <div className="flex flex-col gap-2">
          <button
            onClick={syncYoutube}
            disabled={syncing}
            className="w-full inline-flex items-center justify-center gap-1.5 rounded-lg border border-border px-2.5 py-1.5 text-xs text-muted hover:border-accent hover:text-accent disabled:opacity-40 transition"
          >
            <RefreshCw className={`w-3.5 h-3.5 ${syncing ? "animate-spin" : ""}`} />
            {t("playlists.syncYoutube")}
          </button>
          {playlists.length > 1 && (
            <div className="flex items-center gap-1">
              <select
                value={plSort.key}
                onChange={(e) => setPlSort({ ...plSort, key: e.target.value as PlSort["key"] })}
                aria-label={t("playlists.sortLabel")}
                className="flex-1 min-w-0 bg-card border border-border rounded-md px-1.5 py-1 text-[11px] outline-none focus:border-accent"
              >
                <option value="custom">{t("playlists.railSortCustom")}</option>
                <option value="name">{t("playlists.railSortName")}</option>
                <option value="count">{t("playlists.railSortCount")}</option>
                <option value="duration">{t("playlists.railSortDuration")}</option>
              </select>
              <button
                onClick={() => setPlSort({ ...plSort, dir: plSort.dir === "asc" ? "desc" : "asc" })}
                disabled={plSort.key === "custom"}
                title={plSort.dir === "asc" ? t("playlists.dirAsc") : t("playlists.dirDesc")}
                className="p-1 rounded-md border border-border text-muted enabled:hover:text-fg enabled:hover:border-accent disabled:opacity-30 transition"
              >
                {plSort.dir === "asc" ? (
                  <ArrowUp className="w-3.5 h-3.5" />
                ) : (
                  <ArrowDown className="w-3.5 h-3.5" />
                )}
              </button>
              <button
                onClick={() => setPlSort({ ...plSort, dirtyFirst: !plSort.dirtyFirst })}
                title={t("playlists.dirtyFirst")}
                aria-pressed={plSort.dirtyFirst}
                className={`p-1 rounded-md border transition ${
                  plSort.dirtyFirst
                    ? "border-accent text-accent"
                    : "border-border text-muted hover:text-fg hover:border-accent"
                }`}
              >
                <Pin className="w-3.5 h-3.5" />
              </button>
            </div>
          )}
          <form
            onSubmit={(e) => {
              e.preventDefault();
              if (newName.trim()) createMut.mutate(newName.trim());
            }}
            className="flex items-center gap-1.5"
          >
            <Plus className="w-4 h-4 text-muted shrink-0" />
            <input
              value={newName}
              onChange={(e) => setNewName(e.target.value)}
              placeholder={t("playlists.newPlaylist")}
              disabled={!canWrite}
              className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1 text-xs outline-none focus:border-accent disabled:opacity-50"
            />
          </form>
        </div>
      ),
    },
    {
      id: "list",
      title: t("playlists.listGroup"),
      body: (
        <>
          {playlists.length === 0 &&
            (listQuery.isError && !listQuery.data ? (
              <button
                type="button"
                onClick={() => listQuery.refetch()}
                className="text-xs text-red-400 px-1 py-1 text-left hover:underline"
              >
                {t("common.loadError")} — {t("common.retry")}
              </button>
            ) : listQuery.isLoading ? (
              <div className="text-xs text-muted px-1 py-1">{t("common.loading")}</div>
            ) : (
              <div className="text-xs text-muted px-1 py-1">{t("playlists.noneYet")}</div>
            ))}
          {playlists.length > 0 && visiblePlaylists.length === 0 && (
            <div className="text-xs text-muted px-1 py-1">{t("playlists.noMatches")}</div>
          )}
          <div className="space-y-1">
            {visiblePlaylists.map((pl) => (
              <button
                key={pl.id}
                ref={pl.id === selectedId ? selectedRef : null}
                onClick={() => setSelectedId(pl.id)}
                className={`w-full flex items-center gap-2.5 p-2 rounded-lg border transition text-left ${
                  pl.id === selectedId
                    ? "bg-accent/15 border-accent"
                    : "border-transparent hover:bg-card/60"
                }`}
              >
                <div className="w-[34px] h-[34px] rounded-md bg-card overflow-hidden shrink-0">
                  {pl.cover_thumbnail && (
                    <img src={pl.cover_thumbnail} alt="" className="w-full h-full object-cover" />
                  )}
                </div>
                <div className="min-w-0">
                  <div className="flex items-center gap-1 text-[13px] text-fg">
                    <span className="truncate">{plName(pl)}</span>
                    {(pl.source === "youtube" || pl.yt_playlist_id) && (
                      <Youtube
                        className={`w-3 h-3 shrink-0 ${pl.dirty ? "text-accent" : "text-muted"}`}
                      />
                    )}
                  </div>
                  <div className="text-[11px] text-muted">
                    {t("playlists.itemCount", { count: pl.item_count })}
                    {pl.total_duration_seconds > 0 &&
                      ` · ${formatDuration(pl.total_duration_seconds)}`}
                  </div>
                </div>
              </button>
            ))}
          </div>
        </>
      ),
    },
  ];

  return (
    <SidePanel
      title={t("playlists.title")}
      icon={<ListVideo className="w-4 h-4" />}
      collapsed={collapsed}
      onToggleCollapse={onToggleCollapse}
      editing={editing}
      onToggleEditing={() => setEditing((e) => !e)}
      onReset={() => setLayout(defaultLayout("playlists"))}
      editHint={t("sidebar.editHint")}
    >
      <PanelGroups items={items} layout={layout} setLayout={setLayout} editing={editing} />
    </SidePanel>
  );
}
