/**
 * Chat Messages Display Component
 *
 * Renders the chat message list including welcome state, message history, and streaming indicator
 */

import React, { useEffect, useRef, useState } from "react";
import {
  Button,
  Icon,
  Menu,
  MenuItem,
  Popover,
  Position,
} from "@blueprintjs/core";
import { ChatMessage, ChatMode, Result, VectorSearchUIPayload, VectorSearchResultItem } from "../../types/types";
import { renderMarkdown } from "../../utils/chatMessageUtils";
import {
  CHAT_HELP_RESPONSE,
  LIVE_AI_HELP_RESPONSE,
  WHATS_NEW_RESPONSE,
  WHATS_NEW_VERSION,
  getRandomTip,
} from "./chatHelpConstants";
import { textToSpeech } from "../../../../ai/multimodalAI";
import { CouncilStepDisplay } from "./CouncilStepDisplay";

// Strips the auto-appended " (model)" suffix from a debate speaker name so the
// header can show the role once and the model once (as a separate tag) instead
// of both inline. `buildSpeakerName` appends `(shortModel)` when the
// participant has no customName; this unwinds that for display.
const getDebateDisplayName = (
  speakerName?: string,
  roleLabel?: string,
): string => {
  if (!speakerName) return "Participant";
  if (
    roleLabel &&
    speakerName.startsWith(`${roleLabel} (`) &&
    speakerName.endsWith(")")
  ) {
    return roleLabel;
  }
  return speakerName.replace(/\s+\([^()]*\)(\s*#\d+)?$/, "");
};

// Helper function to detect if content contains KaTeX formulas
const containsKaTeX = (content: string): boolean => {
  return /\$\$.+?\$\$/s.test(content);
};

// Helper function to detect if content contains Roam queries
const containsRoamQuery = (content: string): boolean => {
  return (
    /\{\{\s*(\[\[query\]\]|query)\s*:/i.test(content) ||
    /^:q\s/m.test(content.trim())
  );
};

// Helper function to detect if content contains Roam callouts
const containsCallout = (content: string): boolean => {
  return /\[?\[?>\]?\]?\s+\[\[!(?:NOTE|INFO|SUMMARY|ABSTRACT|TLDR|TIP|HINT|IMPORTANT|SUCCESS|QUESTION|HELP|FAQ|WARNING|CAUTION|ATTENTION|FAILURE|FAIL|MISSING|DANGER|ERROR|BUG|EXAMPLE|QUOTE)\]\]/i.test(
    content,
  );
};

// Render all data-roam-callout spans inside a container using Roam's renderString
const renderCalloutSpans = (container: HTMLElement): void => {
  const calloutSpans = container.querySelectorAll<HTMLElement>(
    "[data-roam-callout]",
  );
  calloutSpans.forEach((span) => {
    const rawCallout = span.getAttribute("data-roam-callout");
    if (!rawCallout) return;
    const wrapper = document.createElement("div");
    wrapper.style.margin = "8px 0";
    try {
      window.roamAlphaAPI?.ui.components.renderString({
        el: wrapper,
        string: rawCallout,
      });
    } catch (error) {
      console.error("Failed to render callout:", error);
      wrapper.textContent = rawCallout;
    }
    span.replaceWith(wrapper);
  });
};

// Helper component to render message content with KaTeX formulas
// Renders markdown structure normally, but uses Roam API for KaTeX formulas
const MessageContent: React.FC<{ content: string; className?: string }> = ({
  content,
  className,
}) => {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    // Check if content has special Roam elements that need renderString
    const hasRoamElements =
      containsKaTeX(content) ||
      containsRoamQuery(content) ||
      containsCallout(content) ||
      /\{\{\[\[(?:audio|video|youtube|pdf)\]\]:\s*https?:[^\s}]+\}\}/i.test(
        content,
      );

    if (hasRoamElements) {
      // If the entire content is a query, render appropriately
      if (containsRoamQuery(content)) {
        const trimmed = content.trim();
        if (
          trimmed.startsWith("{{query:") ||
          trimmed.startsWith("{{[[query]]")
        ) {
          // Native Roam queries: render with renderString for live display
          containerRef.current.innerHTML = "";
          try {
            (window as any).roamAlphaAPI?.ui.components.renderString({
              el: containerRef.current,
              string: content,
            });
          } catch (error) {
            // Fallback to code block if renderString fails synchronously
            const pre = document.createElement("pre");
            pre.style.margin = "0";
            pre.style.whiteSpace = "pre-wrap";
            pre.style.wordBreak = "break-word";
            const code = document.createElement("code");
            code.textContent = content;
            pre.appendChild(code);
            containerRef.current.innerHTML = "";
            containerRef.current.appendChild(pre);
          }
          return;
        } else if (trimmed.startsWith(":q ")) {
          // Datomic :q queries: render as formatted code block
          // (renderString cannot handle :q queries and causes internal Roam errors)
          const pre = document.createElement("pre");
          pre.style.margin = "0";
          pre.style.whiteSpace = "pre-wrap";
          pre.style.wordBreak = "break-word";
          const code = document.createElement("code");
          code.textContent = content;
          pre.appendChild(code);
          containerRef.current.innerHTML = "";
          containerRef.current.appendChild(pre);
          return;
        }
      }
      // First, render the markdown structure normally
      containerRef.current.innerHTML = renderMarkdown(content);

      // Render any callout blocks via Roam's renderString
      renderCalloutSpans(containerRef.current);

      // Then, find all text nodes and render any Roam-specific elements with Roam API
      const textNodes: Node[] = [];
      const walker = document.createTreeWalker(
        containerRef.current,
        NodeFilter.SHOW_TEXT,
        null,
      );

      let node: Node | null;
      while ((node = walker.nextNode())) {
        // Skip text nodes inside <code> or <pre> — their content is raw text, not Roam syntax
        const insideCode = (node.parentNode as Element)?.closest?.("code, pre");
        if (insideCode) continue;

        if (
          node.textContent &&
          (/\$\$.+?\$\$/s.test(node.textContent) ||
            /\{\{\[\[(?:audio|video|youtube|pdf)\]\]:/i.test(node.textContent))
        ) {
          textNodes.push(node);
        }
      }

      // Replace text nodes containing Roam elements with Roam-rendered spans
      textNodes.forEach((textNode) => {
        const text = textNode.textContent || "";
        const parent = textNode.parentNode;

        if (!parent) return;

        // Split by KaTeX formulas and media embeds
        const parts = text.split(
          /(\$\$.+?\$\$|\{\{\[\[(?:audio|video|youtube|pdf)\]\]:\s*https?:[^\s}]+\}\})/is,
        );
        const fragment = document.createDocumentFragment();

        parts.forEach((part) => {
          if (/^\$\$.+?\$\$$/s.test(part)) {
            // This is a KaTeX formula - render with Roam API
            const span = document.createElement("span");
            try {
              window.roamAlphaAPI?.ui.components.renderString({
                el: span,
                string: part,
              });
            } catch (error) {
              console.error("Failed to render KaTeX:", error);
              span.textContent = part; // Fallback to showing raw formula
            }
            fragment.appendChild(span);
          } else if (/^\{\{\[\[(?:audio|video|youtube|pdf)\]\]:/i.test(part)) {
            // This is a media/PDF embed - render with Roam API
            const div = document.createElement("div");
            div.style.display = "block";
            div.style.margin = "8px 0";
            try {
              window.roamAlphaAPI?.ui.components.renderString({
                el: div,
                string: part,
              });
            } catch (error) {
              console.error("Failed to render media embed:", error);
              div.textContent = part; // Fallback to showing raw embed syntax
            }
            fragment.appendChild(div);
          } else if (part) {
            // Regular text
            fragment.appendChild(document.createTextNode(part));
          }
        });

        parent.replaceChild(fragment, textNode);
      });
    } else {
      // No special Roam elements - just use regular markdown rendering
      containerRef.current.innerHTML = renderMarkdown(content);
      renderCalloutSpans(containerRef.current);
    }
  }, [content]);

  return <div ref={containerRef} className={className} />;
};

// Helper component for editing a message using Roam's native block editor
const EditableMessage: React.FC<{
  blockUid: string;
  onSave: () => void;
  onCancel: () => void;
}> = ({ blockUid, onSave, onCancel }) => {
  const editorContainerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!editorContainerRef.current || !blockUid) return;

    // Clear any existing content
    editorContainerRef.current.innerHTML = "";

    // Use Roam's renderBlock API to render the editable block
    try {
      window.roamAlphaAPI.ui.components.renderBlock({
        uid: blockUid,
        el: editorContainerRef.current,
        "zoom-path?": true,
      });
    } catch (error) {
      console.error("Failed to render block for editing:", error);
      editorContainerRef.current.innerHTML =
        '<div class="chat-message-edit-error">Failed to load editor</div>';
    }

    return () => {
      // Cleanup on unmount
      if (editorContainerRef.current) {
        editorContainerRef.current.innerHTML = "";
      }
    };
  }, [blockUid]);

  // Handle Escape key to cancel editing
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        e.preventDefault();
        e.stopPropagation();
        onCancel();
      }
    };

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [onCancel]);

  return (
    <div className="chat-message-edit-wrapper">
      <div ref={editorContainerRef} className="chat-message-edit-container" />
      <div className="chat-message-edit-actions">
        <Button
          icon="tick"
          intent="success"
          small
          onClick={onSave}
          title="Save changes"
        >
          Save
        </Button>
        <Button
          icon="cross"
          minimal
          small
          onClick={onCancel}
          title="Cancel (Esc)"
        >
          Cancel
        </Button>
      </div>
    </div>
  );
};

