import { useEffect, useRef, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
  ArrowDown,
  ArrowUp,
  Check,
  Eye,
  EyeOff,
  History,
  Pencil,
  Plus,
  RefreshCw,
  UserMinus,
} from "lucide-react";
import { api, HttpError, type ManagedChannel, type Tag } from "../lib/api";
import { formatEta, formatViews, relativeTime } from "../lib/format";
import { notify } from "../lib/notifications";
import Tooltip from "./Tooltip";
import DataTable, { type Column } from "./DataTable";
import ChannelLink from "./ChannelLink";
import ChannelDiscovery from "./ChannelDiscovery";
import TagManager from "./TagManager";
import { useConfirm } from "./ConfirmProvider";

type ChannelsView = "subscribed" | "discovery";

export type ChannelStatusFilter = "all" | "needs_full" | "fully_synced" | "hidden";

// Compact total-duration label for a whole channel (hours-scale, so H:MM:SS would be huge).
function fmtTotalDuration(sec: number): string {
  if (!sec) return "—";
  const h = Math.round(sec / 3600);
  return h >= 1 ? `${h.toLocaleString()} h` : `${Math.max(1, Math.round(sec / 60))} m`;
}

const STATUS_FILTERS: { id: ChannelStatusFilter; labelKey: string }[] = [
  { id: "all", labelKey: "channels.filters.all" },
  { id: "needs_full", labelKey: "channels.filters.needsFull" },
  { id: "fully_synced", labelKey: "channels.filters.fullySynced" },
  { id: "hidden", labelKey: "channels.filters.hidden" },
];

