import { useCallback, useEffect, useState, useSyncExternalStore } from "react";
import { useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Bell, Check, History, Monitor, Pause, Play, RefreshCw, RotateCcw, Save, Trash2, User, UserPlus, X } from "lucide-react";
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
import { api, type Invite, type Me } from "../lib/api";
import { formatEta, quotaActionLabel } from "../lib/format";
import Avatar from "./Avatar";
import { notify, type NotifSettings } from "../lib/notifications";
import Tooltip from "./Tooltip";
import { useConfirm } from "./ConfirmProvider";

// The Settings page edits server-persisted preferences as a draft: changes apply locally
// for instant preview but reach the server only on an explicit Save (or revert on Discard).
// App owns the draft + baseline (so the leave-the-page guard can see it); this controller is
// how the panel reads/writes it. See App.tsx.
export type PrefsSaveState = "idle" | "saving" | "saved" | "error";
export interface PrefsController {
  theme: ThemePrefs;
  setTheme: (t: ThemePrefs) => void;
  view: "grid" | "list";
  setView: (v: "grid" | "list") => void;
  perf: boolean;
  setPerf: (v: boolean) => void;
  hints: boolean;
  setHints: (v: boolean) => void;
  notif: NotifSettings;
  setNotif: (n: NotifSettings) => void;
  dirty: boolean;
  save: () => void;
  discard: () => void;
  saveState: PrefsSaveState;
}

type TabId = "appearance" | "notifications" | "sync" | "account";
const TABS: { id: TabId; icon: typeof Monitor }[] = [
  { id: "appearance", icon: Monitor },
  { id: "notifications", icon: Bell },
  { id: "sync", icon: RefreshCw },
  { id: "account", icon: User },
];

// Persist the active tab so a reload (F5) keeps the user where they were instead of
// snapping back to "appearance".
const SETTINGS_TAB_KEY = "siftlode.settingsTab";
function loadSettingsTab(): TabId {
  const v = localStorage.getItem(SETTINGS_TAB_KEY);
  return TABS.some((tabItem) => tabItem.id === v) ? (v as TabId) : "appearance";
}

