import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { archiveChat, clearChat, createPrivateChat, deleteChat, getChats, getSavedMessagesChat, joinChat, leaveChat, pinChat, unarchiveChat, unpinChat } from "../api/chats";
import { globalSearch } from "../api/search";
import type { DiscoverChat, Message, UserSearchItem } from "../chat/types";
import { addContact, addContactByEmail, listContacts, removeContact, updateMyProfile } from "../api/users";
import { useAuthStore } from "../store/authStore";
import { useChatStore } from "../store/chatStore";
import { NewChatPanel } from "./NewChatPanel";
import { SettingsPanel } from "./SettingsPanel";
import { applyAppearancePreferences, getAppPreferences } from "../utils/preferences";

export function ChatList() {
  const chats = useChatStore((s) => s.chats);
  const activeChatId = useChatStore((s) => s.activeChatId);
  const setActiveChatId = useChatStore((s) => s.setActiveChatId);
  const setFocusedMessage = useChatStore((s) => s.setFocusedMessage);
  const loadChats = useChatStore((s) => s.loadChats);
  const me = useAuthStore((s) => s.me);
  const logout = useAuthStore((s) => s.logout);
  const [search, setSearch] = useState("");
  const [userResults, setUserResults] = useState<UserSearchItem[]>([]);
  const [discoverResults, setDiscoverResults] = useState<DiscoverChat[]>([]);
  const [messageResults, setMessageResults] = useState<Message[]>([]);
  const [archivedChats, setArchivedChats] = useState<typeof chats>([]);
  const [searchLoading, setSearchLoading] = useState(false);
  const [tab, setTab] = useState<"all" | "people" | "groups" | "channels" | "archived">("all");
  const [archivedLoading, setArchivedLoading] = useState(false);
  const [contactsLoading, setContactsLoading] = useState(false);
  const [contacts, setContacts] = useState<UserSearchItem[]>([]);
  const [contactsOpen, setContactsOpen] = useState(false);
  const [contactsSearch, setContactsSearch] = useState("");
  const [contactEmail, setContactEmail] = useState("");
  const [contactEmailError, setContactEmailError] = useState<string | null>(null);
  const [ctxChatId, setCtxChatId] = useState<number | null>(null);
  const [ctxPos, setCtxPos] = useState<{ x: number; y: number } | null>(null);
  const [deleteModalChatId, setDeleteModalChatId] = useState<number | null>(null);
  const [deleteForAll, setDeleteForAll] = useState(false);
  const [profileOpen, setProfileOpen] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [profileName, setProfileName] = useState("");
  const [profileUsername, setProfileUsername] = useState("");
  const [profileBio, setProfileBio] = useState("");
  const [profileAvatarUrl, setProfileAvatarUrl] = useState("");
  const [profileAllowPrivateMessages, setProfileAllowPrivateMessages] = useState(true);
  const [profileError, setProfileError] = useState<string | null>(null);
  const [profileSaving, setProfileSaving] = useState(false);
  const sidebarRef = useRef<HTMLElement | null>(null);
  const burgerMenuRef = useRef<HTMLDivElement | null>(null);
  const burgerButtonRef = useRef<HTMLButtonElement | null>(null);
  const deleteModalChat = chats.find((chat) => chat.id === deleteModalChatId) ?? null;
  const canDeleteForEveryone = Boolean(
    deleteModalChat &&
    !deleteModalChat.is_saved &&
    (
      deleteModalChat.type === "group" ||
      deleteModalChat.type === "private" ||
      (deleteModalChat.type === "channel" && (deleteModalChat.my_role === "owner" || deleteModalChat.my_role === "admin"))
    )
  );
  const channelMemberLeaveOnly = Boolean(
    deleteModalChat &&
    deleteModalChat.type === "channel" &&
    deleteModalChat.my_role === "member"
  );

  useEffect(() => {
    void loadChats();
  }, [loadChats]);

  useEffect(() => {
    let cancelled = false;
    void (async () => {
      setArchivedLoading(true);
      try {
        const rows = await getChats(undefined, true);
        if (!cancelled) {
          setArchivedChats(rows);
        }
      } catch {
        if (!cancelled) {
          setArchivedChats([]);
        }
      } finally {
        if (!cancelled) {
          setArchivedLoading(false);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [tab, chats.length]);

  useEffect(() => {
    if (!contactsOpen) {
      return;
    }
    let cancelled = false;
    setContactsLoading(true);
    void (async () => {
      try {
        const rows = await listContacts();
        if (!cancelled) {
          setContacts(rows);
        }
      } catch {
        if (!cancelled) {
          setContacts([]);
        }
      } finally {
        if (!cancelled) {
          setContactsLoading(false);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [contactsOpen]);

  useEffect(() => {
    const term = search.trim();
    if (term.replace("@", "").length < 2) {
      setUserResults([]);
      setDiscoverResults([]);
      setMessageResults([]);
      setSearchLoading(false);
      return;
    }
    let cancelled = false;
    setSearchLoading(true);
    void (async () => {
      try {
        const result = await globalSearch(term);
        if (cancelled) {
          return;
        }
        setUserResults(result.users);
        setDiscoverResults(result.chats);
        setMessageResults(result.messages);
      } catch {
        if (!cancelled) {
          setUserResults([]);
          setDiscoverResults([]);
          setMessageResults([]);
        }
      } finally {
        if (!cancelled) {
          setSearchLoading(false);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [search]);

  useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key !== "Escape") {
        return;
      }
      setCtxChatId(null);
      setCtxPos(null);
      setDeleteModalChatId(null);
      setProfileOpen(false);
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, []);

  useEffect(() => {
    const onPointerDown = (event: MouseEvent) => {
      const target = event.target as Node | null;
      if (ctxChatId) {
        setCtxChatId(null);
        setCtxPos(null);
      }
      if (menuOpen) {
        const inMenu = burgerMenuRef.current?.contains(target ?? null) ?? false;
        const inButton = burgerButtonRef.current?.contains(target ?? null) ?? false;
        if (!inMenu && !inButton) {
          setMenuOpen(false);
        }
      }
    };
    document.addEventListener("mousedown", onPointerDown);
    return () => document.removeEventListener("mousedown", onPointerDown);
  }, [ctxChatId, menuOpen]);

  useEffect(() => {
    applyAppearancePreferences(getAppPreferences());
  }, []);

  useEffect(() => {
    if (!me) {
      return;
    }
    setProfileName(me.name || "");
    setProfileUsername(me.username || "");
    setProfileBio(me.bio || "");
    setProfileAvatarUrl(me.avatar_url || "");
    setProfileAllowPrivateMessages(me.allow_private_messages ?? true);
  }, [me]);

  async function openSavedMessages() {
    const saved = await getSavedMessagesChat();
    const updatedChats = await getChats();
    useChatStore.setState({ chats: updatedChats, activeChatId: saved.id });
  }

  const filteredChats = chats.filter((chat) => {
    if (chat.archived) {
      return false;
    }
    if (tab === "people") {
      return chat.type === "private";
    }
    if (tab === "groups") {
      return chat.type === "group";
    }
    if (tab === "channels") {
      return chat.type === "channel";
    }
    return true;
  });

  const tabs: Array<{ id: "all" | "people" | "groups" | "channels" | "archived"; label: string }> = [
    { id: "all", label: "All" },
    { id: "people", label: "Люди" },
    { id: "groups", label: "Groups" },
    { id: "channels", label: "Каналы" },
    { id: "archived", label: "Архив" }
  ];

  const visibleChats = tab === "archived" ? archivedChats : filteredChats;
  const pinnedVisibleChats = visibleChats.filter((chat) => chat.pinned);
  const regularVisibleChats = visibleChats.filter((chat) => !chat.pinned);

  function renderChatRow(chat: (typeof visibleChats)[number]) {
    return (
      <button
        className={`block w-full border-b border-slate-800/60 px-4 py-3 text-left transition ${
          activeChatId === chat.id ? "bg-sky-500/20" : "hover:bg-slate-800/65"
        }`}
        key={chat.id}
        onClick={() => setActiveChatId(chat.id)}
        onContextMenu={(e) => {
          e.preventDefault();
          const safePos = getSafeContextPosition(e.clientX, e.clientY, 176, 56);
          setCtxChatId(chat.id);
          setCtxPos(safePos);
        }}
      >
        <div className="flex items-start gap-3">
          <div className="relative mt-0.5">
            <div className="flex h-10 w-10 items-center justify-center rounded-full bg-sky-500/30 text-sm font-semibold uppercase text-sky-100">
              {chat.pinned ? "📌" : (chat.display_title || chat.title || chat.type).slice(0, 1)}
            </div>
            {chat.type === "private" && chat.counterpart_is_online ? (
              <span className="absolute bottom-0 right-0 h-2.5 w-2.5 rounded-full border border-slate-900 bg-emerald-400" />
            ) : null}
          </div>
          <div className="min-w-0 flex-1">
            <div className="flex items-center justify-between gap-2">
              <p className="truncate text-sm font-semibold">{chatLabel(chat)}</p>
              {(chat.unread_count ?? 0) > 0 || (chat.unread_mentions_count ?? 0) > 0 ? (
                <span className="inline-flex items-center gap-1">
                  {(chat.unread_mentions_count ?? 0) > 0 ? (
                    <span className="inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-amber-400 px-1.5 py-0.5 text-[10px] font-semibold text-slate-950">
                      @
                    </span>
                  ) : null}
                  {(chat.unread_count ?? 0) > 0 ? (
                    <span className="inline-flex min-w-5 items-center justify-center rounded-full bg-sky-500 px-1.5 py-0.5 text-[10px] font-semibold text-slate-950">
                      {chat.unread_count}
                    </span>
                  ) : null}
                </span>
              ) : (
                <span className="shrink-0 text-[11px] text-slate-400">
                  {formatChatListTime(chat.last_message_created_at)}
                </span>
              )}
            </div>
            <p className="truncate text-xs text-slate-400">{chatPreviewLabel(chat)}</p>
          </div>
        </div>
      </button>
    );
  }

  return (
    <aside ref={sidebarRef} className="relative flex h-full w-full flex-col bg-slate-900/60" onClick={() => { setCtxChatId(null); setCtxPos(null); setMenuOpen(false); }}>
      <div className="border-b border-slate-700/50 px-3 py-3">
        <div className="relative mb-2 flex items-center gap-2">
          <button
            ref={burgerButtonRef}
            className="rounded-lg bg-slate-700/70 px-2 py-2 text-xs"
            onClick={(e) => {
              e.stopPropagation();
              setMenuOpen((v) => !v);
            }}
          >
            ☰
          </button>
          {menuOpen ? (
            <div
              ref={burgerMenuRef}
              className="absolute left-0 top-11 z-40 w-56 rounded-xl border border-slate-700/80 bg-slate-900/95 p-1 shadow-2xl"
              onClick={(e) => e.stopPropagation()}
            >
              <button className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-slate-800" onClick={() => { setProfileOpen(true); setMenuOpen(false); }}>
                My Profile
              </button>
              <button className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-slate-800" onClick={() => { void openSavedMessages(); setMenuOpen(false); }}>
                Saved Messages
              </button>
              <button className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-slate-800" onClick={() => { setContactsOpen(true); setMenuOpen(false); }}>
                Contacts
              </button>
              <button className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-slate-800" onClick={() => { setSettingsOpen(true); setMenuOpen(false); }}>
                Settings
              </button>
              <button
                className="block w-full rounded px-3 py-2 text-left text-sm text-red-300 hover:bg-slate-800"
                onClick={() => {
                  setMenuOpen(false);
                  if (!window.confirm("Log out of your account?")) {
                    return;
                  }
                  logout();
                }}
              >
                Logout
              </button>
            </div>
          ) : null}
          <label className="block flex-1">
            <input
              className="w-full rounded-full border border-slate-700/80 bg-slate-800/80 px-3 py-2 text-sm outline-none placeholder:text-slate-400 focus:border-sky-500"
              placeholder="Search"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
            />
          </label>
          <button className="flex h-8 w-8 items-center justify-center rounded-full bg-sky-500/30 text-xs font-semibold uppercase text-sky-100" onClick={() => setProfileOpen(true)}>
            {(me?.name || me?.username || "u").slice(0, 1)}
          </button>
        </div>
        <div className="flex items-center gap-3 overflow-x-auto pt-1 text-sm">
          {tabs.map((item) => (
            <button
              className={`whitespace-nowrap pb-1.5 ${tab === item.id ? "border-b-2 border-sky-400 font-semibold text-sky-300" : "text-slate-300/80"}`}
              key={item.id}
              onClick={() => setTab(item.id)}
            >
              {item.label}
            </button>
          ))}
        </div>
        {search.trim().replace("@", "").length >= 2 ? (
          <div className="mt-3 rounded-xl border border-slate-700/70 bg-slate-900/70 p-2">
            {searchLoading ? <p className="px-2 py-1 text-xs text-slate-400">Searching...</p> : null}
            {!searchLoading && userResults.length === 0 && discoverResults.length === 0 && messageResults.length === 0 ? (
              <p className="px-2 py-1 text-xs text-slate-400">Nothing found</p>
            ) : null}
            {userResults.length > 0 ? (
              <div className="mb-1">
                <p className="px-2 py-1 text-[10px] uppercase tracking-wide text-slate-400">People</p>
                {userResults.slice(0, 5).map((user) => (
                  <div className="flex items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-slate-800" key={`user-${user.id}`}>
                    <button
                      className="min-w-0 flex-1 text-left"
                      onClick={async () => {
                        const chat = await createPrivateChat(user.id);
                        const updatedChats = await getChats();
                        useChatStore.setState({ chats: updatedChats, activeChatId: chat.id });
                        setSearch("");
                        setUserResults([]);
                        setDiscoverResults([]);
                        setMessageResults([]);
                      }}
                      type="button"
                    >
                      <p className="truncate text-xs font-semibold">{user.name}</p>
                      <p className="truncate text-[11px] text-slate-400">{user.email || `@${user.username}`}</p>
                    </button>
                    <button
                      className="rounded bg-slate-700 px-2 py-1 text-[10px] hover:bg-slate-600"
                      onClick={async () => {
                        await addContact(user.id);
                        if (contactsOpen) {
                          setContacts(await listContacts());
                        }
                      }}
                      type="button"
                    >
                      Add
                    </button>
                  </div>
                ))}
              </div>
            ) : null}
            {discoverResults.length > 0 ? (
              <div>
                <p className="px-2 py-1 text-[10px] uppercase tracking-wide text-slate-400">Groups and Channels</p>
                {discoverResults.slice(0, 5).map((chat) => (
                  <button
                    className="flex w-full items-center justify-between rounded-lg px-2 py-1.5 text-left hover:bg-slate-800"
                    key={`discover-${chat.id}`}
                    onClick={async () => {
                      if (!chat.is_member) {
                        await joinChat(chat.id);
                      }
                      const updatedChats = await getChats();
                      useChatStore.setState({ chats: updatedChats, activeChatId: chat.id });
                      setSearch("");
                      setUserResults([]);
                      setDiscoverResults([]);
                      setMessageResults([]);
                    }}
                  >
                    <div className="min-w-0">
                      <p className="truncate text-xs font-semibold">{chat.display_title || chat.title || (chat.type === "group" ? "Group" : "Channel")}</p>
                      <p className="truncate text-[11px] text-slate-400">{chat.handle ? `@${chat.handle}` : chat.type}</p>
                    </div>
                    <span className="ml-2 shrink-0 text-[10px] text-slate-400">{chat.is_member ? "open" : "join"}</span>
                  </button>
                ))}
              </div>
            ) : null}
            {messageResults.length > 0 ? (
              <div>
                <p className="px-2 py-1 text-[10px] uppercase tracking-wide text-slate-400">Messages</p>
                {messageResults.slice(0, 5).map((message) => (
                  <button
                    className="block w-full rounded-lg px-2 py-2 text-left hover:bg-slate-800"
                    key={`message-${message.id}`}
                    onClick={async () => {
                      setActiveChatId(message.chat_id);
                      setFocusedMessage(message.chat_id, message.id);
                      setSearch("");
                      setUserResults([]);
                      setDiscoverResults([]);
                      setMessageResults([]);
                    }}
                  >
                    <p className="truncate text-[11px] text-slate-400">{chatDisplayNameById(chats, message.chat_id)}</p>
                    <p className="truncate text-xs font-semibold">{message.text || "[media]"}</p>
                  </button>
                ))}
              </div>
            ) : null}
          </div>
        ) : null}
      </div>
      <div className="tg-scrollbar flex-1 overflow-auto">
        {tab === "all" && archivedChats.length > 0 ? (
          <button
            className="block w-full border-b border-slate-800/60 px-4 py-3 text-left transition hover:bg-slate-800/65"
            onClick={() => setTab("archived")}
            type="button"
          >
            <div className="flex items-start gap-3">
              <div className="mt-0.5 flex h-10 w-10 items-center justify-center rounded-full bg-slate-700/70 text-sm font-semibold text-slate-100">
                🗂
              </div>
              <div className="min-w-0 flex-1">
                <div className="flex items-center justify-between gap-2">
                  <p className="truncate text-sm font-semibold">Archived Chats</p>
                  <span className="inline-flex min-w-5 items-center justify-center rounded-full bg-slate-700 px-1.5 py-0.5 text-[10px] font-semibold text-slate-200">
                    {archivedChats.length}
                  </span>
                </div>
                <p className="truncate text-xs text-slate-400">Hidden chats and channels</p>
              </div>
            </div>
          </button>
        ) : null}

        {tab === "archived" && archivedLoading ? (
          <p className="px-4 py-3 text-xs text-slate-400">Loading archived chats...</p>
        ) : null}
        {tab === "archived" && !archivedLoading && archivedChats.length === 0 ? (
          <p className="px-4 py-3 text-xs text-slate-400">Archive is empty</p>
        ) : null}
        {pinnedVisibleChats.length > 0 ? (
          <p className="px-4 py-2 text-[10px] uppercase tracking-wide text-slate-400">Pinned</p>
        ) : null}
        {pinnedVisibleChats.map(renderChatRow)}
        {regularVisibleChats.length > 0 ? (
          <p className="px-4 py-2 text-[10px] uppercase tracking-wide text-slate-400">Chats</p>
        ) : null}
        {regularVisibleChats.map(renderChatRow)}
      </div>
      {contactsOpen ? (
        <div className="absolute inset-0 z-30 flex flex-col bg-slate-900">
          <div className="border-b border-slate-700/50 px-4 py-3">
            <div className="mb-3 flex items-center gap-2">
              <button
                className="rounded-lg bg-slate-700/70 px-2 py-1.5 text-xs"
                onClick={() => {
                  setContactsOpen(false);
                  setContactsSearch("");
                }}
                type="button"
              >
                Back
              </button>
              <p className="text-sm font-semibold">Contacts</p>
            </div>
            <input
              className="w-full rounded-full border border-slate-700/80 bg-slate-800/80 px-3 py-2 text-sm outline-none placeholder:text-slate-400 focus:border-sky-500"
              placeholder="Search contacts"
              value={contactsSearch}
              onChange={(e) => setContactsSearch(e.target.value)}
            />
          </div>
          <div className="border-b border-slate-800/60 px-4 py-3">
            <p className="mb-2 text-[10px] uppercase tracking-wide text-slate-400">Add contact (Email)</p>
            <div className="flex gap-2">
              <input
                className="w-full rounded bg-slate-800 px-3 py-2 text-xs outline-none"
                placeholder="name@example.com"
                type="email"
                value={contactEmail}
                onChange={(e) => {
                  setContactEmail(e.target.value);
                  setContactEmailError(null);
                }}
              />
              <button
                className="rounded bg-sky-500 px-3 py-2 text-xs font-semibold text-slate-950"
                onClick={async () => {
                  const email = contactEmail.trim();
                  if (!email) {
                    return;
                  }
                  try {
                    await addContactByEmail(email);
                    setContactEmail("");
                    setContacts(await listContacts());
                  } catch {
                    setContactEmailError("User with this email not found");
                  }
                }}
                type="button"
              >
                Add
              </button>
            </div>
            {contactEmailError ? <p className="mt-1 text-[11px] text-red-400">{contactEmailError}</p> : null}
          </div>
          <div className="tg-scrollbar flex-1 overflow-auto">
            {contactsLoading ? (
              <p className="px-4 py-3 text-xs text-slate-400">Loading contacts...</p>
            ) : null}
            {!contactsLoading &&
            contacts.filter((user) => {
              const term = contactsSearch.trim().toLowerCase();
              if (!term) {
                return true;
              }
              return (
                (user.name || "").toLowerCase().includes(term) ||
                (user.username || "").toLowerCase().includes(term) ||
                (user.email || "").toLowerCase().includes(term)
              );
            }).length === 0 ? (
              <p className="px-4 py-3 text-xs text-slate-400">No contacts yet</p>
            ) : null}
            {!contactsLoading
              ? contacts
                  .filter((user) => {
                    const term = contactsSearch.trim().toLowerCase();
                    if (!term) {
                      return true;
                    }
                    return (
                      (user.name || "").toLowerCase().includes(term) ||
                      (user.username || "").toLowerCase().includes(term) ||
                      (user.email || "").toLowerCase().includes(term)
                    );
                  })
                  .map((user) => (
                    <div className="flex items-center gap-2 border-b border-slate-800/60 px-4 py-3" key={`contact-${user.id}`}>
                      <button
                        className="min-w-0 flex-1 text-left"
                        onClick={async () => {
                          const chat = await createPrivateChat(user.id);
                          const updatedChats = await getChats();
                          useChatStore.setState({ chats: updatedChats, activeChatId: chat.id });
                          setContactsOpen(false);
                          setContactsSearch("");
                        }}
                        type="button"
                      >
                        <p className="truncate text-sm font-semibold">{user.name}</p>
                        <p className="truncate text-xs text-slate-400">{user.email || `@${user.username}`}</p>
                      </button>
                      <button
                        className="rounded bg-slate-700 px-2 py-1 text-[10px] text-red-200 hover:bg-slate-600"
                        onClick={async () => {
                          await removeContact(user.id);
                          setContacts((prev) => prev.filter((item) => item.id !== user.id));
                        }}
                        type="button"
                      >
                        Remove
                      </button>
                    </div>
                  ))
              : null}
          </div>
        </div>
      ) : null}
      <NewChatPanel />

      {ctxChatId && ctxPos
        ? createPortal(
          <div className="fixed inset-0 z-[99]" onClick={() => { setCtxChatId(null); setCtxPos(null); }}>
            <div className="fixed z-[100] w-44 rounded-lg border border-slate-700/80 bg-slate-900/95 p-1 shadow-2xl" style={{ left: ctxPos.x, top: ctxPos.y }} onClick={(e) => e.stopPropagation()}>
              <button
                className="block w-full rounded px-2 py-1.5 text-left text-sm text-red-300 hover:bg-slate-800"
                onClick={() => {
                  setDeleteModalChatId(ctxChatId);
                  setCtxChatId(null);
                  setCtxPos(null);
                  setDeleteForAll(false);
                }}
              >
                {(() => {
                  const chat = chats.find((c) => c.id === ctxChatId);
                  if (!chat) return "Delete chat";
                  if (chat.is_saved) return "Clear chat";
                  if (chat.type === "channel" && chat.my_role === "member") return "Leave channel";
                  return "Delete chat";
                })()}
              </button>
              <button
                className="block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-slate-800"
                onClick={async () => {
                  const target = chats.find((c) => c.id === ctxChatId) ?? archivedChats.find((c) => c.id === ctxChatId);
                  if (!target) {
                    return;
                  }
                  if (target.archived) {
                    await unarchiveChat(target.id);
                  } else {
                    await archiveChat(target.id);
                  }
                  await loadChats();
                  const nextArchived = await getChats(undefined, true);
                  setArchivedChats(nextArchived);
                  setCtxChatId(null);
                  setCtxPos(null);
                }}
              >
                {(chats.find((c) => c.id === ctxChatId) ?? archivedChats.find((c) => c.id === ctxChatId))?.archived ? "Unarchive" : "Archive"}
              </button>
              <button
                className="block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-slate-800"
                onClick={async () => {
                  const target = chats.find((c) => c.id === ctxChatId) ?? archivedChats.find((c) => c.id === ctxChatId);
                  if (!target) {
                    return;
                  }
                  if (target.pinned) {
                    await unpinChat(target.id);
                  } else {
                    await pinChat(target.id);
                  }
                  await loadChats();
                  if (tab === "archived") {
                    const nextArchived = await getChats(undefined, true);
                    setArchivedChats(nextArchived);
                  }
                  setCtxChatId(null);
                  setCtxPos(null);
                }}
              >
                {(chats.find((c) => c.id === ctxChatId) ?? archivedChats.find((c) => c.id === ctxChatId))?.pinned ? "Unpin chat" : "Pin chat"}
              </button>
            </div>
          </div>,
          document.body
        )
        : null}

      {deleteModalChatId ? (
        <div className="absolute inset-0 z-50 flex items-end justify-center bg-slate-950/55 p-3">
          <div className="w-full rounded-xl border border-slate-700/80 bg-slate-900 p-3">
            <p className="mb-2 text-sm font-semibold">
              {(deleteModalChat?.is_saved
                ? "Clear chat"
                : deleteModalChat?.type === "channel" && deleteModalChat.my_role === "member"
                  ? "Leave channel"
                  : "Delete chat")}
              : {deleteModalChat ? chatLabel(deleteModalChat) : "selected chat"}
            </p>
            {deleteModalChat?.type === "channel" ? (
              <p className="mb-3 text-xs text-slate-400">
                {channelMemberLeaveOnly
                  ? "You will leave this channel."
                  : "Channel deletion removes it for all subscribers."}
              </p>
            ) : null}
            {deleteModalChat?.is_saved ? (
              <p className="mb-3 text-xs text-slate-400">This will clear all messages in Saved Messages.</p>
            ) : null}
            {canDeleteForEveryone ? (
              <label className="mb-3 flex items-center gap-2 text-sm">
                <input checked={deleteForAll} onChange={(e) => setDeleteForAll(e.target.checked)} type="checkbox" />
                Delete for everyone
              </label>
            ) : null}
            <div className="flex gap-2">
              <button
                className="flex-1 rounded bg-red-500 px-3 py-2 text-sm font-semibold text-white"
                onClick={async () => {
                  if (deleteModalChat?.is_saved) {
                    await clearChat(deleteModalChatId);
                    useChatStore.getState().clearChatMessages(deleteModalChatId);
                    setDeleteModalChatId(null);
                    return;
                  }
                  if (channelMemberLeaveOnly) {
                    await leaveChat(deleteModalChatId);
                  } else {
                    await deleteChat(deleteModalChatId, canDeleteForEveryone && deleteForAll);
                  }
                  await loadChats();
                  if (activeChatId === deleteModalChatId) {
                    setActiveChatId(null);
                  }
                  setDeleteModalChatId(null);
                }}
              >
                {deleteModalChat?.is_saved ? "Clear" : channelMemberLeaveOnly ? "Leave" : "Delete"}
              </button>
              <button className="flex-1 rounded bg-slate-700 px-3 py-2 text-sm" onClick={() => setDeleteModalChatId(null)}>
                Cancel
              </button>
            </div>
          </div>
        </div>
      ) : null}

      {profileOpen ? (
        <div className="absolute inset-0 z-50 flex items-end justify-center bg-slate-950/55 p-3">
          <div className="w-full rounded-xl border border-slate-700/80 bg-slate-900 p-3">
            <p className="mb-2 text-sm font-semibold">Edit profile</p>
            <div className="space-y-2">
              <input className="w-full rounded bg-slate-800 px-3 py-2 text-sm" placeholder="Name" value={profileName} onChange={(e) => setProfileName(e.target.value)} />
              <input className="w-full rounded bg-slate-800 px-3 py-2 text-sm" placeholder="Username" value={profileUsername} onChange={(e) => setProfileUsername(e.target.value.replace("@", ""))} />
              <input className="w-full rounded bg-slate-800 px-3 py-2 text-sm" placeholder="Bio (optional)" value={profileBio} onChange={(e) => setProfileBio(e.target.value)} />
              <input className="w-full rounded bg-slate-800 px-3 py-2 text-sm" placeholder="Avatar URL (optional)" value={profileAvatarUrl} onChange={(e) => setProfileAvatarUrl(e.target.value)} />
              <label className="flex items-center gap-2 rounded bg-slate-800 px-3 py-2 text-sm">
                <input
                  type="checkbox"
                  checked={profileAllowPrivateMessages}
                  onChange={(e) => setProfileAllowPrivateMessages(e.target.checked)}
                />
                Allow private messages
              </label>
            </div>
            {profileError ? <p className="mt-2 text-xs text-red-400">{profileError}</p> : null}
            <div className="mt-3 flex gap-2">
              <button
                className="flex-1 rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950 disabled:opacity-60"
                disabled={profileSaving}
                onClick={async () => {
                  setProfileSaving(true);
                  setProfileError(null);
                  try {
                    const updated = await updateMyProfile({
                      name: profileName.trim() || undefined,
                      username: profileUsername.trim() || undefined,
                      bio: profileBio.trim() || null,
                      avatar_url: profileAvatarUrl.trim() || null,
                      allow_private_messages: profileAllowPrivateMessages
                    });
                    useAuthStore.setState({ me: updated });
                    await loadChats();
                    setProfileOpen(false);
                  } catch {
                    setProfileError("Failed to update profile");
                  } finally {
                    setProfileSaving(false);
                  }
                }}
              >
                Save
              </button>
              <button className="flex-1 rounded bg-slate-700 px-3 py-2 text-sm" onClick={() => setProfileOpen(false)}>
                Cancel
              </button>
            </div>
          </div>
        </div>
      ) : null}
      <SettingsPanel open={settingsOpen} onClose={() => setSettingsOpen(false)} />
    </aside>
  );
}
  function getSafeContextPosition(x: number, y: number, menuWidth: number, menuHeight: number): { x: number; y: number } {
    const pad = 8;
    const cursorOffset = 4;
    const wantedX = x + cursorOffset;
    const wantedY = y + cursorOffset;
    const safeX = Math.min(Math.max(pad, wantedX), window.innerWidth - menuWidth - pad);
    const safeY = Math.min(Math.max(pad, wantedY), window.innerHeight - menuHeight - pad);
    return { x: safeX, y: safeY };
  }

function chatLabel(chat: { display_title?: string | null; title: string | null; type: "private" | "group" | "channel"; is_saved?: boolean }): string {
  if (chat.display_title?.trim()) return chat.display_title;
  if (chat.title?.trim()) return chat.title;
  if (chat.is_saved) return "Saved Messages";
  if (chat.type === "private") return "Direct chat";
  if (chat.type === "group") return "Group";
  return "Channel";
}

function chatDisplayNameById(
  chats: Array<{ id: number; display_title?: string | null; title: string | null; type: "private" | "group" | "channel"; is_saved?: boolean }>,
  chatId: number
): string {
  const chat = chats.find((item) => item.id === chatId);
  if (!chat) {
    return "Unknown chat";
  }
  return chatLabel(chat);
}

function chatMetaLabel(chat: {
  type: "private" | "group" | "channel";
  is_saved?: boolean;
  counterpart_is_online?: boolean | null;
  counterpart_last_seen_at?: string | null;
  members_count?: number | null;
  online_count?: number | null;
  subscribers_count?: number | null;
}): string {
  if (chat.is_saved) {
    return "Personal cloud chat";
  }
  if (chat.type === "private") {
    if (chat.counterpart_is_online) {
      return "online";
    }
    if (chat.counterpart_last_seen_at) {
      return `last seen ${formatLastSeen(chat.counterpart_last_seen_at)}`;
    }
    return "offline";
  }
  if (chat.type === "group") {
    const members = chat.members_count ?? 0;
    const online = chat.online_count ?? 0;
    return `${members} members, ${online} online`;
  }
  const subscribers = chat.subscribers_count ?? chat.members_count ?? 0;
  return `${subscribers} subscribers`;
}

function chatPreviewLabel(chat: {
  last_message_text?: string | null;
  last_message_type?: "text" | "image" | "video" | "audio" | "voice" | "file" | "circle_video" | null;
  type: "private" | "group" | "channel";
  is_saved?: boolean;
  counterpart_is_online?: boolean | null;
  counterpart_last_seen_at?: string | null;
  members_count?: number | null;
  online_count?: number | null;
  subscribers_count?: number | null;
}): string {
  if (chat.last_message_type && chat.last_message_type !== "text") {
    if (chat.last_message_type === "image") return "🖼 Photo";
    if (chat.last_message_type === "video") return "🎬 Video";
    if (chat.last_message_type === "audio") return "🎵 Audio";
    if (chat.last_message_type === "voice") return "🎤 Voice message";
    if (chat.last_message_type === "file") return "📎 File";
    if (chat.last_message_type === "circle_video") return "🎥 Video message";
  }
  if (chat.last_message_text?.trim()) {
    return chat.last_message_text.trim();
  }
  return chatMetaLabel(chat);
}

function formatChatListTime(value?: string | null): string {
  if (!value) {
    return "";
  }
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return "";
  }
  const now = new Date();
  const sameDay =
    date.getFullYear() === now.getFullYear() &&
    date.getMonth() === now.getMonth() &&
    date.getDate() === now.getDate();
  if (sameDay) {
    return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  }
  return date.toLocaleDateString([], { day: "2-digit", month: "2-digit" });
}

function formatLastSeen(value: string): string {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return "recently";
  }
  return date.toLocaleString(undefined, {
    day: "2-digit",
    month: "short",
    hour: "2-digit",
    minute: "2-digit"
  });
}