export default function Channels({
  canWrite,
  isAdmin,
  onViewChannel,
  onFilterByTag,
  onFocusChannel,
  focusChannelName,
  statusFilter,
  setStatusFilter,
  onOpenWizard,
}: {
  canWrite: boolean;
  isAdmin: boolean;
  onViewChannel: (id: string, name: string) => void;
  onFilterByTag: (tagId: number, name: string) => void;
  onFocusChannel: (name: string) => void;
  focusChannelName: string | null;
  statusFilter: ChannelStatusFilter;
  setStatusFilter: (f: ChannelStatusFilter) => void;
  onOpenWizard: () => void;
}) {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const confirm = useConfirm();

  // Which tab is showing: the user's subscriptions, or channels discovered from their
  // playlists. Persisted so a reload keeps the active tab (see other siftlode.* keys).
  const [view, setView] = useState<ChannelsView>(() =>
    localStorage.getItem("siftlode.channelsView") === "discovery"
      ? "discovery"
      : "subscribed"
  );
  useEffect(() => {
    localStorage.setItem("siftlode.channelsView", view);
  }, [view]);

  // A YouTube-gated action (sync, backfill, unsubscribe) that 403s means the user hasn't
  // granted the needed scope — surface that with a "Connect" action instead of a vague fail.
  const notifyActionError = (err: unknown, fallbackKey: string) => {
    if (err instanceof HttpError && err.status === 403) {
      notify({
        level: "error",
        message: t("channels.notify.needYouTube"),
        action: { label: t("channels.notify.connect"), onClick: onOpenWizard },
      });
    } else {
      notify({ level: "error", message: t(fallbackKey) });
    }
  };
  const channelsQuery = useQuery({ queryKey: ["channels"], queryFn: api.channels });
  const tagsQuery = useQuery({ queryKey: ["tags"], queryFn: api.tags });
  const statusQuery = useQuery({ queryKey: ["my-status"], queryFn: api.myStatus });
  const [tagManagerOpen, setTagManagerOpen] = useState(false);

  const invalidate = () => {
    qc.invalidateQueries({ queryKey: ["channels"] });
    qc.invalidateQueries({ queryKey: ["tags"] });
    qc.invalidateQueries({ queryKey: ["my-status"] });
  };

  const userTags = (tagsQuery.data ?? []).filter((t) => !t.system);

  const patch = useMutation({
    mutationFn: (v: {
      id: string;
      body: { priority?: number; hidden?: boolean; deep_requested?: boolean };
    }) => api.updateChannel(v.id, v.body),
    // Requesting full history may have just pulled in a channel's recent uploads, so
    // refresh the per-user status and feed too — not only the channel list.
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["channels"] });
      qc.invalidateQueries({ queryKey: ["my-status"] });
      qc.invalidateQueries({ queryKey: ["feed"] });
      qc.invalidateQueries({ queryKey: ["feed-count"] });
    },
  });
  // Priority is updated optimistically in place and NOT re-sorted/refetched, so the list
  // doesn't jump (the server list is priority-sorted; refetching would reorder rows and
  // lose your scroll position). The new order takes effect next time the page loads.
  const bumpPriority = (id: string, current: number, delta: number) => {
    const next = current + delta;
    qc.setQueryData<ManagedChannel[]>(["channels"], (old) =>
      (old ?? []).map((ch) => (ch.id === id ? { ...ch, priority: next } : ch))
    );
    api
      .updateChannel(id, { priority: next })
      .catch(() => qc.invalidateQueries({ queryKey: ["channels"] }));
  };

  // Tagging is updated optimistically in place (no refetch of the whole channel list, which
  // made the toggle feel ~2s slow); only revert by refetching if the server call fails.
  const toggleTag = (id: string, tagId: number, hasTag: boolean) => {
    qc.setQueryData<ManagedChannel[]>(["channels"], (old) =>
      (old ?? []).map((ch) =>
        ch.id === id
          ? {
              ...ch,
              tag_ids: hasTag
                ? ch.tag_ids.filter((x) => x !== tagId)
                : [...ch.tag_ids, tagId],
            }
          : ch
      )
    );
    const call = hasTag
      ? api.detachChannelTag(id, tagId)
      : api.attachChannelTag(id, tagId);
    call.catch(() => qc.invalidateQueries({ queryKey: ["channels"] }));
  };
  const syncSubs = useMutation({
    mutationFn: () => api.syncSubscriptions(),
    onSuccess: (r: { subscriptions?: number }) => {
      invalidate();
      notify({ level: "success", message: t("channels.notify.synced", { count: r.subscriptions ?? 0 }) });
    },
    onError: (e) => notifyActionError(e, "channels.notify.syncFailed"),
  });
  const unsubscribe = useMutation({
    mutationFn: (id: string) => api.unsubscribeChannel(id),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["channels"] });
      qc.invalidateQueries({ queryKey: ["my-status"] });
      notify({ level: "success", message: t("channels.notify.unsubscribed") });
    },
    onError: (e) => notifyActionError(e, "channels.notify.unsubscribeFailed"),
  });
  const resetBackfill = useMutation({
    mutationFn: (id: string) => api.resetChannelBackfill(id),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["channels"] });
      qc.invalidateQueries({ queryKey: ["my-status"] });
      notify({ level: "success", message: t("channels.notify.resetDone") });
    },
    onError: (e) => notifyActionError(e, "channels.notify.resetFailed"),
  });
  const deepAll = useMutation({
    mutationFn: () => api.deepAll(true),
    onSuccess: (r: { updated?: number }) => {
      qc.invalidateQueries({ queryKey: ["channels"] });
      qc.invalidateQueries({ queryKey: ["my-status"] });
      notify({
        level: "success",
        message: t("channels.notify.fullHistoryRequested", { count: r.updated ?? 0 }),
      });
    },
    onError: (e) => notifyActionError(e, "channels.notify.fullHistoryFailed"),
  });

  // The Sync-status filter stays a top-level chip set (not a column filter) because the
  // header's "go to full history" deep-link drives it; the DataTable then handles name
  // search, tag filtering, sort and pagination over whatever the status chip leaves.
  const channels = (channelsQuery.data ?? []).filter((c) =>
    statusFilter === "needs_full"
      ? !c.backfill_done
      : statusFilter === "fully_synced"
        ? c.backfill_done
        : statusFilter === "hidden"
          ? c.hidden
          : true
  );
  const s = statusQuery.data;

  const onView = (c: ManagedChannel) =>
    onViewChannel(c.id, c.title ?? t("channels.row.thisChannel"));
  const onUnsub = async (c: ManagedChannel) => {
    const ok = await confirm({
      title: t("channels.row.unsubscribeOnYoutube"),
      message: t("channels.confirmUnsubscribe", { name: c.title ?? c.id }),
      confirmLabel: t("channels.row.unsubscribeOnYoutube"),
      danger: true,
    });
    if (ok) unsubscribe.mutate(c.id);
  };
  const onReset = async (c: ManagedChannel) => {
    const ok = await confirm({
      title: t("channels.row.backfillThis"),
      message: t("channels.confirmReset", { name: c.title ?? c.id }),
      confirmLabel: t("channels.row.backfillThis"),
    });
    if (ok) resetBackfill.mutate(c.id);
  };

  const columns: Column<ManagedChannel>[] = [
    {
      key: "priority",
      header: t("channels.cols.priority"),
      align: "center",
      width: "56px",
      sortable: true,
      hideInCard: true,
      sortValue: (c) => c.priority,
      render: (c) => <PriorityCell c={c} onPriority={(d) => bumpPriority(c.id, c.priority, d)} />,
    },
    {
      key: "channel",
      header: t("channels.cols.channel"),
      sortable: true,
      sortValue: (c) => (c.title ?? c.id).toLowerCase(),
      filter: { kind: "text", get: (c) => `${c.title ?? ""} ${c.handle ?? ""}` },
      cardPrimary: true,
      render: (c) => (
        <ChannelLink
          id={c.id}
          title={c.title}
          handle={c.handle}
          thumbnailUrl={c.thumbnail_url}
          onView={() => onView(c)}
        />
      ),
    },
    {
      key: "stored",
      header: t("channels.cols.stored"),
      align: "right",
      nowrap: true,
      sortable: true,
      sortValue: (c) => c.stored_videos,
      render: (c) => <span className="text-muted tabular-nums">{c.stored_videos.toLocaleString()}</span>,
    },
    {
      key: "subs",
      header: t("channels.cols.subs"),
      align: "right",
      nowrap: true,
      sortable: true,
      sortValue: (c) => c.subscriber_count ?? -1,
      render: (c) => (
        <span className="text-muted tabular-nums">
          {c.subscriber_count != null ? formatViews(c.subscriber_count) : "—"}
        </span>
      ),
    },
    {
      key: "lastUpload",
      header: t("channels.cols.lastUpload"),
      nowrap: true,
      sortable: true,
      sortValue: (c) => (c.last_video_at ? new Date(c.last_video_at).getTime() : 0),
      render: (c) => <span className="text-muted">{relativeTime(c.last_video_at) || "—"}</span>,
    },
    {
      key: "length",
      header: t("channels.cols.length"),
      align: "right",
      nowrap: true,
      sortable: true,
      sortValue: (c) => c.total_duration_seconds,
      render: (c) => <span className="text-muted tabular-nums">{fmtTotalDuration(c.total_duration_seconds)}</span>,
    },
    {
      key: "types",
      header: t("channels.cols.types"),
      nowrap: true,
      render: (c) => (
        <Tooltip hint={t("channels.cols.typesHint")}>
          <span className="text-muted tabular-nums cursor-help">
            {c.count_normal} / {c.count_short} / {c.count_live}
          </span>
        </Tooltip>
      ),
    },
    {
      key: "sync",
      header: t("channels.cols.sync"),
      nowrap: true,
      cardLabel: false,
      render: (c) => <SyncCell c={c} />,
    },
    {
      key: "tags",
      header: t("channels.cols.tags"),
      cardLabel: false,
      filter: {
        kind: "multi",
        options: userTags.map((tg) => ({ value: String(tg.id), label: tg.name })),
        // OR semantics: show channels carrying any of the selected tags.
        test: (c, values) => values.some((v) => c.tag_ids.includes(Number(v))),
      },
      render: (c) => (
        <TagsCell
          c={c}
          userTags={userTags}
          onToggleTag={(tagId) => toggleTag(c.id, tagId, c.tag_ids.includes(tagId))}
          onTagClick={onFilterByTag}
        />
      ),
    },
    {
      key: "actions",
      header: t("channels.cols.actions"),
      align: "right",
      nowrap: true,
      width: "104px",
      cardLabel: false,
      render: (c) => (
        <ActionsCell
          c={c}
          isAdmin={isAdmin}
          canWrite={canWrite}
          onHide={() => patch.mutate({ id: c.id, body: { hidden: !c.hidden } })}
          onDeep={() => patch.mutate({ id: c.id, body: { deep_requested: !c.deep_requested } })}
          onReset={() => onReset(c)}
          onUnsubscribe={() => onUnsub(c)}
        />
      ),
    },
  ];

  // Status chips sit in the table's controls row (next to paging) — compact and close to
  // where they act.
  const statusChips = (
    <div className="flex flex-wrap items-center gap-1.5">
      {STATUS_FILTERS.map((f) => (
        <button
          key={f.id}
          onClick={() => setStatusFilter(f.id)}
          className={`text-xs px-2.5 py-1 rounded-full border transition ${
            statusFilter === f.id
              ? "bg-accent text-accent-fg border-accent"
              : "bg-card border-border text-muted hover:border-accent"
          }`}
        >
          {t(f.labelKey)}
        </button>
      ))}
    </div>
  );

  const tabs = (
    <div className="px-4 pt-4 max-w-7xl mx-auto">
      <div className="flex flex-wrap items-center gap-1.5">
        {(["subscribed", "discovery"] as ChannelsView[]).map((v) => (
          <button
            key={v}
            onClick={() => setView(v)}
            className={`text-sm px-3 py-1.5 rounded-full border transition ${
              view === v
                ? "bg-accent text-accent-fg border-accent"
                : "bg-card border-border text-muted hover:border-accent"
            }`}
          >
            {t(`channels.tabs.${v}`)}
          </button>
        ))}
      </div>
    </div>
  );

  if (view === "discovery") {
    return (
      <>
        {tabs}
        <ChannelDiscovery canWrite={canWrite} onOpenWizard={onOpenWizard} />
      </>
    );
  }

  return (
    <>
    {tabs}
    <div className="px-4 pb-4 pt-3 max-w-7xl mx-auto">
      {/* Per-user sync status + catalog-wide actions on one row (search/tags filtering
          now lives in the table headers). */}
      <div className="flex items-start justify-between gap-4 flex-wrap mb-4">
        <div className="flex flex-wrap items-center gap-x-5 gap-y-1 text-sm text-muted">
          {s && (
            <>
              <Stat label={t("channels.stats.channels")} value={s.channels_total} hint={t("channels.stats.channelsHint")} />
              <Stat
                label={t("channels.stats.recentSynced")}
                value={`${s.channels_recent_synced}/${s.channels_total}`}
                hint={t("channels.stats.recentSyncedHint")}
              />
              <Stat
                label={t("channels.stats.fullHistory")}
                value={`${s.channels_deep_done}/${s.channels_deep_requested}`}
                hint={t("channels.stats.fullHistoryHint")}
              />
              {s.deep_pending_count > 0 && (
                <Stat
                  label={t("channels.stats.left")}
                  value={formatEta(s.deep_eta_seconds)}
                  hint={t("channels.stats.leftHint", { count: s.deep_pending_count })}
                />
              )}
              <Stat label={t("channels.stats.myVideos")} value={s.my_videos.toLocaleString()} hint={t("channels.stats.myVideosHint")} />
              <Stat
                label={t("channels.stats.quotaLeft")}
                value={s.quota_remaining_today.toLocaleString()}
                hint={t("channels.stats.quotaLeftHint")}
              />
            </>
          )}
        </div>
        <div className="flex items-center gap-2 shrink-0">
        <Tooltip
          side="bottom"
          hint={t("channels.syncSubscriptionsHint")}
        >
          <button
            onClick={() => syncSubs.mutate()}
            disabled={syncSubs.isPending}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            <RefreshCw className={`w-4 h-4 ${syncSubs.isPending ? "animate-spin" : ""}`} />
            {t("channels.syncSubscriptions")}
          </button>
        </Tooltip>
        <Tooltip
          side="bottom"
          hint={t("channels.backfillEverythingHint")}
        >
          <button
            onClick={() => deepAll.mutate()}
            disabled={deepAll.isPending}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            <History className={`w-4 h-4 ${deepAll.isPending ? "animate-pulse" : ""}`} />
            {t("channels.backfillEverything")}
          </button>
        </Tooltip>
        </div>
      </div>

      <p className="text-xs text-muted mb-4 leading-relaxed">
        <Trans
          i18nKey="channels.intro"
          components={[
            <b className="text-fg/80" />,
            <b className="text-fg/80" />,
            <b className="text-fg/80" />,
          ]}
        />
      </p>

      {/* Your tags — read-only overview; add/rename/delete live in the manager dialog. */}
      <div className="flex flex-wrap items-center gap-1.5 mb-4">
        <Tooltip hint={t("channels.tags.yourTagsHint")}>
          <span className="text-xs uppercase tracking-wide text-muted mr-1 cursor-help underline decoration-dotted decoration-muted/40 underline-offset-4">
            {t("channels.tags.yourTags")}
          </span>
        </Tooltip>
        {userTags.map((tg) => (
          <span
            key={tg.id}
            className="text-xs px-2 py-1 rounded-full bg-card border border-border"
          >
            {tg.name}
          </span>
        ))}
        <button
          onClick={() => setTagManagerOpen(true)}
          className="inline-flex items-center gap-1 text-xs px-2 py-1 rounded-full border border-dashed border-border text-muted hover:text-accent hover:border-accent transition"
        >
          <Pencil className="w-3 h-3" />
          {t("channels.tags.manage")}
        </button>
      </div>
      {tagManagerOpen && (
        <TagManager onClose={() => setTagManagerOpen(false)} onFocusChannel={onFocusChannel} />
      )}

      {/* Channel table */}
      {channelsQuery.isLoading ? (
        <div className="text-muted py-8">{t("channels.loading")}</div>
      ) : (
        <DataTable
          rows={channels}
          columns={columns}
          rowKey={(c) => c.id}
          persistKey="siftlode.channelsTable"
          controlsPosition="top"
          controlsLeading={statusChips}
          externalFilter={focusChannelName ? { key: "channel", value: focusChannelName } : null}
          rowClassName={(c) => (c.hidden ? "opacity-60" : "")}
          emptyText={t("channels.empty")}
        />
      )}
    </div>
    </>
  );
}

