import { useState, useEffect, useRef, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useSearch } from "wouter";
import {
  useListConversations,
  getListConversationsQueryKey,
  useListMessages,
  getListMessagesQueryKey,
  useSendMessage,
  useMarkConversationRead,
  useCreateConversation,
  useUpdateConversation,
  useAddConversationParticipants,
  useRemoveConversationParticipant,
  useUpdateConversationState,
  useLeaveConversation,
} from "@workspace/api-client-react";
import { useUpload, type UploadResponse } from "@workspace/object-storage-web";
import { resolveServiceImageUrl } from "@/lib/image-url";
import { useToast } from "@/hooks/use-toast";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAuth } from "@/contexts/AuthContext";
import { io } from "socket.io-client";
import {
  ArrowRight,
  ArrowLeft,
  MessageCircle,
  Plus,
  Send,
  Search,
  Users,
  User as UserIcon,
  Settings,
  UserPlus,
  Trash2,
  MoreVertical,
  Bell,
  BellOff,
  Archive,
  ArchiveRestore,
  LogOut,
  Crown,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { formatTime } from "@/lib/i18n-format";
import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Camera, Loader2, X } from "lucide-react";

const BASE = import.meta.env.BASE_URL.replace(/\/$/, "");

type UserDisplay = {
  id: number;
  username: string;
  displayNameAr?: string | null;
  displayNameEn?: string | null;
};

function pickDisplayName(u: UserDisplay | null | undefined, lang: string): string {
  if (!u) return "";
  return (lang === "ar" ? u.displayNameAr : u.displayNameEn) ?? u.username;
}

type SystemMessageMeta = {
  actor?: UserDisplay | null;
  oldNameAr?: string | null;
  oldNameEn?: string | null;
  newNameAr?: string | null;
  newNameEn?: string | null;
  addedUsers?: UserDisplay[];
  removedUser?: UserDisplay | null;
  promoted?: UserDisplay | null;
  reason?: string | null;
};

type SystemRenderable = {
  kind?: string | null;
  meta?: unknown;
};

function renderSystemMessage(
  msg: SystemRenderable,
  lang: string,
  t: (k: string, opts?: Record<string, unknown>) => string,
): string {
  const meta = (msg.meta ?? {}) as SystemMessageMeta;
  const actor = pickDisplayName(meta.actor, lang) || t("chat.system.someone");
  switch (msg.kind) {
    case "group_renamed": {
      const newName =
        (lang === "ar" ? meta.newNameAr : meta.newNameEn) ??
        (lang === "ar" ? meta.newNameEn : meta.newNameAr) ??
        "";
      return t("chat.system.groupRenamed", { actor, name: newName });
    }
    case "members_added": {
      const names = (meta.addedUsers ?? [])
        .map((u) => pickDisplayName(u, lang))
        .filter(Boolean);
      return t("chat.system.membersAdded", {
        actor,
        names: names.join(t("chat.system.listSeparator")),
        count: names.length,
      });
    }
    case "member_removed": {
      const target = pickDisplayName(meta.removedUser, lang) || t("chat.system.someone");
      return t("chat.system.memberRemoved", { actor, target });
    }
    case "member_left": {
      return t("chat.system.memberLeft", { actor });
    }
    case "admin_promoted": {
      const target = pickDisplayName(meta.promoted, lang) || t("chat.system.someone");
      const key =
        meta.reason === "previous_admin_left"
          ? "chat.system.adminPromotedAfterLeave"
          : "chat.system.adminPromoted";
      return t(key, { target });
    }
    default:
      return "";
  }
}

function getSocket() {
  return io(window.location.origin, {
    path: `${BASE}/api/socket.io`,
  });
}

type ConvMode = "direct" | "group";

