import React from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import TiptapUnderline from "@tiptap/extension-underline";
import TiptapLink from "@tiptap/extension-link";
import {
  Archive,
  ArrowLeft,
  Bold,
  Bot,
  CheckCircle2,
  ChevronDown,
  ChevronRight,
  FileText,
  Flag,
  Italic,
  Link2,
  Lock,
  Mail,
  MessageSquare,
  MoreHorizontal,
  Paperclip,
  Send,
  Underline,
  UserCheck,
  X,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button, buttonVariants } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { ChatInputArea } from "@/components/ui/chat-input-area";
import type {
  AiConvAttachment,
  AiConvChannel,
  AiConvContact,
  AiConvMessage,
  AiConvMode,
  AiConvStatus,
} from "./types";
import {
  ContactAvatar,
  displayContactName,
  PANEL_HEADER_HEIGHT,
} from "./helpers";
import { ConversationStatusChip } from "./list";
import { BubbleAvatar, ChatBubble } from "./bubble";

// ---------------------------------------------------------------------------
// ChatComposer
// ---------------------------------------------------------------------------

export interface AiConvEmailPayload {
  content: string;
  to: string;
  cc: string;
  subject: string;
  /** `true` when composing a reply; `false` when composing a new email. */
  isReply: boolean;
  /** Files attached before sending. */
  attachments?: AiConvAttachment[];
}

/** Formats a byte count as a human-readable size, e.g. 254000 -> "248 KB". */
function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

let attachmentIdCounter = 0;
/** Converts a browser FileList into staged AiConvAttachment entries (no upload — url stays unset). */
function filesToAttachments(files: FileList): AiConvAttachment[] {
  return Array.from(files).map((file) => ({
    id: `pending-attachment-${++attachmentIdCounter}`,
    name: file.name,
    size: formatFileSize(file.size),
  }));
}

export interface ChatComposerProps {
  mode: AiConvMode;
  /** Active reply channel. Defaults to "chat". */
  channel?: AiConvChannel;
  onChannelChange?: (channel: AiConvChannel) => void;
  /**
   * When true, the Email tab is shown in the composer. Defaults to false —
   * consumers must opt in once their tenant's email integration is wired up.
   */
  isEmailIntegrated?: boolean;
  /**
   * Locks the composer to the email channel: the Chat/Email toggle is hidden
   * and the chat panel never renders. For embedded email-only contexts (e.g.
   * the Kanban slide-out's Email & Notes tab). Implies email is integrated;
   * `channel` / `isEmailIntegrated` are ignored while set. Defaults to false.
   */
  emailOnly?: boolean;
  /** Lead's email address — pre-fills the To field in email compose. */
  contactEmail?: string;
  inputValue?: string;
  onInputChange?: (v: string) => void;
  /** Fired when the user sends a chat message. */
  onSend?: (content: string) => void;
  /** Fired when the user sends an email. */
  onSendEmail?: (payload: AiConvEmailPayload) => void;
  onTakeOver?: () => void;
  onLetAiHandle?: () => void;
  /** Called when the user selects files via the attachment button. */
  onAttachFile?: (files: FileList) => void;
  /** Called when the user selects images via the image upload button. */
  onAttachImage?: (files: FileList) => void;
  /** Pre-fills the Subject field with "Re: [emailReplySubject]" for email threads. */
  emailReplySubject?: string;
  /**
   * Hides the "Reply / New email" toggle that otherwise shows whenever
   * `emailReplySubject` is set. For contexts with a dedicated "start a new
   * email" entry point elsewhere (e.g. the Kanban slide-out's Email & Notes
   * tab), so there's only one way to start a new thread. Defaults to false.
   */
  hideEmailModeToggle?: boolean;
  className?: string;
}

function ComposerToolbarButton({
  label,
  icon: Icon,
  pressed,
  onToggle,
}: {
  label: string;
  icon: React.ElementType;
  pressed?: boolean;
  onToggle?: () => void;
}) {
  return (
    <button
      type="button"
      aria-label={label}
      aria-pressed={pressed}
      onClick={onToggle}
      className={cn(
        "flex size-7 items-center justify-center transition-colors",
        pressed
          ? "bg-foreground text-background"
          : "text-muted-foreground hover:bg-muted hover:text-foreground",
      )}
    >
      <Icon className="size-3.5" />
    </button>
  );
}