function Stat({
  label,
  value,
  hint,
}: {
  label: string;
  value: string | number;
  hint?: string;
}) {
  return (
    <Tooltip hint={hint ?? ""}>
      <span className={hint ? "cursor-help" : ""}>
        <span className="text-fg font-semibold">{value}</span> {label}
      </span>
    </Tooltip>
  );
}

function SyncBadge({ ok, label, hint }: { ok: boolean; label: string; hint?: string }) {
  return (
    <Tooltip hint={hint ?? ""}>
      <span
        className={`inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full border ${
          ok ? "border-accent/40 text-accent" : "border-border text-muted"
        }`}
      >
        {ok && <Check className="w-3 h-3" />}
        {label}
      </span>
    </Tooltip>
  );
}

function PriorityCell({ c, onPriority }: { c: ManagedChannel; onPriority: (delta: number) => void }) {
  const { t } = useTranslation();
  return (
    <Tooltip hint={t("channels.row.priorityHint")}>
      <div className="inline-flex flex-col items-center cursor-help">
        <button onClick={() => onPriority(1)} className="text-muted hover:text-accent" aria-label={t("channels.row.raisePriority")}>
          <ArrowUp className="w-3.5 h-3.5" />
        </button>
        <span className="text-xs text-muted tabular-nums">{c.priority}</span>
        <button onClick={() => onPriority(-1)} className="text-muted hover:text-accent" aria-label={t("channels.row.lowerPriority")}>
          <ArrowDown className="w-3.5 h-3.5" />
        </button>
      </div>
    </Tooltip>
  );
}