export default function ChatPage() {
  const { t, i18n } = useTranslation();
  const [, setLocation] = useLocation();
  const search = useSearch();
  const { user } = useAuth();
  const lang = i18n.language;
  const isRtl = lang === "ar";
  const queryClient = useQueryClient();
  const BackIcon = isRtl ? ArrowRight : ArrowLeft;

  const [selectedConvId, setSelectedConvId] = useState<number | null>(null);
  const [messageText, setMessageText] = useState("");
  const [showNewConv, setShowNewConv] = useState(false);
  const [mode, setMode] = useState<ConvMode>("direct");
  const [selectedUserIds, setSelectedUserIds] = useState<number[]>([]);
  const [groupNameAr, setGroupNameAr] = useState("");
  const [groupNameEn, setGroupNameEn] = useState("");
  const [groupAvatarUrl, setGroupAvatarUrl] = useState<string | null>(null);
  const [userSearch, setUserSearch] = useState("");
  const [showSettings, setShowSettings] = useState(false);
  const [showAddMembers, setShowAddMembers] = useState(false);
  const [showActions, setShowActions] = useState(false);
  const [showArchived, setShowArchived] = useState(false);
  const [confirmLeave, setConfirmLeave] = useState(false);
  const [successorChoice, setSuccessorChoice] = useState<number | "auto">("auto");
  const [editNameAr, setEditNameAr] = useState("");
  const [editNameEn, setEditNameEn] = useState("");
  const [addMembersIds, setAddMembersIds] = useState<number[]>([]);
  const [addMembersSearch, setAddMembersSearch] = useState("");
  const bottomRef = useRef<HTMLDivElement>(null);
  const { toast } = useToast();

  const { uploadFile: uploadGroupAvatar, isUploading: isUploadingNewAvatar, progress: newAvatarProgress } = useUpload({
    onSuccess: (resp: UploadResponse) => setGroupAvatarUrl(resp.objectPath),
    onError: (err: Error) =>
      toast({ title: t("chat.avatarUploadFailed"), description: err.message, variant: "destructive" }),
  });

  const { data: conversations, isLoading: convsLoading } = useListConversations({
    query: { queryKey: getListConversationsQueryKey() },
  });

  const { data: messages } = useListMessages(
    selectedConvId!,
    { query: { queryKey: getListMessagesQueryKey(selectedConvId!), enabled: !!selectedConvId } },
  );

  type DirectoryUser = {
    id: number;
    username: string;
    displayNameAr: string | null;
    displayNameEn: string | null;
    avatarUrl: string | null;
    isActive: boolean;
  };
  const { data: users } = useQuery<DirectoryUser[]>({
    queryKey: ["users-directory"],
    queryFn: () =>
      fetch("/api/users/directory", { credentials: "include" }).then((r) => r.json()),
    enabled: showNewConv || showAddMembers,
  });

  const sendMessage = useSendMessage();
  const markRead = useMarkConversationRead();
  const createConv = useCreateConversation();

  // Socket.IO
  useEffect(() => {
    if (!user) return;
    const socket = getSocket();

    if (selectedConvId) {
      socket.emit("join_conversation", selectedConvId);
    }

    socket.on("new_message", () => {
      queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
      if (selectedConvId) {
        queryClient.invalidateQueries({ queryKey: getListMessagesQueryKey(selectedConvId) });
      }
    });

    socket.on("messages_read", () => {
      queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
      if (selectedConvId) {
        queryClient.invalidateQueries({ queryKey: getListMessagesQueryKey(selectedConvId) });
      }
    });

    socket.on("conversation_updated", () => {
      queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
    });

    return () => {
      if (selectedConvId) {
        socket.emit("leave_conversation", selectedConvId);
      }
      socket.disconnect();
    };
  }, [user, selectedConvId, queryClient]);

  // Open conversation specified via ?c=<id> query param (e.g. from notifications)
  useEffect(() => {
    const params = new URLSearchParams(search);
    const convParam = params.get("c");
    if (!convParam) return;
    const convId = Number(convParam);
    if (!Number.isFinite(convId) || convId <= 0) return;
    setSelectedConvId(convId);
    setLocation("/chat", { replace: true });
  }, [search, setLocation]);

  // Notify the current user when they have been promoted to admin of a group
  // (e.g. after the previous admin left). We track per-user the set of group
  // conversation IDs we have already observed the user being admin of; when a
  // new one appears we toast. Using participant.isAdmin rather than only the
  // lastMessage means the cue still fires reliably even if newer messages
  // arrive in the conversation before the user next opens chat.
  useEffect(() => {
    if (!user || !conversations) return;
    const ackKey = `tx:admin-known:${user.id}`;
    let ack: number[] = [];
    try {
      ack = JSON.parse(localStorage.getItem(ackKey) ?? "[]");
      if (!Array.isArray(ack)) ack = [];
    } catch {
      ack = [];
    }
    const known = new Set<number>(ack);

    // Conversations where the current user is currently an admin.
    const currentAdminGroupIds = new Set<number>();
    for (const conv of conversations) {
      if (!conv.isGroup) continue;
      const me = conv.participants?.find((p) => p.id === user.id);
      if (me?.isAdmin) currentAdminGroupIds.add(conv.id);
    }

    // First load for this user/browser: just record the baseline silently
    // so we don't spam toasts about pre-existing admin roles.
    if (known.size === 0 && localStorage.getItem(ackKey) === null) {
      try {
        localStorage.setItem(
          ackKey,
          JSON.stringify(Array.from(currentAdminGroupIds)),
        );
      } catch {
        // ignore storage errors
      }
      return;
    }

    let changed = false;
    for (const convId of currentAdminGroupIds) {
      if (known.has(convId)) continue;
      const conv = conversations.find((c) => c.id === convId);
      if (!conv) continue;
      const convDisplay = convDisplayName(conv);
      toast({
        title: t("chat.actions.youAreAdminTitle"),
        description: t("chat.actions.youAreAdminDescription", {
          name: convDisplay,
        }),
      });
      known.add(convId);
      changed = true;
    }
    // Forget conversations the user is no longer an admin of (or has left)
    // so a future re-promotion will toast again.
    for (const convId of Array.from(known)) {
      if (!currentAdminGroupIds.has(convId)) {
        known.delete(convId);
        changed = true;
      }
    }
    if (changed) {
      try {
        localStorage.setItem(ackKey, JSON.stringify(Array.from(known)));
      } catch {
        // ignore storage errors
      }
    }
  }, [conversations, user, toast, t, lang]);

  // Mark conversation as read when selected
  useEffect(() => {
    if (selectedConvId) {
      markRead.mutate(
        { id: selectedConvId },
        {
          onSuccess: () => {
            queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          },
        },
      );
    }
  }, [selectedConvId]);

  // Scroll to bottom on new messages
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const handleSend = () => {
    if (!messageText.trim() || !selectedConvId) return;
    sendMessage.mutate(
      { id: selectedConvId, data: { content: messageText.trim() } },
      {
        onSuccess: () => {
          setMessageText("");
          queryClient.invalidateQueries({ queryKey: getListMessagesQueryKey(selectedConvId!) });
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
        },
      },
    );
  };

  const resetNewConvState = () => {
    setMode("direct");
    setSelectedUserIds([]);
    setGroupNameAr("");
    setGroupNameEn("");
    setGroupAvatarUrl(null);
    setUserSearch("");
  };

  const openNewConv = () => {
    resetNewConvState();
    setShowNewConv(true);
  };

  const closeNewConv = () => {
    setShowNewConv(false);
    resetNewConvState();
  };

  const switchMode = (next: ConvMode) => {
    setMode(next);
    if (next === "direct") {
      if (selectedUserIds.length > 1) {
        setSelectedUserIds(selectedUserIds.slice(0, 1));
      }
      setGroupNameAr("");
      setGroupNameEn("");
    }
  };

  const toggleUser = (uid: number) => {
    if (mode === "direct") {
      setSelectedUserIds(selectedUserIds.includes(uid) ? [] : [uid]);
    } else {
      setSelectedUserIds(
        selectedUserIds.includes(uid)
          ? selectedUserIds.filter((id) => id !== uid)
          : [...selectedUserIds, uid],
      );
    }
  };

  const filteredUsers = useMemo(() => {
    const list = (users ?? []).filter((u) => u.id !== user?.id);
    const q = userSearch.trim().toLowerCase();
    if (!q) return list;
    return list.filter((u) => {
      const fields = [u.username, u.displayNameAr ?? "", u.displayNameEn ?? ""];
      return fields.some((f) => f.toLowerCase().includes(q));
    });
  }, [users, userSearch, user?.id]);

  const validation: { ok: boolean; message: string | null } = useMemo(() => {
    if (mode === "direct") {
      if (selectedUserIds.length !== 1) {
        return { ok: false, message: t("chat.validation.pickOnePerson") };
      }
      return { ok: true, message: null };
    }
    if (selectedUserIds.length < 2) {
      return { ok: false, message: t("chat.validation.minTwoMembers") };
    }
    if (!groupNameAr.trim() && !groupNameEn.trim()) {
      return { ok: false, message: t("chat.validation.needGroupName") };
    }
    return { ok: true, message: null };
  }, [mode, selectedUserIds, groupNameAr, groupNameEn, t]);

  const handleCreateConv = () => {
    if (!validation.ok) return;
    const isGroup = mode === "group";
    createConv.mutate(
      {
        data: {
          participantIds: selectedUserIds,
          isGroup,
          nameAr: isGroup ? (groupNameAr.trim() || null) : null,
          nameEn: isGroup ? (groupNameEn.trim() || null) : null,
          avatarUrl: isGroup ? groupAvatarUrl : null,
        },
      },
      {
        onSuccess: (conv) => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          setSelectedConvId(conv.id);
          closeNewConv();
        },
      },
    );
  };

  const selectedConv = conversations?.find((c) => c.id === selectedConvId);
  const convDisplayName = (
    conv: NonNullable<typeof selectedConv>,
  ): string => {
    const langName = lang === "ar" ? conv.nameAr : conv.nameEn;
    const otherName = lang === "ar" ? conv.nameEn : conv.nameAr;
    if (langName) return langName;
    if (otherName) return otherName;
    const others = conv.participants
      ?.filter((p) => p.id !== user?.id)
      .map((p) => (lang === "ar" ? p.displayNameAr : p.displayNameEn) ?? p.username);
    if (others?.length) return others.join(", ");
    return t("chat.directMessage");
  };

  const convName = selectedConv ? convDisplayName(selectedConv) : "";
  const isAdminOfSelected = !!selectedConv?.participants?.find(
    (p) => p.id === user?.id && p.isAdmin,
  );

  const updateConv = useUpdateConversation();
  const addParticipants = useAddConversationParticipants();
  const removeParticipant = useRemoveConversationParticipant();
  const updateConvState = useUpdateConversationState();
  const leaveConv = useLeaveConversation();

  const handleToggleMute = () => {
    if (!selectedConv) return;
    const next = !selectedConv.isMuted;
    updateConvState.mutate(
      { id: selectedConv.id, data: { isMuted: next } },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          toast({ title: t(next ? "chat.actions.muted" : "chat.actions.unmuted") });
        },
        onError: (err: Error) =>
          toast({ title: t("chat.actions.actionFailed"), description: err.message, variant: "destructive" }),
      },
    );
  };

  const handleToggleArchive = () => {
    if (!selectedConv) return;
    const next = !selectedConv.isArchived;
    updateConvState.mutate(
      { id: selectedConv.id, data: { isArchived: next } },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          toast({ title: t(next ? "chat.actions.archived" : "chat.actions.unarchived") });
          if (next) {
            setShowActions(false);
            setSelectedConvId(null);
          }
        },
        onError: (err: Error) =>
          toast({ title: t("chat.actions.actionFailed"), description: err.message, variant: "destructive" }),
      },
    );
  };

  const handleLeave = () => {
    if (!selectedConv) return;
    const data =
      successorChoice === "auto"
        ? {}
        : { successorId: successorChoice };
    leaveConv.mutate(
      { id: selectedConv.id, data },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          toast({ title: t("chat.actions.left") });
          setConfirmLeave(false);
          setShowActions(false);
          setSelectedConvId(null);
          setSuccessorChoice("auto");
        },
        onError: (err: Error) =>
          toast({ title: t("chat.actions.actionFailed"), description: err.message, variant: "destructive" }),
      },
    );
  };

  const openSettings = () => {
    if (!selectedConv) return;
    setEditNameAr(selectedConv.nameAr ?? "");
    setEditNameEn(selectedConv.nameEn ?? "");
    setShowSettings(true);
  };

  const closeSettings = () => {
    setShowSettings(false);
    setShowAddMembers(false);
    setAddMembersIds([]);
    setAddMembersSearch("");
  };

  const handleSaveName = () => {
    if (!selectedConvId) return;
    const ar = editNameAr.trim();
    const en = editNameEn.trim();
    if (!ar && !en) {
      toast({
        title: t("chat.settings.needGroupName"),
        variant: "destructive",
      });
      return;
    }
    updateConv.mutate(
      { id: selectedConvId, data: { nameAr: ar || null, nameEn: en || null } },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          toast({ title: t("chat.settings.nameSaved") });
        },
        onError: (err: Error) => {
          toast({
            title: t("chat.settings.saveFailed"),
            description: err.message,
            variant: "destructive",
          });
        },
      },
    );
  };

  const handleAddMembers = () => {
    if (!selectedConvId || addMembersIds.length === 0) return;
    addParticipants.mutate(
      { id: selectedConvId, data: { userIds: addMembersIds } },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          setAddMembersIds([]);
          setAddMembersSearch("");
          setShowAddMembers(false);
          toast({ title: t("chat.settings.membersAdded") });
        },
        onError: (err: Error) => {
          toast({
            title: t("chat.settings.addFailed"),
            description: err.message,
            variant: "destructive",
          });
        },
      },
    );
  };

  const handleRemoveMember = (memberId: number) => {
    if (!selectedConvId) return;
    removeParticipant.mutate(
      { id: selectedConvId, userId: memberId },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
        },
        onError: (err: Error) => {
          toast({
            title: t("chat.settings.removeFailed"),
            description: err.message,
            variant: "destructive",
          });
        },
      },
    );
  };

  const existingMemberIds = useMemo(
    () => new Set(selectedConv?.participants?.map((p) => p.id) ?? []),
    [selectedConv],
  );
  const addableUsers = useMemo(() => {
    const list = (users ?? []).filter((u) => !existingMemberIds.has(u.id));
    const q = addMembersSearch.trim().toLowerCase();
    if (!q) return list;
    return list.filter((u) => {
      const fields = [u.username, u.displayNameAr ?? "", u.displayNameEn ?? ""];
      return fields.some((f) => f.toLowerCase().includes(q));
    });
  }, [users, existingMemberIds, addMembersSearch]);

  const toggleAddMember = (uid: number) => {
    setAddMembersIds((prev) =>
      prev.includes(uid) ? prev.filter((id) => id !== uid) : [...prev, uid],
    );
  };

  const nameDirty =
    !!selectedConv &&
    selectedConv.isGroup &&
    (editNameAr.trim() !== (selectedConv.nameAr ?? "") ||
      editNameEn.trim() !== (selectedConv.nameEn ?? ""));
  const { uploadFile: uploadHeaderAvatar, isUploading: isUploadingHeaderAvatar } = useUpload({
    onSuccess: (resp: UploadResponse) => {
      if (!selectedConvId) return;
      updateConv.mutate(
        { id: selectedConvId, data: { avatarUrl: resp.objectPath } },
        {
          onSuccess: () => {
            queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
          },
          onError: (err: Error) => {
            toast({ title: t("chat.avatarUploadFailed"), description: err.message, variant: "destructive" });
          },
        },
      );
    },
    onError: (err: Error) =>
      toast({ title: t("chat.avatarUploadFailed"), description: err.message, variant: "destructive" }),
  });

  return (
    <div className="min-h-screen os-bg flex flex-col">
      {/* Header */}
      <div className="glass-panel border-b border-slate-200/70 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
        <button
          onClick={() => (selectedConvId ? setSelectedConvId(null) : setLocation("/"))}
          className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
        >
          <BackIcon size={20} />
        </button>
        <div className="flex items-center gap-2 flex-1 min-w-0">
          {selectedConvId && selectedConv ? (
            <div className="relative shrink-0">
              <Avatar className="w-9 h-9">
                {selectedConv.avatarUrl && (
                  <AvatarImage
                    src={resolveServiceImageUrl(selectedConv.avatarUrl) ?? ""}
                    alt=""
                    className="object-cover"
                  />
                )}
                <AvatarFallback
                  className={`font-medium ${
                    selectedConv.isGroup
                      ? "bg-primary/15 text-primary"
                      : "bg-primary/20 text-primary"
                  }`}
                >
                  {selectedConv.isGroup ? (
                    <Users size={14} />
                  ) : (
                    String(convName).slice(0, 2).toUpperCase()
                  )}
                </AvatarFallback>
              </Avatar>
              {selectedConv.isGroup && isAdminOfSelected && (
                <label className="cursor-pointer absolute -bottom-1 -end-1 w-5 h-5 rounded-full bg-white border border-slate-200 shadow-sm flex items-center justify-center text-slate-600 hover:text-primary">
                  <input
                    type="file"
                    accept="image/*"
                    disabled={isUploadingHeaderAvatar || updateConv.isPending}
                    className="hidden"
                    onChange={(e) => {
                      const file = e.target.files?.[0];
                      if (file) void uploadHeaderAvatar(file);
                      e.currentTarget.value = "";
                    }}
                    aria-label={t("chat.changeAvatar")}
                  />
                  {isUploadingHeaderAvatar || updateConv.isPending ? (
                    <Loader2 size={10} className="animate-spin" />
                  ) : (
                    <Camera size={10} />
                  )}
                </label>
              )}
              {selectedConv.isGroup && isAdminOfSelected && selectedConv.avatarUrl && (
                <button
                  type="button"
                  onClick={() => {
                    if (!selectedConvId) return;
                    updateConv.mutate(
                      { id: selectedConvId, data: { avatarUrl: null } },
                      {
                        onSuccess: () => {
                          queryClient.invalidateQueries({ queryKey: getListConversationsQueryKey() });
                        },
                        onError: (err: Error) => {
                          toast({ title: t("chat.settings.saveFailed"), description: err.message, variant: "destructive" });
                        },
                      },
                    );
                  }}
                  disabled={isUploadingHeaderAvatar || updateConv.isPending}
                  className="absolute -top-1 -end-1 w-5 h-5 rounded-full bg-white border border-slate-200 shadow-sm flex items-center justify-center text-slate-600 hover:text-destructive disabled:opacity-50"
                  aria-label={t("chat.removeAvatar")}
                  title={t("chat.removeAvatar")}
                  data-testid="button-remove-group-avatar"
                >
                  <X size={10} />
                </button>
              )}
            </div>
          ) : (
            <MessageCircle size={20} className="text-green-400 shrink-0" />
          )}
          {selectedConvId && selectedConv?.isGroup ? (
            <button
              onClick={openSettings}
              className="flex items-center gap-2 min-w-0 text-start hover:opacity-80 transition-opacity"
              aria-label={t("chat.settings.openSettings")}
            >
              <h1 className="text-lg font-semibold text-foreground truncate">{convName}</h1>
              <Badge className="bg-primary/15 text-primary border-primary/30 text-[10px] shrink-0">
                {t("chat.groupChat")}
              </Badge>
            </button>
          ) : (
            <h1 className="text-lg font-semibold text-foreground truncate">
              {selectedConvId ? convName : t("chat.title")}
            </h1>
          )}
        </div>
        {selectedConvId && selectedConv?.isGroup && (
          <button
            onClick={openSettings}
            className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
            aria-label={t("chat.settings.openSettings")}
          >
            <Settings size={20} />
          </button>
        )}
        {selectedConvId && selectedConv && (
          <button
            onClick={() => setShowActions(true)}
            className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
            aria-label={t("chat.actions.openActions")}
            data-testid="button-chat-actions"
          >
            <MoreVertical size={20} />
          </button>
        )}
        {!selectedConvId && (
          <button
            onClick={openNewConv}
            className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
            aria-label={t("chat.createConversation")}
          >
            <Plus size={20} />
          </button>
        )}
      </div>

      {/* New Conversation Dialog */}
      {showNewConv && (
        <div
          className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4"
          onClick={closeNewConv}
        >
          <div
            className="glass-panel rounded-3xl p-6 w-full max-w-md space-y-4"
            onClick={(e) => e.stopPropagation()}
          >
            <h2 className="font-semibold text-foreground text-lg">
              {t("chat.createConversation")}
            </h2>

            {/* Mode tabs */}
            <div
              role="tablist"
              className="grid grid-cols-2 gap-1 p-1 rounded-2xl bg-slate-100/70"
            >
              <button
                type="button"
                role="tab"
                aria-selected={mode === "direct"}
                onClick={() => switchMode("direct")}
                className={`flex items-center justify-center gap-2 py-2 rounded-xl text-sm font-medium transition-colors ${
                  mode === "direct"
                    ? "bg-white text-foreground shadow-sm"
                    : "text-muted-foreground hover:text-foreground"
                }`}
              >
                <UserIcon size={16} />
                {t("chat.modeDirect")}
              </button>
              <button
                type="button"
                role="tab"
                aria-selected={mode === "group"}
                onClick={() => switchMode("group")}
                className={`flex items-center justify-center gap-2 py-2 rounded-xl text-sm font-medium transition-colors ${
                  mode === "group"
                    ? "bg-white text-foreground shadow-sm"
                    : "text-muted-foreground hover:text-foreground"
                }`}
              >
                <Users size={16} />
                {t("chat.modeGroup")}
              </button>
            </div>

            {/* Group name fields */}
            {mode === "group" && (
              <div className="space-y-2">
                <div className="flex items-center gap-3">
                  <div className="relative">
                    <Avatar className="w-16 h-16 border border-slate-200">
                      {groupAvatarUrl && (
                        <AvatarImage
                          src={resolveServiceImageUrl(groupAvatarUrl) ?? ""}
                          alt=""
                          className="object-cover"
                        />
                      )}
                      <AvatarFallback className="bg-primary/15 text-primary">
                        <Users size={22} />
                      </AvatarFallback>
                    </Avatar>
                    {groupAvatarUrl && !isUploadingNewAvatar && (
                      <button
                        type="button"
                        onClick={() => setGroupAvatarUrl(null)}
                        aria-label={t("chat.removeAvatar")}
                        className="absolute -top-1 -end-1 w-5 h-5 rounded-full bg-white border border-slate-200 text-slate-500 hover:text-destructive shadow-sm flex items-center justify-center"
                      >
                        <X size={12} />
                      </button>
                    )}
                  </div>
                  <label className="cursor-pointer">
                    <input
                      type="file"
                      accept="image/*"
                      disabled={isUploadingNewAvatar}
                      className="hidden"
                      onChange={(e) => {
                        const file = e.target.files?.[0];
                        if (file) void uploadGroupAvatar(file);
                        e.currentTarget.value = "";
                      }}
                    />
                    <span className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-slate-200 bg-white text-sm text-slate-700 hover:bg-slate-50 transition">
                      {isUploadingNewAvatar ? (
                        <>
                          <Loader2 size={14} className="animate-spin" />
                          {t("chat.uploadingAvatar", { progress: newAvatarProgress })}
                        </>
                      ) : (
                        <>
                          <Camera size={14} />
                          {groupAvatarUrl ? t("chat.replaceAvatar") : t("chat.uploadAvatar")}
                        </>
                      )}
                    </span>
                  </label>
                </div>
                <Input
                  value={groupNameAr}
                  onChange={(e) => setGroupNameAr(e.target.value)}
                  placeholder={t("chat.groupNamePlaceholderAr")}
                  aria-label={t("chat.groupNameAr")}
                  dir="rtl"
                  className="bg-white/70 border-slate-200"
                />
                <Input
                  value={groupNameEn}
                  onChange={(e) => setGroupNameEn(e.target.value)}
                  placeholder={t("chat.groupNamePlaceholderEn")}
                  aria-label={t("chat.groupNameEn")}
                  dir="ltr"
                  className="bg-white/70 border-slate-200"
                />
              </div>
            )}

            {/* Search */}
            <div className="relative">
              <Search
                size={16}
                className={`absolute top-1/2 -translate-y-1/2 text-muted-foreground ${
                  isRtl ? "right-3" : "left-3"
                }`}
              />
              <Input
                value={userSearch}
                onChange={(e) => setUserSearch(e.target.value)}
                placeholder={t("chat.searchUsersPlaceholder")}
                className={`bg-white/70 border-slate-200 ${isRtl ? "pr-9" : "pl-9"}`}
              />
            </div>

            {/* Counter */}
            <div className="flex items-center justify-between text-xs">
              <span className="text-muted-foreground">
                {t("chat.participantsCount", { count: selectedUserIds.length })}
              </span>
            </div>

            {/* User list */}
            <ScrollArea className="h-56 -mx-2 px-2">
              <div className="space-y-1">
                {filteredUsers.length === 0 ? (
                  <div className="text-center text-sm text-muted-foreground py-8">
                    {t("chat.noUsersFound")}
                  </div>
                ) : (
                  filteredUsers.map((u) => {
                    const checked = selectedUserIds.includes(u.id);
                    const display =
                      (lang === "ar" ? u.displayNameAr : u.displayNameEn) ?? u.username;
                    return (
                      <button
                        type="button"
                        key={u.id}
                        onClick={() => toggleUser(u.id)}
                        className={`w-full flex items-center gap-3 p-2 rounded-xl text-start transition-colors ${
                          checked
                            ? "bg-primary/10 hover:bg-primary/15"
                            : "hover:bg-slate-100"
                        }`}
                      >
                        <span
                          className={`w-5 h-5 shrink-0 rounded-md border-2 flex items-center justify-center transition-colors ${
                            checked
                              ? "bg-primary border-primary text-primary-foreground"
                              : "border-slate-300"
                          }`}
                        >
                          {checked && (
                            <svg
                              viewBox="0 0 16 16"
                              className="w-3 h-3"
                              fill="none"
                              stroke="currentColor"
                              strokeWidth="3"
                            >
                              <path d="M3 8l3 3 7-7" />
                            </svg>
                          )}
                        </span>
                        <Avatar className="w-8 h-8">
                          <AvatarFallback className="text-xs bg-primary/20 text-primary">
                            {display.slice(0, 2).toUpperCase()}
                          </AvatarFallback>
                        </Avatar>
                        <div className="flex-1 min-w-0 text-start">
                          <div className="text-sm font-medium text-foreground truncate">
                            {display}
                          </div>
                          <div className="text-xs text-muted-foreground truncate">
                            @{u.username}
                          </div>
                        </div>
                      </button>
                    );
                  })
                )}
              </div>
            </ScrollArea>

            {/* Validation message */}
            {validation.message && (
              <p className="text-xs text-amber-600 dark:text-amber-500">
                {validation.message}
              </p>
            )}

            {/* Actions */}
            <div className="flex gap-2">
              <Button variant="outline" className="flex-1" onClick={closeNewConv}>
                {t("common.cancel")}
              </Button>
              <Button
                className="flex-1"
                onClick={handleCreateConv}
                disabled={!validation.ok || createConv.isPending}
              >
                {t("chat.create")}
              </Button>
            </div>
          </div>
        </div>
      )}

      {/* Group Settings Dialog */}
      {showSettings && selectedConv?.isGroup && (
        <div
          className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4"
          onClick={closeSettings}
        >
          <div
            className="glass-panel rounded-3xl p-6 w-full max-w-md space-y-4 max-h-[90vh] overflow-y-auto"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="flex items-center justify-between">
              <h2 className="font-semibold text-foreground text-lg">
                {t("chat.settings.title")}
              </h2>
              <button
                onClick={closeSettings}
                className="p-1 rounded-lg hover:bg-slate-100 text-muted-foreground"
                aria-label={t("common.close")}
              >
                <X size={18} />
              </button>
            </div>

            {/* Name editing (admin only) */}
            {isAdminOfSelected ? (
              <div className="space-y-2">
                <label className="text-xs font-medium text-muted-foreground block">
                  {t("chat.settings.groupName")}
                </label>
                <Input
                  value={editNameAr}
                  onChange={(e) => setEditNameAr(e.target.value)}
                  placeholder={t("chat.groupNamePlaceholderAr")}
                  aria-label={t("chat.groupNameAr")}
                  dir="rtl"
                  className="bg-white/70 border-slate-200"
                />
                <Input
                  value={editNameEn}
                  onChange={(e) => setEditNameEn(e.target.value)}
                  placeholder={t("chat.groupNamePlaceholderEn")}
                  aria-label={t("chat.groupNameEn")}
                  dir="ltr"
                  className="bg-white/70 border-slate-200"
                />
                <Button
                  size="sm"
                  onClick={handleSaveName}
                  disabled={!nameDirty || updateConv.isPending}
                  className="w-full"
                >
                  {updateConv.isPending ? (
                    <Loader2 size={14} className="animate-spin" />
                  ) : (
                    t("chat.settings.saveName")
                  )}
                </Button>
              </div>
            ) : (
              <div className="text-sm text-muted-foreground">
                {t("chat.settings.viewOnly")}
              </div>
            )}

            {/* Members list */}
            <div className="space-y-2">
              <div className="flex items-center justify-between">
                <label className="text-xs font-medium text-muted-foreground">
                  {t("chat.settings.members", {
                    count: selectedConv.participants?.length ?? 0,
                  })}
                </label>
                {isAdminOfSelected && (
                  <button
                    onClick={() => setShowAddMembers(true)}
                    className="inline-flex items-center gap-1 text-xs text-primary hover:underline"
                  >
                    <UserPlus size={14} />
                    {t("chat.settings.addMembers")}
                  </button>
                )}
              </div>
              <div className="space-y-1 max-h-64 overflow-y-auto">
                {selectedConv.participants?.map((p) => {
                  const display =
                    (lang === "ar" ? p.displayNameAr : p.displayNameEn) ?? p.username;
                  const isMe = p.id === user?.id;
                  return (
                    <div
                      key={p.id}
                      className="flex items-center gap-3 p-2 rounded-xl hover:bg-slate-50"
                    >
                      <Avatar className="w-8 h-8">
                        {p.avatarUrl && (
                          <AvatarImage
                            src={resolveServiceImageUrl(p.avatarUrl) ?? ""}
                            alt=""
                            className="object-cover"
                          />
                        )}
                        <AvatarFallback className="text-xs bg-primary/20 text-primary">
                          {display.slice(0, 2).toUpperCase()}
                        </AvatarFallback>
                      </Avatar>
                      <div className="flex-1 min-w-0">
                        <div className="text-sm font-medium text-foreground truncate flex items-center gap-2">
                          {display}
                          {p.isAdmin && (
                            <Badge className="bg-primary/15 text-primary border-primary/30 text-[10px]">
                              {t("chat.settings.admin")}
                            </Badge>
                          )}
                          {isMe && (
                            <span className="text-xs text-muted-foreground">
                              {t("chat.settings.you")}
                            </span>
                          )}
                        </div>
                        <div className="text-xs text-muted-foreground truncate">
                          @{p.username}
                        </div>
                      </div>
                      {isAdminOfSelected && !isMe && (
                        <button
                          onClick={() => handleRemoveMember(p.id)}
                          disabled={removeParticipant.isPending}
                          className="p-2 rounded-lg text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
                          aria-label={t("chat.settings.removeMember")}
                        >
                          <Trash2 size={14} />
                        </button>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Add Members Dialog */}
      {showAddMembers && selectedConv?.isGroup && isAdminOfSelected && (
        <div
          className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-[60] flex items-center justify-center p-4"
          onClick={() => setShowAddMembers(false)}
        >
          <div
            className="glass-panel rounded-3xl p-6 w-full max-w-md space-y-4 max-h-[90vh] overflow-y-auto"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="flex items-center justify-between">
              <h2 className="font-semibold text-foreground text-lg">
                {t("chat.settings.addMembers")}
              </h2>
              <button
                onClick={() => setShowAddMembers(false)}
                className="p-1 rounded-lg hover:bg-slate-100 text-muted-foreground"
                aria-label={t("common.close")}
              >
                <X size={18} />
              </button>
            </div>

            <div className="relative">
              <Search
                size={16}
                className={`absolute top-1/2 -translate-y-1/2 text-muted-foreground ${
                  isRtl ? "right-3" : "left-3"
                }`}
              />
              <Input
                value={addMembersSearch}
                onChange={(e) => setAddMembersSearch(e.target.value)}
                placeholder={t("chat.searchUsersPlaceholder")}
                className={`bg-white/70 border-slate-200 ${isRtl ? "pr-9" : "pl-9"}`}
              />
            </div>

            <div className="text-xs text-muted-foreground">
              {t("chat.participantsCount", { count: addMembersIds.length })}
            </div>

            <ScrollArea className="h-64 -mx-2 px-2">
              <div className="space-y-1">
                {addableUsers.length === 0 ? (
                  <div className="text-center text-sm text-muted-foreground py-8">
                    {t("chat.noUsersFound")}
                  </div>
                ) : (
                  addableUsers.map((u) => {
                    const checked = addMembersIds.includes(u.id);
                    const display =
                      (lang === "ar" ? u.displayNameAr : u.displayNameEn) ?? u.username;
                    return (
                      <button
                        type="button"
                        key={u.id}
                        onClick={() => toggleAddMember(u.id)}
                        className={`w-full flex items-center gap-3 p-2 rounded-xl text-start transition-colors ${
                          checked
                            ? "bg-primary/10 hover:bg-primary/15"
                            : "hover:bg-slate-100"
                        }`}
                      >
                        <span
                          className={`w-5 h-5 shrink-0 rounded-md border-2 flex items-center justify-center transition-colors ${
                            checked
                              ? "bg-primary border-primary text-primary-foreground"
                              : "border-slate-300"
                          }`}
                        >
                          {checked && (
                            <svg
                              viewBox="0 0 16 16"
                              className="w-3 h-3"
                              fill="none"
                              stroke="currentColor"
                              strokeWidth="3"
                            >
                              <path d="M3 8l3 3 7-7" />
                            </svg>
                          )}
                        </span>
                        <Avatar className="w-8 h-8">
                          <AvatarFallback className="text-xs bg-primary/20 text-primary">
                            {display.slice(0, 2).toUpperCase()}
                          </AvatarFallback>
                        </Avatar>
                        <div className="flex-1 min-w-0 text-start">
                          <div className="text-sm font-medium text-foreground truncate">
                            {display}
                          </div>
                          <div className="text-xs text-muted-foreground truncate">
                            @{u.username}
                          </div>
                        </div>
                      </button>
                    );
                  })
                )}
              </div>
            </ScrollArea>

            <div className="flex gap-2">
              <Button
                variant="outline"
                className="flex-1"
                onClick={() => setShowAddMembers(false)}
              >
                {t("common.cancel")}
              </Button>
              <Button
                className="flex-1"
                onClick={handleAddMembers}
                disabled={addMembersIds.length === 0 || addParticipants.isPending}
              >
                {addParticipants.isPending ? (
                  <Loader2 size={14} className="animate-spin" />
                ) : (
                  t("chat.settings.addSelected")
                )}
              </Button>
            </div>
          </div>
        </div>
      )}

      {/* Actions sheet (mute / archive / leave) */}
      {showActions && selectedConv && (
        <div
          className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-[55] flex items-end sm:items-center justify-center p-4"
          onClick={() => setShowActions(false)}
        >
          <div
            className="glass-panel rounded-3xl p-4 w-full max-w-sm space-y-1"
            onClick={(e) => e.stopPropagation()}
            data-testid="dialog-chat-actions"
          >
            <div className="flex items-center justify-between px-2 py-2">
              <h2 className="font-semibold text-foreground text-base">
                {t("chat.actions.title")}
              </h2>
              <button
                onClick={() => setShowActions(false)}
                className="p-1 rounded-lg hover:bg-slate-100 text-muted-foreground"
                aria-label={t("common.close")}
              >
                <X size={18} />
              </button>
            </div>
            <button
              onClick={handleToggleMute}
              disabled={updateConvState.isPending}
              className="w-full flex items-center gap-3 px-3 py-3 rounded-xl hover:bg-slate-100 text-start text-sm text-foreground disabled:opacity-50 transition-colors"
              data-testid="button-toggle-mute"
            >
              {selectedConv.isMuted ? (
                <Bell size={18} className="text-primary" />
              ) : (
                <BellOff size={18} className="text-muted-foreground" />
              )}
              <span className="flex-1">
                {selectedConv.isMuted ? t("chat.actions.unmute") : t("chat.actions.mute")}
              </span>
            </button>
            <button
              onClick={handleToggleArchive}
              disabled={updateConvState.isPending}
              className="w-full flex items-center gap-3 px-3 py-3 rounded-xl hover:bg-slate-100 text-start text-sm text-foreground disabled:opacity-50 transition-colors"
              data-testid="button-toggle-archive"
            >
              {selectedConv.isArchived ? (
                <ArchiveRestore size={18} className="text-primary" />
              ) : (
                <Archive size={18} className="text-muted-foreground" />
              )}
              <span className="flex-1">
                {selectedConv.isArchived ? t("chat.actions.unarchive") : t("chat.actions.archive")}
              </span>
            </button>
            {selectedConv.isGroup && (
              <button
                onClick={() => {
                  setShowActions(false);
                  setSuccessorChoice("auto");
                  setConfirmLeave(true);
                }}
                className="w-full flex items-center gap-3 px-3 py-3 rounded-xl hover:bg-destructive/10 text-start text-sm text-destructive transition-colors"
                data-testid="button-leave-group"
              >
                <LogOut size={18} />
                <span className="flex-1">{t("chat.actions.leave")}</span>
              </button>
            )}
          </div>
        </div>
      )}

      {/* Leave confirmation */}
      {confirmLeave && selectedConv?.isGroup && (() => {
        const others = (selectedConv.participants ?? []).filter(
          (p) => p.id !== user?.id,
        );
        const adminCount = (selectedConv.participants ?? []).filter(
          (p) => p.isAdmin,
        ).length;
        const isSoleAdmin = isAdminOfSelected && adminCount === 1;
        const showSuccessor = isSoleAdmin && others.length > 0;
        const closeDialog = () => {
          setConfirmLeave(false);
          setSuccessorChoice("auto");
        };
        return (
          <div
            className="fixed inset-0 bg-slate-900/40 backdrop-blur-sm z-[60] flex items-center justify-center p-4"
            onClick={closeDialog}
          >
            <div
              className="glass-panel rounded-3xl p-6 w-full max-w-sm space-y-4"
              onClick={(e) => e.stopPropagation()}
              data-testid="dialog-confirm-leave"
            >
              <h2 className="font-semibold text-foreground text-lg">
                {t("chat.actions.leaveConfirmTitle")}
              </h2>
              <p className="text-sm text-muted-foreground">
                {t("chat.actions.leaveConfirmBody", { name: convName })}
              </p>
              {showSuccessor && (
                <div className="space-y-2" data-testid="successor-chooser">
                  <p className="text-sm font-medium text-foreground">
                    {t("chat.actions.successorTitle")}
                  </p>
                  <p className="text-xs text-muted-foreground">
                    {t("chat.actions.successorHelp")}
                  </p>
                  <div className="max-h-48 overflow-y-auto space-y-1 rounded-2xl bg-slate-100/60 p-2">
                    <label
                      className="flex items-center gap-2 px-2 py-2 rounded-xl hover:bg-white/60 cursor-pointer"
                      data-testid="successor-option-auto"
                    >
                      <input
                        type="radio"
                        name="successor"
                        checked={successorChoice === "auto"}
                        onChange={() => setSuccessorChoice("auto")}
                      />
                      <span className="text-sm text-foreground">
                        {t("chat.actions.successorAuto")}
                      </span>
                    </label>
                    {others.map((p) => (
                      <label
                        key={p.id}
                        className="flex items-center gap-2 px-2 py-2 rounded-xl hover:bg-white/60 cursor-pointer"
                        data-testid={`successor-option-${p.id}`}
                      >
                        <input
                          type="radio"
                          name="successor"
                          checked={successorChoice === p.id}
                          onChange={() => setSuccessorChoice(p.id)}
                        />
                        <span className="text-sm text-foreground">
                          {pickDisplayName(
                            {
                              id: p.id,
                              username: p.username,
                              displayNameAr: p.displayNameAr,
                              displayNameEn: p.displayNameEn,
                            },
                            lang,
                          )}
                        </span>
                      </label>
                    ))}
                  </div>
                </div>
              )}
              <div className="flex gap-2">
                <Button
                  variant="outline"
                  className="flex-1"
                  onClick={closeDialog}
                >
                  {t("common.cancel")}
                </Button>
                <Button
                  variant="destructive"
                  className="flex-1"
                  onClick={handleLeave}
                  disabled={leaveConv.isPending}
                  data-testid="button-confirm-leave"
                >
                  {leaveConv.isPending ? (
                    <Loader2 size={14} className="animate-spin" />
                  ) : (
                    t("chat.actions.leave")
                  )}
                </Button>
              </div>
            </div>
          </div>
        );
      })()}

      {selectedConvId ? (
        /* Message View */
        <div className="flex-1 flex flex-col">
          <ScrollArea className="flex-1 p-4">
            <div className="space-y-3">
              {messages?.map((msg) => {
                if (msg.kind && msg.kind !== "user") {
                  const text = renderSystemMessage(msg, lang, t);
                  return (
                    <div key={msg.id} className="flex justify-center" data-testid={`system-message-${msg.kind}`}>
                      <div className="max-w-md text-center text-xs text-muted-foreground bg-slate-100/70 rounded-full px-3 py-1">
                        {text}
                      </div>
                    </div>
                  );
                }
                const isMe = msg.senderId === user?.id;
                const senderName =
                  (lang === "ar" ? msg.sender?.displayNameAr : msg.sender?.displayNameEn) ??
                  msg.sender?.username ??
                  "";
                return (
                  <div key={msg.id} className={`flex ${isMe ? "justify-end" : "justify-start"}`}>
                    <div className={`max-w-xs lg:max-w-md ${isMe ? "" : ""}`}>
                      {!isMe && (
                        <div className="text-xs text-muted-foreground mb-1 px-1">{senderName}</div>
                      )}
                      <div
                        className={`px-4 py-2 rounded-2xl text-sm ${
                          isMe
                            ? "bg-primary text-primary-foreground rounded-ee-sm"
                            : "glass-panel rounded-es-sm"
                        }`}
                      >
                        {msg.content}
                      </div>
                      <div className="text-xs text-muted-foreground mt-1 px-1">
                        {formatTime(msg.createdAt, lang, {
                          hour: "2-digit",
                          minute: "2-digit",
                          hour12: user?.clockHour12 ?? false,
                        })}
                      </div>
                    </div>
                  </div>
                );
              })}
              <div ref={bottomRef} />
            </div>
          </ScrollArea>

          {/* Message Input */}
          <div className="glass-panel border-t border-slate-200/70 p-4">
            <div className="flex gap-2">
              <Input
                value={messageText}
                onChange={(e) => setMessageText(e.target.value)}
                onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && (e.preventDefault(), handleSend())}
                placeholder={t("chat.typeMessage")}
                className="flex-1 bg-white/70 border-slate-200"
              />
              <Button
                onClick={handleSend}
                disabled={!messageText.trim() || sendMessage.isPending}
                size="icon"
              >
                <Send size={16} />
              </Button>
            </div>
          </div>
        </div>
      ) : (
        /* Conversation List */
        <div className="flex-1 p-4 space-y-2">
          {(() => {
            const allConvs = conversations ?? [];
            const archivedCount = allConvs.filter((c) => c.isArchived).length;
            const visibleConvs = allConvs.filter((c) =>
              showArchived ? c.isArchived : !c.isArchived,
            );
            return (
              <>
                <div
                  role="tablist"
                  className="grid grid-cols-2 gap-1 p-1 rounded-2xl bg-slate-100/70 mb-2"
                >
                  <button
                    type="button"
                    role="tab"
                    aria-selected={!showArchived}
                    onClick={() => setShowArchived(false)}
                    className={`py-2 rounded-xl text-sm font-medium transition-colors ${
                      !showArchived
                        ? "bg-white text-foreground shadow-sm"
                        : "text-muted-foreground hover:text-foreground"
                    }`}
                    data-testid="tab-active-chats"
                  >
                    {t("chat.filterAll")}
                  </button>
                  <button
                    type="button"
                    role="tab"
                    aria-selected={showArchived}
                    onClick={() => setShowArchived(true)}
                    className={`py-2 rounded-xl text-sm font-medium transition-colors inline-flex items-center justify-center gap-2 ${
                      showArchived
                        ? "bg-white text-foreground shadow-sm"
                        : "text-muted-foreground hover:text-foreground"
                    }`}
                    data-testid="tab-archived-chats"
                  >
                    <Archive size={14} />
                    {t("chat.filterArchived")}
                    {archivedCount > 0 && (
                      <span className="text-xs text-muted-foreground">({archivedCount})</span>
                    )}
                  </button>
                </div>
                {convsLoading ? (
                  <div className="flex items-center justify-center py-20 text-muted-foreground">
                    {t("common.loading")}
                  </div>
                ) : visibleConvs.length === 0 ? (
                  <div className="flex flex-col items-center justify-center py-20 gap-3 text-muted-foreground">
                    {showArchived ? (
                      <>
                        <Archive size={40} className="opacity-30" />
                        <span>{t("chat.noArchived")}</span>
                      </>
                    ) : (
                      <>
                        <MessageCircle size={40} className="opacity-30" />
                        <span>{t("chat.noConversations")}</span>
                      </>
                    )}
                  </div>
                ) : (
                  visibleConvs.map((conv) => {
              const name = convDisplayName(conv);
              const lastMsg = conv.lastMessage;
              const promotionMeta =
                lastMsg && lastMsg.kind === "admin_promoted"
                  ? ((lastMsg.meta ?? {}) as SystemMessageMeta)
                  : null;
              const newAdminName = promotionMeta
                ? pickDisplayName(promotionMeta.promoted, lang)
                : "";
              const isMeNewAdmin =
                !!promotionMeta?.promoted && promotionMeta.promoted.id === user?.id;

              return (
                <button
                  key={conv.id}
                  onClick={() => setSelectedConvId(conv.id)}
                  className="w-full glass-panel rounded-2xl p-4 flex items-center gap-3 hover:bg-slate-100 transition-colors text-start"
                >
                  <Avatar className="w-10 h-10 shrink-0">
                    {conv.avatarUrl && (
                      <AvatarImage
                        src={resolveServiceImageUrl(conv.avatarUrl) ?? ""}
                        alt=""
                        className="object-cover"
                      />
                    )}
                    <AvatarFallback
                      className={`font-medium ${
                        conv.isGroup
                          ? "bg-primary/15 text-primary"
                          : "bg-primary/20 text-primary"
                      }`}
                    >
                      {conv.isGroup ? (
                        <span className="flex items-center gap-0.5 text-sm">
                          <Users size={12} className="opacity-70" />
                          {String(name).trim().slice(0, 1).toUpperCase()}
                        </span>
                      ) : (
                        String(name).slice(0, 2)
                      )}
                    </AvatarFallback>
                  </Avatar>
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center justify-between gap-2">
                      <div className="flex items-center gap-2 min-w-0">
                        <span className="font-medium text-foreground text-sm truncate">
                          {name}
                        </span>
                        {conv.isGroup && (
                          <Badge className="bg-primary/15 text-primary border-primary/30 text-[10px] shrink-0">
                            {t("chat.groupChat")}
                          </Badge>
                        )}
                        {conv.isMuted && (
                          <BellOff
                            size={12}
                            className="text-muted-foreground shrink-0"
                            aria-label={t("chat.actions.mutedLabel")}
                          />
                        )}
                        {promotionMeta && (
                          <Badge
                            className={`text-[10px] shrink-0 inline-flex items-center gap-1 ${
                              isMeNewAdmin
                                ? "bg-amber-100 text-amber-700 border-amber-300"
                                : "bg-amber-50 text-amber-700 border-amber-200"
                            }`}
                            data-testid={`badge-new-admin-${conv.id}`}
                            aria-label={t("chat.actions.newAdminLabel", {
                              name: isMeNewAdmin
                                ? t("chat.actions.youAreAdminTitle")
                                : newAdminName,
                            })}
                          >
                            <Crown size={10} />
                            {isMeNewAdmin
                              ? t("chat.actions.youAreAdminTitle")
                              : t("chat.actions.newAdminBadge")}
                          </Badge>
                        )}
                      </div>
                      {conv.unreadCount > 0 && !conv.isMuted && (
                        <Badge className="bg-primary/20 text-primary border-primary/30 text-xs shrink-0">
                          {conv.unreadCount}
                        </Badge>
                      )}
                    </div>
                    {lastMsg && (
                      <p className="text-xs text-muted-foreground truncate mt-0.5">
                        {lastMsg.kind && lastMsg.kind !== "user"
                          ? renderSystemMessage(lastMsg, lang, t)
                          : lastMsg.content}
                      </p>
                    )}
                  </div>
                </button>
              );
                  })
                )}
              </>
            );
          })()}
        </div>
      )}
    </div>
  );
}