function ComposerLinkPopover({
  editor,
}: {
  editor: ReturnType<typeof useEditor>;
}) {
  const [open, setOpen] = React.useState(false);
  const [url, setUrl] = React.useState("");

  const handleApply = () => {
    if (url.trim()) {
      editor?.chain().focus().setLink({ href: url.trim() }).run();
    }
    setOpen(false);
    setUrl("");
  };

  return (
    <Popover
      open={open}
      onOpenChange={(newOpen) => {
        if (newOpen && editor?.isActive("link")) {
          editor.chain().focus().unsetLink().run();
          return;
        }
        if (newOpen) setUrl("");
        setOpen(newOpen);
      }}
    >
      <PopoverTrigger
        aria-label="Insert link"
        className={cn(
          "flex size-7 items-center justify-center transition-colors",
          editor?.isActive("link")
            ? "bg-foreground text-background"
            : "text-muted-foreground hover:bg-muted hover:text-foreground",
        )}
      >
        <Link2 className="size-3.5" />
      </PopoverTrigger>
      <PopoverContent className="w-72 p-2" align="start">
        <div className="flex items-center gap-1.5">
          <input
            type="url"
            value={url}
            onChange={(e) => setUrl(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && handleApply()}
            placeholder="https://"
            className="min-w-0 flex-1 border border-border bg-transparent px-2 py-1.5 text-body-small text-foreground outline-none placeholder:text-muted-foreground focus:border-primary"
            autoFocus
          />
          <Button size="sm" className="h-8 px-3" onClick={handleApply}>
            Apply
          </Button>
        </div>
      </PopoverContent>
    </Popover>
  );
}

function ComposerEmailFieldRow({
  label,
  children,
}: {
  label: string;
  children: React.ReactNode;
}) {
  return (
    <div className="flex items-center gap-3 border-b border-border px-4 py-2.5">
      <span className="w-14 shrink-0 text-body-small text-muted-foreground">
        {label}
      </span>
      {children}
    </div>
  );
}

export function ChatComposer({
  mode,
  channel: channelProp = "chat",
  onChannelChange,
  isEmailIntegrated = false,
  emailOnly = false,
  contactEmail = "",
  inputValue = "",
  onInputChange,
  onSend,
  onSendEmail,
  onTakeOver,
  onLetAiHandle,
  onAttachFile,
  onAttachImage,
  emailReplySubject,
  hideEmailModeToggle = false,
  className,
}: ChatComposerProps) {
  // Semi-controlled: owns channel state for uncontrolled usage, notifies parent on change.
  // Force chat when email isn't integrated so the panel never lands on a hidden tab.
  const initialChannelRef = React.useRef(emailOnly ? "email" : channelProp);
  const [channel, setChannel] = React.useState<AiConvChannel>(
    emailOnly ? "email" : isEmailIntegrated ? channelProp : "chat",
  );
  const [emailTo, setEmailTo] = React.useState(contactEmail);
  const [emailCc, setEmailCc] = React.useState("");
  const [showCc, setShowCc] = React.useState(false);
  const [emailSubject, setEmailSubject] = React.useState(
    emailReplySubject ? `Re: ${emailReplySubject}` : "",
  );
  const [emailMode, setEmailMode] = React.useState<"reply" | "new">(
    emailReplySubject ? "reply" : "new",
  );
  const [emailAttachments, setEmailAttachments] = React.useState<
    AiConvAttachment[]
  >([]);
  const emailFileInputRef = React.useRef<HTMLInputElement>(null);

  const handleEmailFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files?.length) {
      setEmailAttachments((prev) => [
        ...prev,
        ...filesToAttachments(e.target.files as FileList),
      ]);
      e.target.value = "";
    }
  };

  const [, forceUpdate] = React.useReducer((x: number) => x + 1, 0);

  const editor = useEditor({
    extensions: [
      StarterKit,
      TiptapUnderline,
      TiptapLink.configure({ openOnClick: false }),
    ],
    content: "",
    onTransaction: () => forceUpdate(),
    editorProps: {
      attributes: {
        class:
          "min-h-[150px] px-4 py-3 text-body-medium text-foreground outline-none prose prose-sm max-w-none [&_p]:m-0 [&_a]:text-primary [&_a]:underline [&_a]:cursor-pointer",
      },
    },
  });

  const handleChannelChange = (c: AiConvChannel) => {
    setChannel(c);
    onChannelChange?.(c);
  };

  const handleNewEmail = () => {
    setEmailSubject("");
    setEmailCc("");
    setShowCc(false);
    setEmailAttachments([]);
    editor?.commands.clearContent();
  };

  const handleEmailModeChange = (mode: "reply" | "new") => {
    setEmailMode(mode);
    if (mode === "new") {
      handleNewEmail();
    } else {
      setEmailSubject(emailReplySubject ? `Re: ${emailReplySubject}` : "");
    }
  };

  return (
    <div
      className={cn(
        "flex flex-col border-t border-border bg-background",
        className,
      )}
    >
      {isEmailIntegrated && !emailOnly && (
        <div className="border-b border-border px-3 py-2">
          <Tabs
            value={channel}
            onValueChange={(v) => v && handleChannelChange(v as AiConvChannel)}
          >
            <TabsList variant="default" className="w-full">
              <TabsTrigger value="chat" className="flex-1 gap-1.5">
                <MessageSquare className="size-3.5" />
                Chat
              </TabsTrigger>
              <TabsTrigger value="email" className="flex-1 gap-1.5">
                <Mail className="size-3.5" />
                Email
              </TabsTrigger>
            </TabsList>
          </Tabs>
        </div>
      )}

      {mode === "ai" ? (
        <div className="flex items-center gap-2 bg-muted/30 px-4 py-2.5 text-[12px] text-muted-foreground">
          <Bot className="size-3.5 shrink-0 text-muted-foreground" />
          <span>AI is handling this conversation.</span>
          <Button
            variant="link"
            size="sm"
            className="h-auto p-0 text-[12px] font-medium text-foreground"
            onClick={onTakeOver}
          >
            Take Over
          </Button>
          <span>to reply directly.</span>
        </div>
      ) : (
        /* Email panel stays in normal flow to anchor container height;
           chat panel is an absolute overlay so both tabs share identical dimensions */
        <div className="relative">
          <div
            className={cn(
              "flex flex-col",
              channel !== "email" && "invisible pointer-events-none",
            )}
            aria-hidden={channel !== "email"}
          >
            <ComposerEmailFieldRow label="To">
              <input
                type="email"
                value={emailTo}
                onChange={(e) => setEmailTo(e.target.value)}
                placeholder="Recipient email"
                className="min-w-0 flex-1 bg-transparent text-body-medium text-foreground outline-none placeholder:text-muted-foreground"
              />
              <button
                type="button"
                onClick={() => setShowCc((v) => !v)}
                className="flex shrink-0 items-center gap-0.5 text-body-small text-muted-foreground hover:text-foreground"
              >
                CC
                <ChevronDown className="size-3.5" />
              </button>
            </ComposerEmailFieldRow>
            {showCc && (
              <ComposerEmailFieldRow label="CC">
                <input
                  type="email"
                  value={emailCc}
                  onChange={(e) => setEmailCc(e.target.value)}
                  placeholder="CC email"
                  className="min-w-0 flex-1 bg-transparent text-body-medium text-foreground outline-none placeholder:text-muted-foreground"
                />
              </ComposerEmailFieldRow>
            )}
            {emailMode !== "reply" && (
              <ComposerEmailFieldRow label="Subject">
                <input
                  type="text"
                  value={emailSubject}
                  onChange={(e) => setEmailSubject(e.target.value)}
                  placeholder="Email subject"
                  className="min-w-0 flex-1 bg-transparent text-body-medium text-foreground outline-none placeholder:text-muted-foreground"
                />
              </ComposerEmailFieldRow>
            )}
            <EditorContent editor={editor} />
            {emailAttachments.length > 0 && (
              <div className="flex flex-wrap gap-2 border-t border-border px-4 py-3">
                {emailAttachments.map((attachment) => (
                  <div
                    key={attachment.id}
                    className="flex max-w-[220px] items-center gap-2 border border-border bg-muted py-1.5 pl-2 pr-1"
                  >
                    <FileText className="size-4 shrink-0 text-muted-foreground" />
                    <div className="flex min-w-0 flex-col leading-tight">
                      <span className="truncate text-caption text-foreground">
                        {attachment.name}
                      </span>
                      <span className="text-caption text-muted-foreground">
                        {attachment.size}
                      </span>
                    </div>
                    <button
                      type="button"
                      aria-label={`Remove ${attachment.name}`}
                      onClick={() =>
                        setEmailAttachments((prev) =>
                          prev.filter((a) => a.id !== attachment.id),
                        )
                      }
                      className="flex size-5 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground"
                    >
                      <X className="size-3" />
                    </button>
                  </div>
                ))}
              </div>
            )}
            <div className="flex items-center justify-between border-t border-border px-3 py-2">
              <div className="flex items-center gap-0.5">
                {emailReplySubject && !hideEmailModeToggle && (
                  <>
                    <ToggleGroup
                      type="single"
                      variant="outline"
                      size="sm"
                      spacing={0}
                      value={[emailMode]}
                      onValueChange={(values) => {
                        const v = values[0] as "reply" | "new" | undefined;
                        if (v) handleEmailModeChange(v);
                      }}
                      className="mr-1.5"
                    >
                      <ToggleGroupItem value="reply">Reply</ToggleGroupItem>
                      <ToggleGroupItem value="new">New email</ToggleGroupItem>
                    </ToggleGroup>
                    <Separator orientation="vertical" className="mr-1.5 h-4" />
                  </>
                )}
                <ComposerToolbarButton
                  label="Bold"
                  icon={Bold}
                  pressed={editor?.isActive("bold")}
                  onToggle={() => editor?.chain().focus().toggleBold().run()}
                />
                <ComposerToolbarButton
                  label="Italic"
                  icon={Italic}
                  pressed={editor?.isActive("italic")}
                  onToggle={() => editor?.chain().focus().toggleItalic().run()}
                />
                <ComposerToolbarButton
                  label="Underline"
                  icon={Underline}
                  pressed={editor?.isActive("underline")}
                  onToggle={() =>
                    editor?.chain().focus().toggleUnderline().run()
                  }
                />
                <Separator orientation="vertical" className="mx-1.5 h-4" />
                <ComposerLinkPopover editor={editor} />
                <ComposerToolbarButton
                  label={
                    emailAttachments.length > 0
                      ? `Attach file — ${emailAttachments.length} attached`
                      : "Attach file"
                  }
                  icon={Paperclip}
                  pressed={emailAttachments.length > 0}
                  onToggle={() => emailFileInputRef.current?.click()}
                />
                <input
                  ref={emailFileInputRef}
                  type="file"
                  multiple
                  className="sr-only"
                  tabIndex={-1}
                  onChange={handleEmailFileChange}
                />
              </div>
              <div className="flex items-center gap-2">
                <Button
                  size="sm"
                  onClick={() => {
                    const html = editor?.getHTML() ?? "";
                    onSendEmail?.({
                      content: html,
                      to: emailTo,
                      cc: emailCc,
                      subject: emailSubject,
                      isReply: emailMode === "reply",
                      attachments: emailAttachments,
                    });
                    editor?.commands.clearContent();
                    setEmailAttachments([]);
                  }}
                  disabled={!editor || editor.isEmpty || !emailTo.trim()}
                >
                  <Send className="mr-1.5 size-3.5" />
                  Send Email
                </Button>
              </div>
            </div>
          </div>

          {/* Chat compose — absolute overlay, fills exact same height as email panel */}
          {channel === "chat" && (
            <div className="absolute inset-0 flex flex-col gap-3 p-4">
              <ChatInputArea
                value={inputValue}
                onChange={(v) => onInputChange?.(v)}
                onSend={(text) => onSend?.(text)}
                onAttachFile={onAttachFile}
                onAttachImage={onAttachImage}
                placeholder="Reply to lead…"
                hint={false}
              />
              {initialChannelRef.current !== "email" && (
                <Button
                  variant="outline"
                  size="sm"
                  className="self-start"
                  onClick={onLetAiHandle}
                >
                  <Bot className="mr-1.5 size-3.5" />
                  Let AI Handle
                </Button>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// ChatThread
// ---------------------------------------------------------------------------

export interface ChatThreadProps {
  contact: AiConvContact;
  status: AiConvStatus;
  /** Hides the status chip next to the contact name — for threads with no real status yet (e.g. composing a brand-new email). */
  hideStatusChip?: boolean;
  mode: AiConvMode;
  messages: AiConvMessage[];
  isAiTyping?: boolean;
  /** Active reply channel — "chat" (default) or "email". */
  channel?: AiConvChannel;
  onChannelChange?: (channel: AiConvChannel) => void;
  /** When true, the Email tab is shown in the composer. Defaults to false. */
  isEmailIntegrated?: boolean;
  /**
   * Locks the thread to the email channel: the composer's Chat/Email toggle
   * is hidden and AI hand-off actions never show. For embedded email-only
   * contexts (e.g. the Kanban slide-out's Email & Notes tab). Implies email
   * is integrated; `channel` / `isEmailIntegrated` are ignored while set.
   */
  emailOnly?: boolean;
  inputValue?: string;
  onInputChange?: (v: string) => void;
  /** Fired when the user sends a chat message. */
  onSend?: (content: string) => void;
  /** Fired when the user sends an email. */
  onSendEmail?: (payload: AiConvEmailPayload) => void;
  onTakeOver?: () => void;
  onLetAiHandle?: () => void;
  /** Called when the user selects files via the attachment button in the composer. */
  onAttachFile?: (files: FileList) => void;
  /** Called when the user selects images via the image upload button in the composer. */
  onAttachImage?: (files: FileList) => void;
  /** Pre-fills the email Subject field with "Re: [emailReplySubject]" for reply threads. */
  emailReplySubject?: string;
  /** Hides the composer's "Reply / New email" toggle. See `ChatComposerProps`. Defaults to false. */
  hideEmailModeToggle?: boolean;
  onReopen?: () => void;
  /** Marks an open conversation as resolved. Shows a "Mark as Closed" menu item when set. */
  onClose?: () => void;
  onMarkUrgent?: () => void;
  onUnmarkUrgent?: () => void;
  onArchive?: () => void;
  onAssignToAdvisor?: () => void;
  /** True when older messages can be loaded (e.g. paginated history). */
  hasMoreMessages?: boolean;
  /** True while a `onLoadMoreMessages` request is in-flight. */
  isLoadingMoreMessages?: boolean;
  /** Fired when the consumer should fetch older messages. */
  onLoadMoreMessages?: () => void;
  /** Back to conversation list. Renders on mobile only unless `showBackButton` is set. */
  onBack?: () => void;
  /**
   * Show the back button at every breakpoint (default: mobile only). For
   * embedded drill-in layouts where the list and thread swap in place.
   */
  showBackButton?: boolean;
  /** Mobile only — show lead info panel. */
  onShowLeadInfo?: () => void;
  className?: string;
}

export function ChatThread({
  contact,
  status,
  hideStatusChip = false,
  mode,
  messages,
  isAiTyping = false,
  channel: channelProp,
  onChannelChange,
  isEmailIntegrated,
  emailOnly = false,
  inputValue,
  onInputChange,
  onSend,
  onSendEmail,
  onTakeOver,
  onLetAiHandle,
  onAttachFile,
  onAttachImage,
  emailReplySubject,
  hideEmailModeToggle,
  onReopen,
  onClose,
  onMarkUrgent,
  onUnmarkUrgent,
  onArchive,
  onAssignToAdvisor,
  hasMoreMessages,
  isLoadingMoreMessages,
  onLoadMoreMessages,
  onBack,
  showBackButton = false,
  onShowLeadInfo,
  className,
}: ChatThreadProps) {
  const channel = emailOnly ? "email" : channelProp;
  const aiIsHandling = mode === "ai";
  const isClosed = status === "closed";
  const hasUrgentAction =
    status === "needs-attention"
      ? Boolean(onUnmarkUrgent)
      : Boolean(onMarkUrgent);
  const hasMenuActions =
    Boolean(onShowLeadInfo) ||
    hasUrgentAction ||
    Boolean(onAssignToAdvisor) ||
    (!isClosed && Boolean(onClose)) ||
    Boolean(onArchive);

  const scrollRef = React.useRef<HTMLDivElement>(null);
  // Captures scrollHeight just before older messages are prepended, so we can
  // restore the user's visible scroll offset once the new nodes render.
  const preLoadScrollHeightRef = React.useRef<number | null>(null);

  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
    if (!hasMoreMessages || isLoadingMoreMessages || !onLoadMoreMessages) {
      return;
    }
    if (e.currentTarget.scrollTop <= 80) {
      preLoadScrollHeightRef.current = e.currentTarget.scrollHeight;
      onLoadMoreMessages();
    }
  };

  // Tracks the last "tail" message id so we can tell an append (new message,
  // tail changed) apart from a prepend (older history loaded, tail unchanged).
  const prevLastMessageIdRef = React.useRef<string | undefined>(undefined);
  const prevContactIdRef = React.useRef<string>(contact.id);

  React.useLayoutEffect(() => {
    const el = scrollRef.current;
    if (!el) return;

    // Prepend (older messages just loaded) — restore scroll so the user
    // stays anchored to the message they were reading.
    if (preLoadScrollHeightRef.current !== null) {
      el.scrollTop = el.scrollHeight - preLoadScrollHeightRef.current;
      preLoadScrollHeightRef.current = null;
      prevLastMessageIdRef.current = messages[messages.length - 1]?.id;
      prevContactIdRef.current = contact.id;
      return;
    }

    const currentLastId = messages[messages.length - 1]?.id;
    const contactChanged = prevContactIdRef.current !== contact.id;
    const tailChanged = prevLastMessageIdRef.current !== currentLastId;

    // Opening a conversation or appending a new message (sent, received,
    // or system) — pin to the bottom.
    if (contactChanged || tailChanged) {
      el.scrollTop = el.scrollHeight;
    }

    prevLastMessageIdRef.current = currentLastId;
    prevContactIdRef.current = contact.id;
  }, [contact.id, messages]);

  // Typing indicator adds DOM height — keep the view pinned to bottom.
  React.useLayoutEffect(() => {
    if (!isAiTyping) return;
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [isAiTyping]);

  return (
    <div className={cn("flex flex-col bg-background", className)}>
      {/* Header */}
      <div
        className={cn(
          PANEL_HEADER_HEIGHT,
          "flex items-center gap-3 border-b border-border px-4",
        )}
      >
        {onBack && (
          <Button
            variant="ghost"
            size="icon"
            className={cn("size-8 shrink-0", !showBackButton && "md:hidden")}
            onClick={onBack}
            aria-label="Back to conversations"
          >
            <ArrowLeft className="size-4" />
          </Button>
        )}

        <ContactAvatar name={contact.name} size="md" />

        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2">
            <span className="text-label-small text-foreground">
              {displayContactName(contact.name)}
            </span>
            {!hideStatusChip && (
              <ConversationStatusChip status={status} showDot />
            )}
          </div>
          {contact.email && (
            <p className="text-body-small text-muted-foreground">
              {contact.email}
            </p>
          )}
        </div>

        <div className="flex shrink-0 items-center gap-2">
          <div className="hidden items-center gap-2 md:flex">
            {isClosed && onReopen && (
              <Button variant="outline" size="sm" onClick={onReopen}>
                Reopen
              </Button>
            )}
            {!isClosed && aiIsHandling && (
              <Button size="sm" onClick={onTakeOver}>
                Take Over
              </Button>
            )}
            {!isClosed && !aiIsHandling && channel !== "email" && (
              <Button variant="outline" size="sm" onClick={onLetAiHandle}>
                <Bot className="mr-1.5 size-3.5" />
                Let AI Handle
              </Button>
            )}
          </div>

          {hasMenuActions && (
            <DropdownMenu>
              <DropdownMenuTrigger
                className={cn(
                  buttonVariants({ variant: "ghost", size: "icon" }),
                  "size-8",
                )}
                aria-label="More actions"
              >
                <MoreHorizontal className="size-4" />
              </DropdownMenuTrigger>
              <DropdownMenuContent>
                {onShowLeadInfo && (
                  <>
                    <DropdownMenuItem
                      className="md:hidden"
                      onClick={onShowLeadInfo}
                    >
                      <ChevronRight className="mr-2 size-4" />
                      Lead Info
                    </DropdownMenuItem>
                    <DropdownMenuSeparator className="md:hidden" />
                  </>
                )}
                {status === "needs-attention"
                  ? onUnmarkUrgent && (
                      <DropdownMenuItem onClick={onUnmarkUrgent}>
                        <Flag className="mr-2 size-4" />
                        Unmark Urgent
                      </DropdownMenuItem>
                    )
                  : onMarkUrgent && (
                      <DropdownMenuItem onClick={onMarkUrgent}>
                        <Flag className="mr-2 size-4" />
                        Mark as Urgent
                      </DropdownMenuItem>
                    )}
                {onAssignToAdvisor && (
                  <DropdownMenuItem onClick={onAssignToAdvisor}>
                    <UserCheck className="mr-2 size-4" />
                    Assign to advisor
                  </DropdownMenuItem>
                )}
                {!isClosed && onClose && (
                  <DropdownMenuItem onClick={onClose}>
                    <CheckCircle2 className="mr-2 size-4" />
                    Mark as Closed
                  </DropdownMenuItem>
                )}
                {onArchive && (
                  <>
                    <DropdownMenuSeparator />
                    <DropdownMenuItem onClick={onArchive}>
                      <Archive className="mr-2 size-4" />
                      Archive
                    </DropdownMenuItem>
                  </>
                )}
              </DropdownMenuContent>
            </DropdownMenu>
          )}
        </div>
      </div>

      {/* Messages */}
      <div
        ref={scrollRef}
        onScroll={handleScroll}
        className="flex flex-1 flex-col gap-4 overflow-y-auto p-4"
        tabIndex={0}
      >
        {isLoadingMoreMessages && (
          <div className="flex justify-center py-1 text-caption text-muted-foreground">
            Loading older messages...
          </div>
        )}
        {messages.length === 0 ? (
          <div className="flex flex-1 flex-col items-center justify-center gap-2 text-muted-foreground">
            <MessageSquare className="size-8 opacity-30" />
            <p className="text-body-small">No messages yet</p>
          </div>
        ) : (
          messages.map((msg) => (
            <ChatBubble key={msg.id} message={msg} channel={channel} />
          ))
        )}

        {isAiTyping && !isClosed && (
          <div className="flex gap-2.5">
            <BubbleAvatar role="bot" />
            <div className="flex flex-col gap-1">
              <span className="text-caption text-muted-foreground">
                AI Assistant
              </span>
              <div className="flex items-center gap-1 border border-border bg-muted/60 px-3 py-2.5">
                <span className="size-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:0ms]" />
                <span className="size-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:150ms]" />
                <span className="size-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:300ms]" />
              </div>
            </div>
          </div>
        )}
      </div>

      {/* Composer / locked banner */}
      {isClosed ? (
        <div className="flex items-center gap-3 border-t border-border bg-muted/30 px-4 py-3 text-body-small text-muted-foreground">
          <Lock className="size-3.5 shrink-0" />
          <span>This conversation is closed.</span>
          <Button
            variant="outline"
            size="sm"
            className="ml-auto"
            onClick={onReopen}
          >
            Reopen
          </Button>
        </div>
      ) : (
        <ChatComposer
          mode={mode}
          channel={channel}
          onChannelChange={onChannelChange}
          isEmailIntegrated={isEmailIntegrated}
          emailOnly={emailOnly}
          contactEmail={contact.email}
          inputValue={inputValue}
          onInputChange={onInputChange}
          onSend={onSend}
          onSendEmail={onSendEmail}
          onTakeOver={onTakeOver}
          onLetAiHandle={onLetAiHandle}
          onAttachFile={onAttachFile}
          onAttachImage={onAttachImage}
          emailReplySubject={emailReplySubject}
          hideEmailModeToggle={hideEmailModeToggle}
        />
      )}
    </div>
  );
}