// Settings as a page (Design B): rendered in the main content area, not an overlay. It keeps
// the frosted `.glass` surface so it still refracts the ambient backdrop. The page title is
// shown by the Header; the tab rail stays as in-page sub-navigation.
export default function SettingsPanel({
  me,
  prefs,
  onOpenWizard,
}: {
  me: Me;
  prefs: PrefsController;
  onOpenWizard: () => void;
}) {
  const { t } = useTranslation();
  const [tab, setTabState] = useState<TabId>(loadSettingsTab);
  const setTab = (id: TabId) => {
    setTabState(id);
    localStorage.setItem(SETTINGS_TAB_KEY, id);
  };

  return (
    <div className="p-4 max-w-3xl w-full mx-auto">
      <div className="glass rounded-2xl overflow-hidden">
        <div className="flex">
          {/* Vertical tab rail — never wraps; active is a clear accent pill. */}
          <nav className="w-32 sm:w-36 shrink-0 border-r border-border/60 p-2 flex flex-col gap-1">
            {TABS.map((tabItem) => {
              const active = tab === tabItem.id;
              return (
                <button
                  key={tabItem.id}
                  onClick={() => setTab(tabItem.id)}
                  className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-left transition ${
                    active
                      ? "bg-accent text-accent-fg font-medium shadow-md shadow-accent/20"
                      : "text-muted hover:text-fg hover:bg-card/60"
                  }`}
                >
                  <tabItem.icon className="w-4 h-4 shrink-0" />
                  {t(`settings.tabs.${tabItem.id}`)}
                </button>
              );
            })}
          </nav>

          {/* All tabs stacked in one grid cell so the panel sizes to the tallest tab
              (stable height, no jump on switch); the active one is shown on top. */}
          <div className="grid flex-1 min-w-0">
            {TABS.map((tabItem) => (
              <div
                key={tabItem.id}
                className={`[grid-area:1/1] p-4 ${
                  tab === tabItem.id ? "" : "invisible pointer-events-none"
                }`}
              >
                {tabItem.id === "appearance" && <Appearance prefs={prefs} />}
                {tabItem.id === "notifications" && <Notifications prefs={prefs} />}
                {tabItem.id === "sync" && <Sync me={me} />}
                {tabItem.id === "account" && <Account me={me} onOpenWizard={onOpenWizard} />}
              </div>
            ))}
          </div>
        </div>

        {/* Save/Discard bar — spans the whole card (a change can come from any tab). Only the
            Appearance/Notifications prefs are drafted; the Sync/Account tabs act immediately. */}
        <PrefsSaveBar prefs={prefs} />
      </div>
    </div>
  );
}

function PrefsSaveBar({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  // Stay mounted while a transient "saved"/"error" message is fading, even after dirty clears.
  if (!prefs.dirty && prefs.saveState === "idle") return null;
  const saving = prefs.saveState === "saving";
  return (
    <div className="flex items-center justify-between gap-3 border-t border-border/60 px-4 py-3 bg-card/40">
      <span className="text-sm text-muted">
        {prefs.saveState === "saved"
          ? <span className="text-emerald-500 inline-flex items-center gap-1.5"><Check className="w-4 h-4" />{t("settings.save.saved")}</span>
          : prefs.saveState === "error"
            ? <span className="text-red-500">{t("settings.save.failed")}</span>
            : t("settings.save.unsaved")}
      </span>
      <div className="flex items-center gap-2">
        <button
          onClick={prefs.discard}
          disabled={saving || !prefs.dirty}
          className="glass-card glass-hover flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm disabled:opacity-40 transition"
        >
          <RotateCcw className="w-4 h-4" />
          {t("settings.save.discard")}
        </button>
        <button
          onClick={prefs.save}
          disabled={saving || !prefs.dirty}
          className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm bg-accent text-accent-fg font-medium shadow-md shadow-accent/20 disabled:opacity-40 transition"
        >
          <Save className="w-4 h-4" />
          {saving ? t("settings.save.saving") : t("settings.save.save")}
        </button>
      </div>
    </div>
  );
}

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="mb-6">
      <div className="text-xs uppercase tracking-wide text-muted mb-2">{title}</div>
      {children}
    </div>
  );
}

function Row({
  label,
  hint,
  children,
}: {
  label: string;
  hint?: string;
  children: React.ReactNode;
}) {
  return (
    <div className="flex items-center justify-between gap-3 py-1.5 text-sm">
      <Tooltip hint={hint ?? ""}>
        <span
          className={
            hint ? "underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help" : ""
          }
        >
          {label}
        </span>
      </Tooltip>
      {children}
    </div>
  );
}

function Switch({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
  return (
    <button
      type="button"
      onClick={() => onChange(!checked)}
      className={`w-9 h-5 rounded-full transition relative shrink-0 ${checked ? "bg-accent" : "bg-border"}`}
    >
      <span
        className={`absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-all ${
          checked ? "left-[18px]" : "left-0.5"
        }`}
      />
    </button>
  );
}

function Appearance({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  // Controlled by App's prefs draft: each change applies locally for preview but is only
  // persisted on an explicit Save (handled by the panel's Save/Discard bar).
  const { theme, setTheme, view, setView, perf, setPerf, hints, setHints } = prefs;

  return (
    <>
      <Section title={t("settings.appearance.colorScheme")}>
        <div className="grid grid-cols-4 gap-2">
          {SCHEMES.map((s) => (
            <Tooltip key={s.id} hint={s.name}>
              <button
                onClick={() => setTheme({ ...theme, scheme: s.id as Scheme })}
                className={`h-9 w-full rounded-lg border-2 transition ${
                  theme.scheme === s.id ? "border-fg" : "border-transparent"
                }`}
                style={{ background: s.swatch }}
              />
            </Tooltip>
          ))}
        </div>
      </Section>

      <Section title={t("settings.appearance.display")}>
        <Row label={t("settings.appearance.darkMode")}>
          <Switch
            checked={theme.mode === "dark"}
            onChange={(v) => setTheme({ ...theme, mode: v ? "dark" : "light" })}
          />
        </Row>
        <Row label={t("settings.appearance.listView")} hint={t("settings.appearance.listViewHint")}>
          <Switch checked={view === "list"} onChange={(v) => setView(v ? "list" : "grid")} />
        </Row>
        <Row
          label={t("settings.appearance.performanceMode")}
          hint={t("settings.appearance.performanceModeHint")}
        >
          <Switch checked={perf} onChange={setPerf} />
        </Row>
        <Row
          label={t("settings.appearance.showHints")}
          hint={t("settings.appearance.showHintsHint")}
        >
          <Switch checked={hints} onChange={setHints} />
        </Row>
      </Section>

      <Section title={t("settings.appearance.textSize")}>
        <input
          type="range"
          min={0.9}
          max={1.3}
          step={0.02}
          value={theme.fontScale}
          onChange={(e) => setTheme({ ...theme, fontScale: Number(e.target.value) })}
          className="w-full accent-accent"
        />
        <div className="text-right text-xs text-muted">{Math.round(theme.fontScale * 100)}%</div>
      </Section>
    </>
  );
}

function Notifications({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  // Controlled by App's prefs draft (live preview via configureNotifications there); the
  // panel's Save bar persists it. The "send test" button below still fires immediately.
  const { notif: cfg, setNotif } = prefs;
  const update = (p: Partial<NotifSettings>) => setNotif({ ...cfg, ...p });
  const autoOn = cfg.durationMs > 0;

  return (
    <Section title={t("settings.notifications.title")}>
      <Row
        label={t("settings.notifications.sound")}
        hint={t("settings.notifications.soundHint")}
      >
        <Switch checked={cfg.sound} onChange={(v) => update({ sound: v })} />
      </Row>
      <Row
        label={t("settings.notifications.autoDismiss")}
        hint={t("settings.notifications.autoDismissHint")}
      >
        <Switch checked={autoOn} onChange={(v) => update({ durationMs: v ? 6000 : 0 })} />
      </Row>
      {autoOn && (
        <div className="py-1.5 text-sm">
          <div className="flex items-center justify-between">
            <span className="text-muted text-xs">{t("settings.notifications.dismissAfter")}</span>
            <span className="text-muted text-xs">{(cfg.durationMs / 1000).toFixed(0)}s</span>
          </div>
          <input
            type="range"
            min={2000}
            max={15000}
            step={1000}
            value={cfg.durationMs}
            onChange={(e) => update({ durationMs: Number(e.target.value) })}
            className="w-full accent-accent mt-1"
          />
        </div>
      )}
      <button
        onClick={() =>
          notify({
            level: "info",
            title: t("settings.notifications.testTitle"),
            message: t("settings.notifications.testMessage"),
            sound: true,
          })
        }
        className="mt-2 text-sm text-accent hover:underline"
      >
        {t("settings.notifications.sendTest")}
      </button>
    </Section>
  );
}

function Sync({ me }: { me: Me }) {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const status = useQuery({ queryKey: ["my-status"], queryFn: api.myStatus });
  const usage = useQuery({ queryKey: ["my-usage"], queryFn: api.myUsage });
  const s = status.data;
  const syncSubs = useMutation({
    mutationFn: () => api.syncSubscriptions(),
    onSuccess: (r: { subscriptions?: number }) => {
      qc.invalidateQueries({ queryKey: ["my-status"] });
      qc.invalidateQueries({ queryKey: ["channels"] });
      notify({
        level: "success",
        message: t("settings.sync.synced", { count: r.subscriptions ?? 0 }),
      });
    },
    onError: () => notify({ level: "error", message: t("settings.sync.syncFailed") }),
  });
  const pauseResume = useMutation({
    mutationFn: (paused: boolean) => (paused ? api.resumeSync() : api.pauseSync()),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["my-status"] }),
  });
  const deepAll = useMutation({
    mutationFn: () => api.deepAll(true),
    onSuccess: (r: { updated?: number }) => {
      qc.invalidateQueries({ queryKey: ["my-status"] });
      qc.invalidateQueries({ queryKey: ["channels"] });
      notify({
        level: "success",
        message: t("settings.sync.fullHistoryRequested", { count: r.updated ?? 0 }),
      });
    },
    onError: () => notify({ level: "error", message: t("settings.sync.fullHistoryFailed") }),
  });

  return (
    <>
      <Section title={t("settings.sync.myStatus")}>
        {s ? (
          <div className="space-y-1.5 text-sm">
            <Row label={t("settings.sync.channels")} hint={t("settings.sync.channelsHint")}>
              {s.channels_total}
            </Row>
            <Row
              label={t("settings.sync.recentSynced")}
              hint={t("settings.sync.recentSyncedHint")}
            >
              {`${s.channels_recent_synced}/${s.channels_total}`}
            </Row>
            <Row
              label={t("settings.sync.fullHistory")}
              hint={t("settings.sync.fullHistoryHint")}
            >
              {`${s.channels_deep_done}/${s.channels_deep_requested}`}
            </Row>
            {s.deep_pending_count > 0 && (
              <Row
                label={t("settings.sync.fullHistoryEta")}
                hint={t("settings.sync.fullHistoryEtaHint", { count: s.deep_pending_count })}
              >
                {formatEta(s.deep_eta_seconds)}
              </Row>
            )}
            <Row label={t("settings.sync.myVideos")} hint={t("settings.sync.myVideosHint")}>
              {s.my_videos.toLocaleString()}
            </Row>
            <Row
              label={t("settings.sync.quotaLeft")}
              hint={t("settings.sync.quotaLeftHint")}
            >
              {s.quota_remaining_today.toLocaleString()}
            </Row>
          </div>
        ) : (
          <div className="text-muted text-sm">{t("settings.sync.loading")}</div>
        )}
      </Section>

      <Section title={t("settings.sync.apiUsage")}>
        {usage.data ? (
          <>
            <div className="space-y-1.5 text-sm">
              <Row label={t("settings.sync.today")} hint={t("settings.sync.todayHint")}>
                {usage.data.today.toLocaleString()}
              </Row>
              <Row label={t("settings.sync.last7d")}>{usage.data.last_7d.toLocaleString()}</Row>
              <Row label={t("settings.sync.last30d")}>{usage.data.last_30d.toLocaleString()}</Row>
              <Row label={t("settings.sync.allTime")}>{usage.data.all_time.toLocaleString()}</Row>
            </div>
            {Object.keys(usage.data.by_action).length > 0 && (
              <div className="mt-2 pt-2 border-t border-border space-y-1 text-xs text-muted">
                <div className="uppercase tracking-wide">{t("settings.sync.byAction")}</div>
                {Object.entries(usage.data.by_action)
                  .sort((a, b) => b[1] - a[1])
                  .map(([action, units]) => (
                    <div key={action} className="flex justify-between gap-2">
                      <span>{quotaActionLabel(action)}</span>
                      <span className="tabular-nums">{units.toLocaleString()}</span>
                    </div>
                  ))}
              </div>
            )}
          </>
        ) : (
          <div className="text-muted text-sm">{t("settings.sync.noUsage")}</div>
        )}
      </Section>

      {!me.is_demo && (
        <Section title={t("settings.sync.actions")}>
          <Tooltip hint={t("settings.sync.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("settings.sync.syncSubscriptions")}
            </button>
          </Tooltip>
          <Tooltip hint={t("settings.sync.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 mt-2"
            >
              <History className={`w-4 h-4 ${deepAll.isPending ? "animate-pulse" : ""}`} />
              {t("settings.sync.backfillEverything")}
            </button>
          </Tooltip>
        </Section>
      )}

      {me.role === "admin" && s && (
        <Section title={t("settings.sync.admin")}>
          <Tooltip hint={t("settings.sync.adminPauseHint")}>
            <button
              onClick={() => pauseResume.mutate(s.paused)}
              className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm transition"
            >
              {s.paused ? <Play className="w-4 h-4" /> : <Pause className="w-4 h-4" />}
              {s.paused
                ? t("settings.sync.resumeBackgroundSync")
                : t("settings.sync.pauseBackgroundSync")}
            </button>
          </Tooltip>
        </Section>
      )}
    </>
  );
}

function AccessRow({
  title,
  granted,
  grantedHint,
  enableHint,
  onEnable,
}: {
  title: string;
  granted: boolean;
  grantedHint: string;
  enableHint: string;
  onEnable: () => void;
}) {
  const { t } = useTranslation();
  return (
    <div className="flex items-start justify-between gap-3 py-2">
      <div className="min-w-0">
        <div className="text-sm font-medium">{title}</div>
        <p className="text-xs text-muted leading-relaxed mt-0.5">
          {granted ? grantedHint : enableHint}
        </p>
      </div>
      {granted ? (
        <span className="shrink-0 text-[11px] px-2 py-1 rounded-full border border-accent/40 text-accent">
          {t("settings.account.granted")}
        </span>
      ) : (
        <Tooltip hint={t("settings.account.enableHint")}>
          <button
            onClick={onEnable}
            className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
          >
            {t("settings.account.enable")}
          </button>
        </Tooltip>
      )}
    </div>
  );
}

function Account({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
  const { t } = useTranslation();
  return (
    <>
    <Section title={t("settings.account.title")}>
      <div className="flex items-center gap-3 mb-3">
        <Avatar
          src={me.avatar_url}
          fallback={me.display_name ?? me.email}
          className="w-12 h-12 rounded-full"
        />
        <div className="min-w-0">
          <div className="font-semibold truncate">{me.display_name ?? me.email.split("@")[0]}</div>
          <div className="text-xs text-muted truncate">{me.email}</div>
          <div className="text-xs text-muted capitalize">{me.role}</div>
        </div>
      </div>
    </Section>

    {me.is_demo ? (
      <Section title={t("settings.account.youtubeAccess")}>
        <p className="text-xs text-muted leading-relaxed">
          {t("settings.account.demoNotice")}
        </p>
      </Section>
    ) : (
      <Section title={t("settings.account.youtubeAccess")}>
        <p className="text-xs text-muted leading-relaxed mb-1">
          {t("settings.account.youtubeAccessIntro")}
        </p>
        <div className="divide-y divide-border">
          <AccessRow
            title={t("settings.account.readTitle")}
            granted={me.can_read}
            grantedHint={t("settings.account.readGrantedHint")}
            enableHint={t("settings.account.readEnableHint")}
            onEnable={() => {
              window.location.href = "/auth/upgrade?access=read";
            }}
          />
          <AccessRow
            title={t("settings.account.writeTitle")}
            granted={me.can_write}
            grantedHint={t("settings.account.writeGrantedHint")}
            enableHint={t("settings.account.writeEnableHint")}
            onEnable={() => {
              window.location.href = "/auth/upgrade?access=write";
            }}
          />
        </div>
        <button
          onClick={onOpenWizard}
          className="mt-2 text-sm text-accent hover:underline"
        >
          {t("settings.account.walkMeThrough")}
        </button>
      </Section>
    )}
    {me.role === "admin" && <AdminInvites />}
    {me.role === "admin" && <AdminDemo />}
    </>
  );
}

function AdminInvites() {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const invites = useQuery({ queryKey: ["admin-invites"], queryFn: () => api.adminInvites() });
  const [newEmail, setNewEmail] = useState("");
  const refresh = () => {
    qc.invalidateQueries({ queryKey: ["admin-invites"] });
    qc.invalidateQueries({ queryKey: ["me"] }); // updates the pending badge
  };
  const approve = useMutation({
    mutationFn: (id: number) => api.approveInvite(id),
    onSuccess: () => {
      refresh();
      notify({ level: "success", message: t("settings.invites.approved") });
    },
    onError: () => notify({ level: "error", message: t("settings.invites.approveFailed") }),
  });
  const deny = useMutation({
    mutationFn: (id: number) => api.denyInvite(id),
    onSuccess: refresh,
    onError: () => notify({ level: "error", message: t("settings.invites.denyFailed") }),
  });
  const add = useMutation({
    mutationFn: (email: string) => api.addInvite(email),
    onSuccess: () => {
      setNewEmail("");
      refresh();
      notify({ level: "success", message: t("settings.invites.addedToWhitelist") });
    },
    onError: () => notify({ level: "error", message: t("settings.invites.addFailed") }),
  });

  const list = invites.data ?? [];
  const pending = list.filter((i) => i.status === "pending");
  const decided = list.filter((i) => i.status !== "pending");

  return (
    <Section title={t("settings.invites.title")}>
      {pending.length === 0 ? (
        <p className="text-sm text-muted">{t("settings.invites.noPending")}</p>
      ) : (
        <div className="space-y-1.5">
          {pending.map((i) => (
            <InviteRow
              key={i.id}
              inv={i}
              onApprove={() => approve.mutate(i.id)}
              onDeny={() => deny.mutate(i.id)}
              busy={approve.isPending || deny.isPending}
            />
          ))}
        </div>
      )}

      <form
        onSubmit={(e) => {
          e.preventDefault();
          if (newEmail.trim()) add.mutate(newEmail.trim());
        }}
        className="flex items-center gap-2 mt-3"
      >
        <input
          type="email"
          value={newEmail}
          onChange={(e) => setNewEmail(e.target.value)}
          placeholder={t("settings.invites.addPlaceholder")}
          className="flex-1 min-w-0 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-none focus:border-accent"
        />
        <button
          type="submit"
          className="shrink-0 glass-card glass-hover flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm transition"
        >
          <UserPlus className="w-4 h-4" /> {t("settings.invites.add")}
        </button>
      </form>

      {decided.length > 0 && (
        <details className="mt-3">
          <summary className="text-xs text-muted cursor-pointer">
            {t("settings.invites.decided", { count: decided.length })}
          </summary>
          <div className="mt-2 space-y-1 text-xs text-muted">
            {decided.map((i) => (
              <div key={i.id} className="flex items-center justify-between gap-2">
                <span className="truncate">{i.email}</span>
                <span className={i.status === "approved" ? "text-accent" : "text-red-400"}>
                  {i.status}
                </span>
              </div>
            ))}
          </div>
        </details>
      )}
    </Section>
  );
}

function AdminDemo() {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const confirm = useConfirm();
  const list = useQuery({ queryKey: ["demo-whitelist"], queryFn: () => api.adminDemoWhitelist() });
  const [newEmail, setNewEmail] = useState("");

  const add = useMutation({
    mutationFn: (email: string) => api.addDemoWhitelist(email),
    onSuccess: () => {
      setNewEmail("");
      qc.invalidateQueries({ queryKey: ["demo-whitelist"] });
      notify({ level: "success", message: t("settings.demo.added") });
    },
    onError: () => notify({ level: "error", message: t("settings.demo.addFailed") }),
  });
  const remove = useMutation({
    mutationFn: (id: number) => api.removeDemoWhitelist(id),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["demo-whitelist"] }),
    onError: () => notify({ level: "error", message: t("settings.demo.removeFailed") }),
  });
  const reset = useMutation({
    mutationFn: () => api.resetDemo(),
    onSuccess: (r: { playlists_seeded: number }) =>
      notify({
        level: "success",
        message: t("settings.demo.resetDone", { count: r.playlists_seeded }),
      }),
    onError: () => notify({ level: "error", message: t("settings.demo.resetFailed") }),
  });

  const rows = list.data ?? [];

  return (
    <Section title={t("settings.demo.title")}>
      <p className="text-xs text-muted leading-relaxed mb-2">{t("settings.demo.intro")}</p>

      {rows.length > 0 && (
        <div className="space-y-1.5 mb-3">
          {rows.map((r) => (
            <div key={r.id} className="glass-card flex items-center gap-2 p-2.5 rounded-xl">
              <div className="min-w-0 flex-1">
                <div className="text-sm truncate">{r.email}</div>
                {r.added_by && (
                  <div className="text-[11px] text-muted truncate">
                    {t("settings.demo.addedBy", { who: r.added_by })}
                  </div>
                )}
              </div>
              <Tooltip hint={t("settings.demo.removeHint")}>
                <button
                  onClick={() => remove.mutate(r.id)}
                  disabled={remove.isPending}
                  className="shrink-0 p-1.5 rounded-lg border border-border text-muted hover:text-red-400 disabled:opacity-50 transition"
                  aria-label={t("settings.demo.remove")}
                >
                  <Trash2 className="w-4 h-4" />
                </button>
              </Tooltip>
            </div>
          ))}
        </div>
      )}

      <form
        onSubmit={(e) => {
          e.preventDefault();
          if (newEmail.trim()) add.mutate(newEmail.trim());
        }}
        className="flex items-center gap-2"
      >
        <input
          type="email"
          value={newEmail}
          onChange={(e) => setNewEmail(e.target.value)}
          placeholder={t("settings.demo.addPlaceholder")}
          className="flex-1 min-w-0 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-none focus:border-accent"
        />
        <button
          type="submit"
          className="shrink-0 glass-card glass-hover flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm transition"
        >
          <UserPlus className="w-4 h-4" /> {t("settings.demo.add")}
        </button>
      </form>

      <div className="mt-4 pt-3 border-t border-border">
        <Tooltip hint={t("settings.demo.resetHint")}>
          <button
            onClick={async () => {
              if (
                await confirm({
                  title: t("settings.demo.resetTitle"),
                  message: t("settings.demo.resetConfirm"),
                  confirmLabel: t("settings.demo.reset"),
                  danger: true,
                })
              )
                reset.mutate();
            }}
            disabled={reset.isPending}
            className="glass-card glass-hover flex items-center gap-2 px-3 py-2 rounded-xl text-sm disabled:opacity-50 transition"
          >
            <RotateCcw className={`w-4 h-4 ${reset.isPending ? "animate-spin" : ""}`} />
            {t("settings.demo.reset")}
          </button>
        </Tooltip>
      </div>
    </Section>
  );
}

function InviteRow({
  inv,
  onApprove,
  onDeny,
  busy,
}: {
  inv: Invite;
  onApprove: () => void;
  onDeny: () => void;
  busy: boolean;
}) {
  const { t } = useTranslation();
  return (
    <div className="glass-card flex items-center gap-2 p-2.5 rounded-xl">
      <div className="min-w-0 flex-1">
        <div className="text-sm truncate">{inv.email}</div>
        {inv.requested_at && (
          <div className="text-[11px] text-muted">
            {t("settings.invites.requested", {
              time: new Date(inv.requested_at).toLocaleString(),
            })}
          </div>
        )}
      </div>
      <Tooltip hint={t("settings.invites.approveHint")}>
        <button
          onClick={onApprove}
          disabled={busy}
          className="shrink-0 p-1.5 rounded-lg border border-accent/40 text-accent hover:bg-accent/10 disabled:opacity-50 transition"
          aria-label={t("settings.invites.approve")}
        >
          <Check className="w-4 h-4" />
        </button>
      </Tooltip>
      <Tooltip hint={t("settings.invites.denyHint")}>
        <button
          onClick={onDeny}
          disabled={busy}
          className="shrink-0 p-1.5 rounded-lg border border-border text-muted hover:text-red-400 disabled:opacity-50 transition"
          aria-label={t("settings.invites.deny")}
        >
          <X className="w-4 h-4" />
        </button>
      </Tooltip>
    </div>
  );
}
