"use client";

import { useEffect, useMemo, useRef, useState, useCallback } from "react";
import {
  Bot,
  Bookmark,
  ChevronDown,
  ChevronLeft,
  Download,
  Globe,
  Hash,
  Image as ImageIcon,
  Info,
  Lock,
  MessageSquareText,
  Paperclip,
  Phone,
  Plus,
  Search,
  Send,
  Settings,
  Shield,
  Smile,
  Sparkles,
  Star,
  Trash2,
  UserMinus,
  UserPlus,
  Users2,
  Video,
  X,
} from "lucide-react";
import { useRouter, useSearchParams } from "next/navigation";
import Image from "next/image";
import { FilePreviewModal } from "@/components/attachments/file-preview-modal";
import { FileThumbnail } from "@/components/attachments/file-thumbnail";
import { Avatar } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Modal } from "@/components/ui/modal";
import { Skeleton } from "@/components/ui/skeleton";
import { useChat } from "@/hooks/use-chat";
import { useTeams } from "@/hooks/use-teams";
import { cn } from "@/lib/utils";
import { formatFileSize, isPreviewableFile } from "@/lib/file-utils";
import { useWorkspaceStore } from "@/stores/workspace-store";
import type {
  Attachment,
  ChatDirectoryProfile,
  ChatMessage,
  ChatThread,
  ChatThreadRole,
} from "@/types";

// ─── Constants ───────────────────────────────────────────────────────────────

const EMOJI_OPTIONS = ["👍", "❤️", "😂", "🔥", "✅", "👀", "🎉", "💯"];
const QUICK_EMOJIS  = ["👍", "❤️", "😂", "🔥", "✅"];

// ─── Pure helpers ────────────────────────────────────────────────────────────

function threadTitle(t: ChatThread, uid: string | null) {
  if (t.thread_type === "direct") {
    const o = t.participants?.find((p) => p.user_id !== uid)?.user;
    return o?.full_name || o?.username || o?.email || "Chat";
  }
  return t.title || "Unbenannt";
}

function threadPreview(t: ChatThread, uid: string | null) {
  if (t.last_message?.body) return t.last_message.body;
  if (t.thread_type === "direct") {
    const o = t.participants?.find((p) => p.user_id !== uid)?.user;
    return o?.username ? `@${o.username}` : "Direkte Nachricht";
  }
  if (t.thread_type === "group") return `${t.participants?.length || 0} Mitglieder`;
  return t.description || "Team-Kanal";
}

function contextBadge(t: ChatThread) {
  const m: Record<string, string> = {
    team: "Team", project: "Projekt", task: "Aufgabe",
    note: "Notiz", document: "Dokument", customer: "Kunde",
  };
  return m[t.context_type || ""] || (t.thread_type === "direct" ? "DM" : "Workspace");
}

function teamInitials(name: string) {
  return name.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
}

function fmtTime(d: string) {
  return new Date(d).toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
}

function fmtDateSep(d: string) {
  const date = new Date(d);
  const diff = Math.round(
    (new Date().setHours(0, 0, 0, 0) - new Date(date).setHours(0, 0, 0, 0)) / 86400000
  );
  if (diff === 0) return "Heute";
  if (diff === 1) return "Gestern";
  if (diff < 7) return date.toLocaleDateString("de-DE", { weekday: "long" });
  return date.toLocaleDateString("de-DE", { day: "2-digit", month: "long", year: "numeric" });
}

function needsDateSep(msgs: ChatMessage[], i: number) {
  if (i === 0) return true;
  return new Date(msgs[i].created_at).toDateString() !== new Date(msgs[i - 1].created_at).toDateString();
}

function isGrouped(msgs: ChatMessage[], i: number) {
  if (i === 0) return false;
  const c = msgs[i], p = msgs[i - 1];
  return c.sender_id === p.sender_id &&
    new Date(c.created_at).getTime() - new Date(p.created_at).getTime() < 300_000;
}

function renderBody(body: string) {
  return body.split(/(https?:\/\/[^\s]+)/g).map((s, i) =>
    /^https?:\/\//.test(s)
      ? <a key={i} href={s} target="_blank" rel="noreferrer" className="text-indigo-400 hover:text-indigo-300 underline underline-offset-2 transition-colors">{s}</a>
      : <span key={i}>{s}</span>
  );
}

const ROLE_THEMES: Record<
  ChatThreadRole,
  {
    label: string;
    nameClass: string;
    badgeClass: string;
    dotClass: string;
    metaClass: string;
    surfaceClass: string;
    selectClass: string;
  }
> = {
  owner: {
    label: "Owner",
    nameClass: "text-amber-100",
    badgeClass:
      "border-amber-400/20 bg-gradient-to-r from-amber-500/14 to-orange-500/10 text-amber-100",
    dotClass: "bg-gradient-to-r from-amber-300 to-orange-400",
    metaClass: "text-amber-100/55",
    surfaceClass: "border-amber-400/12 bg-amber-500/[0.035]",
    selectClass: "border-amber-400/22 text-amber-100",
  },
  admin: {
    label: "Admin",
    nameClass: "text-sky-100",
    badgeClass:
      "border-sky-400/20 bg-gradient-to-r from-sky-500/14 to-indigo-500/10 text-sky-100",
    dotClass: "bg-gradient-to-r from-sky-300 to-indigo-400",
    metaClass: "text-sky-100/55",
    surfaceClass: "border-sky-400/12 bg-sky-500/[0.035]",
    selectClass: "border-sky-400/22 text-sky-100",
  },
  member: {
    label: "Member",
    nameClass: "text-emerald-100",
    badgeClass:
      "border-emerald-400/20 bg-gradient-to-r from-emerald-500/14 to-teal-500/10 text-emerald-100",
    dotClass: "bg-gradient-to-r from-emerald-300 to-teal-400",
    metaClass: "text-emerald-100/55",
    surfaceClass: "border-emerald-400/12 bg-emerald-500/[0.035]",
    selectClass: "border-emerald-400/20 text-emerald-100",
  },
  viewer: {
    label: "Viewer",
    nameClass: "text-slate-200",
    badgeClass:
      "border-slate-300/18 bg-gradient-to-r from-slate-400/12 to-slate-500/10 text-slate-200",
    dotClass: "bg-gradient-to-r from-slate-200 to-slate-400",
    metaClass: "text-slate-200/50",
    surfaceClass: "border-slate-300/10 bg-slate-400/[0.03]",
    selectClass: "border-slate-300/18 text-slate-200",
  },
};

function roleTheme(role: ChatThreadRole | null | undefined) {
  return role ? ROLE_THEMES[role] : null;
}

function roleLabel(role: ChatThreadRole) {
  return ROLE_THEMES[role].label;
}

function roleNameClass(
  role: ChatThreadRole | null | undefined,
  own = false,
  isDirect = false
) {
  const theme = roleTheme(role);
  if (theme) return theme.nameClass;
  if (isDirect) return own ? "text-indigo-300" : "text-sky-300";
  return own ? "text-indigo-100" : "text-gray-100";
}

function RolePill({ role, className }: { role: ChatThreadRole; className?: string }) {
  const theme = roleTheme(role);
  if (!theme) return null;

  return (
    <span
      className={cn(
        "inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-[9px] font-bold uppercase tracking-[0.16em] backdrop-blur-sm",
        theme.badgeClass,
        className
      )}
    >
      <span className={cn("h-1.5 w-1.5 rounded-full", theme.dotClass)} />
      {theme.label}
    </span>
  );
}

function canEditManagedParticipantRole(
  actorRole: ChatThreadRole | null | undefined,
  actorUserId: string | null | undefined,
  targetRole: ChatThreadRole,
  targetUserId: string
) {
  if (!actorRole || !actorUserId) return false;
  if (actorUserId === targetUserId) return false;
  if (actorRole === "owner") return true;
  if (actorRole !== "admin") return false;
  return targetRole !== "owner";
}

function canAssignManagedRole(
  actorRole: ChatThreadRole | null | undefined,
  nextRole: ChatThreadRole
) {
  if (!actorRole) return false;
  if (actorRole === "owner") return true;
  if (actorRole !== "admin") return false;
  return nextRole !== "owner";
}

function canRemoveManagedParticipant(
  actorRole: ChatThreadRole | null | undefined,
  actorUserId: string | null | undefined,
  targetRole: ChatThreadRole,
  targetUserId: string
) {
  if (!actorRole || !actorUserId) return false;
  if (actorUserId === targetUserId) return false;
  if (actorRole === "owner") return true;
  if (actorRole !== "admin") return false;
  return targetRole !== "owner";
}

// ─── Page ────────────────────────────────────────────────────────────────────