// Helper component for expandable content in confirmation preview
const ExpandableContent: React.FC<{
  content: string;
  truncateAt?: number;
  className?: string;
}> = ({ content, truncateAt = 80, className }) => {
  const [isExpanded, setIsExpanded] = useState(false);
  const needsTruncation = content.length > truncateAt;

  if (!needsTruncation) {
    return <span className={className}>{content || "(empty)"}</span>;
  }

  return (
    <span className={className}>
      {isExpanded ? content : content.substring(0, truncateAt) + "..."}
      <button
        className="tool-confirmation-expand-btn"
        onClick={(e) => {
          e.stopPropagation();
          setIsExpanded(!isExpanded);
        }}
        title={isExpanded ? "Show less" : "Show full content"}
      >
        {isExpanded ? "▲" : "▼"}
      </button>
    </span>
  );
};

interface PendingToolConfirmation {
  toolName: string;
  toolCallId: string;
  args: Record<string, any>;
  timestamp: number;
}

export interface PendingUserChoice {
  commandId: string;
  title: string;
  hintsEnabled?: boolean;
  options: Array<{
    id: string;
    label: string;
    type: "radio" | "checkbox" | "text" | "slider";
    choices?: Array<{ value: string; label: string; hint?: string }>;
    defaultValue?: string;
    placeholder?: string;
    min?: number;
    max?: number;
    step?: number;
  }>;
  timestamp: number;
}

/**
 * Inline choice form component — used by both ask_user_choice tool and PDF export bypass
 */
/**
 * Renders a single choice item (radio or checkbox) with optional hint tooltip
 */
const ChoiceItem: React.FC<{
  choice: { value: string; label: string; hint?: string };
  hintsVisible: boolean;
  children: React.ReactNode;
}> = ({ choice, hintsVisible, children }) => {
  if (hintsVisible && choice.hint) {
    return (
      <Popover
        content={<div className="user-choice-hint-popover">{choice.hint}</div>}
        interactionKind="hover"
        position={Position.RIGHT}
        hoverOpenDelay={200}
        hoverCloseDelay={100}
        minimal
      >
        {children as React.ReactElement}
      </Popover>
    );
  }
  return <>{children}</>;
};

const UserChoiceForm: React.FC<{
  pendingChoice: PendingUserChoice;
  onSubmit: (selectedOptions: Record<string, string>) => void;
  onCancel: () => void;
}> = ({ pendingChoice, onSubmit, onCancel }) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [selections, setSelections] = useState<Record<string, string>>(() => {
    const defaults: Record<string, string> = {};
    pendingChoice.options.forEach((opt) => {
      if (opt.type === "text") {
        defaults[opt.id] = opt.defaultValue || "";
      } else if (opt.type === "checkbox") {
        defaults[opt.id] = "";
      } else if (opt.type === "slider") {
        const min = opt.min ?? 0;
        const max = opt.max ?? 10;
        defaults[opt.id] =
          opt.defaultValue || String(Math.round((min + max) / 2));
      } else {
        defaults[opt.id] = opt.defaultValue || opt.choices?.[0]?.value || "";
      }
    });
    return defaults;
  });
  const [hintsVisible, setHintsVisible] = useState(false);

  // Auto-focus and scroll into view on mount
  useEffect(() => {
    if (containerRef.current) {
      containerRef.current.scrollIntoView({
        behavior: "smooth",
        block: "nearest",
      });
      containerRef.current.focus();
    }
  }, []);

  const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
    if (e.key === "Enter" && (e.target as HTMLElement).tagName !== "TEXTAREA") {
      e.preventDefault();
      onSubmit(selections);
      return;
    }
    if (e.key === "Escape") {
      e.preventDefault();
      onCancel();
      return;
    }
    // Number keys 1-9 for quick radio/checkbox selection
    const num = parseInt(e.key, 10);
    if (num >= 1 && num <= 9) {
      // Don't intercept when typing in text inputs
      const tag = (e.target as HTMLElement).tagName;
      if (tag === "TEXTAREA" || tag === "INPUT") {
        const inputType = (e.target as HTMLInputElement).type;
        if (
          inputType === "text" ||
          inputType === "number" ||
          tag === "TEXTAREA"
        )
          return;
      }
      // Find radio/checkbox groups
      const choiceGroups = pendingChoice.options.filter(
        (opt) =>
          (opt.type === "radio" || opt.type === "checkbox" || !opt.type) &&
          opt.choices,
      );
      if (choiceGroups.length === 0) return;
      // Apply to first (or only) choice group
      const group = choiceGroups[0];
      const idx = num - 1;
      if (group.choices && idx < group.choices.length) {
        e.preventDefault();
        const choiceValue = group.choices[idx].value;
        if (group.type === "checkbox") {
          toggleCheckbox(group.id, choiceValue);
        } else {
          setSelections((prev) => ({ ...prev, [group.id]: choiceValue }));
        }
      }
    }
  };

  // Check if any choice has a hint
  const hasHints =
    pendingChoice.hintsEnabled &&
    pendingChoice.options.some((opt) => opt.choices?.some((c) => c.hint));

  const toggleCheckbox = (groupId: string, value: string) => {
    setSelections((prev) => {
      const current = prev[groupId]
        ? prev[groupId].split(",").filter(Boolean)
        : [];
      const updated = current.includes(value)
        ? current.filter((v) => v !== value)
        : [...current, value];
      return { ...prev, [groupId]: updated.join(",") };
    });
  };

  return (
    <div
      className="full-results-chat-user-choice"
      ref={containerRef}
      tabIndex={0}
      onKeyDown={handleKeyDown}
    >
      <div className="user-choice-header">
        <Icon icon="info-sign" size={14} />
        <span>{pendingChoice.title}</span>
        {hasHints && (
          <Button
            minimal
            small
            icon="lightbulb"
            intent={hintsVisible ? "warning" : "none"}
            className="user-choice-hint-toggle"
            title={hintsVisible ? "Hide hints" : "Show hints"}
            onClick={() => setHintsVisible(!hintsVisible)}
          />
        )}
      </div>
      {pendingChoice.options.map((optGroup) => (
        <div key={optGroup.id} className="user-choice-group">
          <div className="user-choice-label">{optGroup.label}</div>

          {/* Radio: single choice */}
          {(optGroup.type === "radio" || !optGroup.type) &&
            optGroup.choices && (
              <div className="user-choice-options">
                {optGroup.choices.map((choice) => (
                  <ChoiceItem
                    key={choice.value}
                    choice={choice}
                    hintsVisible={hintsVisible}
                  >
                    <label className="user-choice-radio">
                      <input
                        type="radio"
                        name={`user-choice-${pendingChoice.commandId}-${optGroup.id}`}
                        value={choice.value}
                        checked={selections[optGroup.id] === choice.value}
                        onChange={() =>
                          setSelections((prev) => ({
                            ...prev,
                            [optGroup.id]: choice.value,
                          }))
                        }
                      />
                      <span>{choice.label}</span>
                      {hintsVisible && choice.hint && (
                        <Icon
                          icon="lightbulb"
                          size={11}
                          className="user-choice-hint-icon"
                        />
                      )}
                    </label>
                  </ChoiceItem>
                ))}
              </div>
            )}

          {/* Checkbox: multiple choice */}
          {optGroup.type === "checkbox" && optGroup.choices && (
            <div className="user-choice-options">
              {optGroup.choices.map((choice) => {
                const selectedValues = selections[optGroup.id]
                  ? selections[optGroup.id].split(",").filter(Boolean)
                  : [];
                return (
                  <ChoiceItem
                    key={choice.value}
                    choice={choice}
                    hintsVisible={hintsVisible}
                  >
                    <label className="user-choice-checkbox">
                      <input
                        type="checkbox"
                        value={choice.value}
                        checked={selectedValues.includes(choice.value)}
                        onChange={() =>
                          toggleCheckbox(optGroup.id, choice.value)
                        }
                      />
                      <span>{choice.label}</span>
                      {hintsVisible && choice.hint && (
                        <Icon
                          icon="lightbulb"
                          size={11}
                          className="user-choice-hint-icon"
                        />
                      )}
                    </label>
                  </ChoiceItem>
                );
              })}
            </div>
          )}

          {/* Text: free-form input */}
          {optGroup.type === "text" && (
            <div className="user-choice-text-wrapper">
              <textarea
                className="user-choice-text-input"
                placeholder={optGroup.placeholder || "Type your answer..."}
                value={selections[optGroup.id] || ""}
                onChange={(e) =>
                  setSelections((prev) => ({
                    ...prev,
                    [optGroup.id]: e.target.value,
                  }))
                }
                rows={2}
              />
            </div>
          )}

          {/* Slider: range value */}
          {optGroup.type === "slider" &&
            (() => {
              const min = optGroup.min ?? 0;
              const max = optGroup.max ?? 10;
              const step = optGroup.step ?? 1;
              const currentVal =
                selections[optGroup.id] || String(Math.round((min + max) / 2));
              return (
                <div className="user-choice-slider-wrapper">
                  <input
                    type="range"
                    className="user-choice-slider"
                    min={min}
                    max={max}
                    step={step}
                    value={currentVal}
                    onChange={(e) =>
                      setSelections((prev) => ({
                        ...prev,
                        [optGroup.id]: e.target.value,
                      }))
                    }
                  />
                  <div className="user-choice-slider-labels">
                    <span className="user-choice-slider-min">{min}</span>
                    <span className="user-choice-slider-value">
                      {currentVal}
                    </span>
                    <span className="user-choice-slider-max">{max}</span>
                  </div>
                </div>
              );
            })()}
        </div>
      ))}
      <div className="user-choice-buttons">
        <Button
          intent="primary"
          icon="tick"
          small
          onClick={() => onSubmit(selections)}
        >
          Confirm
        </Button>
        <Button intent="none" icon="cross" small onClick={onCancel}>
          Cancel
        </Button>
      </div>
    </div>
  );
};

