import { useState, useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { api } from "../api/client";
import { listBands } from "../api/bands";
import { listInvites, revokeInvite } from "../api/invites";
import { useBandStore } from "../stores/bandStore";
import { getInitials } from "../utils";

// ── Types ─────────────────────────────────────────────────────────────────────

interface MemberRead {
  id: string;
  display_name: string;
  email: string;
  avatar_url: string | null;
  nc_username: string | null;
  nc_url: string | null;
  nc_configured: boolean;
}

interface BandMember {
  id: string;
  display_name: string;
  email: string;
  role: string;
  joined_at: string;
}

interface BandInvite {
  id: string;
  token: string;
  role: string;
  expires_at: string | null;
  is_used: boolean;
}

interface Band {
  id: string;
  name: string;
  slug: string;
  genre_tags: string[];
  nc_folder_path: string | null;
}

type Section = "profile" | "members" | "storage" | "band";

// ── Helpers ───────────────────────────────────────────────────────────────────

function formatExpiry(expiresAt: string | null | undefined): string {
  if (!expiresAt) return "No expiry";
  const date = new Date(expiresAt);
  const diffHours = Math.floor((date.getTime() - Date.now()) / (1000 * 60 * 60));
  if (diffHours <= 0) return "Expired";
  if (diffHours < 24) return `Expires in ${diffHours}h`;
  return `Expires in ${Math.floor(diffHours / 24)}d`;
}

function isActive(invite: BandInvite): boolean {
  return !invite.is_used && !!invite.expires_at && new Date(invite.expires_at) > new Date();
}

// ── Shared style helpers ──────────────────────────────────────────────────────

const border = "rgba(255,255,255,0.06)";
const borderBright = "rgba(255,255,255,0.12)";

function Label({ children }: { children: React.ReactNode }) {
  return (
    <div style={{ fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.08em", color: "rgba(232,233,240,0.35)", marginBottom: 6 }}>
      {children}
    </div>
  );
}

function SectionHeading({ title, subtitle }: { title: string; subtitle?: string }) {
  return (
    <div style={{ marginBottom: 24 }}>
      <h2 style={{ margin: 0, fontSize: 18, fontWeight: 800, letterSpacing: -0.4, color: "#e8e9f0" }}>{title}</h2>
      {subtitle && <p style={{ margin: "4px 0 0", fontSize: 12, color: "rgba(232,233,240,0.4)", lineHeight: 1.5 }}>{subtitle}</p>}
    </div>
  );
}

function Divider() {
  return <div style={{ height: 1, background: border, margin: "24px 0" }} />;
}

function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
  const [focused, setFocused] = useState(false);
  return (
    <input
      {...props}
      onFocus={(e) => { setFocused(true); props.onFocus?.(e); }}
      onBlur={(e) => { setFocused(false); props.onBlur?.(e); }}
      style={{
        width: "100%", padding: "8px 12px",
        background: "#101c18",
        border: `1px solid ${focused ? "rgba(20,184,166,0.4)" : border}`,
        borderRadius: 8, color: "#e8e9f0",
        fontSize: 13, fontFamily: "inherit",
        outline: "none", boxSizing: "border-box",
        transition: "border-color 0.15s",
        ...props.style,
      }}
    />
  );
}

function SaveBtn({ pending, saved, onClick }: { pending: boolean; saved: boolean; onClick: () => void }) {
  return (
    <button
      onClick={onClick}
      disabled={pending}
      style={{
        padding: "8px 20px", borderRadius: 8,
        background: saved ? "rgba(52,211,153,0.12)" : "linear-gradient(135deg, #0d9488, #06b6d4)",
        border: saved ? "1px solid rgba(52,211,153,0.3)" : "none",
        color: saved ? "#34d399" : "white",
        cursor: pending ? "default" : "pointer",
        fontSize: 13, fontWeight: 600, fontFamily: "inherit",
        transition: "all 0.2s",
        boxShadow: saved ? "none" : "0 2px 12px rgba(20,184,166,0.3)",
      }}
    >
      {pending ? "Saving…" : saved ? "Saved ✓" : "Save"}
    </button>
  );
}

// ── Profile section ───────────────────────────────────────────────────────────

function ProfileSection({ me }: { me: MemberRead }) {
  const qc = useQueryClient();
  const [displayName, setDisplayName] = useState(me.display_name ?? "");
  const [avatarUrl, setAvatarUrl] = useState(me.avatar_url ?? "");
  const [uploading, setUploading] = useState(false);
  const [saved, setSaved] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => { setAvatarUrl(me.avatar_url ?? ""); }, [me.avatar_url]);

  const resizeImage = (file: File, max: number): Promise<File> =>
    new Promise((resolve, reject) => {
      const img = new Image();
      const reader = new FileReader();
      reader.onload = (ev) => {
        if (typeof ev.target?.result !== "string") { reject(new Error("read failed")); return; }
        img.onload = () => {
          const ratio = Math.min(max / img.width, max / img.height, 1);
          const canvas = document.createElement("canvas");
          canvas.width = img.width * ratio;
          canvas.height = img.height * ratio;
          const ctx = canvas.getContext("2d");
          if (!ctx) { reject(new Error("no ctx")); return; }
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
          canvas.toBlob((blob) => {
            if (!blob) { reject(new Error("no blob")); return; }
            resolve(new File([blob], file.name, { type: "image/jpeg", lastModified: Date.now() }));
          }, "image/jpeg", 0.8);
        };
        img.onerror = reject;
        img.src = ev.target!.result;
      };
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });

  const saveMutation = useMutation({
    mutationFn: () => api.patch<MemberRead>("/auth/me/settings", {
      display_name: displayName || undefined,
      avatar_url: avatarUrl || undefined,
    }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["me"] });
      setSaved(true); setError(null);
      setTimeout(() => setSaved(false), 2500);
    },
    onError: (err) => setError(err instanceof Error ? err.message : "Save failed"),
  });

  return (
    <div>
      <SectionHeading title="Profile" />

      {/* Avatar row */}
      <div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 24 }}>
        {avatarUrl ? (
          <img src={avatarUrl} alt="avatar" style={{ width: 64, height: 64, borderRadius: "50%", objectFit: "cover", flexShrink: 0 }} />
        ) : (
          <div style={{ width: 64, height: 64, borderRadius: "50%", background: "rgba(52,211,153,0.12)", border: "2px solid rgba(52,211,153,0.25)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 22, fontWeight: 700, color: "#34d399", flexShrink: 0 }}>
            {getInitials(me.display_name)}
          </div>
        )}
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <input type="file" accept="image/*" id="avatar-upload" style={{ display: "none" }}
            onChange={async (e) => {
              const file = e.target.files?.[0];
              if (!file) return;
              setUploading(true);
              try {
                const processed = file.size > 4 * 1024 * 1024 ? await resizeImage(file, 800) : file;
                const form = new FormData();
                form.append("file", processed, processed.name || file.name);
                const resp = await api.upload<MemberRead>("/auth/me/avatar", form);
                setAvatarUrl(resp.avatar_url || "");
                qc.invalidateQueries({ queryKey: ["me"] });
                qc.invalidateQueries({ queryKey: ["comments"] });
              } catch (err) {
                setError(err instanceof Error ? err.message : "Upload failed");
              } finally { setUploading(false); }
            }}
          />
          <label htmlFor="avatar-upload" style={{ padding: "7px 14px", borderRadius: 8, background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", color: "#2dd4bf", cursor: "pointer", fontSize: 12, fontWeight: 600 }}>
            {uploading ? "Uploading…" : "Upload"}
          </label>
          <button
            onClick={async () => {
              const seed = Math.random().toString(36).slice(2, 15);
              const url = `https://api.dicebear.com/9.x/identicon/svg?seed=${seed}&backgroundType=gradientLinear&size=128`;
              try {
                await api.patch<MemberRead>("/auth/me/settings", { avatar_url: url });
                setAvatarUrl(url);
                qc.invalidateQueries({ queryKey: ["me"] });
                qc.invalidateQueries({ queryKey: ["comments"] });
              } catch (err) { setError(err instanceof Error ? err.message : "Failed"); }
            }}
            style={{ padding: "7px 14px", borderRadius: 8, background: "transparent", border: `1px solid ${border}`, color: "rgba(232,233,240,0.5)", cursor: "pointer", fontSize: 12, fontFamily: "inherit" }}
          >
            Generate
          </button>
          {avatarUrl && (
            <button
              onClick={async () => {
                try {
                  await api.patch<MemberRead>("/auth/me/settings", { avatar_url: "" });
                  setAvatarUrl("");
                  qc.invalidateQueries({ queryKey: ["me"] });
                  qc.invalidateQueries({ queryKey: ["comments"] });
                } catch (err) { setError(err instanceof Error ? err.message : "Failed"); }
              }}
              style={{ padding: "7px 14px", borderRadius: 8, background: "transparent", border: `1px solid rgba(244,63,94,0.2)`, color: "#f87171", cursor: "pointer", fontSize: 12, fontFamily: "inherit" }}
            >
              Remove
            </button>
          )}
        </div>
      </div>

      {/* Display name */}
      <div style={{ marginBottom: 16 }}>
        <Label>Display name</Label>
        <Input value={displayName} onChange={(e) => setDisplayName(e.target.value)} />
      </div>

      {/* Email (read-only) */}
      <div style={{ marginBottom: 24 }}>
        <Label>Email</Label>
        <div style={{ fontSize: 13, color: "rgba(232,233,240,0.45)", padding: "8px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}>
          {me.email}
        </div>
      </div>

      {error && <p style={{ color: "#f87171", fontSize: 12, marginBottom: 12 }}>{error}</p>}
      <SaveBtn pending={saveMutation.isPending} saved={saved} onClick={() => saveMutation.mutate()} />
    </div>
  );
}

// ── Storage section (NC credentials + scan folder) ────────────────────────────

function StorageSection({ bandId, band, amAdmin, me }: { bandId: string; band: Band; amAdmin: boolean; me: MemberRead }) {
  const qc = useQueryClient();

  // NC credentials state
  const [ncUrl, setNcUrl] = useState(me.nc_url ?? "");
  const [ncUsername, setNcUsername] = useState(me.nc_username ?? "");
  const [ncPassword, setNcPassword] = useState("");
  const [ncSaved, setNcSaved] = useState(false);
  const [ncError, setNcError] = useState<string | null>(null);

  // Scan folder state
  const [editingPath, setEditingPath] = useState(false);
  const [folderInput, setFolderInput] = useState("");
  const [scanning, setScanning] = useState(false);
  const [scanProgress, setScanProgress] = useState<string | null>(null);
  const [scanMsg, setScanMsg] = useState<string | null>(null);

  const ncMutation = useMutation({
    mutationFn: () => api.patch<MemberRead>("/auth/me/settings", {
      nc_url: ncUrl || undefined,
      nc_username: ncUsername || undefined,
      nc_password: ncPassword || undefined,
    }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["me"] });
      setNcSaved(true); setNcError(null); setNcPassword("");
      setTimeout(() => setNcSaved(false), 2500);
    },
    onError: (err) => setNcError(err instanceof Error ? err.message : "Save failed"),
  });

  const pathMutation = useMutation({
    mutationFn: (nc_folder_path: string) => api.patch(`/bands/${bandId}`, { nc_folder_path }),
    onSuccess: () => { qc.invalidateQueries({ queryKey: ["band", bandId] }); setEditingPath(false); },
  });

  async function startScan() {
    if (scanning) return;
    setScanning(true); setScanMsg(null); setScanProgress("Starting scan…");
    try {
      const resp = await fetch(`/api/v1/bands/${bandId}/nc-scan/stream`, { credentials: "include" });
      if (!resp.ok || !resp.body) throw new Error(`HTTP ${resp.status}`);
      const reader = resp.body.getReader();
      const decoder = new TextDecoder();
      let buf = "";
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += decoder.decode(value, { stream: true });
        const lines = buf.split("\n"); buf = lines.pop() ?? "";
        for (const line of lines) {
          if (!line.trim()) continue;
          let ev: Record<string, unknown>;
          try { ev = JSON.parse(line); } catch { continue; }
          if (ev.type === "progress") setScanProgress(ev.message as string);
          else if (ev.type === "song" || ev.type === "session") {
            qc.invalidateQueries({ queryKey: ["sessions", bandId] });
          } else if (ev.type === "done") {
            const s = ev.stats as { found: number; imported: number; skipped: number };
            setScanMsg(s.imported > 0
              ? `Imported ${s.imported} new song${s.imported !== 1 ? "s" : ""} (${s.skipped} already registered).`
              : s.found === 0 ? "No audio files found."
              : `All ${s.found} file${s.found !== 1 ? "s" : ""} already registered.`);
            setTimeout(() => setScanMsg(null), 6000);
          } else if (ev.type === "error") setScanMsg(`Scan error: ${ev.message}`);
        }
      }
    } catch (err) { setScanMsg(err instanceof Error ? err.message : "Scan failed"); }
    finally { setScanning(false); setScanProgress(null); }
  }

  const defaultPath = `bands/${band.slug}/`;
  const currentPath = band.nc_folder_path ?? defaultPath;

  return (
    <div>
      <SectionHeading title="Storage" subtitle="Configure Nextcloud credentials and your band's recording folder." />

      {/* NC Connection */}
      <div style={{ marginBottom: 8 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 16 }}>
          <div style={{ width: 8, height: 8, borderRadius: "50%", background: me.nc_configured ? "#34d399" : "rgba(232,233,240,0.25)", flexShrink: 0, boxShadow: me.nc_configured ? "0 0 6px rgba(52,211,153,0.5)" : "none" }} />
          <span style={{ fontSize: 12, color: me.nc_configured ? "#34d399" : "rgba(232,233,240,0.4)" }}>
            {me.nc_configured ? "Nextcloud connected" : "Nextcloud not configured"}
          </span>
        </div>

        <div style={{ padding: "14px 16px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 10, marginBottom: 16 }}>
          <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 8, marginBottom: 12 }}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 600, color: "#e8e9f0", marginBottom: 2 }}>Nextcloud Connection</div>
              <div style={{ fontSize: 11, color: "rgba(232,233,240,0.35)" }}>
                Your personal credentials — will move to per-band config in a future update.
              </div>
            </div>
          </div>

          <div style={{ display: "grid", gap: 12 }}>
            <div>
              <Label>Nextcloud URL</Label>
              <Input value={ncUrl} onChange={(e) => setNcUrl(e.target.value)} placeholder="https://cloud.example.com" />
            </div>
            <div>
              <Label>Username</Label>
              <Input value={ncUsername} onChange={(e) => setNcUsername(e.target.value)} />
            </div>
            <div>
              <Label>Password / App Password</Label>
              <Input type="password" value={ncPassword} onChange={(e) => setNcPassword(e.target.value)} placeholder={me.nc_configured ? "••••••••  (leave blank to keep)" : ""} />
              <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", marginTop: 4 }}>
                Use an app password from Nextcloud → Settings → Security.
              </div>
            </div>
          </div>

          {ncError && <p style={{ color: "#f87171", fontSize: 12, margin: "12px 0 0" }}>{ncError}</p>}
          <div style={{ marginTop: 14 }}>
            <SaveBtn pending={ncMutation.isPending} saved={ncSaved} onClick={() => ncMutation.mutate()} />
          </div>
        </div>
      </div>

      {/* Scan folder — admin only */}
      {amAdmin && (
        <>
          <Divider />
          <div style={{ fontSize: 13, fontWeight: 600, color: "#e8e9f0", marginBottom: 4 }}>Scan Folder</div>
          <div style={{ fontSize: 12, color: "rgba(232,233,240,0.35)", marginBottom: 16, lineHeight: 1.55 }}>
            RehearsalHub reads recordings from your Nextcloud — files are never copied to our servers.
          </div>

          <div style={{ background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 10, padding: "12px 16px", marginBottom: 14 }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
              <div>
                <Label>Scan path</Label>
                <code style={{ fontSize: 13, color: "#34d399", fontFamily: "monospace" }}>{currentPath}</code>
              </div>
              {!editingPath && (
                <button
                  onClick={() => { setFolderInput(band.nc_folder_path ?? ""); setEditingPath(true); }}
                  style={{ padding: "4px 10px", background: "transparent", border: `1px solid ${border}`, borderRadius: 6, color: "rgba(232,233,240,0.42)", cursor: "pointer", fontSize: 11, fontFamily: "inherit" }}
                >
                  Edit
                </button>
              )}
            </div>

            {editingPath && (
              <div style={{ marginTop: 12 }}>
                <Input value={folderInput} onChange={(e) => setFolderInput(e.target.value)} placeholder={defaultPath} style={{ fontFamily: "monospace" }} />
                <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
                  <button onClick={() => pathMutation.mutate(folderInput)} disabled={pathMutation.isPending}
                    style={{ padding: "6px 14px", background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", borderRadius: 6, color: "#2dd4bf", cursor: "pointer", fontSize: 12, fontWeight: 600, fontFamily: "inherit" }}>
                    {pathMutation.isPending ? "Saving…" : "Save"}
                  </button>
                  <button onClick={() => setEditingPath(false)}
                    style={{ padding: "6px 14px", background: "transparent", border: `1px solid ${border}`, borderRadius: 6, color: "rgba(232,233,240,0.42)", cursor: "pointer", fontSize: 12, fontFamily: "inherit" }}>
                    Cancel
                  </button>
                </div>
              </div>
            )}
          </div>

          <button
            onClick={startScan} disabled={scanning}
            style={{ padding: "7px 16px", background: scanning ? "transparent" : "rgba(52,211,153,0.08)", border: `1px solid ${scanning ? border : "rgba(52,211,153,0.25)"}`, borderRadius: 8, color: scanning ? "rgba(232,233,240,0.28)" : "#34d399", cursor: scanning ? "default" : "pointer", fontSize: 12, fontFamily: "inherit", transition: "all 0.12s" }}>
            {scanning ? "Scanning…" : "⟳ Scan Nextcloud"}
          </button>

          {scanning && scanProgress && (
            <div style={{ marginTop: 10, background: "rgba(255,255,255,0.03)", border: `1px solid ${border}`, borderRadius: 8, color: "rgba(232,233,240,0.42)", fontSize: 12, padding: "8px 14px", fontFamily: "monospace" }}>
              {scanProgress}
            </div>
          )}
          {scanMsg && (
            <div style={{ marginTop: 10, background: "rgba(52,211,153,0.06)", border: "1px solid rgba(52,211,153,0.25)", borderRadius: 8, color: "#34d399", fontSize: 12, padding: "8px 14px" }}>
              {scanMsg}
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ── Members section ───────────────────────────────────────────────────────────

function MembersSection({ bandId, band, amAdmin, members, membersLoading }: { bandId: string; band: Band; amAdmin: boolean; members: BandMember[] | undefined; membersLoading: boolean }) {
  const qc = useQueryClient();
  const [inviteLink, setInviteLink] = useState<string | null>(null);

  const { data: invitesData, isLoading: invitesLoading } = useQuery({
    queryKey: ["invites", bandId],
    queryFn: () => listInvites(bandId),
    enabled: amAdmin, retry: false,
  });

  const inviteMutation = useMutation({
    mutationFn: () => api.post<BandInvite>(`/bands/${bandId}/invites`, {}),
    onSuccess: (invite) => {
      const url = `${window.location.origin}/invite/${invite.token}`;
      setInviteLink(url);
      navigator.clipboard.writeText(url).catch(() => {});
      qc.invalidateQueries({ queryKey: ["invites", bandId] });
    },
  });

  const removeMutation = useMutation({
    mutationFn: (memberId: string) => api.delete(`/bands/${bandId}/members/${memberId}`),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["members", bandId] }),
  });

  const revokeMutation = useMutation({
    mutationFn: (inviteId: string) => revokeInvite(inviteId),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["invites", bandId] }),
  });

  const activeInvites = invitesData?.invites.filter(isActive) ?? [];

  return (
    <div>
      <SectionHeading title="Members" subtitle={`Who has access to ${band.name}'s recordings.`} />

      {/* Invite button — admin only */}
      {amAdmin && (
        <div style={{ marginBottom: 16 }}>
          <button
            onClick={() => inviteMutation.mutate()} disabled={inviteMutation.isPending}
            style={{ padding: "8px 16px", background: "rgba(20,184,166,0.12)", border: "1px solid rgba(20,184,166,0.3)", borderRadius: 8, color: "#2dd4bf", cursor: "pointer", fontSize: 13, fontWeight: 600, fontFamily: "inherit" }}>
            {inviteMutation.isPending ? "Generating…" : "+ Generate invite link"}
          </button>
        </div>
      )}

      {inviteLink && (
        <div style={{ background: "rgba(20,184,166,0.06)", border: "1px solid rgba(20,184,166,0.22)", borderRadius: 10, padding: "12px 16px", marginBottom: 16 }}>
          <p style={{ color: "rgba(232,233,240,0.35)", fontSize: 11, margin: "0 0 5px" }}>Invite link (copied · valid 72h):</p>
          <code style={{ color: "#2dd4bf", fontSize: 12, wordBreak: "break-all", fontFamily: "monospace" }}>{inviteLink}</code>
          <button onClick={() => setInviteLink(null)} style={{ display: "block", marginTop: 6, background: "none", border: "none", color: "rgba(232,233,240,0.28)", cursor: "pointer", fontSize: 11, padding: 0, fontFamily: "inherit" }}>
            Dismiss
          </button>
        </div>
      )}

      {/* Member list */}
      {membersLoading ? (
        <p style={{ color: "rgba(232,233,240,0.28)", fontSize: 13 }}>Loading…</p>
      ) : (
        <div style={{ display: "grid", gap: 6, marginBottom: 20 }}>
          {members?.map((m) => (
            <div key={m.id} style={{ background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 10, padding: "10px 14px", display: "flex", alignItems: "center", gap: 10 }}>
              <div style={{ width: 32, height: 32, borderRadius: "50%", background: "rgba(52,211,153,0.12)", border: "1px solid rgba(52,211,153,0.25)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 700, color: "#34d399", flexShrink: 0 }}>
                {getInitials(m.display_name)}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: "rgba(232,233,240,0.75)" }}>{m.display_name}</div>
                <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", marginTop: 1 }}>{m.email}</div>
              </div>
              <span style={{ fontSize: 10, fontFamily: "monospace", padding: "2px 7px", borderRadius: 4, background: m.role === "admin" ? "rgba(20,184,166,0.1)" : "rgba(255,255,255,0.05)", color: m.role === "admin" ? "#2dd4bf" : "rgba(232,233,240,0.38)", border: `1px solid ${m.role === "admin" ? "rgba(20,184,166,0.28)" : border}`, whiteSpace: "nowrap" }}>
                {m.role}
              </span>
              {amAdmin && m.role !== "admin" && (
                <button onClick={() => removeMutation.mutate(m.id)} disabled={removeMutation.isPending}
                  style={{ background: "rgba(244,63,94,0.08)", border: "1px solid rgba(244,63,94,0.2)", borderRadius: 6, color: "#f87171", cursor: "pointer", fontSize: 11, padding: "3px 8px", fontFamily: "inherit" }}>
                  Remove
                </button>
              )}
            </div>
          ))}
        </div>
      )}

      {/* Role legend */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: amAdmin ? 0 : 0 }}>
        <div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}>
          <div style={{ fontSize: 12, color: "#2dd4bf", marginBottom: 3 }}>Admin</div>
          <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", lineHeight: 1.55 }}>Upload, delete, manage members and storage</div>
        </div>
        <div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 8 }}>
          <div style={{ fontSize: 12, color: "rgba(232,233,240,0.45)", marginBottom: 3 }}>Member</div>
          <div style={{ fontSize: 11, color: "rgba(232,233,240,0.28)", lineHeight: 1.55 }}>Listen, comment, annotate recordings</div>
        </div>
      </div>

      {/* Pending invites — admin only */}
      {amAdmin && (
        <>
          <Divider />
          <div style={{ fontSize: 13, fontWeight: 600, color: "#e8e9f0", marginBottom: 12 }}>Pending Invites</div>
          {invitesLoading ? (
            <p style={{ color: "rgba(232,233,240,0.28)", fontSize: 13 }}>Loading invites…</p>
          ) : activeInvites.length === 0 ? (
            <p style={{ color: "rgba(232,233,240,0.28)", fontSize: 13 }}>No pending invites.</p>
          ) : (
            <div style={{ display: "grid", gap: 6 }}>
              {activeInvites.map((invite) => (
                <div key={invite.id} style={{ background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 10, padding: "10px 14px", display: "flex", alignItems: "center", gap: 10 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <code style={{ fontSize: 11, color: "rgba(232,233,240,0.35)", fontFamily: "monospace" }}>{invite.token.slice(0, 8)}…{invite.token.slice(-4)}</code>
                    <div style={{ fontSize: 11, color: "rgba(232,233,240,0.25)", marginTop: 2 }}>{formatExpiry(invite.expires_at)} · {invite.role}</div>
                  </div>
                  <button onClick={() => navigator.clipboard.writeText(`${window.location.origin}/invite/${invite.token}`).catch(() => {})}
                    style={{ background: "transparent", border: `1px solid ${border}`, borderRadius: 6, color: "rgba(232,233,240,0.42)", cursor: "pointer", fontSize: 11, padding: "3px 8px", fontFamily: "inherit" }}>
                    Copy
                  </button>
                  <button onClick={() => revokeMutation.mutate(invite.id)} disabled={revokeMutation.isPending}
                    style={{ background: "rgba(244,63,94,0.08)", border: "1px solid rgba(244,63,94,0.2)", borderRadius: 6, color: "#f87171", cursor: "pointer", fontSize: 11, padding: "3px 8px", fontFamily: "inherit" }}>
                    Revoke
                  </button>
                </div>
              ))}
            </div>
          )}
          <p style={{ fontSize: 11, color: "rgba(232,233,240,0.2)", marginTop: 8 }}>No account needed to accept an invite.</p>
        </>
      )}
    </div>
  );
}

// ── Band section ──────────────────────────────────────────────────────────────

function BandSection({ bandId, band }: { bandId: string; band: Band }) {
  const qc = useQueryClient();
  const [nameInput, setNameInput] = useState(band.name);
  const [tagInput, setTagInput] = useState("");
  const [tags, setTags] = useState<string[]>(band.genre_tags);
  const [saved, setSaved] = useState(false);

  const updateMutation = useMutation({
    mutationFn: (payload: { name?: string; genre_tags?: string[] }) => api.patch(`/bands/${bandId}`, payload),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["band", bandId] });
      qc.invalidateQueries({ queryKey: ["bands"] });
      setSaved(true); setTimeout(() => setSaved(false), 2500);
    },
  });

  function addTag() {
    const t = tagInput.trim();
    if (t && !tags.includes(t)) setTags((p) => [...p, t]);
    setTagInput("");
  }

  return (
    <div>
      <SectionHeading title="Band" subtitle="Identity settings and danger zone." />

      <div style={{ marginBottom: 14 }}>
        <Label>Band name</Label>
        <Input value={nameInput} onChange={(e) => setNameInput(e.target.value)} />
      </div>

      <div style={{ marginBottom: 20 }}>
        <Label>Genre tags</Label>
        <div style={{ display: "flex", gap: 5, flexWrap: "wrap", marginBottom: 8 }}>
          {tags.map((t) => (
            <span key={t} style={{ background: "rgba(20,184,166,0.1)", color: "#2dd4bf", fontSize: 11, padding: "3px 10px", borderRadius: 20, display: "flex", alignItems: "center", gap: 5 }}>
              {t}
              <button onClick={() => setTags((p) => p.filter((x) => x !== t))} style={{ background: "none", border: "none", color: "#2dd4bf", cursor: "pointer", fontSize: 13, padding: 0, lineHeight: 1, fontFamily: "inherit" }}>×</button>
            </span>
          ))}
        </div>
        <div style={{ display: "flex", gap: 6 }}>
          <Input value={tagInput} onChange={(e) => setTagInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && addTag()} placeholder="Add tag…" style={{ flex: undefined, width: "auto" }} />
          <button onClick={addTag} style={{ padding: "7px 12px", background: "transparent", border: `1px solid ${border}`, borderRadius: 8, color: "rgba(232,233,240,0.42)", cursor: "pointer", fontSize: 12, fontFamily: "inherit" }}>+</button>
        </div>
      </div>

      <SaveBtn pending={updateMutation.isPending} saved={saved} onClick={() => updateMutation.mutate({ name: nameInput.trim() || band.name, genre_tags: tags })} />

      <Divider />

      {/* Danger zone */}
      <div style={{ border: "1px solid rgba(244,63,94,0.18)", borderRadius: 10, padding: "16px 18px", background: "rgba(244,63,94,0.03)" }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: "#f87171", marginBottom: 4 }}>Delete this band</div>
        <div style={{ fontSize: 12, color: "rgba(244,63,94,0.45)", marginBottom: 12, lineHeight: 1.5 }}>
          Removes all members and deletes comments. Storage files are NOT deleted.
        </div>
        <button style={{ background: "rgba(244,63,94,0.08)", border: "1px solid rgba(244,63,94,0.2)", borderRadius: 7, color: "#f87171", cursor: "pointer", padding: "6px 14px", fontSize: 12, fontFamily: "inherit" }}>
          Delete band
        </button>
      </div>
    </div>
  );
}