export default function ChatPage() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const fileRef = useRef<HTMLInputElement>(null);
  const bottomRef = useRef<HTMLDivElement>(null);
  const activeTeamId = useWorkspaceStore((s) => s.activeTeamId);
  const setActiveTeamId = useWorkspaceStore((s) => s.setActiveTeamId);

  const {
    loading, user, contacts, incomingRequests, outgoingRequests, threads,
    messagesByThread, directoryResults,
    fetchThreadMessages, markThreadRead, searchDirectory,
    sendContactRequest, respondToContactRequest, openDirectThread,
    createGroupThread, createTeamChannel, sendMessage, sendMessageWithAttachments,
    updateThread, deleteThread, addThreadMember, updateThreadMemberRole, removeThreadMember,
    updateMessage, deleteMessage, toggleReaction, toggleImportant,
    createTaskFromMessage, createNoteFromMessage,
  } = useChat();
  const { teams, activeTeam, membersByTeam } = useTeams();

  // ── State ──
  const [view, setView] = useState<"dm" | "team">(searchParams.get("scope") === "team" ? "team" : "dm");
  const [threadId, setThreadId] = useState<string | null>(searchParams.get("thread"));
  const [query, setQuery] = useState("");
  const [composer, setComposer] = useState("");
  const [sending, setSending] = useState(false);
  const [files, setFiles] = useState<File[]>([]);
  const [editId, setEditId] = useState<string | null>(null);
  const [editBody, setEditBody] = useState("");
  const [hoverId, setHoverId] = useState<string | null>(null);
  const [emojiPick, setEmojiPick] = useState<string | null>(null);
  const [composerEmoji, setComposerEmoji] = useState(false);
  const [details, setDetails] = useState(false);
  const [chatInfo, setChatInfo] = useState(false);
  const [err, setErr] = useState("");
  const [preview, setPreview] = useState<Attachment | null>(null);

  // modals
  const [addContactOpen, setAddContactOpen] = useState(false);
  const [addContactQ, setAddContactQ] = useState("");
  const [groupOpen, setGroupOpen] = useState(false);
  const [groupName, setGroupName] = useState("");
  const [groupMembers, setGroupMembers] = useState<string[]>([]);
  const [chanOpen, setChanOpen] = useState(false);
  const [chanTeam, setChanTeam] = useState("");
  const [chanName, setChanName] = useState("");
  const [chanDesc, setChanDesc] = useState("");
  const [chanPerm, setChanPerm] = useState<"members" | "admins">("members");
  const [chanPrivate, setChanPrivate] = useState(false);
  const [chanAnnouncement, setChanAnnouncement] = useState(false);
  const [chanMembers, setChanMembers] = useState<string[]>([]);

  const [manageOpen, setManageOpen] = useState(false);
  const [manageTitle, setManageTitle] = useState("");
  const [manageDesc, setManageDesc] = useState("");
  const [managePerm, setManagePerm] = useState<"members" | "admins">("members");
  const [managePrivate, setManagePrivate] = useState(false);
  const [manageAnnouncement, setManageAnnouncement] = useState(false);

  // ── Effects ──
  useEffect(() => {
    if (!activeTeamId && teams.length > 0) setActiveTeamId(teams[0].id);
  }, [activeTeamId, setActiveTeamId, teams]);

  useEffect(() => {
    setChanTeam(activeTeamId || teams[0]?.id || "");
  }, [activeTeamId, teams]);

  useEffect(() => {
    if (query.trim().length < 2) { void searchDirectory(""); return; }
    const t = setTimeout(() => void searchDirectory(query), 200);
    return () => clearTimeout(t);
  }, [query, searchDirectory]);

  useEffect(() => {
    if (!addContactOpen || addContactQ.trim().length < 2) return;
    const t = setTimeout(() => void searchDirectory(addContactQ), 200);
    return () => clearTimeout(t);
  }, [addContactOpen, addContactQ, searchDirectory]);

  // ── Derived ──
  const isTeam = view === "team" && !!activeTeamId;

  const dmThreads = useMemo(() => threads.filter((t) => t.thread_type === "direct"), [threads]);
  const grpThreads = useMemo(() => threads.filter((t) => t.thread_type === "group"), [threads]);
  const chanThreads = useMemo(
    () => threads.filter((t) => t.thread_type === "channel" && (!activeTeamId || t.team_id === activeTeamId)),
    [activeTeamId, threads]
  );

  const searchResults = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return threads;
    return threads.filter((t) => threadTitle(t, user?.id || null).toLowerCase().includes(q));
  }, [query, threads, user?.id]);

  // Auto-select thread
  useEffect(() => {
    if (threadId && threads.some((t) => t.id === threadId)) return;
    const pool = isTeam ? chanThreads : [...dmThreads, ...grpThreads];
    setThreadId(pool[0]?.id || threads[0]?.id || null);
  }, [chanThreads, dmThreads, grpThreads, isTeam, threadId, threads]);

  useEffect(() => {
    if (!threadId) return;
    void fetchThreadMessages(threadId).then(() => markThreadRead(threadId)).catch(() => {});
  }, [fetchThreadMessages, markThreadRead, threadId]);

  const thread = useMemo(() => threads.find((t) => t.id === threadId) || null, [threadId, threads]);
  const msgs = useMemo(() => (threadId ? messagesByThread[threadId] || [] : []), [messagesByThread, threadId]);

  useEffect(() => {
    // Depend on this thread's own message count only — messagesByThread
    // holds every loaded thread, and background activity in a *different*
    // thread must not yank the currently open conversation back to the
    // bottom while the user is scrolled up reading history.
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [threadId, msgs.length]);
  const allParticipants = useMemo(() => thread?.participants || [], [thread]);
  const participants = useMemo(() => (thread?.participants || []).filter((p) => p.user_id !== user?.id), [thread, user?.id]);
  const selfParticipant = useMemo(
    () => allParticipants.find((participant) => participant.user_id === user?.id) || null,
    [allParticipants, user?.id]
  );
  const participantRoleByUserId = useMemo(
    () => new Map(allParticipants.map((participant) => [participant.user_id, participant.role])),
    [allParticipants]
  );
  const team = useMemo(() => {
    if (!thread?.team_id) return activeTeam;
    return teams.find((t) => t.id === thread.team_id) || activeTeam;
  }, [activeTeam, thread, teams]);
  const actionMsg = useMemo(() => msgs.slice().reverse().find((m) => m.is_important) || msgs.at(-1) || null, [msgs]);
  const canManageThread = !!selfParticipant && ["owner", "admin"].includes(selfParticipant.role);
  const canWriteThread = useMemo(() => {
    if (!thread || !selfParticipant) return false;
    if (selfParticipant.role === "owner" || selfParticipant.role === "admin") return true;
    if (selfParticipant.role === "viewer") return false;
    if (thread.is_announcement) return false;
    return thread.posting_permission === "members";
  }, [selfParticipant, thread]);

  const dmByContact = useMemo(() => {
    const map = new Map<string, ChatThread>();
    dmThreads.forEach((t) => {
      const o = t.participants?.find((p) => p.user_id !== user?.id);
      if (o) map.set(o.user_id, t);
    });
    return map;
  }, [dmThreads, user?.id]);

  const candidates = useMemo(() => {
    const map = new Map<string, { id: string; name: string; username: string | null; avatar: string | null; hint: string }>();
    contacts.forEach((c) => {
      if (!c.contact || c.contact_user_id === user?.id) return;
      map.set(c.contact_user_id, {
        id: c.contact_user_id,
        name: c.contact.full_name || c.contact.username || c.contact.email || "Kontakt",
        username: c.contact.username || null, avatar: c.contact.avatar_url || null, hint: "Kontakt",
      });
    });
    Object.entries(membersByTeam).forEach(([tid, members]) => {
      const tn = teams.find((t) => t.id === tid)?.name || "Team";
      members.forEach((m) => {
        if (!m.profile || m.user_id === user?.id) return;
        const ex = map.get(m.user_id);
        if (ex) { ex.hint += ` · ${tn}`; return; }
        map.set(m.user_id, {
          id: m.user_id, name: m.profile.full_name || m.profile.username || m.profile.email || "Mitglied",
          username: m.profile.username || null, avatar: m.profile.avatar_url || null, hint: tn,
        });
      });
    });
    return Array.from(map.values()).sort((a, b) => a.name.localeCompare(b.name, "de"));
  }, [contacts, membersByTeam, teams, user?.id]);

  const manageCandidates = useMemo(() => {
    if (!thread) return [];

    const existing = new Set((thread.participants || []).map((participant) => participant.user_id));

    if (thread.thread_type === "group") {
      return candidates.filter((candidate) => !existing.has(candidate.id));
    }

    if (thread.thread_type === "channel" && thread.team_id) {
      return (membersByTeam[thread.team_id] || [])
        .filter((member) => member.user_id !== user?.id)
        .filter((member) => !existing.has(member.user_id))
        .map((member) => ({
          id: member.user_id,
          name:
            member.profile?.full_name ||
            member.profile?.username ||
            member.profile?.email ||
            "Mitglied",
          username: member.profile?.username || null,
          avatar: member.profile?.avatar_url || null,
          hint: member.role,
        }));
    }

    return [];
  }, [candidates, membersByTeam, thread, user?.id]);
  const createChannelCandidates = useMemo(() => {
    if (!chanPrivate || !chanTeam) return [];
    return (membersByTeam[chanTeam] || []).filter((member) => member.user_id !== user?.id);
  }, [chanPrivate, chanTeam, membersByTeam, user?.id]);

  useEffect(() => {
    if (!manageOpen || !thread) return;

    setManageTitle(thread.title || "");
    setManageDesc(thread.description || "");
    setManagePerm(thread.posting_permission || "members");
    setManagePrivate(Boolean(thread.is_private));
    setManageAnnouncement(Boolean(thread.is_announcement));
  }, [manageOpen, thread]);

  // ── Actions ──
  function pick(id: string) {
    const t = threads.find((x) => x.id === id);
    setThreadId(id); setDetails(false);
    if (t?.team_id) setActiveTeamId(t.team_id);
    setView(t?.thread_type === "channel" ? "team" : "dm");
  }

  function goDM() { setView("dm"); setDetails(false); setThreadId(dmThreads[0]?.id || grpThreads[0]?.id || null); }

  function goTeam(id: string) {
    setActiveTeamId(id); setView("team"); setDetails(false);
    setThreadId(threads.find((t) => t.thread_type === "channel" && t.team_id === id)?.id || null);
  }

  async function openDM(uid: string) {
    try { pick(await openDirectThread(uid)); } catch (e) { setErr(e instanceof Error ? e.message : "Fehler"); }
  }

  async function addContact(p: ChatDirectoryProfile) {
    try {
      const r = await sendContactRequest(p.id);
      if (r.status === "already_connected" || r.status === "auto_accepted") {
        setAddContactOpen(false); setAddContactQ(""); void searchDirectory("");
        pick(await openDirectThread(p.id));
      } else if (r.status === "sent") {
        setAddContactOpen(false); setAddContactQ(""); void searchDirectory("");
        setErr("Kontaktanfrage gesendet!");
      } else if (r.status === "pending") {
        setErr("Anfrage wurde bereits gesendet.");
      }
    } catch (e) { setErr(e instanceof Error ? e.message : "Fehler"); }
  }

  async function doSend() {
    if (!thread || (!composer.trim() && files.length === 0)) return;
    if (!canWriteThread) {
      setErr("Du hast in diesem Chat nur Leserechte.");
      return;
    }
    if (sending) return;
    setSending(true);
    try {
      if (files.length > 0) {
        await sendMessageWithAttachments(thread, { body: composer || "" }, files);
      } else {
        await sendMessage(thread.id, { body: composer });
      }
      setComposer(""); setFiles([]); if (fileRef.current) fileRef.current.value = "";
    } catch (e) {
      console.error("Chat send error:", e);
      setErr(e instanceof Error ? e.message : "Senden fehlgeschlagen");
    } finally {
      setSending(false);
    }
  }

  async function doEdit() {
    if (!editId) return;
    try { await updateMessage(editId, editBody); setEditId(null); setEditBody(""); }
    catch (e) { setErr(e instanceof Error ? e.message : "Bearbeiten fehlgeschlagen"); }
  }

  async function doCreateGroup() {
    if (!groupName.trim() || groupMembers.length === 0) { setErr("Name und mindestens eine Person nötig."); return; }
    try {
      const id = await createGroupThread(groupName, groupMembers);
      setGroupOpen(false); setGroupName(""); setGroupMembers([]); pick(id);
    } catch (e) { setErr(e instanceof Error ? e.message : "Gruppe erstellen fehlgeschlagen"); }
  }

  async function doCreateChannel() {
    const tid = chanTeam || activeTeam?.id || teams[0]?.id;
    if (!tid) { setErr("Kein Team vorhanden."); return; }
    if (!chanName.trim()) { setErr("Kanalname fehlt."); return; }
    try {
      const id = await createTeamChannel({
        teamId: tid,
        title: chanName,
        description: chanDesc,
        postingPermission: chanPerm,
        announcementOnly: chanAnnouncement,
        isPrivate: chanPrivate,
        memberIds: chanMembers,
      });
      setChanOpen(false);
      setChanName("");
      setChanDesc("");
      setChanPerm("members");
      setChanPrivate(false);
      setChanAnnouncement(false);
      setChanMembers([]);
      goTeam(tid); pick(id);
    } catch (e) { setErr(e instanceof Error ? e.message : "Kanal erstellen fehlgeschlagen"); }
  }

  function openManageThread() {
    if (!thread || !canManageThread) return;
    setManageOpen(true);
  }

  async function doSaveThreadSettings() {
    if (!thread) return;
    if (!manageTitle.trim()) {
      setErr("Name oder Titel fehlt.");
      return;
    }

    try {
      await updateThread(thread.id, {
        title: manageTitle,
        description: manageDesc,
        postingPermission: managePerm,
        announcementOnly: manageAnnouncement,
        isPrivate: thread.thread_type === "channel" ? managePrivate : undefined,
      });
      setManageOpen(false);
    } catch (e) {
      setErr(e instanceof Error ? e.message : "Chat konnte nicht aktualisiert werden");
    }
  }

  async function doAddMember(userId: string) {
    if (!thread) return;
    try {
      await addThreadMember(thread.id, userId, "member");
    } catch (e) {
      setErr(e instanceof Error ? e.message : "Mitglied konnte nicht hinzugefügt werden");
    }
  }

  async function doChangeMemberRole(userId: string, role: ChatThreadRole) {
    if (!thread) return;
    try {
      await updateThreadMemberRole(thread.id, userId, role);
    } catch (e) {
      setErr(e instanceof Error ? e.message : "Rolle konnte nicht aktualisiert werden");
    }
  }

  async function doRemoveMember(userId: string) {
    if (!thread) return;
    try {
      await removeThreadMember(thread.id, userId);
    } catch (e) {
      setErr(e instanceof Error ? e.message : "Mitglied konnte nicht entfernt werden");
    }
  }

  async function doDeleteThread() {
    if (!thread) return;
    const label = thread.thread_type === "channel" ? "Kanal" : "Gruppe";
    if (!window.confirm(`${label} wirklich löschen? Alle Nachrichten in diesem Chat gehen dabei verloren.`)) {
      return;
    }

    try {
      const deletedId = thread.id;
      await deleteThread(deletedId);
      setManageOpen(false);
      setDetails(false);
      setThreadId((current) => (current === deletedId ? null : current));
    } catch (e) {
      setErr(e instanceof Error ? e.message : "Chat konnte nicht gelöscht werden");
    }
  }

  function aiSummary() {
    if (!thread) return;
    const text = msgs.slice(-20).map((m) => `${m.sender?.full_name || "?"}: ${m.body}`).join("\n");
    router.push(`/ai?prompt=${encodeURIComponent(`Fasse den Chat "${threadTitle(thread, user?.id || null)}" zusammen.\n${text || "Keine Nachrichten."}`)}`);
  }

  const onKey = useCallback((e: React.KeyboardEvent) => {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); void doSend(); }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [canWriteThread, composer, files, thread]);

  // ── Loading ──
  if (loading) {
    return (
      <div className="flex h-[calc(100vh-7rem)] overflow-hidden rounded-2xl border border-white/[0.08] shadow-2xl">
        <Skeleton className="hidden w-[72px] shrink-0 lg:block" />
        <Skeleton className="hidden shrink-0 md:block md:w-[280px]" />
        <Skeleton className="flex-1" />
      </div>
    );
  }

  // ═══════════════════════════════════════════════════════════════════════════
  //  RENDER
  // ═══════════════════════════════════════════════════════════════════════════

  return (
    <>
      {err && (
        <div className="mb-3 flex items-center gap-2 rounded-xl border border-rose-500/20 bg-rose-500/10 px-4 py-2.5 text-sm text-rose-400 backdrop-blur-sm">
          <span className="flex-1">{err}</span>
          <button onClick={() => setErr("")}><X className="h-3.5 w-3.5" /></button>
        </div>
      )}

      <div className="flex h-[calc(100vh-7rem)] overflow-hidden rounded-2xl border border-white/[0.08] shadow-[0_0_80px_-20px_rgba(99,102,241,0.15)]">

        {/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            COL 1 · SERVER RAIL
            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
        <nav className="hidden w-[72px] shrink-0 flex-col items-center gap-2 bg-gradient-to-b from-[#111318] to-[#0d0f13] py-3 lg:flex">
          {/* DM */}
          <button onClick={goDM} title="Direktnachrichten" className={cn(
            "group relative flex h-12 w-12 items-center justify-center transition-all duration-300",
            !isTeam
              ? "rounded-2xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-lg shadow-indigo-500/30"
              : "rounded-[24px] bg-white/[0.06] text-gray-500 hover:rounded-2xl hover:bg-indigo-500/15 hover:text-indigo-400 hover:shadow-lg hover:shadow-indigo-500/10"
          )}>
            <MessageSquareText className="h-5 w-5" />
            {!isTeam && <span className="absolute -left-0.5 top-1/2 h-10 w-1 -translate-y-1/2 rounded-r-full bg-white shadow-[0_0_8px_rgba(255,255,255,0.6)]" />}
          </button>

          <div className="mx-3 h-px w-8 bg-gradient-to-r from-transparent via-white/10 to-transparent" />

          {/* Teams */}
          <div className="flex flex-1 flex-col items-center gap-2 overflow-y-auto scrollbar-none">
            {teams.map((t) => {
              const active = isTeam && activeTeamId === t.id;
              return (
                <button key={t.id} onClick={() => goTeam(t.id)} title={t.name} className={cn(
                  "group relative flex h-12 w-12 items-center justify-center text-sm font-bold transition-all duration-300",
                  active
                    ? "rounded-2xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-lg shadow-indigo-500/30"
                    : "rounded-[24px] bg-white/[0.06] text-gray-500 hover:rounded-2xl hover:bg-indigo-500/15 hover:text-indigo-400 hover:shadow-lg hover:shadow-indigo-500/10"
                )}>
                  {teamInitials(t.name)}
                  {active && <span className="absolute -left-0.5 top-1/2 h-10 w-1 -translate-y-1/2 rounded-r-full bg-white shadow-[0_0_8px_rgba(255,255,255,0.6)]" />}
                  <span className="pointer-events-none absolute left-full z-50 ml-4 hidden whitespace-nowrap rounded-xl bg-black/90 px-3 py-2 text-xs font-semibold text-white shadow-2xl opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100 lg:block">
                    {t.name}
                  </span>
                </button>
              );
            })}
          </div>

          <div className="mx-3 h-px w-8 bg-gradient-to-r from-transparent via-white/10 to-transparent" />
          <button className="flex h-12 w-12 items-center justify-center rounded-[24px] bg-white/[0.06] text-emerald-400 transition-all duration-300 hover:rounded-2xl hover:bg-emerald-500/15 hover:shadow-lg hover:shadow-emerald-500/10" title="Team erstellen">
            <Plus className="h-5 w-5" />
          </button>
        </nav>

        {/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            COL 2 · SIDEBAR
            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
        <aside className={cn(
          "w-full shrink-0 flex-col bg-gradient-to-b from-[#1a1c22] to-[#16181d] border-r border-white/[0.06] md:flex md:w-[280px]",
          threadId ? "hidden" : "flex"
        )}>
          {/* Header */}
          <div className="flex h-14 items-center justify-between px-4">
            <h2 className="bg-gradient-to-r from-white to-gray-400 bg-clip-text text-[15px] font-bold text-transparent">
              {isTeam && activeTeam ? activeTeam.name : "Nachrichten"}
            </h2>
            <div className="flex items-center">
              <button onClick={() => setAddContactOpen(true)} className="rounded-lg p-1.5 text-gray-500 transition-all hover:bg-white/[0.08] hover:text-indigo-400" title="Kontakt hinzufügen">
                <UserPlus className="h-4 w-4" />
              </button>
              <button onClick={() => setGroupOpen(true)} className="rounded-lg p-1.5 text-gray-500 transition-all hover:bg-white/[0.08] hover:text-indigo-400" title="Neue Gruppe">
                <Users2 className="h-4 w-4" />
              </button>
              {teams.length > 0 && (
                <button onClick={() => setChanOpen(true)} className="rounded-lg p-1.5 text-gray-500 transition-all hover:bg-white/[0.08] hover:text-indigo-400" title="Neuer Kanal">
                  <Hash className="h-4 w-4" />
                </button>
              )}
            </div>
          </div>

          {/* Search */}
          <div className="px-3 pb-2">
            <div className="flex items-center gap-2 rounded-xl bg-white/[0.04] px-3 py-2 ring-1 ring-white/[0.06] transition-all focus-within:ring-indigo-500/40 focus-within:bg-white/[0.06]">
              <Search className="h-3.5 w-3.5 text-gray-600" />
              <input value={query} onChange={(e) => setQuery(e.target.value)}
                placeholder="Suchen..." className="w-full bg-transparent text-sm text-gray-300 placeholder:text-gray-600 focus:outline-none" />
              {query && <button onClick={() => setQuery("")}><X className="h-3 w-3 text-gray-600 hover:text-gray-400" /></button>}
            </div>
          </div>

          {/* Mobile team tabs */}
          <div className="flex gap-1 overflow-x-auto px-3 pb-2 lg:hidden">
            <button onClick={goDM} className={cn("shrink-0 rounded-lg px-3 py-1.5 text-xs font-medium transition-all",
              !isTeam ? "bg-gradient-to-r from-indigo-500 to-violet-600 text-white shadow-md" : "bg-white/[0.04] text-gray-500")}>
              DMs
            </button>
            {teams.map((t) => (
              <button key={t.id} onClick={() => goTeam(t.id)} className={cn("shrink-0 rounded-lg px-3 py-1.5 text-xs font-medium transition-all",
                isTeam && activeTeamId === t.id ? "bg-gradient-to-r from-indigo-500 to-violet-600 text-white shadow-md" : "bg-white/[0.04] text-gray-500")}>
                {t.name}
              </button>
            ))}
          </div>

          {/* Thread list */}
          <div className="flex-1 overflow-y-auto px-2 pb-2">
            {query.trim().length >= 2 ? (
              <>
                {searchResults.length > 0 && (
                  <Section title="Chats" count={searchResults.length}>
                    {searchResults.map((t) => <ThreadRow key={t.id} t={t} uid={user?.id || null} active={threadId === t.id} onClick={() => pick(t.id)} />)}
                  </Section>
                )}
                <Section title="Personen" count={directoryResults.length}>
                  {directoryResults.length === 0
                    ? <p className="px-2 py-4 text-center text-xs text-gray-600">Keine Ergebnisse</p>
                    : directoryResults.map((p) => {
                        const known = contacts.some((c) => c.contact_user_id === p.id);
                        const pending = outgoingRequests.some((r) => r.recipient_id === p.id);
                        const isSelf = p.id === user?.id;
                        if (isSelf) return null;
                        return (
                          <button key={p.id} onClick={() => known ? void openDM(p.id) : pending ? undefined : void addContact(p)}
                            className={cn("flex w-full items-center gap-2.5 rounded-xl px-2.5 py-2 text-left transition-all hover:bg-white/[0.04]", pending && "opacity-60")}>
                            <Avatar src={p.avatar_url} name={p.full_name || p.username} size="sm" />
                            <div className="min-w-0 flex-1">
                              <p className="truncate text-sm font-medium text-gray-200">{p.full_name || p.username}</p>
                              <p className="truncate text-xs text-gray-600">@{p.username}</p>
                            </div>
                            <span className={cn("rounded-lg px-2 py-0.5 text-[10px] font-semibold",
                              known ? "bg-white/[0.06] text-gray-400" : pending ? "bg-amber-500/10 text-amber-400" : "bg-indigo-500/15 text-indigo-400")}>
                              {known ? "Chat" : pending ? "Angefragt" : "Hinzufügen"}
                            </span>
                          </button>
                        );
                      })
                  }
                </Section>
              </>
            ) : (
              <>
                {/* Requests */}
                {(incomingRequests.length + outgoingRequests.length) > 0 && (
                  <Section title="Anfragen" count={incomingRequests.length + outgoingRequests.length}>
                    {incomingRequests.map((r) => (
                      <div key={r.id} className="rounded-xl bg-gradient-to-r from-white/[0.03] to-transparent p-2.5 ring-1 ring-white/[0.06]">
                        <div className="flex items-center gap-2">
                          <Avatar src={r.sender?.avatar_url} name={r.sender?.full_name || "?"} size="sm" />
                          <div className="min-w-0 flex-1">
                            <p className="truncate text-sm font-medium text-gray-200">{r.sender?.full_name || r.sender?.email}</p>
                            <p className="truncate text-xs text-gray-500">@{r.sender?.username || "—"}</p>
                          </div>
                        </div>
                        <div className="mt-2 flex gap-1.5">
                          <button onClick={async () => { try { await respondToContactRequest(r.id, "accepted"); } catch (e) { setErr(e instanceof Error ? e.message : "Fehler beim Annehmen"); } }}
                            className="flex-1 rounded-lg bg-gradient-to-r from-indigo-500 to-violet-600 py-1.5 text-xs font-semibold text-white shadow-md shadow-indigo-500/20 transition-all hover:shadow-lg hover:shadow-indigo-500/30">
                            Annehmen
                          </button>
                          <button onClick={async () => { try { await respondToContactRequest(r.id, "declined"); } catch (e) { setErr(e instanceof Error ? e.message : "Fehler beim Ablehnen"); } }}
                            className="flex-1 rounded-lg bg-white/[0.06] py-1.5 text-xs font-medium text-gray-400 hover:bg-white/10">
                            Ablehnen
                          </button>
                        </div>
                      </div>
                    ))}
                    {outgoingRequests.map((r) => (
                      <div key={r.id} className="flex items-center gap-2 rounded-xl bg-white/[0.02] p-2.5 ring-1 ring-white/[0.04]">
                        <Avatar src={r.recipient?.avatar_url} name={r.recipient?.full_name || "?"} size="sm" />
                        <div className="min-w-0 flex-1">
                          <p className="truncate text-sm font-medium text-gray-400">{r.recipient?.full_name || r.recipient?.email}</p>
                          <p className="text-[10px] text-gray-600">Anfrage gesendet</p>
                        </div>
                        <button onClick={async () => { try { await respondToContactRequest(r.id, "cancelled"); } catch (e) { setErr(e instanceof Error ? e.message : "Fehler"); } }}
                          className="rounded-lg px-2 py-1 text-[10px] font-medium text-gray-500 ring-1 ring-white/[0.06] hover:bg-white/[0.06] hover:text-gray-300">
                          Zurückziehen
                        </button>
                      </div>
                    ))}
                  </Section>
                )}

                {/* DMs */}
                <Section title="Direktnachrichten" count={contacts.length} onAdd={() => setAddContactOpen(true)}>
                  {contacts.length === 0
                    ? <div className="px-2 py-5 text-center">
                        <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-2xl bg-gradient-to-br from-indigo-500/10 to-violet-500/10">
                          <Users2 className="h-6 w-6 text-indigo-400/40" />
                        </div>
                        <p className="mt-2 text-xs text-gray-500">Noch keine Kontakte</p>
                        <p className="mt-0.5 text-[11px] text-gray-600">Suche nach @username</p>
                      </div>
                    : contacts.map((c) => {
                        const dt = dmByContact.get(c.contact_user_id);
                        const name = c.contact?.full_name || c.contact?.username || "Kontakt";
                        return (
                          <button key={c.contact_user_id} onClick={() => void openDM(c.contact_user_id)}
                            className={cn("group flex w-full items-center gap-2.5 rounded-xl px-2.5 py-2 text-left transition-all duration-200",
                              threadId === dt?.id
                                ? "bg-gradient-to-r from-indigo-500/10 to-violet-500/5 text-white ring-1 ring-indigo-500/20"
                                : "text-gray-400 hover:bg-white/[0.04] hover:text-gray-200"
                            )}>
                            <div className="relative shrink-0">
                              <Avatar src={c.contact?.avatar_url} name={name} size="sm" />
                              <span className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full border-2 border-[#1a1c22] bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]" />
                            </div>
                            <div className="min-w-0 flex-1">
                              <p className="truncate text-sm font-medium text-sky-300">{name}</p>
                              <p className={cn(
                                "truncate text-[11px]",
                                dt?.last_message?.body ? "text-gray-600" : "text-sky-300/95"
                              )}>
                                {dt?.last_message?.body || `@${c.contact?.username || ""}`}
                              </p>
                            </div>
                            {dt?.unread && <span className="h-2.5 w-2.5 shrink-0 rounded-full bg-indigo-500 shadow-[0_0_8px_rgba(99,102,241,0.6)]" />}
                          </button>
                        );
                      })
                  }
                </Section>

                {/* Channels */}
                {activeTeam && (
                  <Section title={`Kanäle · ${activeTeam.name}`} count={chanThreads.length} onAdd={() => setChanOpen(true)}>
                    {chanThreads.length === 0
                      ? <div className="px-2 py-4 text-center">
                          <div className="mx-auto flex h-10 w-10 items-center justify-center rounded-xl bg-white/[0.04]">
                            <Hash className="h-5 w-5 text-gray-700" />
                          </div>
                          <p className="mt-2 text-xs text-gray-600">Noch keine Kanäle</p>
                        </div>
                      : chanThreads.map((t) => <ThreadRow key={t.id} t={t} uid={user?.id || null} active={threadId === t.id} onClick={() => pick(t.id)} prefix="# " />)
                    }
                  </Section>
                )}

                {/* Groups */}
                <Section title="Gruppen" count={grpThreads.length} onAdd={() => setGroupOpen(true)}>
                  {grpThreads.length === 0
                    ? <p className="px-2 py-3 text-center text-xs text-gray-600">Noch keine Gruppen</p>
                    : grpThreads.map((t) => <ThreadRow key={t.id} t={t} uid={user?.id || null} active={threadId === t.id} onClick={() => pick(t.id)} />)
                  }
                </Section>
              </>
            )}
          </div>

          {/* User footer */}
          <div className="flex items-center gap-2.5 border-t border-white/[0.06] bg-[#111318]/80 px-3 py-2.5 backdrop-blur-sm">
            <div className="relative shrink-0">
              <Avatar src={undefined} name={user?.email || "Du"} size="sm" />
              <span className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full border-2 border-[#111318] bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]" />
            </div>
            <div className="min-w-0 flex-1">
              <p className="truncate text-sm font-semibold text-white">{user?.email?.split("@")[0] || "Du"}</p>
              <p className="text-[11px] font-medium text-emerald-400">Online</p>
            </div>
            <button className="rounded-lg p-1.5 text-gray-500 transition-all hover:bg-white/[0.08] hover:text-white"><Settings className="h-4 w-4" /></button>
          </div>
        </aside>

        {/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            COL 3 · CHAT AREA
            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */}
        <main className={cn(
          "relative min-w-0 flex-1 flex-col bg-gradient-to-b from-[#1e2028] to-[#1a1c22] md:flex",
          threadId ? "flex" : "hidden"
        )}>
          {!thread ? (
            <div className="flex flex-1 flex-col items-center justify-center gap-5">
              <div className="relative">
                <div className="absolute inset-0 animate-pulse rounded-full bg-indigo-500/20 blur-2xl" />
                <div className="relative flex h-24 w-24 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500/20 to-violet-500/20 ring-1 ring-white/[0.08]">
                  <MessageSquareText className="h-12 w-12 text-indigo-400" />
                </div>
              </div>
              <div className="text-center">
                <h2 className="bg-gradient-to-r from-white to-gray-400 bg-clip-text text-2xl font-bold text-transparent">
                  {activeTeam ? `Willkommen bei ${activeTeam.name}` : "Wähle einen Chat"}
                </h2>
                <p className="mt-2 max-w-xs text-sm text-gray-500">
                  Wähle einen Kontakt, eine Gruppe oder einen Kanal aus der Seitenleiste.
                </p>
              </div>
            </div>
          ) : (
            <>
              {/* Header */}
              <header className="flex h-14 items-center justify-between border-b border-white/[0.06] bg-white/[0.02] px-4 backdrop-blur-sm">
                <div className="flex min-w-0 items-center">
                <button onClick={() => setThreadId(null)} title="Zurück"
                  className="mr-1 shrink-0 rounded-lg p-2 text-gray-500 transition-all hover:bg-white/[0.06] hover:text-white md:hidden">
                  <ChevronLeft className="h-4 w-4" />
                </button>
                <button onClick={() => setChatInfo(true)} className="flex min-w-0 items-center gap-3 rounded-xl px-1 py-1 -ml-1 transition-all hover:bg-white/[0.04] active:scale-[0.98]">
                  {thread.thread_type === "channel"
                    ? <div className="flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500/15 to-violet-500/10 ring-1 ring-indigo-500/20">
                        <Hash className="h-4.5 w-4.5 text-indigo-400" />
                      </div>
                    : thread.thread_type === "group"
                      ? <div className="flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500/15 to-violet-500/10 ring-1 ring-indigo-500/20">
                          <Users2 className="h-4.5 w-4.5 text-indigo-400" />
                        </div>
                      : <div className="relative shrink-0">
                          <Avatar src={participants[0]?.user?.avatar_url} name={participants[0]?.user?.full_name || "Chat"} size="sm" />
                          <span className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full border-2 border-[#1e2028] bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]" />
                        </div>
                  }
                  <div className="min-w-0 text-left">
                    <div className="flex min-w-0 items-center gap-2">
                      <h3 className={cn(
                        "truncate text-[15px] font-bold",
                        thread.thread_type === "direct" ? "text-sky-300" : "text-white"
                      )}>
                        {thread.thread_type === "channel" ? "# " : ""}{threadTitle(thread, user?.id || null)}
                      </h3>
                      {thread.thread_type !== "direct" && selfParticipant && (
                        <RolePill role={selfParticipant.role} className="shrink-0" />
                      )}
                    </div>
                    <p className="truncate text-[11px] text-gray-500">
                      {thread.description || `${thread.participants?.length || 0} Teilnehmer`}
                      {team && thread.thread_type === "channel" && ` · ${team.name}`}
                    </p>
                  </div>
                </button>
                </div>

                <div className="flex items-center">
                  {[
                    { icon: Bot, fn: aiSummary, tip: "KI-Zusammenfassung" },
                    { icon: Phone, fn: () => {}, tip: "Anrufen" },
                    { icon: Video, fn: () => {}, tip: "Video" },
                  ].map(({ icon: Icon, fn, tip }) => (
                    <button key={tip} onClick={fn} title={tip}
                      className="hidden rounded-lg p-2 text-gray-500 transition-all hover:bg-white/[0.06] hover:text-indigo-400 md:block">
                      <Icon className="h-4 w-4" />
                    </button>
                  ))}
                  <div className="mx-1.5 hidden h-4 w-px bg-white/[0.08] md:block" />
                  {canManageThread && thread.thread_type !== "direct" && (
                    <button onClick={openManageThread} title="Verwalten"
                      className="rounded-lg p-2 text-gray-500 transition-all hover:bg-white/[0.06] hover:text-indigo-400">
                      <Settings className="h-4 w-4" />
                    </button>
                  )}
                  <button onClick={() => setDetails((d) => !d)} title="Details"
                    className={cn("rounded-lg p-2 transition-all",
                      details ? "bg-indigo-500/15 text-indigo-400 ring-1 ring-indigo-500/20" : "text-gray-500 hover:bg-white/[0.06] hover:text-indigo-400")}>
                    <Info className="h-4 w-4" />
                  </button>
                </div>
              </header>

              {/* Messages + Detail */}
              <div className="flex flex-1 overflow-hidden">
                <div className="flex-1 overflow-y-auto px-4 py-4">
                  {msgs.length === 0 ? (
                    <div className="flex h-full flex-col items-center justify-center gap-4">
                      <div className="relative">
                        <div className="absolute inset-0 animate-pulse rounded-full bg-indigo-500/10 blur-xl" />
                        <div className="relative flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500/10 to-violet-500/10 ring-1 ring-white/[0.06]">
                          <Sparkles className="h-8 w-8 text-indigo-400/50" />
                        </div>
                      </div>
                      <div className="text-center">
                        <p className="text-sm font-semibold text-gray-300">Starte die Konversation</p>
                        <p className="mt-1 text-xs text-gray-600">Schreibe die erste Nachricht</p>
                      </div>
                    </div>
                  ) : (
                    <div>
                      {msgs.map((m, i) => {
                        const own = m.sender_id === user?.id;
                        const editing = editId === m.id;
                        const hovered = hoverId === m.id;
                        const grouped = isGrouped(msgs, i);
                        const dateSep = needsDateSep(msgs, i);
                        const name = m.sender?.full_name || m.sender?.username || m.sender?.email || "?";
                        const senderRole = thread.thread_type === "direct"
                          ? null
                          : participantRoleByUserId.get(m.sender_id) || null;

                        return (
                          <div key={m.id}>
                            {dateSep && (
                              <div className="my-5 flex items-center gap-4">
                                <div className="h-px flex-1 bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" />
                                <span className="rounded-full bg-white/[0.04] px-4 py-1 text-[11px] font-semibold text-gray-500 ring-1 ring-white/[0.06]">
                                  {fmtDateSep(m.created_at)}
                                </span>
                                <div className="h-px flex-1 bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" />
                              </div>
                            )}

                            <div
                              className={cn("group relative -mx-2 rounded-xl px-2 py-0.5 transition-all duration-200",
                                hovered && "bg-white/[0.02]",
                                m.is_important && "border-l-2 border-amber-400/60 bg-amber-500/[0.03] pl-4"
                              )}
                              onMouseEnter={() => setHoverId(m.id)}
                              onMouseLeave={() => { setHoverId(null); setEmojiPick(null); }}
                            >
                              {/* Hover toolbar */}
                              {hovered && !editing && (
                                <div className="absolute -top-4 right-2 z-10 flex items-center rounded-xl border border-white/[0.08] bg-[#1a1c22]/95 shadow-2xl backdrop-blur-md">
                                  {QUICK_EMOJIS.map((e) => (
                                    <button key={e} onClick={() => void toggleReaction(thread.id, m.id, e)}
                                      className="px-2 py-1.5 text-sm transition-transform hover:scale-125 hover:bg-white/[0.06]">{e}</button>
                                  ))}
                                  <div className="h-5 w-px bg-white/[0.08]" />
                                  <button onClick={() => void toggleImportant(thread.id, m)} title="Wichtig"
                                    className="p-2 text-gray-500 hover:bg-white/[0.06] hover:text-amber-400">
                                    <Star className={cn("h-3.5 w-3.5", m.is_important && "fill-amber-400 text-amber-400")} />
                                  </button>
                                  <button onClick={() => void createTaskFromMessage(thread, m)} title="Task"
                                    className="p-2 text-gray-500 hover:bg-white/[0.06] hover:text-white">
                                    <Bookmark className="h-3.5 w-3.5" />
                                  </button>
                                  {own && (
                                    <>
                                      <button onClick={() => { setEditId(m.id); setEditBody(m.body); }} title="Bearbeiten"
                                        className="p-2 text-gray-500 hover:bg-white/[0.06] hover:text-white">
                                        <svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                                          <path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" /><path d="M18.5 2.5a2.12 2.12 0 013 3L12 15l-4 1 1-4 9.5-9.5z" />
                                        </svg>
                                      </button>
                                      <button onClick={() => void deleteMessage(m.id)} title="Löschen"
                                        className="p-2 text-gray-500 hover:bg-white/[0.06] hover:text-rose-400">
                                        <X className="h-3.5 w-3.5" />
                                      </button>
                                    </>
                                  )}
                                  <button onClick={() => setEmojiPick(emojiPick === m.id ? null : m.id)} title="Emoji"
                                    className="p-2 text-gray-500 hover:bg-white/[0.06] hover:text-white">
                                    <Smile className="h-3.5 w-3.5" />
                                  </button>
                                </div>
                              )}

                              {emojiPick === m.id && (
                                <div className="absolute -top-14 right-2 z-20 flex gap-1 rounded-2xl border border-white/[0.08] bg-[#1a1c22]/95 p-2 shadow-2xl backdrop-blur-md">
                                  {EMOJI_OPTIONS.map((e) => (
                                    <button key={e} onClick={() => { void toggleReaction(thread.id, m.id, e); setEmojiPick(null); }}
                                      className="rounded-lg p-1.5 text-lg transition-transform hover:scale-125 hover:bg-white/[0.08]">{e}</button>
                                  ))}
                                </div>
                              )}

                              <div className={cn("flex gap-3", grouped ? "pl-[52px]" : "mt-4")}>
                                {!grouped && <Avatar src={m.sender?.avatar_url} name={name} size="sm" className="mt-0.5 h-10 w-10 shrink-0 ring-2 ring-white/[0.06]" />}
                                <div className="min-w-0 flex-1">
                                  {!grouped && (
                                    <div className="flex flex-wrap items-center gap-2">
                                      {senderRole && <RolePill role={senderRole} />}
                                      <span className={cn("text-sm font-bold", roleNameClass(senderRole, own, thread.thread_type === "direct"))}>{name}</span>
                                      <span className="text-[11px] text-gray-600">{fmtTime(m.created_at)}</span>
                                      {m.edited_at && <span className="text-[10px] italic text-gray-700">(bearbeitet)</span>}
                                      {m.is_important && <Star className="h-3 w-3 fill-amber-400 text-amber-400" />}
                                    </div>
                                  )}

                                  {editing ? (
                                    <div className="mt-1 space-y-2">
                                      <textarea value={editBody} onChange={(e) => setEditBody(e.target.value)}
                                        className="w-full rounded-xl border border-indigo-500/30 bg-white/[0.04] px-3 py-2 text-sm text-gray-200 focus:outline-none focus:ring-1 focus:ring-indigo-500" rows={3} />
                                      <div className="flex gap-2 text-xs">
                                        <button onClick={() => { setEditId(null); setEditBody(""); }} className="text-gray-500 hover:text-white">Abbrechen</button>
                                        <button onClick={() => void doEdit()} className="rounded-lg bg-gradient-to-r from-indigo-500 to-violet-600 px-3 py-1 font-semibold text-white shadow-md shadow-indigo-500/20">Speichern</button>
                                      </div>
                                    </div>
                                  ) : (
                                    <p className="whitespace-pre-wrap text-[14px] leading-relaxed text-gray-300">{renderBody(m.body)}</p>
                                  )}

                                  {(m.attachments || []).length > 0 && (
                                    <div className="mt-2 grid max-w-md gap-2 sm:grid-cols-2">
                                      {m.attachments!.map((a) => (
                                        <div key={a.id} className="overflow-hidden rounded-xl border border-white/[0.06] bg-white/[0.02]">
                                          {isPreviewableFile(a.mime_type, a.file_name)
                                            ? <button onClick={() => setPreview(a)} className="block w-full"><FileThumbnail fileName={a.file_name} publicUrl={a.public_url} mimeType={a.mime_type} className="h-32 w-full" /></button>
                                            : <div className="flex h-16 items-center justify-center"><Paperclip className="h-5 w-5 text-gray-700" /></div>
                                          }
                                          <div className="flex items-center justify-between px-2.5 py-2">
                                            <div className="min-w-0">
                                              <p className="truncate text-xs font-medium text-gray-300">{a.file_name}</p>
                                              <p className="text-[10px] text-gray-600">{formatFileSize(a.size_bytes)}</p>
                                            </div>
                                            <a href={a.public_url} target="_blank" rel="noreferrer" className="p-1 text-gray-600 hover:text-indigo-400"><Download className="h-3.5 w-3.5" /></a>
                                          </div>
                                        </div>
                                      ))}
                                    </div>
                                  )}

                                  {(m.links || []).length > 0 && (
                                    <div className="mt-1.5 flex flex-wrap gap-1">
                                      {m.links!.map((l) => (
                                        <span key={l.id} className="inline-flex items-center gap-1 rounded-lg bg-indigo-500/10 px-2 py-0.5 text-[11px] font-semibold text-indigo-400 ring-1 ring-indigo-500/20">
                                          <Bookmark className="h-3 w-3" />{l.entity_type}
                                        </span>
                                      ))}
                                    </div>
                                  )}

                                  {(m.reactions || []).length > 0 && (
                                    <div className="mt-2 flex flex-wrap gap-1">
                                      {Object.entries(
                                        (m.reactions || []).reduce<Record<string, { n: number; own: boolean }>>((a, r) => {
                                          if (!a[r.emoji]) a[r.emoji] = { n: 0, own: false };
                                          a[r.emoji].n++;
                                          if (r.user_id === user?.id) a[r.emoji].own = true;
                                          return a;
                                        }, {})
                                      ).map(([emoji, { n, own }]) => (
                                        <button key={`${m.id}-${emoji}`} onClick={() => void toggleReaction(thread.id, m.id, emoji)}
                                          className={cn("flex items-center gap-1 rounded-full px-2.5 py-1 text-xs transition-all",
                                            own
                                              ? "bg-indigo-500/15 text-indigo-400 ring-1 ring-indigo-500/30 shadow-sm shadow-indigo-500/10"
                                              : "bg-white/[0.04] text-gray-400 ring-1 ring-white/[0.06] hover:bg-white/[0.08]"
                                          )}>
                                          <span>{emoji}</span><span className="font-semibold">{n}</span>
                                        </button>
                                      ))}
                                    </div>
                                  )}
                                </div>
                              </div>
                            </div>
                          </div>
                        );
                      })}
                      <div ref={bottomRef} />
                    </div>
                  )}
                </div>

                {/* Detail panel */}
                {details && (
                  <aside className="hidden w-[260px] shrink-0 overflow-y-auto border-l border-white/[0.06] bg-gradient-to-b from-[#1a1c22] to-[#16181d] p-4 md:block">
                    <div className="space-y-5">
                      <div className="flex items-center justify-between">
                        <h4 className="bg-gradient-to-r from-white to-gray-400 bg-clip-text text-sm font-bold text-transparent">Details</h4>
                        <button onClick={() => setDetails(false)} className="rounded-lg p-1 text-gray-500 hover:bg-white/[0.06] hover:text-white"><X className="h-4 w-4" /></button>
                      </div>

                      <div className="space-y-2.5 rounded-xl bg-white/[0.03] p-3 ring-1 ring-white/[0.06]">
                        {[
                          ["Typ", contextBadge(thread)],
                          ["Team", team?.name || "Persönlich"],
                          ["Teilnehmer", String(thread.participants?.length || 0)],
                          [
                            "Lesen",
                            thread.thread_type === "channel" && thread.team_id
                              ? thread.is_private ? "Privat" : "Ganzes Team"
                              : "Nur Mitglieder",
                          ],
                          [
                            "Schreiben",
                            thread.is_announcement
                              ? "Nur Admins/Owner"
                              : thread.posting_permission === "admins"
                                ? "Nur Admins/Owner"
                                : "Mitglieder",
                          ],
                          ["Wichtig", String(msgs.filter((x) => x.is_important).length)],
                        ].map(([k, v]) => (
                          <div key={k} className="flex justify-between text-xs">
                            <span className="text-gray-500">{k}</span>
                            <span className="font-semibold text-gray-300">{v}</span>
                          </div>
                        ))}
                      </div>

                      <div>
                        <p className="mb-3 text-[11px] font-bold uppercase tracking-widest text-gray-600">Teilnehmer</p>
                        <div className="space-y-2.5">
                          {participants.map((p) => (
                            <div key={p.user_id} className={cn("flex items-center gap-2.5 rounded-xl border px-2.5 py-2", roleTheme(p.role)?.surfaceClass || "border-white/[0.06] bg-white/[0.03]")}>
                              <div className="relative shrink-0">
                              <Avatar src={p.user?.avatar_url} name={p.user?.full_name || "?"} size="sm" />
                              <span className="absolute -bottom-0.5 -right-0.5 h-2.5 w-2.5 rounded-full border-2 border-[#1a1c22] bg-emerald-400" />
                            </div>
                            <div className="min-w-0">
                              <div className="flex flex-wrap items-center gap-1.5">
                                <p className={cn("truncate text-xs font-semibold", roleNameClass(p.role))}>
                                  {p.user?.full_name || p.user?.email}
                                </p>
                                <RolePill role={p.role} />
                              </div>
                              <p className={cn("truncate text-[10px]", roleTheme(p.role)?.metaClass || "text-gray-600")}>
                                {p.user?.username ? `@${p.user.username}` : roleLabel(p.role)}
                              </p>
                            </div>
                          </div>
                        ))}
                      </div>
                      </div>

                      <div>
                        <p className="mb-3 text-[11px] font-bold uppercase tracking-widest text-gray-600">Aktionen</p>
                        {[
                          ...(canManageThread && thread.thread_type !== "direct"
                            ? [{ icon: Settings, label: "Kanal/Gruppe verwalten", fn: openManageThread }]
                            : []),
                          { icon: Bot, label: "KI-Zusammenfassung", fn: aiSummary },
                          { icon: Sparkles, label: "Task erstellen", fn: () => actionMsg && void createTaskFromMessage(thread, actionMsg) },
                          { icon: MessageSquareText, label: "Als Notiz speichern", fn: () => actionMsg && void createNoteFromMessage(thread, actionMsg) },
                        ].map(({ icon: I, label, fn }) => (
                          <button key={label} onClick={fn}
                            className="flex w-full items-center gap-2.5 rounded-xl px-3 py-2 text-xs font-medium text-gray-400 transition-all hover:bg-white/[0.04] hover:text-indigo-400">
                            <I className="h-3.5 w-3.5" />{label}
                          </button>
                        ))}
                      </div>
                    </div>
                  </aside>
                )}
              </div>

              {/* Composer */}
              <div className="px-4 pb-4">
                <input ref={fileRef} type="file" multiple disabled={!canWriteThread} className="hidden" onChange={(e) => {
                  if (e.target.files && e.target.files.length > 0) {
                    const selected = Array.from(e.target.files);
                    setFiles((f) => [...f, ...selected]);
                  }
                  if (fileRef.current) fileRef.current.value = "";
                }} />

                {!canWriteThread && (
                  <div className="mb-2 rounded-xl border border-amber-500/20 bg-amber-500/10 px-3 py-2 text-xs text-amber-300">
                    In diesem Chat hast du aktuell nur Leserechte.
                  </div>
                )}

                {files.length > 0 && (
                  <div className="mb-2 flex flex-wrap gap-2">
                    {files.map((f, i) => {
                      const isImage = f.type.startsWith("image/");
                      return (
                        <div key={`${f.name}-${i}`} className="group relative overflow-hidden rounded-xl ring-1 ring-white/[0.08] bg-white/[0.03]">
                          {isImage ? (
                            <div className="relative h-20 w-20">
                              <Image src={URL.createObjectURL(f)} alt={f.name} fill unoptimized className="object-cover" />
                              <div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
                            </div>
                          ) : (
                            <div className="flex h-20 w-20 flex-col items-center justify-center gap-1 bg-white/[0.02]">
                              <Paperclip className="h-5 w-5 text-indigo-400" />
                              <span className="max-w-[70px] truncate px-1 text-[9px] text-gray-400">{f.name}</span>
                            </div>
                          )}
                          <button onClick={() => setFiles((a) => a.filter((_, j) => j !== i))}
                            className="absolute right-1 top-1 flex h-5 w-5 items-center justify-center rounded-full bg-black/60 text-gray-300 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-red-500/80 hover:text-white">
                            <X className="h-3 w-3" />
                          </button>
                          <div className="absolute bottom-0 left-0 right-0 bg-black/50 px-1.5 py-0.5 backdrop-blur-sm">
                            <p className="truncate text-[9px] text-gray-300">{formatFileSize(f.size)}</p>
                          </div>
                        </div>
                      );
                    })}
                  </div>
                )}

                <div className={cn(
                  "flex items-end gap-2 rounded-2xl bg-white/[0.04] px-4 py-3 ring-1 ring-white/[0.06] transition-all",
                  canWriteThread && "focus-within:ring-indigo-500/30 focus-within:shadow-[0_0_20px_-5px_rgba(99,102,241,0.15)]"
                )}>
                  <button onClick={() => canWriteThread && fileRef.current?.click()} disabled={!canWriteThread}
                    className="mb-0.5 rounded-xl p-1.5 text-gray-500 transition-all hover:bg-white/[0.06] hover:text-indigo-400 disabled:cursor-not-allowed disabled:opacity-40">
                    <Plus className="h-5 w-5" />
                  </button>

                  <textarea value={composer} onChange={(e) => setComposer(e.target.value)} onKeyDown={onKey}
                    disabled={!canWriteThread}
                    placeholder={
                      canWriteThread
                        ? `Nachricht an ${thread.thread_type === "channel" ? "#" : ""}${threadTitle(thread, user?.id || null)}`
                        : "Nur Lesen aktiviert"
                    }
                    rows={1} className="max-h-28 min-h-[36px] flex-1 resize-none bg-transparent text-sm text-gray-200 placeholder:text-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60" />

                  <div className="mb-0.5 flex items-center gap-1 relative">
                    <button disabled={!canWriteThread} onClick={() => setComposerEmoji((o) => !o)}
                      className={cn("rounded-xl p-1.5 transition-all disabled:cursor-not-allowed disabled:opacity-40",
                        composerEmoji ? "bg-indigo-500/15 text-indigo-400 ring-1 ring-indigo-500/20" : "text-gray-500 hover:bg-white/[0.06] hover:text-indigo-400"
                      )}>
                      <Smile className="h-5 w-5" />
                    </button>
                    {composerEmoji && (
                      <div className="absolute bottom-full right-0 z-30 mb-2 flex gap-1 rounded-2xl border border-white/[0.08] bg-[#1a1c22]/95 p-2 shadow-2xl backdrop-blur-md">
                        {EMOJI_OPTIONS.map((e) => (
                          <button key={e} onClick={() => { setComposer((c) => c + e); setComposerEmoji(false); }}
                            className="rounded-lg p-1.5 text-lg transition-transform hover:scale-125 hover:bg-white/[0.08]">{e}</button>
                        ))}
                      </div>
                    )}
                    <button onClick={() => void doSend()}
                      disabled={!canWriteThread || sending || (!composer.trim() && files.length === 0)}
                      className={cn("rounded-xl p-2 transition-all duration-300",
                        canWriteThread && (composer.trim() || files.length > 0)
                          ? "bg-gradient-to-r from-indigo-500 to-violet-600 text-white shadow-lg shadow-indigo-500/30 hover:shadow-xl hover:shadow-indigo-500/40"
                          : "text-gray-700"
                      )}>
                      <Send className="h-5 w-5" />
                    </button>
                  </div>
                </div>
              </div>
            </>
          )}
        </main>
      </div>

      {/* ═══ CHAT INFO PANEL (WhatsApp-like) ═══ */}
      {thread && chatInfo && (() => {
        const allAttachments = msgs.flatMap((m) => (m.attachments || []).map((a) => ({ ...a, _senderName: m.sender?.full_name || m.sender?.username || "?", _sentAt: m.created_at })));
        const mediaAttachments = allAttachments.filter((a) => a.mime_type?.startsWith("image/") || a.mime_type?.startsWith("video/"));
        const docAttachments = allAttachments.filter((a) => !a.mime_type?.startsWith("image/") && !a.mime_type?.startsWith("video/"));
        const threadName = threadTitle(thread, user?.id || null);
        const otherUser = thread.thread_type === "direct" ? participants[0]?.user : null;

        return (
          <div className="fixed inset-0 z-50 flex">
            {/* Backdrop */}
            <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setChatInfo(false)} />

            {/* Panel */}
            <div className="relative ml-auto flex h-full w-full max-w-md flex-col bg-gradient-to-b from-[#1a1c22] to-[#14161a] shadow-2xl animate-in slide-in-from-right duration-200">
              {/* Header */}
              <div className="flex items-center gap-3 border-b border-white/[0.06] px-4 py-3">
                <button onClick={() => setChatInfo(false)} className="rounded-xl p-1.5 text-gray-400 transition-all hover:bg-white/[0.06] hover:text-white">
                  <ChevronLeft className="h-5 w-5" />
                </button>
                <h3 className="text-sm font-bold text-white">Chat-Info</h3>
              </div>

              <div className="flex-1 overflow-y-auto">
                {/* Profile / Chat Header */}
                <div className="flex flex-col items-center border-b border-white/[0.06] px-6 py-8">
                  {thread.thread_type === "direct" ? (
                    <div className="relative">
                      <div className="absolute -inset-3 rounded-full bg-gradient-to-br from-indigo-500/20 to-violet-500/20 blur-xl" />
                      <Avatar src={otherUser?.avatar_url} name={otherUser?.full_name || "Chat"} size="lg" />
                      <span className="absolute -bottom-1 -right-1 h-4 w-4 rounded-full border-[3px] border-[#1a1c22] bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.6)]" />
                    </div>
                  ) : thread.thread_type === "group" ? (
                    <div className="relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-br from-indigo-500/20 to-violet-500/20 ring-1 ring-white/[0.08]">
                      <Users2 className="h-10 w-10 text-indigo-400" />
                    </div>
                  ) : (
                    <div className="relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-br from-indigo-500/20 to-violet-500/20 ring-1 ring-white/[0.08]">
                      <Hash className="h-10 w-10 text-indigo-400" />
                    </div>
                  )}
                  <h2 className={cn("mt-4 text-lg font-bold", thread.thread_type === "direct" ? "text-sky-300" : "bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent")}>
                    {thread.thread_type === "channel" ? "# " : ""}{threadName}
                  </h2>
                  {thread.thread_type === "direct" && otherUser?.username && (
                    <p className="mt-0.5 text-sm text-gray-500">@{otherUser.username}</p>
                  )}
                  {thread.description && (
                    <p className="mt-2 max-w-xs text-center text-xs text-gray-500">{thread.description}</p>
                  )}
                  <div className="mt-3 flex items-center gap-4">
                    <div className="flex flex-col items-center">
                      <span className="text-lg font-bold text-white">{msgs.length}</span>
                      <span className="text-[10px] text-gray-600">Nachrichten</span>
                    </div>
                    <div className="h-6 w-px bg-white/[0.06]" />
                    <div className="flex flex-col items-center">
                      <span className="text-lg font-bold text-white">{allAttachments.length}</span>
                      <span className="text-[10px] text-gray-600">Dateien</span>
                    </div>
                    <div className="h-6 w-px bg-white/[0.06]" />
                    <div className="flex flex-col items-center">
                      <span className="text-lg font-bold text-white">{thread.participants?.length || 0}</span>
                      <span className="text-[10px] text-gray-600">Teilnehmer</span>
                    </div>
                  </div>
                </div>

                {/* Media & Files */}
                {mediaAttachments.length > 0 && (
                  <div className="border-b border-white/[0.06] p-4">
                    <div className="mb-3 flex items-center gap-2">
                      <ImageIcon className="h-4 w-4 text-indigo-400" />
                      <h4 className="text-xs font-bold uppercase tracking-widest text-gray-500">Medien</h4>
                      <span className="ml-auto rounded-md bg-white/[0.04] px-1.5 py-0.5 text-[10px] font-semibold text-gray-500 ring-1 ring-white/[0.06]">{mediaAttachments.length}</span>
                    </div>
                    <div className="grid grid-cols-3 gap-1.5">
                      {mediaAttachments.map((a) => (
                        <button key={a.id} onClick={() => { setPreview(a); setChatInfo(false); }}
                          className="group relative aspect-square overflow-hidden rounded-xl ring-1 ring-white/[0.06] transition-all hover:ring-indigo-500/30">
                          <Image src={a.public_url} alt={a.file_name} fill unoptimized className="object-cover transition-transform group-hover:scale-105" />
                          <div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent opacity-0 transition-opacity group-hover:opacity-100" />
                          <span className="absolute bottom-1 left-1 rounded-md bg-black/60 px-1 py-0.5 text-[8px] text-gray-300 opacity-0 backdrop-blur-sm group-hover:opacity-100">
                            {fmtTime(a._sentAt)}
                          </span>
                        </button>
                      ))}
                    </div>
                  </div>
                )}

                {docAttachments.length > 0 && (
                  <div className="border-b border-white/[0.06] p-4">
                    <div className="mb-3 flex items-center gap-2">
                      <Paperclip className="h-4 w-4 text-indigo-400" />
                      <h4 className="text-xs font-bold uppercase tracking-widest text-gray-500">Dokumente</h4>
                      <span className="ml-auto rounded-md bg-white/[0.04] px-1.5 py-0.5 text-[10px] font-semibold text-gray-500 ring-1 ring-white/[0.06]">{docAttachments.length}</span>
                    </div>
                    <div className="space-y-1.5">
                      {docAttachments.map((a) => (
                        <a key={a.id} href={a.public_url} target="_blank" rel="noreferrer"
                          className="flex items-center gap-3 rounded-xl bg-white/[0.03] px-3 py-2.5 ring-1 ring-white/[0.06] transition-all hover:bg-white/[0.06] hover:ring-indigo-500/20">
                          <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-indigo-500/10">
                            <Paperclip className="h-4 w-4 text-indigo-400" />
                          </div>
                          <div className="min-w-0 flex-1">
                            <p className="truncate text-xs font-medium text-gray-200">{a.file_name}</p>
                            <p className="text-[10px] text-gray-600">{formatFileSize(a.size_bytes)} · {fmtTime(a._sentAt)}</p>
                          </div>
                          <Download className="h-4 w-4 shrink-0 text-gray-600" />
                        </a>
                      ))}
                    </div>
                  </div>
                )}

                {allAttachments.length === 0 && (
                  <div className="border-b border-white/[0.06] p-4">
                    <div className="mb-3 flex items-center gap-2">
                      <ImageIcon className="h-4 w-4 text-gray-600" />
                      <h4 className="text-xs font-bold uppercase tracking-widest text-gray-500">Medien & Dateien</h4>
                    </div>
                    <div className="flex flex-col items-center py-6">
                      <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-white/[0.03] ring-1 ring-white/[0.06]">
                        <ImageIcon className="h-6 w-6 text-gray-700" />
                      </div>
                      <p className="mt-2 text-xs text-gray-600">Noch keine Medien geteilt</p>
                    </div>
                  </div>
                )}

                {/* Participants */}
                {thread.thread_type !== "direct" && (
                  <div className="p-4">
                    <div className="mb-3 flex items-center gap-2">
                      <Users2 className="h-4 w-4 text-indigo-400" />
                      <h4 className="text-xs font-bold uppercase tracking-widest text-gray-500">
                        {thread.participants?.length || 0} Teilnehmer
                      </h4>
                    </div>
                    <div className="space-y-1">
                      {allParticipants.map((p) => (
                        <div key={p.user_id} className="flex items-center gap-2.5 rounded-xl px-2.5 py-2 transition-all hover:bg-white/[0.04]">
                          <div className="relative shrink-0">
                            <Avatar src={p.user?.avatar_url} name={p.user?.full_name || "?"} size="sm" />
                            <span className="absolute -bottom-0.5 -right-0.5 h-2.5 w-2.5 rounded-full border-2 border-[#1a1c22] bg-emerald-400" />
                          </div>
                          <div className="min-w-0 flex-1">
                            <div className="flex items-center gap-1.5">
                              <p className={cn("truncate text-xs font-semibold", roleNameClass(p.role))}>{p.user?.full_name || p.user?.email}</p>
                              {p.user_id === user?.id && <span className="text-[9px] text-gray-600">Du</span>}
                            </div>
                            <p className="truncate text-[10px] text-gray-600">
                              {p.user?.username ? `@${p.user.username}` : roleLabel(p.role)}
                            </p>
                          </div>
                          <RolePill role={p.role} />
                        </div>
                      ))}
                    </div>
                  </div>
                )}

                {/* DM partner details */}
                {thread.thread_type === "direct" && otherUser && (
                  <div className="p-4">
                    <div className="mb-3 flex items-center gap-2">
                      <Info className="h-4 w-4 text-indigo-400" />
                      <h4 className="text-xs font-bold uppercase tracking-widest text-gray-500">Details</h4>
                    </div>
                    <div className="space-y-2 rounded-xl bg-white/[0.03] p-3 ring-1 ring-white/[0.06]">
                      {[
                        ["Name", otherUser.full_name || "—"],
                        ["Username", otherUser.username ? `@${otherUser.username}` : "—"],
                        ["E-Mail", otherUser.email || "—"],
                      ].map(([k, v]) => (
                        <div key={k} className="flex justify-between text-xs">
                          <span className="text-gray-500">{k}</span>
                          <span className="font-medium text-gray-300">{v}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                )}
              </div>
            </div>
          </div>
        );
      })()}

      {/* ═══ MODALS ═══ */}

      <Modal open={addContactOpen} onClose={() => { setAddContactOpen(false); setAddContactQ(""); void searchDirectory(""); }}
        title="Kontakt hinzufügen" className="max-w-lg">
        <div className="space-y-3">
          <input value={addContactQ} onChange={(e) => setAddContactQ(e.target.value)}
            placeholder="Name oder @username suchen..." autoFocus
            className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
          <div className="max-h-72 space-y-0.5 overflow-y-auto rounded-xl border border-border/40 p-2">
            {addContactQ.trim().length < 2
              ? <p className="py-6 text-center text-sm text-muted-foreground">Mindestens 2 Zeichen eingeben</p>
              : directoryResults.length === 0
                ? <p className="py-6 text-center text-sm text-muted-foreground">Keine Ergebnisse</p>
                : directoryResults.filter((p) => p.id !== user?.id).map((p) => {
                    const known = contacts.some((c) => c.contact_user_id === p.id);
                    const pending = outgoingRequests.some((r) => r.recipient_id === p.id);
                    return (
                      <div key={p.id} className={cn("flex items-center gap-3 rounded-xl px-3 py-2 hover:bg-accent/40", pending && "opacity-60")}>
                        <Avatar src={p.avatar_url} name={p.full_name || p.username} size="sm" />
                        <div className="min-w-0 flex-1">
                          <p className="truncate text-sm font-medium">{p.full_name || p.username}</p>
                          <p className="truncate text-xs text-muted-foreground">@{p.username}</p>
                        </div>
                        {known
                          ? <button onClick={() => { setAddContactOpen(false); void openDM(p.id); }} className="rounded-lg bg-muted px-2.5 py-1 text-[10px] font-medium">Chat</button>
                          : pending
                            ? <span className="rounded-lg bg-amber-500/10 px-2.5 py-1 text-[10px] font-semibold text-amber-400">Angefragt</span>
                            : <button onClick={() => void addContact(p)} className="rounded-lg bg-indigo-500/10 px-2.5 py-1 text-[10px] font-semibold text-indigo-400 hover:bg-indigo-500/20">Hinzufügen</button>
                        }
                      </div>
                    );
                  })
            }
          </div>
        </div>
      </Modal>

      <Modal open={groupOpen} onClose={() => { setGroupOpen(false); setGroupName(""); setGroupMembers([]); }}
        title="Neue Gruppe erstellen" className="max-w-lg">
        <div className="space-y-3">
          <input value={groupName} onChange={(e) => setGroupName(e.target.value)}
            placeholder="Gruppenname z.B. Marketing, Sprint..."
            className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
          <div className="max-h-60 space-y-0.5 overflow-y-auto rounded-xl border border-border/40 p-2">
            {candidates.length === 0
              ? <p className="py-4 text-center text-sm text-muted-foreground">Keine Personen verfügbar</p>
              : candidates.map((c) => {
                  const sel = groupMembers.includes(c.id);
                  return (
                    <label key={c.id} className={cn("flex cursor-pointer items-center gap-3 rounded-xl px-3 py-2 transition-colors",
                      sel ? "bg-indigo-500/10 ring-1 ring-indigo-500/20" : "hover:bg-accent/40")}>
                      <input type="checkbox" checked={sel} onChange={(e) =>
                        setGroupMembers((p) => e.target.checked ? [...p, c.id] : p.filter((x) => x !== c.id))
                      } className="rounded" />
                      <Avatar src={c.avatar} name={c.name} size="sm" />
                      <div className="min-w-0 flex-1">
                        <p className="truncate text-sm font-medium">{c.name}</p>
                        <p className="truncate text-xs text-muted-foreground">{c.username ? `@${c.username}` : c.hint}</p>
                      </div>
                    </label>
                  );
                })
            }
          </div>
          <div className="flex justify-end gap-2">
            <Button variant="outline" onClick={() => setGroupOpen(false)}>Abbrechen</Button>
            <button onClick={() => void doCreateGroup()}
              className="rounded-xl bg-gradient-to-r from-indigo-500 to-violet-600 px-5 py-2 text-sm font-semibold text-white shadow-md shadow-indigo-500/20 hover:shadow-lg">
              Erstellen
            </button>
          </div>
        </div>
      </Modal>

      <Modal open={chanOpen} onClose={() => {
        setChanOpen(false);
        setChanName("");
        setChanDesc("");
        setChanPerm("members");
        setChanPrivate(false);
        setChanAnnouncement(false);
        setChanMembers([]);
      }}
        title="Neuer Kanal" className="max-w-lg">
        <div className="space-y-3">
          {teams.length > 1 && (
            <select value={chanTeam} onChange={(e) => setChanTeam(e.target.value)}
              className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm">
              {teams.map((t) => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
          )}
          <input value={chanName} onChange={(e) => setChanName(e.target.value)}
            placeholder="Kanalname z.B. general, entwicklung..."
            className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
          <textarea value={chanDesc} onChange={(e) => setChanDesc(e.target.value)}
            placeholder="Beschreibung (optional)"
            className="h-16 w-full rounded-xl border border-border/60 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
          <select value={chanPrivate ? "private" : "team"} onChange={(e) => setChanPrivate(e.target.value === "private")}
            className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm">
            <option value="team">Lesen: Ganzes Team</option>
            <option value="private">Lesen: Nur ausgewählte Mitglieder</option>
          </select>
          <select value={chanPerm} onChange={(e) => setChanPerm(e.target.value as "members" | "admins")}
            className="h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm">
            <option value="members">Alle können schreiben</option>
            <option value="admins">Nur Admins</option>
          </select>
          <label className="flex items-center gap-3 rounded-xl border border-border/40 px-3 py-2 text-sm">
            <input type="checkbox" checked={chanAnnouncement} onChange={(e) => setChanAnnouncement(e.target.checked)} />
            <span>Als Ankündigungskanal anlegen (nur Admins/Owner schreiben)</span>
          </label>
          {chanPrivate && (
            <div className="rounded-xl border border-border/40 p-2">
              <p className="px-2 pb-2 text-xs font-semibold text-muted-foreground">
                Mitglieder mit Lesezugriff
              </p>
              <div className="max-h-56 space-y-0.5 overflow-y-auto">
                {createChannelCandidates.length === 0
                  ? <p className="px-2 py-4 text-center text-sm text-muted-foreground">Keine Teammitglieder verfügbar</p>
                  : createChannelCandidates.map((member) => {
                      const selected = chanMembers.includes(member.user_id);
                      return (
                        <label key={member.user_id} className={cn(
                          "flex cursor-pointer items-center gap-3 rounded-xl px-3 py-2 transition-colors",
                          selected ? "bg-indigo-500/10 ring-1 ring-indigo-500/20" : "hover:bg-accent/40"
                        )}>
                          <input
                            type="checkbox"
                            checked={selected}
                            onChange={(e) =>
                              setChanMembers((prev) =>
                                e.target.checked
                                  ? [...prev, member.user_id]
                                  : prev.filter((id) => id !== member.user_id)
                              )
                            }
                          />
                          <Avatar
                            src={member.profile?.avatar_url}
                            name={member.profile?.full_name || member.profile?.email || "Mitglied"}
                            size="sm"
                          />
                          <div className="min-w-0 flex-1">
                            <p className="truncate text-sm font-medium">
                              {member.profile?.full_name || member.profile?.email || "Mitglied"}
                            </p>
                            <p className="truncate text-xs text-muted-foreground">
                              {member.profile?.username ? `@${member.profile.username}` : member.role}
                            </p>
                          </div>
                        </label>
                      );
                    })
                }
              </div>
            </div>
          )}
          <div className="flex justify-end gap-2">
            <Button variant="outline" onClick={() => setChanOpen(false)}>Abbrechen</Button>
            <button onClick={() => void doCreateChannel()}
              className="rounded-xl bg-gradient-to-r from-indigo-500 to-violet-600 px-5 py-2 text-sm font-semibold text-white shadow-md shadow-indigo-500/20 hover:shadow-lg">
              Erstellen
            </button>
          </div>
        </div>
      </Modal>

      {thread && (
        <Modal open={manageOpen} onClose={() => setManageOpen(false)}
          title={thread.thread_type === "channel" ? "Kanal verwalten" : "Gruppe verwalten"} className="max-w-2xl">
          <div className="space-y-5">
            <div className="grid gap-3 sm:grid-cols-2">
              <div className="sm:col-span-2">
                <label className="text-sm font-medium">Name</label>
                <input value={manageTitle} onChange={(e) => setManageTitle(e.target.value)}
                  className="mt-1 h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
              </div>

              <div className="sm:col-span-2">
                <label className="text-sm font-medium">Beschreibung</label>
                <textarea value={manageDesc} onChange={(e) => setManageDesc(e.target.value)}
                  className="mt-1 h-20 w-full rounded-xl border border-border/60 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500" />
              </div>

              <div>
                <label className="text-sm font-medium">Schreibrecht</label>
                <select value={managePerm} onChange={(e) => setManagePerm(e.target.value as "members" | "admins")}
                  className="mt-1 h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm">
                  <option value="members">Mitglieder</option>
                  <option value="admins">Nur Admins/Owner</option>
                </select>
              </div>

              {thread.thread_type === "channel" && (
                <div>
                  <label className="text-sm font-medium">Leserecht</label>
                  <select value={managePrivate ? "private" : "team"} onChange={(e) => setManagePrivate(e.target.value === "private")}
                    className="mt-1 h-10 w-full rounded-xl border border-border/60 bg-background px-3 text-sm">
                    <option value="team">Ganzes Team</option>
                    <option value="private">Nur ausgewählte Mitglieder</option>
                  </select>
                </div>
              )}
            </div>

            <label className="flex items-center gap-3 rounded-xl border border-border/40 px-3 py-2 text-sm">
              <input type="checkbox" checked={manageAnnouncement} onChange={(e) => setManageAnnouncement(e.target.checked)} />
              <span>Ankündigungsmodus aktivieren (nur Admins/Owner schreiben)</span>
            </label>

            <div className="grid gap-4 lg:grid-cols-[1.1fr_0.9fr]">
              <div className="rounded-xl border border-border/40 p-3">
                <div className="mb-3 flex items-center gap-2">
                  <Shield className="h-4 w-4 text-indigo-500" />
                  <p className="text-sm font-semibold">Mitglieder und Rollen</p>
                </div>

                <div className="space-y-2">
                  {allParticipants.map((participant) => {
                    const isSelfEntry = participant.user_id === user?.id;
                    const canEditRole = canEditManagedParticipantRole(
                      selfParticipant?.role,
                      user?.id,
                      participant.role,
                      participant.user_id
                    );
                    const canRemove = canRemoveManagedParticipant(
                      selfParticipant?.role,
                      user?.id,
                      participant.role,
                      participant.user_id
                    );
                    const removeBlockedByPublicChannel =
                      thread.thread_type === "channel" && !thread.is_private;
                    const removeDisabled = removeBlockedByPublicChannel || !canRemove;
                    const removeTitle = removeBlockedByPublicChannel
                      ? "Öffentliche Team-Kanäle nutzen das ganze Team als Leser"
                      : isSelfEntry
                        ? "Dich selbst kannst du hier nicht entfernen"
                        : !canRemove
                          ? "Admins können keinen Owner entfernen"
                          : "Mitglied entfernen";

                    return (
                      <div
                        key={participant.user_id}
                        className={cn(
                          "flex flex-wrap items-center gap-2 rounded-xl border px-3 py-2",
                          roleTheme(participant.role)?.surfaceClass || "border-border/40"
                        )}
                      >
                      <Avatar src={participant.user?.avatar_url} name={participant.user?.full_name || participant.user?.email || "Mitglied"} size="sm" />
                      <div className="min-w-0 flex-1">
                        <div className="flex flex-wrap items-center gap-2">
                          <p className={cn("truncate text-sm font-medium", roleNameClass(participant.role))}>
                            {participant.user?.full_name || participant.user?.email || "Mitglied"}
                          </p>
                          <RolePill role={participant.role} />
                        </div>
                        <p className={cn("truncate text-xs", roleTheme(participant.role)?.metaClass || "text-muted-foreground")}>
                          {participant.user?.username ? `@${participant.user.username}` : roleLabel(participant.role)}
                        </p>
                      </div>
                      <select
                        value={participant.role}
                        onChange={(e) => void doChangeMemberRole(participant.user_id, e.target.value as ChatThreadRole)}
                        disabled={!canEditRole}
                        className={cn(
                          "h-9 rounded-lg border bg-background px-2 text-xs disabled:cursor-not-allowed disabled:opacity-50",
                          roleTheme(participant.role)?.selectClass || "border-border/60"
                        )}
                      >
                        <option value="owner" disabled={!canAssignManagedRole(selfParticipant?.role, "owner")}>Owner</option>
                        <option value="admin">Admin</option>
                        <option value="member">Member</option>
                        <option value="viewer">Viewer</option>
                      </select>
                      {isSelfEntry && (
                        <span className="text-[10px] font-medium text-amber-300">
                          Eigenen Rang kannst du nicht ändern
                        </span>
                      )}
                      {!isSelfEntry && selfParticipant?.role === "admin" && participant.role === "owner" && (
                        <span className="text-[10px] font-medium text-amber-300">
                          Admins können keinen Owner verwalten
                        </span>
                      )}
                      <button
                        onClick={() => void doRemoveMember(participant.user_id)}
                        disabled={removeDisabled}
                        className="rounded-lg p-2 text-muted-foreground transition-colors hover:bg-accent hover:text-rose-500 disabled:cursor-not-allowed disabled:opacity-40"
                        title={removeTitle}
                      >
                        <UserMinus className="h-4 w-4" />
                      </button>
                      </div>
                    );
                  })}
                </div>

                {thread.thread_type === "channel" && !thread.is_private && (
                  <p className="mt-3 text-xs text-muted-foreground">
                    Öffentliche Team-Kanäle sind für das ganze Team lesbar. Speichere zuerst auf privat um, wenn du Leser gezielt auswählen willst.
                  </p>
                )}
              </div>

              <div className="rounded-xl border border-border/40 p-3">
                <div className="mb-3 flex items-center gap-2">
                  {thread.thread_type === "channel" ? (
                    managePrivate ? <Lock className="h-4 w-4 text-indigo-500" /> : <Globe className="h-4 w-4 text-indigo-500" />
                  ) : (
                    <Users2 className="h-4 w-4 text-indigo-500" />
                  )}
                  <p className="text-sm font-semibold">Personen hinzufügen</p>
                </div>

                <div className="max-h-72 space-y-2 overflow-y-auto">
                  {manageCandidates.length === 0 ? (
                    <p className="rounded-xl border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground">
                      Keine weiteren Personen verfügbar.
                    </p>
                  ) : (
                    manageCandidates.map((candidate) => (
                      <div key={candidate.id} className="flex items-center gap-3 rounded-xl border border-border/40 px-3 py-2">
                        <Avatar src={candidate.avatar} name={candidate.name} size="sm" />
                        <div className="min-w-0 flex-1">
                          <p className="truncate text-sm font-medium">{candidate.name}</p>
                          <p className="truncate text-xs text-muted-foreground">
                            {candidate.username ? `@${candidate.username}` : candidate.hint}
                          </p>
                        </div>
                        <Button size="sm" variant="outline" onClick={() => void doAddMember(candidate.id)}>
                          <Plus className="h-3.5 w-3.5" />
                          Hinzufügen
                        </Button>
                      </div>
                    ))
                  )}
                </div>
              </div>
            </div>

            <div className="flex flex-col gap-3 border-t border-border/50 pt-4 sm:flex-row sm:items-center sm:justify-between">
              <button onClick={() => void doDeleteThread()}
                className="inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm font-medium text-rose-500 transition-colors hover:bg-rose-500/10">
                <Trash2 className="h-4 w-4" />
                {thread.thread_type === "channel" ? "Kanal löschen" : "Gruppe löschen"}
              </button>

              <div className="flex justify-end gap-2">
                <Button variant="outline" onClick={() => setManageOpen(false)}>Schließen</Button>
                <Button onClick={() => void doSaveThreadSettings()}>
                  <Settings className="h-4 w-4" />
                  Änderungen speichern
                </Button>
              </div>
            </div>
          </div>
        </Modal>
      )}

      {preview && (
        <FilePreviewModal open={!!preview} onClose={() => setPreview(null)}
          fileName={preview.file_name} publicUrl={preview.public_url} mimeType={preview.mime_type} />
      )}
    </>
  );
}

// ─── Sidebar Section ─────────────────────────────────────────────────────────

function Section({ title, count, onAdd, children }: {
  title: string; count?: number; onAdd?: () => void; children: React.ReactNode;
}) {
  const [open, setOpen] = useState(true);
  return (
    <div className="mb-1.5">
      <div className="flex items-center px-1 py-1.5">
        <button onClick={() => setOpen((o) => !o)}
          className="flex min-w-0 flex-1 items-center gap-1 text-[11px] font-bold uppercase tracking-widest text-gray-600 transition-colors hover:text-gray-400">
          <ChevronDown className={cn("h-3 w-3 transition-transform duration-200", !open && "-rotate-90")} />
          <span className="truncate">{title}</span>
          {count !== undefined && count > 0 && (
            <span className="ml-auto rounded-md bg-white/[0.04] px-1.5 py-0.5 text-[10px] font-semibold text-gray-500 ring-1 ring-white/[0.06]">{count}</span>
          )}
        </button>
        {onAdd && (
          <button onClick={onAdd} className="rounded-lg p-1 text-gray-600 transition-all hover:bg-white/[0.06] hover:text-indigo-400">
            <Plus className="h-3 w-3" />
          </button>
        )}
      </div>
      {open && <div className="space-y-0.5">{children}</div>}
    </div>
  );
}

// ─── Thread Row ──────────────────────────────────────────────────────────────

function ThreadRow({ t, uid, active, onClick, prefix = "" }: {
  t: ChatThread; uid: string | null; active: boolean; onClick: () => void; prefix?: string;
}) {
  return (
    <button onClick={onClick} className={cn(
      "flex w-full items-center gap-2.5 rounded-xl px-2.5 py-2 text-left transition-all duration-200",
      active
        ? "bg-gradient-to-r from-indigo-500/10 to-violet-500/5 text-white ring-1 ring-indigo-500/20 shadow-sm shadow-indigo-500/5"
        : "text-gray-400 hover:bg-white/[0.04] hover:text-gray-200"
    )}>
      {t.thread_type === "channel"
        ? <div className={cn("flex h-8 w-8 shrink-0 items-center justify-center rounded-xl transition-colors",
            active ? "bg-indigo-500/20 text-indigo-400 ring-1 ring-indigo-500/30" : "bg-white/[0.04] text-gray-600 ring-1 ring-white/[0.06]"
          )}><Hash className="h-4 w-4" /></div>
        : t.thread_type === "group"
          ? <div className={cn("flex h-8 w-8 shrink-0 items-center justify-center rounded-xl transition-colors",
              active ? "bg-indigo-500/20 text-indigo-400 ring-1 ring-indigo-500/30" : "bg-white/[0.04] text-gray-600 ring-1 ring-white/[0.06]"
            )}><Users2 className="h-4 w-4" /></div>
          : <div className="relative shrink-0">
              <Avatar src={t.participants?.find((p) => p.user_id !== uid)?.user?.avatar_url} name={threadTitle(t, uid)} size="sm" />
              <span className="absolute -bottom-0.5 -right-0.5 h-2.5 w-2.5 rounded-full border-2 border-[#1a1c22] bg-emerald-400" />
            </div>
      }
      <div className="min-w-0 flex-1">
        <p className={cn(
          "truncate text-sm font-medium",
          t.thread_type === "direct" ? "text-sky-300" : undefined
        )}>
          {prefix}{threadTitle(t, uid)}
        </p>
        <p className={cn(
          "truncate text-[11px]",
          t.thread_type === "direct" && !t.last_message?.body ? "text-sky-300/95" : "text-gray-600"
        )}>
          {threadPreview(t, uid)}
        </p>
      </div>
      {t.unread && <span className="h-2.5 w-2.5 shrink-0 rounded-full bg-indigo-500 shadow-[0_0_8px_rgba(99,102,241,0.6)]" />}
    </button>
  );
}