// Status only — the backfill *action* lives in the Actions column. When both recent and
// full history are in, collapse to a single "fully synced" chip; otherwise show what's
// present plus the missing/queued state.
function SyncCell({ c }: { c: ManagedChannel }) {
  const { t } = useTranslation();
  if (c.recent_synced && c.backfill_done) {
    return <SyncBadge ok label={t("channels.row.fullySynced")} hint={t("channels.row.fullySyncedHint")} />;
  }
  return (
    <div className="flex flex-wrap items-center gap-1">
      <SyncBadge
        ok={c.recent_synced}
        label={t("channels.row.recent")}
        hint={c.recent_synced ? t("channels.row.recentSyncedHint") : t("channels.row.recentNotSyncedHint")}
      />
      {c.backfill_done ? (
        <SyncBadge ok label={t("channels.row.full")} hint={t("channels.row.fullHint")} />
      ) : c.deep_requested ? (
        <SyncBadge ok={false} label={t("channels.row.fullHistoryQueued")} hint={t("channels.row.queuedRequestedHint")} />
      ) : c.deep_in_queue ? (
        <SyncBadge ok={false} label={t("channels.row.fullHistoryComing")} hint={t("channels.row.queuedByOtherHint")} />
      ) : (
        <SyncBadge ok={false} label={t("channels.row.full")} hint={t("channels.row.fullNotFetchedHint")} />
      )}
    </div>
  );
}