// ── Left nav ──────────────────────────────────────────────────────────────────

function NavItem({ label, active, onClick }: { label: string; active: boolean; onClick: () => void }) {
  const [hovered, setHovered] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      style={{
        width: "100%", textAlign: "left",
        padding: "8px 10px", borderRadius: 8,
        border: "none", cursor: "pointer",
        fontSize: 13, fontFamily: "inherit", fontWeight: active ? 600 : 400,
        background: active ? "rgba(20,184,166,0.1)" : hovered ? "rgba(255,255,255,0.04)" : "transparent",
        color: active ? "#2dd4bf" : hovered ? "rgba(232,233,240,0.75)" : "rgba(232,233,240,0.45)",
        transition: "all 0.12s",
        position: "relative",
      }}
    >
      {active && <div style={{ position: "absolute", left: 0, top: "20%", bottom: "20%", width: 2, borderRadius: "0 2px 2px 0", background: "linear-gradient(to bottom, #0d9488, #22d3ee)" }} />}
      {label}
    </button>
  );
}

function NavLabel({ children }: { children: React.ReactNode }) {
  return (
    <div style={{ fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.08em", color: "rgba(232,233,240,0.25)", padding: "12px 10px 5px" }}>
      {children}
    </div>
  );
}

// ── SettingsPage ──────────────────────────────────────────────────────────────

export function SettingsPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  const { activeBandId, setActiveBandId } = useBandStore();
  const [isMobile, setIsMobile] = useState(false);

  useEffect(() => {
    const check = () => setIsMobile(window.innerWidth < 768);
    check();
    window.addEventListener("resize", check);
    return () => window.removeEventListener("resize", check);
  }, []);

  const section = (searchParams.get("section") ?? "profile") as Section;
  const go = (s: Section) => setSearchParams({ section: s }, { replace: true });

  // Data
  const { data: me } = useQuery({ queryKey: ["me"], queryFn: () => api.get<MemberRead>("/auth/me") });
  const { data: bands } = useQuery({ queryKey: ["bands"], queryFn: listBands });
  const { data: band } = useQuery({
    queryKey: ["band", activeBandId],
    queryFn: () => api.get<Band>(`/bands/${activeBandId}`),
    enabled: !!activeBandId,
  });
  const { data: members, isLoading: membersLoading } = useQuery({
    queryKey: ["members", activeBandId],
    queryFn: () => api.get<BandMember[]>(`/bands/${activeBandId}/members`),
    enabled: !!activeBandId,
  });

  const amAdmin = !!me && (members?.some((m) => m.id === me.id && m.role === "admin") ?? false);

  if (!me) return <div style={{ padding: 32, color: "rgba(232,233,240,0.35)" }}>Loading…</div>;

  // ── Mobile: list → detail drill-down ─────────────────────────────────────

  if (isMobile) {
    const sections: { key: Section; label: string; group: string }[] = [
      { key: "profile", label: "Profile", group: "Account" },
      ...(activeBandId && band ? [
        { key: "members" as Section, label: "Members", group: band.name },
        { key: "storage" as Section, label: "Storage", group: band.name },
        ...(amAdmin ? [{ key: "band" as Section, label: "Band", group: band.name }] : []),
      ] : []),
    ];

    return (
      <div style={{ height: "100%", display: "flex", flexDirection: "column", overflowY: "auto" }}>
        {/* Back to list */}
        {section !== "profile" || searchParams.has("section") ? (
          <div>
            <button onClick={() => setSearchParams({})}
              style={{ display: "flex", alignItems: "center", gap: 6, padding: "12px 16px", background: "none", border: "none", cursor: "pointer", color: "#2dd4bf", fontSize: 13, fontFamily: "inherit" }}>
              ← Settings
            </button>
            <div style={{ padding: "0 16px 24px" }}>
              {section === "profile" && <ProfileSection me={me} />}
              {section === "members" && activeBandId && band && <MembersSection bandId={activeBandId} band={band} amAdmin={amAdmin} members={members} membersLoading={membersLoading} />}
              {section === "storage" && activeBandId && band && <StorageSection bandId={activeBandId} band={band} amAdmin={amAdmin} me={me} />}
              {section === "band" && activeBandId && band && amAdmin && <BandSection bandId={activeBandId} band={band} />}
            </div>
          </div>
        ) : (
          <div style={{ padding: "20px 16px" }}>
            <h1 style={{ fontSize: 22, fontWeight: 800, color: "#e8e9f0", margin: "0 0 20px", letterSpacing: -0.5 }}>Settings</h1>
            {sections.map((s, i) => {
              const showGroupLabel = i === 0 || sections[i - 1].group !== s.group;
              return (
                <div key={s.key}>
                  {showGroupLabel && <NavLabel>{s.group}</NavLabel>}
                  <button onClick={() => go(s.key)}
                    style={{ width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between", padding: "13px 12px", background: "rgba(255,255,255,0.02)", border: `1px solid ${border}`, borderRadius: 10, cursor: "pointer", color: "#e8e9f0", fontFamily: "inherit", fontSize: 13, marginBottom: 6 }}>
                    {s.label}
                    <span style={{ color: "rgba(232,233,240,0.3)", fontSize: 16 }}>›</span>
                  </button>
                </div>
              );
            })}
          </div>
        )}
      </div>
    );
  }

  // ── Desktop: two-column ───────────────────────────────────────────────────

  return (
    <div style={{ display: "flex", height: "100%", overflow: "hidden" }}>

      {/* Left nav */}
      <nav style={{ width: 220, minWidth: 220, background: "#0c1612", borderRight: `1px solid ${border}`, padding: "20px 12px", display: "flex", flexDirection: "column", overflow: "hidden", flexShrink: 0 }}>
        <h1 style={{ fontSize: 16, fontWeight: 800, color: "#e8e9f0", margin: "0 0 4px 8px", letterSpacing: -0.3 }}>Settings</h1>
        <div style={{ height: 1, background: border, margin: "12px 0" }} />

        <NavLabel>Account</NavLabel>
        <NavItem label="Profile" active={section === "profile"} onClick={() => go("profile")} />

        {activeBandId && band && (
          <>
            <div style={{ height: 1, background: border, margin: "10px 0" }} />

            {/* Band switcher in nav */}
            <div style={{ padding: "0 10px 8px" }}>
              <div style={{ fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.08em", color: "rgba(232,233,240,0.25)", marginBottom: 6 }}>Band</div>
              <select
                value={activeBandId}
                onChange={(e) => {
                  setActiveBandId(e.target.value);
                  // Stay on same section if possible
                  setSearchParams({ section }, { replace: true });
                }}
                style={{ width: "100%", padding: "6px 8px", background: "#101c18", border: `1px solid ${borderBright}`, borderRadius: 7, color: "#e8e9f0", fontSize: 12, fontFamily: "inherit", cursor: "pointer", outline: "none" }}
              >
                {bands?.map((b) => (
                  <option key={b.id} value={b.id}>{b.name}</option>
                ))}
              </select>
            </div>

            <NavItem label="Members" active={section === "members"} onClick={() => go("members")} />
            <NavItem label="Storage" active={section === "storage"} onClick={() => go("storage")} />
            {amAdmin && <NavItem label="Band" active={section === "band"} onClick={() => go("band")} />}
          </>
        )}

        {!activeBandId && (
          <div style={{ padding: "12px 10px", fontSize: 12, color: "rgba(232,233,240,0.25)", lineHeight: 1.55 }}>
            No band active. Navigate to a band to see band settings.
          </div>
        )}
      </nav>

      {/* Content */}
      <div style={{ flex: 1, overflowY: "auto", padding: "32px 40px" }}>
        <div style={{ maxWidth: 560 }}>
          {section === "profile" && <ProfileSection me={me} />}
          {section === "members" && activeBandId && band && (
            <MembersSection bandId={activeBandId} band={band} amAdmin={amAdmin} members={members} membersLoading={membersLoading} />
          )}
          {section === "storage" && activeBandId && band && (
            <StorageSection bandId={activeBandId} band={band} amAdmin={amAdmin} me={me} />
          )}
          {section === "band" && activeBandId && band && amAdmin && (
            <BandSection bandId={activeBandId} band={band} />
          )}
          {section === "band" && activeBandId && !amAdmin && (
            <div style={{ color: "rgba(232,233,240,0.35)", fontSize: 13 }}>Only admins can access band settings.</div>
          )}
          {(section === "members" || section === "storage" || section === "band") && !activeBandId && (
            <div style={{ color: "rgba(232,233,240,0.35)", fontSize: 13 }}>No active band selected.</div>
          )}
        </div>
      </div>
    </div>
  );
}