/**
 * Vector Search Results component - renders structured search results
 * with collapsible cards, relevance badges, and add-to-context buttons.
 */
const VectorSearchResults: React.FC<{
  data: VectorSearchUIPayload;
  onAddResults?: (results: Result[]) => void;
}> = ({ data, onAddResults }) => {
  const [expandedIndices, setExpandedIndices] = useState<Set<number>>(
    new Set()
  );
  const [addedIndices, setAddedIndices] = useState<Set<number>>(new Set());
  const [allAdded, setAllAdded] = useState(false);
  const PREVIEW_LINES = 5;
  const PREVIEW_CHARS = 500;

  const toggleExpand = (index: number) => {
    setExpandedIndices((prev) => {
      const next = new Set(prev);
      if (next.has(index)) next.delete(index);
      else next.add(index);
      return next;
    });
  };

  const buildResultObjects = (items: VectorSearchResultItem[]): Result[] => {
    const results: Result[] = [];
    const isRoamSource = (s: string) => s === "roam-pages" || s === "roam-dnp";

    for (const item of items) {
      if (isRoamSource(item.source) && (item.firstTopBlockUid || item.blockUids.length > 0)) {
        // Use firstTopBlockUid (top-level block, not page UID) for context.
        // Context expansion will fetch its content + children from the graph.
        const uid = item.firstTopBlockUid || item.blockUids[0];
        results.push({
          uid,
          pageTitle: item.pageTitle,
          source: "vector-search",
          addedByAgent: true,
          addedAt: new Date().toISOString(),
        });
      } else if (isRoamSource(item.source)) {
        // Roam result without any block UIDs — resolve page UID from title
        let pageUid: string | undefined;
        try {
          const api = (window as any).roamAlphaAPI;
          if (api) {
            pageUid = api.q(
              `[:find ?uid . :where [?p :node/title "${item.pageTitle.replace(/"/g, '\\"')}"] [?p :block/uid ?uid]]`
            ) as string | undefined;
          }
        } catch {}
        results.push({
          uid: pageUid,
          pageTitle: item.pageTitle,
          isPage: true,
          source: "vector-search",
          addedByAgent: true,
          addedAt: new Date().toISOString(),
        });
      } else {
        // Uploaded file result - no block UID, include content directly
        results.push({
          content: item.content,
          text: item.content.slice(0, 200),
          pageTitle: item.pageTitle,
          source: "vector-search",
          addedByAgent: true,
          addedAt: new Date().toISOString(),
        });
      }
    }
    return results;
  };

  const handleAddOne = (item: VectorSearchResultItem) => {
    if (!onAddResults) return;
    onAddResults(buildResultObjects([item]));
    setAddedIndices((prev) => new Set(prev).add(item.index));
  };

  const handleAddAll = () => {
    if (!onAddResults) return;
    onAddResults(buildResultObjects(data.results));
    setAllAdded(true);
    setAddedIndices(new Set(data.results.map((r) => r.index)));
  };

  const getRelevanceClass = (score: number): string => {
    if (score >= 80) return "vs-relevance-high";
    if (score >= 60) return "vs-relevance-medium";
    if (score >= 40) return "vs-relevance-low";
    return "vs-relevance-poor";
  };

  return (
    <div className="vs-results-container">
      <div className="vs-results-header">
        <span className="vs-results-summary">
          {data.results.length} result{data.results.length !== 1 ? "s" : ""} for
          &ldquo;{data.query}&rdquo;
          <span className="vs-results-time">{data.executionTime}s</span>
        </span>
        {onAddResults && (
          <button
            className={`vs-add-all-btn ${allAdded ? "vs-added" : ""}`}
            onClick={handleAddAll}
            disabled={allAdded}
            title={allAdded ? "All results added to context" : "Add all results to context"}
          >
            <Icon icon={allAdded ? "tick" : "plus"} size={12} />
            {allAdded ? "Added all" : "Add all to context"}
          </button>
        )}
      </div>

      <div className="vs-results-list">
        {data.results.map((item) => {
          const lines = item.content.split("\n");
          const isLongByLines = lines.length > PREVIEW_LINES;
          const isLongByChars = item.content.length > PREVIEW_CHARS;
          const isLong = isLongByLines || isLongByChars;
          const isExpanded = expandedIndices.has(item.index);
          const isAdded = addedIndices.has(item.index);
          let displayContent: string;
          if (isLong && !isExpanded) {
            // Truncate by lines first, then by characters
            let preview = lines.slice(0, PREVIEW_LINES).join("\n");
            if (preview.length > PREVIEW_CHARS) {
              preview = preview.slice(0, PREVIEW_CHARS) + "…";
            }
            displayContent = preview;
          } else {
            displayContent = item.content;
          }

          return (
            <div key={item.index} className="vs-result-card">
              <div className="vs-result-header">
                <span className="vs-result-title">
                  {(item.source === "roam-pages" || item.source === "roam-dnp") ? (
                    <>
                      {item.source === "roam-dnp" && (
                        <Icon icon="calendar" size={11} className="vs-source-icon vs-dnp-icon" />
                      )}
                      <span
                        className="vs-page-ref vs-clickable"
                        onClick={() => {
                          try {
                            const api = (window as any).roamAlphaAPI;
                            if (api?.ui?.mainWindow?.openPage) {
                              const pageUid = api.q(
                                `[:find ?uid . :where [?p :node/title "${item.pageTitle.replace(/"/g, '\\"')}"] [?p :block/uid ?uid]]`
                              );
                              if (pageUid) {
                                api.ui.mainWindow.openPage({ page: { uid: pageUid } });
                              }
                            }
                          } catch (e) {
                            console.warn("Failed to open page:", e);
                          }
                        }}
                        title={`Open [[${item.pageTitle}]] in Roam`}
                      >
                        [[{item.pageTitle}]]
                      </span>
                    </>
                  ) : (
                    <span className="vs-file-name">{item.pageTitle}</span>
                  )}
                </span>
                <span
                  className={`vs-relevance-badge ${getRelevanceClass(item.score)}`}
                  title={`Relevance: ${item.score}%`}
                >
                  {item.score}%
                </span>
              </div>

              <div
                className="vs-result-content"
                dangerouslySetInnerHTML={{
                  __html: renderMarkdown(displayContent),
                }}
              />

              <div className="vs-result-actions">
                {isLong && (
                  <button
                    className="vs-expand-btn"
                    onClick={() => toggleExpand(item.index)}
                  >
                    <Icon
                      icon={isExpanded ? "chevron-up" : "chevron-down"}
                      size={12}
                    />
                    {isExpanded ? "Show less" : "Show more"}
                  </button>
                )}
                {onAddResults && item.source !== "user-upload" && (
                  <button
                    className={`vs-add-btn ${isAdded ? "vs-added" : ""}`}
                    onClick={() => handleAddOne(item)}
                    disabled={isAdded}
                    title={isAdded ? "Added to context" : "Add to context"}
                  >
                    <Icon icon={isAdded ? "tick" : "plus"} size={12} />
                    {isAdded ? "Added" : "Add to context"}
                  </button>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

interface ChatMessagesDisplayProps {
  chatMessages: ChatMessage[];
  isTyping: boolean;
  isStreaming: boolean;
  streamingContent: string;
  toolUsageHistory: Array<{
    toolName: string;
    details: string;
    timestamp: number;
    intermediateMessage?: string;
    response?: string;
    vectorSearchData?: VectorSearchUIPayload;
  }>;
  modelTokensLimit: number;
  chatAccessMode: "Balanced" | "Full Access";
  chatMode: ChatMode;
  hasSearchResults: boolean;
  onCopyMessage: (content: string) => void;
  onDeleteMessage: (index: number) => void;
  onRetryMessage: (index: number) => void;
  onSuggestionClick: (suggestion: string) => void;
  onHelpButtonClick: (
    type: "chat" | "liveai" | "tip" | "helpabout" | "whatsnew",
    promptOrContent: string,
  ) => void;
  messagesContainerRef: React.RefObject<HTMLDivElement>;
  // Tool confirmation props
  pendingToolConfirmation: PendingToolConfirmation | null;
  onToolConfirmationResponse: (
    approved: boolean,
    alwaysApprove?: boolean,
    declineReason?: string,
  ) => void;
  declineReasonInput: string;
  setDeclineReasonInput: (value: string) => void;
  // User choice props
  pendingUserChoice: PendingUserChoice | null;
  onUserChoiceResponse: (selectedOptions: Record<string, string>) => void;
  onUserChoiceCancelled: () => void;
  // Edit message props
  editingMessageIndex: number | null;
  onEditMessage: (index: number) => void;
  onSaveEdit: (index: number) => void;
  onCancelEdit: (index: number) => void;
  // Optional status message shown instead of "Thinking..." during long operations
  statusMessage?: string;
  // Callback to add results to context
  onAddResults?: (results: Result[]) => void;
  // Debate mode control callbacks
  onContinueDebate?: () => void;
  onPickDebateSpeaker?: (participantIndex: number) => void;
  onHumanDebateMessage?: (text: string) => void;
  // Human-in-loop: which participant the human's next typed message is
  // directed at (−1 / null = no specific addressee, next speaker picked
  // automatically). Lives in the parent so it's visible from both the
  // speaker-picker card and the chat input area.
  humanDebateTarget?: number | null;
  onHumanDebateTargetChange?: (targetIndex: number | null) => void;
}

export const ChatMessagesDisplay: React.FC<ChatMessagesDisplayProps> = ({
  chatMessages,
  isTyping,
  isStreaming,
  streamingContent,
  toolUsageHistory,
  modelTokensLimit,
  chatAccessMode,
  chatMode,
  hasSearchResults,
  onCopyMessage,
  onDeleteMessage,
  onRetryMessage,
  onSuggestionClick,
  onHelpButtonClick,
  messagesContainerRef,
  pendingToolConfirmation,
  onToolConfirmationResponse,
  declineReasonInput,
  setDeclineReasonInput,
  pendingUserChoice,
  onUserChoiceResponse,
  onUserChoiceCancelled,
  editingMessageIndex,
  onEditMessage,
  onSaveEdit,
  onCancelEdit,
  statusMessage,
  onAddResults,
  onContinueDebate,
  onPickDebateSpeaker,
  onHumanDebateMessage,
  humanDebateTarget,
  onHumanDebateTargetChange,
}) => {
  // Memoize the initial random tip so it doesn't change on every render
  const [initialTip] = React.useState(() => getRandomTip("chat"));

  // Track whether the user has seen the current "What's New" version
  const [hasSeenWhatsNew, setHasSeenWhatsNew] = React.useState(() => {
    const seenVersion = localStorage.getItem("liveai-whats-new-seen-version");
    return seenVersion === WHATS_NEW_VERSION;
  });

  // Keyboard shortcuts for tool confirmation (Enter = Accept, Escape = Decline)
  useEffect(() => {
    if (!pendingToolConfirmation) return;

    const handleKeyDown = (e: KeyboardEvent) => {
      // Don't trigger if user is typing in the decline reason input
      if ((e.target as HTMLElement)?.tagName === "INPUT") return;

      if (e.key === "Enter") {
        e.preventDefault();
        onToolConfirmationResponse(true, false);
      } else if (e.key === "Escape") {
        e.preventDefault();
        onToolConfirmationResponse(false, false, declineReasonInput);
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [pendingToolConfirmation, onToolConfirmationResponse, declineReasonInput]);

  // Function to render help buttons based on help type
  const renderHelpButtons = (
    helpType?: "chat" | "liveai" | "tip" | "whatsnew",
  ) => {
    const showHelpAbout = helpType === "chat" || helpType === "liveai";

    return (
      <>
        {chatMode === "agent" && !helpType && (
          <div
            style={{ fontSize: "12px", color: "#5c7080", marginBottom: "4px" }}
          >
            For more detailed help, ask your question with the{" "}
            <strong>Help tool</strong> enabled in{" "}
            <Icon icon="wrench" size={12} style={{ marginRight: "4px" }} />{" "}
            Agent mode.
          </div>
        )}
        <div className="full-results-chat-suggestions">
          {helpType !== "whatsnew" && (
            <button
              className={!hasSeenWhatsNew ? "whats-new-unseen" : undefined}
              onClick={() => {
                if (!hasSeenWhatsNew) {
                  localStorage.setItem(
                    "liveai-whats-new-seen-version",
                    WHATS_NEW_VERSION
                  );
                  setHasSeenWhatsNew(true);
                }
                onHelpButtonClick("whatsnew", WHATS_NEW_RESPONSE);
              }}
              disabled={isTyping}
            >
              <Icon icon="star" size={12} style={{ marginRight: "4px" }} />
              What's New
              {!hasSeenWhatsNew && <span className="whats-new-badge" />}
            </button>
          )}
          {helpType !== "chat" && (
            <button
              onClick={() => onHelpButtonClick("chat", CHAT_HELP_RESPONSE)}
              disabled={isTyping}
            >
              <Icon icon="chat" size={12} style={{ marginRight: "4px" }} />
              About this chat
            </button>
          )}
          {helpType !== "liveai" && (
            <button
              onClick={() => onHelpButtonClick("liveai", LIVE_AI_HELP_RESPONSE)}
              disabled={isTyping}
            >
              <Icon icon="lightning" size={12} style={{ marginRight: "4px" }} />
              About Live AI
            </button>
          )}
          {showHelpAbout && (
            <button
              onClick={() =>
                onHelpButtonClick(
                  "helpabout",
                  "Help me about By using the help tool, help me understand and use the following feature in a simple and guided way:",
                )
              }
              disabled={isTyping}
            >
              <Icon icon="help" size={12} style={{ marginRight: "4px" }} />
              Help about...
            </button>
          )}
          <button
            onClick={() => onHelpButtonClick("tip", getRandomTip("chat"))}
            disabled={isTyping}
          >
            <Icon icon="lightbulb" size={12} style={{ marginRight: "4px" }} />
            Random Tip
          </button>
        </div>
      </>
    );
  };

  // Initial help buttons for welcome screen (without helpType)
  const helpButtons = renderHelpButtons();

  return (
    <div className="full-results-chat-messages" ref={messagesContainerRef}>
      {chatMessages.length === 0 ? (
        <>
          {/* Help message with icon and random tip */}
          {!hasSearchResults && (
            <div className="full-results-chat-welcome">
              <div className="full-results-chat-help-avatar">
                <Icon icon="help" size={18} intent="primary" />
              </div>
              <div className="full-results-chat-assistant-message chat-help">
                <div
                  style={{
                    display: "flex",
                    alignItems: "flex-start",
                    gap: "8px",
                  }}
                >
                  <Icon
                    icon="lightbulb"
                    size={14}
                    intent="warning"
                    style={{ marginTop: "2px", flexShrink: 0 }}
                  />
                  <div
                    style={{ flex: 1 }}
                    dangerouslySetInnerHTML={{
                      __html: renderMarkdown(initialTip.replace(/^💡\s*/, "")),
                    }}
                  />
                </div>
                {helpButtons}
              </div>
            </div>
          )}

          {/* Main welcome message */}
          <div className="full-results-chat-welcome">
            <div className="full-results-chat-assistant-avatar">🤖</div>
            <div className="full-results-chat-assistant-message">
              {hasSearchResults ? (
                <>
                  Hi! I can help you analyze and understand your search results.
                  What would you like to know?
                </>
              ) : (
                <>Hi! I'm here to help. What can I assist you with today?</>
              )}
              {hasSearchResults && (
                <div className="full-results-chat-suggestions">
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "Give me a short, clear summary of these results highlighting the most important points",
                      )
                    }
                  >
                    Summarize
                  </button>
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "What are the key insights and takeaways from these results?",
                      )
                    }
                  >
                    Key insights
                  </button>
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "What connections exist between these items? Look for page references, tags, block references, and thematic links",
                      )
                    }
                  >
                    Find connections
                  </button>
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "Help me find specific information about [topic] that might be buried in these results",
                      )
                    }
                  >
                    Retrieval
                  </button>
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "What patterns or recurring themes can you extract from these results?",
                      )
                    }
                  >
                    Extract patterns
                  </button>
                  {/* TODO: Future evolution - Deep Analysis mode
                {chatMode === "agent" && (
                  <button
                    onClick={() =>
                      onSuggestionClick(
                        "Can you find related results that might expand on these topics?"
                      )
                    }
                  >
                    <Icon icon="search" size={12} style={{marginRight: '4px'}} />Expand results
                  </button>
                )}
                */}
                </div>
              )}
              {hasSearchResults && (
                <div className="full-results-chat-feature-hint">
                  <strong>
                    {(chatAccessMode === "Balanced" ? "🛡️ " : "🔓 ") +
                      chatAccessMode}
                  </strong>{" "}
                  mode:{" "}
                  {chatAccessMode === "Balanced"
                    ? `2 children levels maximum in blocks, 4 levels in pages, and context limited to ${Math.floor(
                        (modelTokensLimit * 0.5) / 1000,
                      )}k tokens (50% of model context window, approx. ${Math.floor(
                        (modelTokensLimit * 2) / 1000 / 6,
                      )}k words)`
                    : `up to 4 children levels in blocks, full content of pages and broader context up to ${Math.floor(
                        (modelTokensLimit * 0.9) / 1000,
                      )}k tokens (90% of model context window, approx. ${Math.floor(
                        (modelTokensLimit * 3.8) / 1000 / 6,
                      )}k words)`}{" "}
                  {/* TODO: Future evolution - Deep Analysis mode
                  {chatMode === "agent" ? (
                    <>
                      💡 <strong>Deep Analysis mode</strong>: I'll analyze your
                      results first, then search for related content if needed!
                    </>
                  ) : (
                    <>
                      💡 <strong>Chat mode</strong>: I'll focus on analyzing the
                      content you've selected without additional searches.
                    </>
                  )}
                  */}
                </div>
              )}
            </div>
          </div>
        </>
      ) : (
        chatMessages.map((message, index) => {
          const shouldShowCommandName =
            message.role === "user" &&
            message.commandName &&
            message.commandPrompt !== "prompt";

          // If message has no content but has a command, display just the command name
          const displayContent = shouldShowCommandName
            ? message.content
              ? `**[${message.commandName}]**\n\n${message.content}`
              : `**[${message.commandName}]**`
            : message.content;

          // Debate mode: per-turn message, rendered as a chat bubble with speaker header.
          if (message.councilStep?.type === "debate-turn") {
            const step = message.councilStep;
            const stableKey = step.turnId || `debate-turn-${index}`;
            const isStreaming = !step.isComplete;
            // Use the same chat-bubble classes as normal user/assistant messages so
            // participants visually match the rest of the chat. Only the header
            // (round + speaker name) is debate-specific.
            const roleClass = step.isHuman ? "user" : "assistant";
            return (
              <div
                key={stableKey}
                className={`full-results-chat-message ${roleClass} debate-turn-message`}
              >
                <div className="full-results-chat-avatar">
                  {step.isHuman ? "👤" : "🤖"}
                </div>
                <div className="full-results-chat-content">
                  <div className="full-results-chat-text debate-turn-bubble">
                    <div className="debate-turn-header">
                      <span className="debate-turn-round">
                        R{step.round || 1}
                      </span>
                      {(() => {
                        const displayName = getDebateDisplayName(
                          step.speakerName,
                          step.roleLabel,
                        );
                        return (
                          <>
                            <span className="debate-turn-speaker">
                              {displayName}
                            </span>
                            {!step.isHuman && step.modelDisplayName && (
                              <span className="debate-turn-model">
                                ({step.modelDisplayName})
                              </span>
                            )}
                            {step.roleLabel &&
                              step.roleLabel !== displayName &&
                              step.roleLabel !== "Participant" && (
                                <span className="debate-turn-rolelabel">
                                  {step.roleLabel}
                                </span>
                              )}
                          </>
                        );
                      })()}
                      {step.isConclusion && (
                        <span className="debate-turn-badge debate-turn-badge-concluded">
                          CONCLUDED
                        </span>
                      )}
                      {step.isPass && (
                        <span className="debate-turn-badge debate-turn-badge-pass">
                          PASS
                        </span>
                      )}
                    </div>
                    <div
                      dangerouslySetInnerHTML={{
                        __html: renderMarkdown(message.content || ""),
                      }}
                    />
                    {isStreaming && (
                      <span
                        style={{
                          marginLeft: 2,
                          opacity: 0.6,
                          fontSize: 14,
                        }}
                      >
                        ▍
                      </span>
                    )}
                  </div>
                  {step.isComplete &&
                    message.tokensIn !== undefined &&
                    message.tokensOut !== undefined && (
                      <div
                        className="full-results-chat-tokens"
                        style={{ fontSize: 10, marginTop: 4, opacity: 0.6 }}
                      >
                        in: {message.tokensIn.toLocaleString()}, out:{" "}
                        {message.tokensOut.toLocaleString()}
                      </div>
                    )}
                </div>
              </div>
            );
          }

          // Debate: short reaction — smaller indented bubble.
          if (message.councilStep?.type === "debate-reaction") {
            const step = message.councilStep;
            const stableKey = step.turnId || `debate-reaction-${index}`;
            const isStreaming = !step.isComplete;
            return (
              <div
                key={stableKey}
                className="full-results-chat-message assistant debate-reaction-message"
                style={{
                  marginLeft: 32,
                  marginTop: -2,
                  marginBottom: 4,
                  opacity: 0.92,
                }}
              >
                <div
                  className="full-results-chat-avatar"
                  style={{ opacity: 0.65, fontSize: 14 }}
                >
                  ↪
                </div>
                <div className="full-results-chat-content">
                  <div className="full-results-chat-text debate-reaction-bubble">
                    <div className="debate-turn-header debate-reaction-header">
                      <span className="debate-reaction-label">reacts:</span>
                      <span className="debate-turn-speaker">
                        {getDebateDisplayName(step.speakerName, step.roleLabel)}
                      </span>
                      {step.modelDisplayName && (
                        <span className="debate-turn-model">
                          ({step.modelDisplayName})
                        </span>
                      )}
                      {step.reactorOf && (
                        <span className="debate-reaction-target">
                          → {getDebateDisplayName(step.reactorOf)}
                        </span>
                      )}
                      {step.isPass && (
                        <span className="debate-turn-badge debate-turn-badge-pass">
                          PASS
                        </span>
                      )}
                    </div>
                    <div
                      style={{ fontSize: 12, fontStyle: step.isPass ? "italic" : undefined }}
                      dangerouslySetInnerHTML={{
                        __html: renderMarkdown(message.content || ""),
                      }}
                    />
                    {isStreaming && (
                      <span
                        style={{
                          marginLeft: 2,
                          opacity: 0.6,
                          fontSize: 12,
                        }}
                      >
                        ▍
                      </span>
                    )}
                  </div>
                </div>
              </div>
            );
          }

          // Debate: "Continue" button posted when round limit reached.
          if (message.councilStep?.type === "debate-continue") {
            return (
              <div
                key={index}
                className="full-results-chat-message assistant debate-continue-message"
              >
                <div className="full-results-chat-avatar">🏛️</div>
                <div className="full-results-chat-content">
                  <div style={{ fontSize: 12, marginBottom: 6, opacity: 0.8 }}>
                    Round limit reached. Continue the debate, or start a new
                    topic.
                  </div>
                  <Button
                    small
                    icon="play"
                    intent="primary"
                    disabled={!onContinueDebate || isTyping}
                    onClick={() => onContinueDebate && onContinueDebate()}
                  >
                    Continue debate
                  </Button>
                </div>
              </div>
            );
          }

          // Debate: speaker picker strip (human-in-loop).
          if (message.councilStep?.type === "debate-speaker-picker") {
            const state = message.debateState;
            if (!state) return null;
            // Compute suggestion hints: addressee from last main turn + fair-rotation pick.
            let addresseeIdx = -1;
            for (let k = state.turns.length - 1; k >= 0; k -= 1) {
              if (state.turns[k].isReaction) continue;
              const sugg = state.turns[k].nextSuggestion;
              if (sugg) {
                const idx = state.speakerNames.indexOf(sugg);
                if (idx >= 0 && idx !== state.turns[k].participantIndex) {
                  addresseeIdx = idx;
                }
                break;
              }
              break;
            }
            const showFairPickHint = state.ordering === "random";
            return (
              <div
                key={index}
                className="full-results-chat-message assistant debate-speaker-picker"
              >
                <div className="full-results-chat-avatar">🎙️</div>
                <div className="full-results-chat-content">
                  <div style={{ fontSize: 12, marginBottom: 6, opacity: 0.8 }}>
                    Who speaks next?
                  </div>
                  <div
                    style={{
                      display: "flex",
                      flexWrap: "wrap",
                      gap: 6,
                    }}
                  >
                    {state.speakerNames.map((name, i) => {
                      const isNext =
                        !showFairPickHint &&
                        addresseeIdx < 0 &&
                        i === state.nextParticipantIndex;
                      const isAddressed = i === addresseeIdx;
                      const isFairPickHint =
                        showFairPickHint &&
                        !isAddressed &&
                        i === state.nextParticipantIndex;
                      const highlighted =
                        isAddressed || isNext || isFairPickHint;
                      const suffix = isAddressed
                        ? " (addressed)"
                        : isNext
                          ? " (next)"
                          : isFairPickHint
                            ? " (fair pick)"
                            : "";
                      return (
                        <Button
                          key={i}
                          small
                          icon="chat"
                          intent={highlighted ? "primary" : undefined}
                          disabled={!onPickDebateSpeaker || isTyping}
                          onClick={() =>
                            onPickDebateSpeaker && onPickDebateSpeaker(i)
                          }
                        >
                          {name}
                          {suffix}
                        </Button>
                      );
                    })}
                  </div>
                  <div
                    style={{
                      fontSize: 11,
                      marginTop: 8,
                      opacity: 0.8,
                    }}
                  >
                    Or type a message in the chat input below to speak
                    yourself. {addresseeIdx >= 0 && "Hint: the last speaker addressed someone directly."}
                  </div>
                  {onHumanDebateTargetChange && (
                    <div
                      style={{
                        display: "flex",
                        flexWrap: "wrap",
                        alignItems: "center",
                        gap: 6,
                        marginTop: 6,
                        fontSize: 11,
                      }}
                    >
                      <span style={{ opacity: 0.7 }}>Address to:</span>
                      <Button
                        small
                        minimal
                        intent={
                          humanDebateTarget == null ? "primary" : undefined
                        }
                        active={humanDebateTarget == null}
                        onClick={() => onHumanDebateTargetChange(null)}
                      >
                        Anyone
                      </Button>
                      {state.speakerNames.map((name, i) => (
                        <Button
                          key={i}
                          small
                          minimal
                          intent={
                            humanDebateTarget === i ? "primary" : undefined
                          }
                          active={humanDebateTarget === i}
                          onClick={() => onHumanDebateTargetChange(i)}
                        >
                          {name}
                        </Button>
                      ))}
                    </div>
                  )}
                </div>
              </div>
            );
          }

          // Council step messages get their own rendering
          if (message.councilStep) {
            const isSynthesis = message.councilStep.type === "synthesis";
            const isFinalGeneration =
              message.councilStep.type === "generation" &&
              !message.councilStep.isIntermediate;
            const showFooter = isSynthesis || isFinalGeneration;
            return (
              <div key={index} className="full-results-chat-message assistant">
                <div className="full-results-chat-avatar">🏛️</div>
                <div className="full-results-chat-content">
                  <CouncilStepDisplay
                    content={message.content}
                    councilStep={message.councilStep}
                    tokensIn={message.tokensIn}
                    tokensOut={message.tokensOut}
                  />
                  {showFooter && (
                    <div className="full-results-chat-message-footer">
                      <span className="full-results-chat-timestamp">
                        {message.timestamp.toLocaleTimeString()}
                        {message.tokensIn !== undefined &&
                          message.tokensOut !== undefined && (
                            <span className="full-results-chat-tokens">
                              {" "}
                              • Tokens in: {message.tokensIn.toLocaleString()},
                              out: {message.tokensOut.toLocaleString()}
                            </span>
                          )}
                      </span>
                      <span className="full-results-chat-copy-link">
                        <Popover
                          content={
                            <Menu>
                              <MenuItem
                                icon="clipboard"
                                text="Copy to clipboard"
                                onClick={() => onCopyMessage(message.content)}
                              />
                            </Menu>
                          }
                          position={Position.BOTTOM_RIGHT}
                        >
                          <Button icon="more" minimal small />
                        </Popover>
                      </span>
                    </div>
                  )}
                </div>
              </div>
            );
          }

          // Check if this is a help message
          const isHelpMsg = message.isHelpMessage;
          const isTipMessage = message.content.startsWith("💡");

          // Check if this message is being edited
          const isEditing = editingMessageIndex === index;

          return (
            <div
              key={index}
              className={`full-results-chat-message ${message.role}${isEditing ? " editing" : ""}`}
            >
              {/* Use help avatar for help messages */}
              <div
                className={
                  isHelpMsg
                    ? "full-results-chat-help-avatar"
                    : "full-results-chat-avatar"
                }
              >
                {message.role === "user" ? (
                  "👤"
                ) : isHelpMsg ? (
                  <Icon icon="help" size={18} intent="primary" />
                ) : (
                  "🤖"
                )}
              </div>
              <div className="full-results-chat-content">
                {/* Display tool usage if attached to this message */}
                {message.toolUsage && message.toolUsage.length > 0 && (
                  <div className="full-results-chat-tool-history">
                    {message.toolUsage.map((toolUsage, index) => (
                      <div
                        key={`${toolUsage.timestamp}-${index}`}
                        className="full-results-chat-tool-usage-item"
                      >
                        {/* Display intermediate message if present */}
                        {toolUsage.intermediateMessage && (
                          <div
                            className="full-results-chat-intermediate-message"
                            dangerouslySetInnerHTML={{
                              __html: renderMarkdown(
                                toolUsage.intermediateMessage,
                              ),
                            }}
                          />
                        )}
                        <div className="full-results-chat-tool-name">
                          <Icon icon="wrench" size={12} /> {toolUsage.toolName}
                        </div>
                        <div
                          className="full-results-chat-tool-details"
                          dangerouslySetInnerHTML={{
                            __html: renderMarkdown(toolUsage.details),
                          }}
                        />
                        {/* Display vector search results or plain tool response */}
                        {toolUsage.vectorSearchData ? (
                          <VectorSearchResults
                            data={toolUsage.vectorSearchData}
                            onAddResults={onAddResults}
                          />
                        ) : toolUsage.response ? (
                          <div
                            className="full-results-chat-tool-response"
                            dangerouslySetInnerHTML={{
                              __html: renderMarkdown(toolUsage.response),
                            }}
                          />
                        ) : null}
                      </div>
                    ))}
                  </div>
                )}

                {isEditing && message.roamBlockUid ? (
                  // Editing mode - use Roam's block editor
                  <EditableMessage
                    blockUid={message.roamBlockUid}
                    onSave={() => onSaveEdit(index)}
                    onCancel={() => onCancelEdit(index)}
                  />
                ) : isHelpMsg ? (
                  // Help message with special styling and buttons inside
                  <div className="full-results-chat-assistant-message chat-help">
                    {isTipMessage ? (
                      <div
                        style={{
                          display: "flex",
                          alignItems: "flex-start",
                          gap: "8px",
                        }}
                      >
                        <Icon
                          icon="lightbulb"
                          size={16}
                          style={{ marginTop: "2px", flexShrink: 0 }}
                        />
                        <div
                          style={{ flex: 1 }}
                          dangerouslySetInnerHTML={{
                            __html: renderMarkdown(
                              displayContent.replace(/^💡\s*/, ""),
                            ),
                          }}
                        />
                      </div>
                    ) : (
                      <div
                        dangerouslySetInnerHTML={{
                          __html: renderMarkdown(displayContent),
                        }}
                      />
                    )}
                    {renderHelpButtons(message.helpType)}
                  </div>
                ) : message.queryContent ? (
                  // Query result message with badge
                  <div className="chat-query-result-wrapper">
                    <div className="chat-query-badge">
                      <Icon icon="search" size={12} />
                      <span>
                        {message.queryType === "roam"
                          ? "Roam Query"
                          : "Datomic Query"}
                      </span>
                      {message.queryResultCount !== undefined && (
                        <span className="chat-query-result-count">
                          {message.queryResultCount} result
                          {message.queryResultCount !== 1 ? "s" : ""}
                        </span>
                      )}
                    </div>
                    <pre className="chat-query-code-block">
                      <code>{displayContent}</code>
                    </pre>
                    {message.queryType === "roam" && (
                      <MessageContent
                        content={displayContent}
                        className="full-results-chat-text chat-query-content"
                      />
                    )}
                  </div>
                ) : (
                  // Regular message
                  <MessageContent
                    content={displayContent}
                    className="full-results-chat-text"
                  />
                )}
                <div className="full-results-chat-message-footer">
                  {message.role === "assistant" && !isEditing && (
                    <>
                      <span className="full-results-chat-timestamp">
                        {message.timestamp.toLocaleTimeString()}
                        {message.tokensIn !== undefined &&
                          message.tokensOut !== undefined && (
                            <span className="full-results-chat-tokens">
                              {" "}
                              • Tokens in: {message.tokensIn.toLocaleString()},
                              out: {message.tokensOut.toLocaleString()}
                            </span>
                          )}
                      </span>

                      <span className="full-results-chat-copy-link">
                        <Popover
                          content={
                            <Menu>
                              <MenuItem
                                icon="edit"
                                text="Edit message"
                                onClick={() => onEditMessage(index)}
                                disabled={
                                  isTyping || editingMessageIndex !== null
                                }
                              />
                              <MenuItem
                                icon="clipboard"
                                text="Copy to clipboard"
                                onClick={() => onCopyMessage(message.content)}
                              />
                              <MenuItem
                                icon="refresh"
                                text="Retry"
                                onClick={() => onRetryMessage(index)}
                              />
                              <MenuItem
                                icon="volume-up"
                                text="Text to Speech (via OpenAI)"
                                title="Esc or click again to stop"
                                onClick={async () => {
                                  await textToSpeech(
                                    message.content,
                                    undefined,
                                  );
                                }}
                              />
                              <MenuItem
                                icon="trash"
                                text="Delete chat turn"
                                intent="danger"
                                onClick={() => onDeleteMessage(index)}
                              />
                            </Menu>
                          }
                          position={Position.BOTTOM_RIGHT}
                        >
                          <Button icon="more" minimal small />
                        </Popover>
                      </span>
                    </>
                  )}
                  {message.role === "user" && !isEditing && !isHelpMsg && (
                    <span className="full-results-chat-user-actions">
                      <Popover
                        content={
                          <Menu>
                            <MenuItem
                              icon="edit"
                              text="Edit message"
                              onClick={() => onEditMessage(index)}
                              disabled={
                                isTyping || editingMessageIndex !== null
                              }
                            />
                            <MenuItem
                              icon="clipboard"
                              text="Copy to clipboard"
                              onClick={() => onCopyMessage(message.content)}
                            />
                          </Menu>
                        }
                        position={Position.BOTTOM_RIGHT}
                      >
                        <Button icon="more" minimal small />
                      </Popover>
                    </span>
                  )}
                </div>
              </div>
            </div>
          );
        })
      )}

      {(isTyping || isStreaming || toolUsageHistory.length > 0) && (
        <div className="full-results-chat-message assistant">
          <div className="full-results-chat-avatar">🤖</div>
          <div className="full-results-chat-content">
            {/* Display tool usage history (stacked) */}
            {toolUsageHistory.length > 0 && (
              <div className="full-results-chat-tool-history">
                {toolUsageHistory.map((toolUsage, index) => (
                  <div
                    key={`${toolUsage.timestamp}-${index}`}
                    className="full-results-chat-tool-usage-item"
                  >
                    {/* Display intermediate message if present */}
                    {toolUsage.intermediateMessage && (
                      <div
                        className="full-results-chat-intermediate-message"
                        dangerouslySetInnerHTML={{
                          __html: renderMarkdown(toolUsage.intermediateMessage),
                        }}
                      />
                    )}
                    <div className="full-results-chat-tool-name">
                      <Icon icon="wrench" size={12} /> {toolUsage.toolName}
                    </div>
                    <div
                      className="full-results-chat-tool-details"
                      dangerouslySetInnerHTML={{
                        __html: renderMarkdown(toolUsage.details),
                      }}
                    />
                    {/* Display vector search results or plain response during streaming */}
                    {toolUsage.vectorSearchData ? (
                      <VectorSearchResults
                        data={toolUsage.vectorSearchData}
                        onAddResults={onAddResults}
                      />
                    ) : toolUsage.response ? (
                      <div
                        className="full-results-chat-tool-response"
                        dangerouslySetInnerHTML={{
                          __html: renderMarkdown(toolUsage.response),
                        }}
                      />
                    ) : null}
                  </div>
                ))}
              </div>
            )}

            {/* Display streaming content or typing indicator */}
            {isStreaming && streamingContent ? (
              <MessageContent
                content={streamingContent}
                className="full-results-chat-text streaming"
              />
            ) : (isTyping || isStreaming) && toolUsageHistory.length === 0 ? (
              <div className="full-results-chat-typing">
                {statusMessage || "Thinking..."}
              </div>
            ) : null}

            {/* Tool confirmation UI */}
            {pendingToolConfirmation && (
              <div className="full-results-chat-tool-confirmation">
                <div className="tool-confirmation-header">
                  <Icon icon="warning-sign" size={14} intent="warning" />
                  <span>
                    Confirm{" "}
                    {(() => {
                      const opCount =
                        pendingToolConfirmation.args?.operation_count;
                      const label =
                        {
                          create_block: "block insertion",
                          create_page: "page creation",
                          update_block: "block update",
                          delete_block: "block deletion",
                          run_smartblock: "SmartBlock execution",
                        }[pendingToolConfirmation.toolName] || "operation";
                      return opCount > 1
                        ? `batch ${label} (${opCount} blocks)`
                        : label;
                    })()}
                    ?
                  </span>
                </div>

                {/* Preview for update_block */}
                {pendingToolConfirmation.toolName === "update_block" &&
                  pendingToolConfirmation.args?.operations && (
                    <div className="tool-confirmation-preview">
                      {pendingToolConfirmation.args.operations.map(
                        (op: any, i: number) => (
                          <div key={i} className="tool-confirmation-diff-item">
                            <div className="tool-confirmation-diff-block-header">
                              Block (({op.block_uid}))
                              {op.current_content && (
                                <span className="tool-confirmation-diff-context">
                                  : {op.current_content.substring(0, 60)}
                                  {op.current_content.length > 60 ? "..." : ""}
                                </span>
                              )}
                            </div>
                            {!op.move_to && op.position_context && (
                              <div className="tool-confirmation-diff-row tool-confirmation-diff-location">
                                Location: {op.position_context}
                              </div>
                            )}
                            {op.new_content !== undefined && (
                              <div className="tool-confirmation-diff-row">
                                <span className="tool-confirmation-diff-label">
                                  Content:
                                </span>
                                <ExpandableContent
                                  content={op.current_content || "(empty)"}
                                  className="tool-confirmation-diff-old"
                                />
                                <span className="tool-confirmation-diff-arrow">
                                  &rarr;
                                </span>
                                <ExpandableContent
                                  content={op.new_content}
                                  className="tool-confirmation-diff-new"
                                />
                              </div>
                            )}
                            {op.new_heading !== undefined && (
                              <div className="tool-confirmation-diff-row">
                                <span className="tool-confirmation-diff-label">
                                  Heading:
                                </span>
                                <span className="tool-confirmation-diff-old">
                                  {op.current_heading === 0
                                    ? "none"
                                    : `H${op.current_heading}`}
                                </span>
                                <span className="tool-confirmation-diff-arrow">
                                  &rarr;
                                </span>
                                <span className="tool-confirmation-diff-new">
                                  {op.new_heading === 0
                                    ? "none"
                                    : `H${op.new_heading}`}
                                </span>
                              </div>
                            )}
                            {op.new_open !== undefined && (
                              <div className="tool-confirmation-diff-row">
                                <span className="tool-confirmation-diff-label">
                                  State:
                                </span>
                                <span className="tool-confirmation-diff-old">
                                  {op.current_open ? "expanded" : "collapsed"}
                                </span>
                                <span className="tool-confirmation-diff-arrow">
                                  &rarr;
                                </span>
                                <span className="tool-confirmation-diff-new">
                                  {op.new_open ? "expanded" : "collapsed"}
                                </span>
                              </div>
                            )}
                            {op.move_to && (
                              <div className="tool-confirmation-diff-row">
                                <span className="tool-confirmation-diff-label">
                                  Position:
                                </span>
                                <span className="tool-confirmation-diff-old">
                                  {op.move_from || "current"}
                                </span>
                                <span className="tool-confirmation-diff-arrow">
                                  &rarr;
                                </span>
                                <span className="tool-confirmation-diff-new">
                                  {op.move_to}
                                </span>
                              </div>
                            )}
                          </div>
                        ),
                      )}
                    </div>
                  )}

                {/* Preview for delete_block */}
                {pendingToolConfirmation.toolName === "delete_block" &&
                  pendingToolConfirmation.args?.blocks && (
                    <div className="tool-confirmation-preview">
                      {pendingToolConfirmation.args.blocks.map(
                        (block: any, i: number) => (
                          <div
                            key={i}
                            className="tool-confirmation-diff-item tool-confirmation-diff-delete"
                          >
                            <div className="tool-confirmation-diff-block-header">
                              Block (({block.block_uid})):
                              <ExpandableContent
                                content={block.content || "(empty)"}
                                className="tool-confirmation-diff-context"
                              />
                            </div>
                            {block.position_context && (
                              <div className="tool-confirmation-diff-row tool-confirmation-diff-location">
                                Location: {block.position_context}
                              </div>
                            )}
                            {block.descendant_count > 0 && (
                              <div className="tool-confirmation-diff-row tool-confirmation-diff-warning">
                                + {block.descendant_count} descendant block(s)
                                will also be deleted
                              </div>
                            )}
                          </div>
                        ),
                      )}
                    </div>
                  )}

                {/* Preview for create_block / create_page */}
                {(pendingToolConfirmation.toolName === "create_block" ||
                  pendingToolConfirmation.toolName === "create_page") &&
                  pendingToolConfirmation.args?.markdown_content && (
                    <div className="tool-confirmation-preview">
                      {/* Page context banner */}
                      {pendingToolConfirmation.toolName === "create_page" &&
                        pendingToolConfirmation.args?.page_title && (
                          <div className="tool-confirmation-page-context">
                            Creating new page{" "}
                            <strong>
                              [[{pendingToolConfirmation.args.page_title}]]
                            </strong>
                          </div>
                        )}
                      {pendingToolConfirmation.toolName === "create_block" &&
                        (pendingToolConfirmation.args?.page_name ||
                          pendingToolConfirmation.args
                            ?.containing_page_name) && (
                          <div className="tool-confirmation-page-context">
                            {pendingToolConfirmation.args.is_page
                              ? `Inserting ${
                                  pendingToolConfirmation.args.insertion_position?.includes(
                                    "top-level",
                                  )
                                    ? "at top-level of"
                                    : "into"
                                } page `
                              : "On page "}
                            <strong>
                              [[
                              {pendingToolConfirmation.args.page_name ||
                                pendingToolConfirmation.args
                                  .containing_page_name}
                              ]]
                            </strong>
                          </div>
                        )}

                      {/* Parent and insertion context with integrated marker */}
                      {pendingToolConfirmation.args.outline_preview && (
                        <div className="tool-confirmation-insertion-context">
                          <div className="tool-confirmation-section-label">
                            Insertion location:
                          </div>
                          <pre
                            className="tool-confirmation-outline-preview"
                            dangerouslySetInnerHTML={{
                              __html:
                                pendingToolConfirmation.args.outline_preview.replace(
                                  /{{MARKER}}(.*?){{\/MARKER}}/g,
                                  '<span style="color: #28a745; font-weight: 600;">$1</span>',
                                ),
                            }}
                          />
                        </div>
                      )}

                      {/* Content to insert */}
                      {/* <div className="tool-confirmation-content-to-insert">
                        <div className="tool-confirmation-section-label">Content to insert:</div>
                        <ExpandableContent
                          content={pendingToolConfirmation.args.markdown_content}
                          className="tool-confirmation-diff-new"
                        />
                      </div> */}
                    </div>
                  )}

                {/* Preview for run_smartblock */}
                {pendingToolConfirmation.toolName === "run_smartblock" &&
                  pendingToolConfirmation.args && (
                    <div className="tool-confirmation-preview">
                      <div className="tool-confirmation-smartblock-summary">
                        Run '<strong>{pendingToolConfirmation.args.src_name || pendingToolConfirmation.args.src_uid}</strong>' SmartBlock
                        {pendingToolConfirmation.args.target_name
                          ? <> on '<strong>[[{pendingToolConfirmation.args.target_name}]]</strong>'</>
                          : pendingToolConfirmation.args.target_uid
                            ? <> in '<strong>(({pendingToolConfirmation.args.target_uid}))</strong>'</>
                            : null}
                        {pendingToolConfirmation.args.variables &&
                          Object.keys(pendingToolConfirmation.args.variables).length > 0 && (
                            <div className="tool-confirmation-smartblock-variables">
                              Variables: {Object.entries(pendingToolConfirmation.args.variables)
                                .map(([k, v]) => `${k}=${v}`)
                                .join(", ")}
                            </div>
                          )}
                      </div>
                    </div>
                  )}

                {pendingToolConfirmation.args?.non_atomic && (
                  <div className="tool-confirmation-non-atomic-warning">
                    <Icon icon="info-sign" size={12} />
                    <span>Batch operations are not atomic — if one fails, previously completed operations won't be rolled back.</span>
                  </div>
                )}

                <div className="tool-confirmation-buttons">
                  <Button
                    intent="success"
                    icon="tick"
                    small
                    onClick={() => onToolConfirmationResponse(true, false)}
                  >
                    Accept
                  </Button>
                  <Button
                    intent="primary"
                    icon="tick-circle"
                    small
                    onClick={() => onToolConfirmationResponse(true, true)}
                  >
                    Always in this chat
                  </Button>
                  <Button
                    intent="danger"
                    icon="cross"
                    small
                    onClick={() =>
                      onToolConfirmationResponse(
                        false,
                        false,
                        declineReasonInput,
                      )
                    }
                  >
                    Decline
                  </Button>
                </div>
                <div className="tool-confirmation-feedback">
                  <input
                    type="text"
                    placeholder="Optional: explain why you're declining..."
                    value={declineReasonInput}
                    onChange={(e) => setDeclineReasonInput(e.target.value)}
                    onKeyDown={(e) => {
                      if (e.key === "Enter") {
                        onToolConfirmationResponse(
                          false,
                          false,
                          declineReasonInput,
                        );
                      }
                    }}
                    className="tool-confirmation-input"
                  />
                </div>
              </div>
            )}

            {/* User choice form UI */}
            {pendingUserChoice && (
              <UserChoiceForm
                pendingChoice={pendingUserChoice}
                onSubmit={onUserChoiceResponse}
                onCancel={onUserChoiceCancelled}
              />
            )}
          </div>
        </div>
      )}
    </div>
  );
};