function TagsCell({
  c,
  userTags,
  onToggleTag,
  onTagClick,
}: {
  c: ManagedChannel;
  userTags: Tag[];
  onToggleTag: (tagId: number) => void;
  onTagClick: (tagId: number, name: string) => void;
}) {
  const { t } = useTranslation();
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement | null>(null);
  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (!ref.current?.contains(e.target as Node)) setOpen(false);
    };
    const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);
  if (userTags.length === 0) return null;
  // Show only the tags actually attached to this channel; the “+” opens a per-channel
  // picker to attach/detach (kept the convenient "kirakás" without listing every tag
  // on every row). Tag create/delete still lives in the top "YOUR TAGS" row.
  const attached = userTags.filter((tg) => c.tag_ids.includes(tg.id));
  return (
    <div ref={ref} className="relative flex flex-wrap items-center gap-1 min-w-[160px]">
      {attached.map((tg) => (
        <button
          key={tg.id}
          onClick={() => onTagClick(tg.id, tg.name)}
          title={t("channels.row.filterFeedByTag", { name: tg.name })}
          className="text-[10px] px-1.5 py-0.5 rounded-full bg-accent text-accent-fg border border-accent hover:opacity-80 transition"
        >
          {tg.name}
        </button>
      ))}
      <button
        onClick={() => setOpen((o) => !o)}
        title={t("channels.row.editTags")}
        aria-label={t("channels.row.editTags")}
        className="w-5 h-5 inline-flex items-center justify-center rounded-full border border-dashed border-border text-muted hover:text-accent hover:border-accent transition"
      >
        <Plus className="w-3 h-3" />
      </button>
      {open && (
        <div className="glass-menu absolute left-0 top-full mt-1 z-30 w-44 rounded-xl p-1.5 animate-[popIn_0.16s_ease]">
          {userTags.map((tg) => {
            const on = c.tag_ids.includes(tg.id);
            return (
              <button
                key={tg.id}
                onClick={() => onToggleTag(tg.id)}
                className={`w-full flex items-center gap-2 text-left text-xs px-2 py-1.5 rounded-md transition ${
                  on ? "text-fg" : "text-muted hover:bg-card"
                }`}
              >
                <span
                  className={`w-3.5 h-3.5 rounded border flex items-center justify-center shrink-0 ${
                    on ? "bg-accent border-accent text-accent-fg" : "border-border"
                  }`}
                >
                  {on && <Check className="w-2.5 h-2.5" />}
                </span>
                {tg.name}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

function ActionsCell({
  c,
  isAdmin,
  canWrite,
  onHide,
  onDeep,
  onReset,
  onUnsubscribe,
}: {
  c: ManagedChannel;
  isAdmin: boolean;
  canWrite: boolean;
  onHide: () => void;
  onDeep: () => void;
  onReset: () => void;
  onUnsubscribe: () => void;
}) {
  const { t } = useTranslation();
  // Admins get a "reset" trigger that re-fetches the channel from scratch regardless of
  // state — always actionable. Regular users get the per-user "request full history" opt-in,
  // which only applies until the back-catalog is in (or already coming for everyone).
  const done = c.backfill_done;
  const requested = !done && c.deep_requested;
  const optInActionable = !done && !c.deep_in_queue;
  const backfillHint = isAdmin
    ? t("channels.row.resetHint")
    : c.deep_in_queue
      ? t("channels.row.queuedByOtherHint")
      : requested
        ? t("channels.row.queuedRequestedHint")
        : done
          ? t("channels.row.fullHint")
          : t("channels.row.backfillThisHint");
  const enabled = isAdmin || optInActionable;
  return (
    <div className="inline-flex items-center gap-2">
      <Tooltip hint={backfillHint}>
        <button
          onClick={!enabled ? undefined : isAdmin ? onReset : onDeep}
          disabled={!enabled}
          className={`shrink-0 transition ${
            requested
              ? "text-accent"
              : enabled
                ? "text-muted hover:text-fg"
                : "text-muted/40 cursor-default"
          }`}
          aria-label={isAdmin ? t("channels.row.resetBackfill") : t("channels.row.backfillThis")}
        >
          <History className="w-4 h-4" />
        </button>
      </Tooltip>
      <Tooltip hint={c.hidden ? t("channels.row.hiddenHint") : t("channels.row.hideHint")}>
        <button onClick={onHide} className="text-muted hover:text-fg shrink-0" aria-label={c.hidden ? t("channels.row.unhide") : t("channels.row.hideFromFeed")}>
          {c.hidden ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
        </button>
      </Tooltip>
      {canWrite && (
        <Tooltip hint={t("channels.row.unsubscribeHint")}>
          <button
            onClick={onUnsubscribe}
            className="text-muted hover:text-red-400 shrink-0"
            aria-label={t("channels.row.unsubscribeOnYoutube")}
          >
            <UserMinus className="w-4 h-4" />
          </button>
        </Tooltip>
      )}
    </div>
  );
}
