// @File: src/pages/ClientWhatsappChat.tsx
// @Desc: WhatsApp-style two-pane chat client for /client/whatsapp/chat.
//        Left pane: conversation list with search + unread filter +
//        "new chat" composer. Right pane: message thread with green
//        outbound bubbles, delivery tick marks, attachment upload,
//        and send-on-Enter composer. Wired to /api/client/whatsapp/*.
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Link } from "react-router-dom";
import {
  ArrowLeft,
  Check,
  CheckCheck,
  Clock,
  Image as ImageIcon,
  Loader2,
  MessageSquarePlus,
  Paperclip,
  RefreshCw,
  Search,
  Send,
  Smile,
  X,
} from "lucide-react";
import { toast } from "sonner";

import {
  waListConversations,
  waListMessages,
  waMarkRead,
  waSendMessage,
  type WhatsappConversation,
  type WhatsappDeliveryStatus,
  type WhatsappMessage,
} from "@/lib/whatsappApi";
import { useWhatsappChatSocket } from "@/hooks/useWhatsappChatSocket";

function formatLastSeen(ts: string | null): string {
  if (!ts) return "offline";
  const d = new Date(ts);
  if (Number.isNaN(d.getTime())) return "offline";
  const diff = Date.now() - d.getTime();
  if (diff < 60_000) return "last seen just now";
  if (diff < 3_600_000) {
    const m = Math.floor(diff / 60_000);
    return `last seen ${m} min ago`;
  }
  const today = new Date();
  if (d.toDateString() === today.toDateString()) {
    return `last seen today at ${formatTime(ts)}`;
  }
  const yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  if (d.toDateString() === yesterday.toDateString()) {
    return `last seen yesterday at ${formatTime(ts)}`;
  }
  return `last seen ${d.toLocaleDateString([], { month: "short", day: "numeric" })}`;
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const CHAT_BG =
  "repeating-linear-gradient(135deg, #ECE5DD 0px, #ECE5DD 4px, #E3D9CD 4px, #E3D9CD 6px)";

function formatTime(ts: string | null): string {
  if (!ts) return "";
  const d = new Date(ts);
  if (Number.isNaN(d.getTime())) return "";
  return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
}

function formatDateLabel(ts: string | null): string {
  if (!ts) return "";
  const d = new Date(ts);
  if (Number.isNaN(d.getTime())) return "";
  const today = new Date();
  const yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  const sameDay = (a: Date, b: Date) =>
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate();
  if (sameDay(d, today)) return "Today";
  if (sameDay(d, yesterday)) return "Yesterday";
  return d.toLocaleDateString([], { year: "numeric", month: "short", day: "numeric" });
}

function initialsFor(name: string | null, phone: string): string {
  const source = (name && name.trim()) || phone.replace(/\D/g, "");
  if (!source) return "?";
  const parts = source.split(/\s+/).filter(Boolean);
  if (parts.length >= 2) {
    return (parts[0][0] + parts[1][0]).toUpperCase();
  }
  return source.slice(-2).toUpperCase();
}

function previewText(msg: WhatsappConversation["last_message"]): string {
  if (!msg) return "";
  if (msg.message_type && msg.message_type !== "text") {
    const label = msg.message_type.charAt(0).toUpperCase() + msg.message_type.slice(1);
    return `📎 ${label}`;
  }
  return msg.body || "";
}

function DeliveryTicks({ status }: { status: WhatsappDeliveryStatus }) {
  if (status === "queued" || status === "received") {
    return <Clock className="inline h-3.5 w-3.5 text-slate-400" aria-label="Queued" />;
  }
  if (status === "failed") {
    return <X className="inline h-3.5 w-3.5 text-red-500" aria-label="Failed" />;
  }
  if (status === "sent") {
    return <Check className="inline h-3.5 w-3.5 text-slate-500" aria-label="Sent" />;
  }
  if (status === "delivered") {
    return <CheckCheck className="inline h-3.5 w-3.5 text-slate-500" aria-label="Delivered" />;
  }
  if (status === "read") {
    return (
      <CheckCheck
        className="inline h-3.5 w-3.5 text-sky-500"
        aria-label="Read"
      />
    );
  }
  return null;
}

// ---------------------------------------------------------------------------
// Subcomponents
// ---------------------------------------------------------------------------

interface ConversationRowProps {
  conv: WhatsappConversation;
  active: boolean;
  onClick: () => void;
}

function ConversationRow({ conv, active, onClick }: ConversationRowProps) {
  const preview = previewText(conv.last_message);
  const ts = conv.last_message?.created_at || conv.last_message_at;
  return (
    <button
      type="button"
      onClick={onClick}
      className={`flex w-full items-center gap-3 border-b border-slate-100 px-3 py-3 text-left transition hover:bg-slate-50 ${
        active ? "bg-emerald-50" : "bg-white"
      }`}
    >
      <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-emerald-500 to-emerald-700 text-sm font-semibold text-white">
        {initialsFor(conv.display_name, conv.phone_number)}
      </div>
      <div className="min-w-0 flex-1">
        <div className="flex items-center justify-between gap-2">
          <span className="truncate text-sm font-semibold text-slate-900">
            {conv.display_name || conv.phone_number}
          </span>
          <span className="shrink-0 text-[11px] text-slate-500">{formatTime(ts)}</span>
        </div>
        <div className="mt-0.5 flex items-center justify-between gap-2">
          <span className="truncate text-xs text-slate-500">
            {conv.last_message?.direction === "outbound" && (
              <span className="mr-1 align-middle">
                <DeliveryTicks
                  status={conv.last_message.delivery_status as WhatsappDeliveryStatus}
                />
              </span>
            )}
            {preview || "No messages yet"}
          </span>
          {conv.unread_count > 0 && (
            <span className="ml-auto inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-emerald-500 px-1.5 text-[11px] font-semibold text-white">
              {conv.unread_count > 99 ? "99+" : conv.unread_count}
            </span>
          )}
        </div>
      </div>
    </button>
  );
}

interface MessageBubbleProps {
  msg: WhatsappMessage;
}

function MessageBubble({ msg }: MessageBubbleProps) {
  const outbound = msg.direction === "outbound";
  return (
    <div className={`flex ${outbound ? "justify-end" : "justify-start"}`}>
      <div
        className={`relative max-w-[78%] rounded-lg px-3 py-2 shadow-sm ${
          outbound ? "bg-[#DCF8C6] text-slate-900" : "bg-white text-slate-900"
        }`}
      >
        {msg.media_url && (
          <a
            href={msg.media_url}
            target="_blank"
            rel="noopener noreferrer"
            className="mb-1 block"
          >
            {msg.message_type === "image" ? (
              <img
                src={msg.media_url}
                alt="attachment"
                className="max-h-60 w-full rounded-md object-cover"
              />
            ) : (
              <span className="inline-flex items-center gap-2 rounded-md bg-black/5 px-2 py-1 text-xs text-slate-700">
                <Paperclip className="h-3.5 w-3.5" />
                {msg.message_type} attachment
              </span>
            )}
          </a>
        )}
        {msg.body && (
          <p className="whitespace-pre-wrap break-words text-[14px] leading-snug">
            {msg.body}
          </p>
        )}
        <div className="mt-1 flex items-center justify-end gap-1 text-[11px] text-slate-500">
          <span>{formatTime(msg.created_at)}</span>
          {outbound && <DeliveryTicks status={msg.delivery_status} />}
        </div>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Main page
// ---------------------------------------------------------------------------

export default function ClientWhatsappChat() {
  const [conversations, setConversations] = useState<WhatsappConversation[]>([]);
  const [loadingList, setLoadingList] = useState(true);
  const [search, setSearch] = useState("");
  const [unreadOnly, setUnreadOnly] = useState(false);
  const [activeContactId, setActiveContactId] = useState<number | null>(null);
  const [messages, setMessages] = useState<WhatsappMessage[]>([]);
  const [loadingMessages, setLoadingMessages] = useState(false);
  const [composer, setComposer] = useState("");
  const [sending, setSending] = useState(false);
  const [attachment, setAttachment] = useState<{
    url: string;
    mime: string;
    type: "image" | "video" | "audio" | "document";
    name: string;
  } | null>(null);
  const [newChatOpen, setNewChatOpen] = useState(false);
  const [newChatPhone, setNewChatPhone] = useState("");
  const [newChatBody, setNewChatBody] = useState("");
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const inputRef = useRef<HTMLInputElement | null>(null);

  // --- fetchers ---
  const loadConversations = useCallback(async () => {
    try {
      setLoadingList(true);
      const items = await waListConversations({
        unreadOnly,
        search: search.trim() || undefined,
      });
      setConversations(items);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to load chats");
    } finally {
      setLoadingList(false);
    }
  }, [unreadOnly, search]);

  const loadMessages = useCallback(async (contactId: number) => {
    try {
      setLoadingMessages(true);
      const items = await waListMessages(contactId, { limit: 200 });
      setMessages(items);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to load messages");
    } finally {
      setLoadingMessages(false);
    }
  }, []);

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

  // Auto-refresh conversation list every 20s.
  useEffect(() => {
    const id = window.setInterval(() => {
      void loadConversations();
    }, 20000);
    return () => window.clearInterval(id);
  }, [loadConversations]);

  useEffect(() => {
    if (activeContactId === null) {
      setMessages([]);
      return;
    }
    void loadMessages(activeContactId);
  }, [activeContactId, loadMessages]);

  // Scroll thread to bottom on message update.
  useEffect(() => {
    const el = scrollRef.current;
    if (el) {
      el.scrollTop = el.scrollHeight;
    }
  }, [messages.length, activeContactId]);

  // Auto mark-as-read when opening a conversation.
  useEffect(() => {
    if (activeContactId === null) return;
    const conv = conversations.find((c) => c.contact_id === activeContactId);
    if (!conv || conv.unread_count === 0) return;
    waMarkRead(activeContactId)
      .then(() => {
        setConversations((prev) =>
          prev.map((c) =>
            c.contact_id === activeContactId ? { ...c, unread_count: 0 } : c,
          ),
        );
      })
      .catch(() => {
        // Silent — non-critical.
      });
  }, [activeContactId, conversations]);

  const activeConversation = useMemo(
    () => conversations.find((c) => c.contact_id === activeContactId) || null,
    [conversations, activeContactId],
  );

  // --- realtime presence + typing + message push via WebSocket ---
  const handleRealtimeMessage = useCallback(
    (raw: Record<string, unknown>) => {
      const incoming = raw as unknown as WhatsappMessage;
      if (!incoming || typeof incoming !== "object") return;
      if (activeContactId !== null && incoming.contact_id === activeContactId) {
        setMessages((prev) => {
          if (prev.some((m) => m.id === incoming.id)) return prev;
          return [...prev, incoming];
        });
      }
      void loadConversations();
    },
    [activeContactId, loadConversations],
  );

  const { presenceFor, isTyping, publishTyping } = useWhatsappChatSocket({
    onMessage: handleRealtimeMessage,
  });

  const activePresence = presenceFor(activeContactId);
  const activeTyping = isTyping(activeContactId);

  // Throttle composer typing broadcasts: emit `true` once, then `false` after
  // the user stops typing for ~2s. Also reset when the conversation changes.
  const typingStateRef = useRef<{ sent: boolean; timer: number | null }>({
    sent: false,
    timer: null,
  });
  useEffect(() => {
    return () => {
      if (typingStateRef.current.timer !== null) {
        window.clearTimeout(typingStateRef.current.timer);
        typingStateRef.current.timer = null;
      }
    };
  }, []);
  useEffect(() => {
    // On conversation switch, cancel any pending typing state for the previous.
    if (typingStateRef.current.timer !== null) {
      window.clearTimeout(typingStateRef.current.timer);
      typingStateRef.current.timer = null;
    }
    typingStateRef.current.sent = false;
  }, [activeContactId]);

  const notifyTyping = useCallback(
    (value: string) => {
      if (activeContactId === null) return;
      const hasText = value.trim().length > 0;
      const state = typingStateRef.current;
      if (hasText && !state.sent) {
        publishTyping(activeContactId, true);
        state.sent = true;
      }
      if (state.timer !== null) {
        window.clearTimeout(state.timer);
      }
      state.timer = window.setTimeout(() => {
        if (state.sent && activeContactId !== null) {
          publishTyping(activeContactId, false);
        }
        state.sent = false;
        state.timer = null;
      }, 2_000);
    },
    [activeContactId, publishTyping],
  );

  // --- send handlers ---
  const resetComposer = useCallback(() => {
    setComposer("");
    setAttachment(null);
    if (fileInputRef.current) fileInputRef.current.value = "";
    // Stop broadcasting typing once we successfully send.
    const state = typingStateRef.current;
    if (state.timer !== null) {
      window.clearTimeout(state.timer);
      state.timer = null;
    }
    if (state.sent && activeContactId !== null) {
      publishTyping(activeContactId, false);
    }
    state.sent = false;
  }, [activeContactId, publishTyping]);

  const onPickAttachment = useCallback((file: File | null) => {
    if (!file) return;
    const objectUrl = URL.createObjectURL(file);
    const mime = file.type || "application/octet-stream";
    let type: "image" | "video" | "audio" | "document" = "document";
    if (mime.startsWith("image/")) type = "image";
    else if (mime.startsWith("video/")) type = "video";
    else if (mime.startsWith("audio/")) type = "audio";
    setAttachment({ url: objectUrl, mime, type, name: file.name });
  }, []);

  const clearAttachment = useCallback(() => {
    if (attachment?.url?.startsWith("blob:")) {
      URL.revokeObjectURL(attachment.url);
    }
    setAttachment(null);
    if (fileInputRef.current) fileInputRef.current.value = "";
  }, [attachment]);

  const handleSend = useCallback(async () => {
    if (sending || activeContactId === null) return;
    const body = composer.trim();
    if (!body && !attachment) return;
    setSending(true);
    try {
      const sent = await waSendMessage(activeContactId, {
        body: body || null,
        message_type: attachment?.type ?? "text",
        media_url: attachment?.url ?? null,
        media_mime: attachment?.mime ?? null,
      });
      setMessages((prev) => [...prev, sent]);
      resetComposer();
      // Best-effort refresh of the sidebar preview.
      void loadConversations();
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to send message");
    } finally {
      setSending(false);
    }
  }, [sending, activeContactId, composer, attachment, resetComposer, loadConversations]);

  const handleNewChatSend = useCallback(async () => {
    const phone = newChatPhone.trim();
    const body = newChatBody.trim();
    if (!phone) {
      toast.error("Please enter a phone number");
      return;
    }
    if (!body) {
      toast.error("Please enter a message");
      return;
    }
    setSending(true);
    try {
      const sent = await waSendMessage(null, {
        phone_number: phone,
        body,
        message_type: "text",
      });
      setNewChatOpen(false);
      setNewChatPhone("");
      setNewChatBody("");
      await loadConversations();
      if (sent.contact_id !== null) {
        setActiveContactId(sent.contact_id);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to start chat");
    } finally {
      setSending(false);
    }
  }, [newChatPhone, newChatBody, loadConversations]);

  // Group messages by day for "Today/Yesterday" separators.
  const grouped = useMemo(() => {
    const groups: { label: string; items: WhatsappMessage[] }[] = [];
    let currentLabel = "";
    for (const m of messages) {
      const label = formatDateLabel(m.created_at);
      if (label !== currentLabel) {
        groups.push({ label, items: [m] });
        currentLabel = label;
      } else {
        groups[groups.length - 1].items.push(m);
      }
    }
    return groups;
  }, [messages]);

  const mobileActive = activeContactId !== null;

  return (
    <div className="flex min-h-screen flex-col bg-slate-50">
      {/* Top bar */}
      <header className="flex items-center gap-3 border-b border-slate-200 bg-[#128C7E] px-4 py-3 text-white">
        <Link
          to="/client/dashboard"
          className="inline-flex h-8 w-8 items-center justify-center rounded-full hover:bg-white/10"
          aria-label="Back to dashboard"
        >
          <ArrowLeft className="h-4 w-4" />
        </Link>
        <div className="min-w-0 flex-1">
          <h1 className="text-sm font-semibold uppercase tracking-wide">WhatsApp Chat</h1>
          <p className="truncate text-xs text-white/80">
            Manage conversations from your connected devices.
          </p>
        </div>
        <Link
          to="/client/whatsapp/ai"
          className="mr-1 hidden items-center gap-1 rounded-full bg-white/10 px-3 py-1 text-xs font-medium hover:bg-white/20 sm:inline-flex"
          aria-label="AI auto-reply settings"
        >
          ✨ AI
        </Link>
        <Link
          to="/client/whatsapp/campaigns"
          className="mr-1 hidden items-center gap-1 rounded-full bg-white/10 px-3 py-1 text-xs font-medium hover:bg-white/20 sm:inline-flex"
          aria-label="Campaigns"
        >
          📣 Campaigns
        </Link>
        <Link
          to="/client/whatsapp/workflows"
          className="mr-1 hidden items-center gap-1 rounded-full bg-white/10 px-3 py-1 text-xs font-medium hover:bg-white/20 sm:inline-flex"
          aria-label="Workflows"
        >
          ⚡ Workflows
        </Link>
        <button
          type="button"
          onClick={() => loadConversations()}
          className="inline-flex h-8 w-8 items-center justify-center rounded-full hover:bg-white/10"
          aria-label="Refresh"
        >
          <RefreshCw className="h-4 w-4" />
        </button>
      </header>

      {/* Main two-pane body */}
      <div className="mx-auto flex w-full max-w-6xl flex-1 flex-col overflow-hidden bg-white shadow-sm md:my-6 md:flex-row md:rounded-lg md:border md:border-slate-200">
        {/* LEFT PANE */}
        <aside
          className={`flex w-full flex-col border-r border-slate-200 md:w-[340px] ${
            mobileActive ? "hidden md:flex" : "flex"
          }`}
        >
          <div className="border-b border-slate-200 bg-slate-50 p-3">
            <div className="flex items-center gap-2">
              <div className="relative flex-1">
                <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
                <input
                  type="search"
                  value={search}
                  onChange={(e) => setSearch(e.target.value)}
                  placeholder="Search or start a new chat"
                  className="w-full rounded-full border border-slate-200 bg-white py-2 pl-9 pr-3 text-sm outline-none transition focus:border-emerald-500"
                />
              </div>
              <button
                type="button"
                onClick={() => setNewChatOpen(true)}
                className="inline-flex h-9 w-9 items-center justify-center rounded-full bg-emerald-500 text-white shadow-sm transition hover:bg-emerald-600"
                aria-label="New chat"
              >
                <MessageSquarePlus className="h-4 w-4" />
              </button>
            </div>
            <div className="mt-2 flex items-center gap-2 text-xs">
              <button
                type="button"
                onClick={() => setUnreadOnly(false)}
                className={`rounded-full px-3 py-1 transition ${
                  !unreadOnly
                    ? "bg-emerald-500 text-white"
                    : "bg-white text-slate-600 hover:bg-slate-100"
                }`}
              >
                All
              </button>
              <button
                type="button"
                onClick={() => setUnreadOnly(true)}
                className={`rounded-full px-3 py-1 transition ${
                  unreadOnly
                    ? "bg-emerald-500 text-white"
                    : "bg-white text-slate-600 hover:bg-slate-100"
                }`}
              >
                Unread
              </button>
            </div>
          </div>

          <div className="min-h-0 flex-1 overflow-y-auto">
            {loadingList && conversations.length === 0 ? (
              <div className="flex h-full items-center justify-center p-6 text-slate-400">
                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                <span className="text-sm">Loading chats…</span>
              </div>
            ) : conversations.length === 0 ? (
              <div className="p-6 text-center text-sm text-slate-500">
                {unreadOnly
                  ? "No unread conversations."
                  : "No conversations yet. Start one with the + button."}
              </div>
            ) : (
              conversations.map((c) => (
                <ConversationRow
                  key={c.contact_id}
                  conv={c}
                  active={c.contact_id === activeContactId}
                  onClick={() => setActiveContactId(c.contact_id)}
                />
              ))
            )}
          </div>
        </aside>

        {/* RIGHT PANE */}
        <section
          className={`flex min-h-[60vh] flex-1 flex-col ${
            mobileActive ? "flex" : "hidden md:flex"
          }`}
          style={{ backgroundImage: CHAT_BG }}
        >
          {!activeConversation ? (
            <div className="flex flex-1 flex-col items-center justify-center p-10 text-center text-slate-600">
              <div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-100 text-emerald-700">
                <Smile className="h-8 w-8" />
              </div>
              <h2 className="text-lg font-semibold text-slate-800">
                Select a conversation
              </h2>
              <p className="mt-1 max-w-sm text-sm text-slate-500">
                Pick a chat on the left to view the message thread, or click the +
                button to start a new chat with any phone number.
              </p>
            </div>
          ) : (
            <>
              <header className="flex items-center gap-3 border-b border-slate-200 bg-white/90 px-4 py-2 backdrop-blur">
                <button
                  type="button"
                  onClick={() => setActiveContactId(null)}
                  className="inline-flex h-8 w-8 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100 md:hidden"
                  aria-label="Back to chat list"
                >
                  <ArrowLeft className="h-4 w-4" />
                </button>
                <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-emerald-500 to-emerald-700 text-sm font-semibold text-white">
                  {initialsFor(
                    activeConversation.display_name,
                    activeConversation.phone_number,
                  )}
                </div>
                <div className="min-w-0 flex-1">
                  <p className="truncate text-sm font-semibold text-slate-900">
                    {activeConversation.display_name || activeConversation.phone_number}
                  </p>
                  <p className="truncate text-xs text-slate-500">
                    {activeTyping ? (
                      <span className="font-medium text-emerald-600">typing…</span>
                    ) : activePresence?.online ? (
                      <span className="inline-flex items-center gap-1">
                        <span className="inline-block h-2 w-2 rounded-full bg-emerald-500" />
                        <span>online</span>
                      </span>
                    ) : (
                      <span>{formatLastSeen(activePresence?.last_seen_at ?? null)}</span>
                    )}
                    <span className="mx-1 text-slate-300">·</span>
                    <span>{activeConversation.phone_number}</span>
                    {activeConversation.is_opted_out ? (
                      <span className="text-rose-500"> · opted out</span>
                    ) : null}
                  </p>
                </div>
              </header>

              <div
                ref={scrollRef}
                className="min-h-0 flex-1 overflow-y-auto px-4 py-4"
              >
                {loadingMessages && messages.length === 0 ? (
                  <div className="flex h-full items-center justify-center text-slate-500">
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                    <span className="text-sm">Loading messages…</span>
                  </div>
                ) : messages.length === 0 ? (
                  <div className="flex h-full items-center justify-center text-slate-500">
                    <p className="text-sm">
                      No messages yet — say hi to get the conversation started.
                    </p>
                  </div>
                ) : (
                  <div className="flex flex-col gap-3">
                    {grouped.map((group) => (
                      <div key={group.label} className="flex flex-col gap-2">
                        <div className="sticky top-0 z-10 flex justify-center">
                          <span className="rounded-full bg-white/80 px-3 py-0.5 text-[11px] font-medium text-slate-600 shadow-sm">
                            {group.label}
                          </span>
                        </div>
                        {group.items.map((m) => (
                          <MessageBubble key={m.id} msg={m} />
                        ))}
                      </div>
                    ))}
                  </div>
                )}
              </div>

              {/* Composer */}
              <footer className="border-t border-slate-200 bg-[#F0F2F5] px-3 py-2">
                {attachment && (
                  <div className="mb-2 flex items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-2">
                    {attachment.type === "image" ? (
                      <img
                        src={attachment.url}
                        alt={attachment.name}
                        className="h-10 w-10 rounded object-cover"
                      />
                    ) : (
                      <div className="flex h-10 w-10 items-center justify-center rounded bg-slate-100 text-slate-500">
                        <Paperclip className="h-4 w-4" />
                      </div>
                    )}
                    <div className="min-w-0 flex-1">
                      <p className="truncate text-xs font-medium text-slate-700">
                        {attachment.name}
                      </p>
                      <p className="text-[10px] text-slate-500">{attachment.mime}</p>
                    </div>
                    <button
                      type="button"
                      onClick={clearAttachment}
                      className="rounded-full p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700"
                      aria-label="Remove attachment"
                    >
                      <X className="h-4 w-4" />
                    </button>
                  </div>
                )}
                <div className="flex items-end gap-2">
                  <input
                    ref={fileInputRef}
                    type="file"
                    accept="image/*,video/*,audio/*,application/pdf"
                    hidden
                    onChange={(e) => onPickAttachment(e.target.files?.[0] ?? null)}
                  />
                  <button
                    type="button"
                    onClick={() => fileInputRef.current?.click()}
                    className="inline-flex h-10 w-10 items-center justify-center rounded-full text-slate-500 hover:bg-slate-200"
                    aria-label="Attach media"
                  >
                    <ImageIcon className="h-5 w-5" />
                  </button>
                  <input
                    ref={inputRef}
                    type="text"
                    value={composer}
                    onChange={(e) => {
                      setComposer(e.target.value);
                      notifyTyping(e.target.value);
                    }}
                    onKeyDown={(e) => {
                      if (e.key === "Enter" && !e.shiftKey) {
                        e.preventDefault();
                        void handleSend();
                      }
                    }}
                    disabled={activeConversation.is_opted_out || sending}
                    placeholder={
                      activeConversation.is_opted_out
                        ? "This contact has opted out of messages."
                        : "Type a message"
                    }
                    className="min-w-0 flex-1 rounded-full border border-slate-200 bg-white px-4 py-2 text-sm outline-none transition focus:border-emerald-500 disabled:cursor-not-allowed disabled:bg-slate-100"
                  />
                  <button
                    type="button"
                    onClick={() => void handleSend()}
                    disabled={
                      sending ||
                      activeConversation.is_opted_out ||
                      (!composer.trim() && !attachment)
                    }
                    className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-emerald-500 text-white shadow-sm transition hover:bg-emerald-600 disabled:cursor-not-allowed disabled:bg-slate-300"
                    aria-label="Send"
                  >
                    {sending ? (
                      <Loader2 className="h-4 w-4 animate-spin" />
                    ) : (
                      <Send className="h-4 w-4" />
                    )}
                  </button>
                </div>
              </footer>
            </>
          )}
        </section>
      </div>

      {/* New chat modal */}
      {newChatOpen && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/50 px-4">
          <div className="w-full max-w-md rounded-lg bg-white p-5 shadow-lg">
            <div className="mb-3 flex items-center justify-between">
              <h3 className="text-base font-semibold text-slate-900">
                Start a new chat
              </h3>
              <button
                type="button"
                onClick={() => setNewChatOpen(false)}
                className="rounded-full p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700"
                aria-label="Close"
              >
                <X className="h-4 w-4" />
              </button>
            </div>
            <label className="mb-3 block text-sm">
              <span className="mb-1 block text-xs font-medium text-slate-600">
                Phone number
              </span>
              <input
                type="tel"
                value={newChatPhone}
                onChange={(e) => setNewChatPhone(e.target.value)}
                placeholder="+966 5X XXX XXXX"
                className="w-full rounded-md border border-slate-200 px-3 py-2 text-sm outline-none focus:border-emerald-500"
              />
            </label>
            <label className="mb-4 block text-sm">
              <span className="mb-1 block text-xs font-medium text-slate-600">
                First message
              </span>
              <textarea
                value={newChatBody}
                onChange={(e) => setNewChatBody(e.target.value)}
                rows={4}
                placeholder="Hello! How can we help?"
                className="w-full rounded-md border border-slate-200 px-3 py-2 text-sm outline-none focus:border-emerald-500"
              />
            </label>
            <div className="flex justify-end gap-2">
              <button
                type="button"
                onClick={() => setNewChatOpen(false)}
                className="rounded-md border border-slate-200 px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-100"
              >
                Cancel
              </button>
              <button
                type="button"
                onClick={() => void handleNewChatSend()}
                disabled={sending}
                className="inline-flex items-center gap-2 rounded-md bg-emerald-500 px-3 py-1.5 text-sm font-medium text-white hover:bg-emerald-600 disabled:bg-slate-300"
              >
                {sending && <Loader2 className="h-4 w-4 animate-spin" />}
                Send
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}