import { screen, within, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
  afterEach,
  beforeAll,
  beforeEach,
  describe,
  expect,
  it,
  vi,
} from "vitest";
import { renderWithProviders } from "test-utils";
import { ConversationName } from "#/components/features/conversation/conversation-name";
import { ConversationNameContextMenu } from "#/components/features/conversation/conversation-name-context-menu";
import { BrowserRouter } from "react-router";
import type { Conversation } from "#/api/open-hands.types";

// Hoisted mocks for controllable return values
const {
  mockMutate,
  mockDisplaySuccessToast,
  useActiveConversationMock,
  useConfigMock,
} = vi.hoisted(() => ({
  mockMutate: vi.fn(),
  mockDisplaySuccessToast: vi.fn(),
  useActiveConversationMock: vi.fn(() => ({
    data: {
      conversation_id: "test-conversation-id",
      title: "Test Conversation",
      status: "RUNNING",
    },
  })),
  useConfigMock: vi.fn(() => ({
    data: {
      app_mode: "oss",
    },
  })),
}));

vi.mock("#/hooks/query/use-active-conversation", () => ({
  useActiveConversation: () => useActiveConversationMock(),
}));

vi.mock("#/hooks/query/use-config", () => ({
  useConfig: () => useConfigMock(),
}));

vi.mock("#/hooks/mutation/use-update-conversation", () => ({
  useUpdateConversation: () => ({
    mutate: mockMutate,
  }),
}));

vi.mock("#/utils/custom-toast-handlers", () => ({
  displaySuccessToast: mockDisplaySuccessToast,
}));

// Mock react-i18next
vi.mock("react-i18next", async () => {
  const actual = await vi.importActual("react-i18next");
  return {
    ...actual,
    useTranslation: () => ({
      t: (key: string) => {
        const translations: Record<string, string> = {
          CONVERSATION$TITLE_UPDATED: "Conversation title updated",
          BUTTON$RENAME: "Rename",
          BUTTON$EXPORT_CONVERSATION: "Export Conversation",
          BUTTON$DOWNLOAD_VIA_VSCODE: "Download via VS Code",
          BUTTON$SHOW_AGENT_TOOLS_AND_METADATA: "Show Agent Tools",
          CONVERSATION$SHOW_SKILLS: "Show Skills",
          BUTTON$DISPLAY_COST: "Display Cost",
          COMMON$CLOSE_CONVERSATION_STOP_RUNTIME:
            "Close Conversation (Stop Sandbox)",
          COMMON$DELETE_CONVERSATION: "Delete Conversation",
          CONVERSATION$SHARE_PUBLICLY: "Share Publicly",
          CONVERSATION$LINK_COPIED: "Link copied to clipboard",
          BUTTON$COPY_TO_CLIPBOARD: "Copy to Clipboard",
          BUTTON$OPEN_IN_NEW_TAB: "Open in New Tab",
        };
        return translations[key] || key;
      },
      i18n: {
        changeLanguage: () => new Promise(() => {}),
      },
    }),
  };
});

// Helper function to render ConversationName with Router context
const renderConversationNameWithRouter = () => {
  return renderWithProviders(
    <BrowserRouter>
      <ConversationName />
    </BrowserRouter>,
  );
};

describe("ConversationName", () => {
  beforeAll(() => {
    vi.stubGlobal("window", {
      open: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      location: {
        origin: "http://localhost:3000",
      },
    });
  });

  afterEach(() => {
    vi.resetAllMocks();
  });

  it("should render the conversation name in view mode", () => {
    renderConversationNameWithRouter();

    const container = screen.getByTestId("conversation-name");
    const titleElement = within(container).getByTestId(
      "conversation-name-title",
    );

    expect(container).toBeInTheDocument();
    expect(titleElement).toBeInTheDocument();
    expect(titleElement).toHaveTextContent("Test Conversation");
  });

  it("should switch to edit mode on double click", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");

    // Initially should be in view mode
    expect(titleElement).toBeInTheDocument();
    expect(
      screen.queryByTestId("conversation-name-input"),
    ).not.toBeInTheDocument();

    // Double click to enter edit mode
    await user.dblClick(titleElement);

    // Should now be in edit mode
    expect(
      screen.queryByTestId("conversation-name-title"),
    ).not.toBeInTheDocument();
    const inputElement = screen.getByTestId("conversation-name-input");
    expect(inputElement).toBeInTheDocument();
    expect(inputElement).toHaveValue("Test Conversation");
  });

  it("should update conversation title when input loses focus with valid value", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    await user.clear(inputElement);
    await user.type(inputElement, "New Conversation Title");
    await user.tab(); // Trigger blur event

    // Verify that the update function was called
    expect(mockMutate).toHaveBeenCalledWith(
      {
        conversationId: "test-conversation-id",
        newTitle: "New Conversation Title",
      },
      expect.any(Object),
    );
  });

  it("should not update conversation when title is unchanged", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    // Keep the same title
    await user.tab();

    // Should still have the original title
    expect(inputElement).toHaveValue("Test Conversation");
  });

  it("should not call the API if user attempts to save an unchanged title", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");

    // Verify the input has the original title
    expect(inputElement).toHaveValue("Test Conversation");

    // Trigger blur without changing the title
    await user.tab();

    // Verify that the API was NOT called
    expect(mockMutate).not.toHaveBeenCalled();
  });

  it("should reset input value when title is empty and blur", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    await user.clear(inputElement);
    await user.tab();

    // Should reset to original title
    expect(inputElement).toHaveValue("Test Conversation");
  });

  it("should trim whitespace from input value", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    await user.clear(inputElement);
    await user.type(inputElement, "  Trimmed Title  ");
    await user.tab();

    // Should call mutation with trimmed value
    expect(mockMutate).toHaveBeenCalledWith(
      {
        conversationId: "test-conversation-id",
        newTitle: "Trimmed Title",
      },
      expect.any(Object),
    );
  });

  it("should handle Enter key to save changes", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    await user.clear(inputElement);
    await user.type(inputElement, "New Title");
    await user.keyboard("{Enter}");

    // Should have the new title
    expect(inputElement).toHaveValue("New Title");
  });

  it("should prevent event propagation when clicking input in edit mode", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    const clickEvent = new MouseEvent("click", { bubbles: true });
    const preventDefaultSpy = vi.spyOn(clickEvent, "preventDefault");
    const stopPropagationSpy = vi.spyOn(clickEvent, "stopPropagation");

    inputElement.dispatchEvent(clickEvent);

    expect(preventDefaultSpy).toHaveBeenCalled();
    expect(stopPropagationSpy).toHaveBeenCalled();
  });

  it("should return to view mode after blur", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    // Should be in edit mode
    expect(screen.getByTestId("conversation-name-input")).toBeInTheDocument();

    await user.tab();

    // Should be back in view mode
    expect(screen.getByTestId("conversation-name-title")).toBeInTheDocument();
    expect(
      screen.queryByTestId("conversation-name-input"),
    ).not.toBeInTheDocument();
  });

  it("renders prettified model text and raw model in the tooltip", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
        llm_model: "openai/gpt-4o",
      } as Conversation,
    });

    renderConversationNameWithRouter();

    const model = screen.getByTestId("conversation-name-llm-model");
    expect(model).toBeInTheDocument();
    expect(model).toHaveTextContent("GPT-4o");
    expect(model).toHaveAttribute("title", "openai/gpt-4o");
    expect(model.querySelector("svg")).toBeInTheDocument();
  });

  it("should render plain 'ACP' for ACP-agent conversations", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
        agent_kind: "acp",
      } as unknown as Conversation,
    });

    renderConversationNameWithRouter();

    const model = screen.getByTestId("conversation-name-llm-model");
    expect(model).toHaveTextContent("ACP");
    expect(model).toHaveAttribute("title", "ACP");
  });

  it("should render the provider brand label when acp_server matches a known provider", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
        agent_kind: "acp",
        acp_server: "claude-code",
      } as unknown as Conversation,
    });
    useConfigMock.mockReturnValue({
      data: {
        app_mode: "oss",
        acp_providers: [
          {
            key: "claude-code",
            display_name: "Claude Code",
            default_command: ["npx", "-y", "@agentclientprotocol/claude-agent-acp"],
          },
        ],
      },
    } as unknown as ReturnType<typeof useConfigMock>);

    renderConversationNameWithRouter();

    const model = screen.getByTestId("conversation-name-llm-model");
    expect(model).toHaveTextContent("Claude Code");
  });

  it("renders the underlying model for an ACP conversation that exposes one", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
        agent_kind: "acp",
        acp_server: "claude-code",
        llm_model: "anthropic/claude-opus-4-1",
      } as unknown as Conversation,
    });
    useConfigMock.mockReturnValue({
      data: {
        app_mode: "oss",
        acp_providers: [
          {
            key: "claude-code",
            display_name: "Claude Code",
            default_command: ["npx", "-y", "@agentclientprotocol/claude-agent-acp"],
            available_models: [
              { id: "anthropic/claude-opus-4-1", label: "Claude Opus 4.1" },
            ],
          },
        ],
      },
    } as unknown as ReturnType<typeof useConfigMock>);

    renderConversationNameWithRouter();

    const model = screen.getByTestId("conversation-name-llm-model");
    expect(model).toHaveTextContent("Claude Opus 4.1");
    expect(model).toHaveAttribute(
      "title",
      "Claude Code · anthropic/claude-opus-4-1",
    );
  });

  it("falls back to the raw model id for an ACP conversation not in the registry", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
        agent_kind: "acp",
        acp_server: "claude-code",
        llm_model: "anthropic/claude-opus-4-1",
      } as unknown as Conversation,
    });
    useConfigMock.mockReturnValue({
      data: {
        app_mode: "oss",
        acp_providers: [
          {
            key: "claude-code",
            display_name: "Claude Code",
            default_command: ["npx", "-y", "@agentclientprotocol/claude-agent-acp"],
          },
        ],
      },
    } as unknown as ReturnType<typeof useConfigMock>);

    renderConversationNameWithRouter();

    const model = screen.getByTestId("conversation-name-llm-model");
    expect(model).toHaveTextContent("anthropic/claude-opus-4-1");
    expect(model).toHaveAttribute(
      "title",
      "Claude Code · anthropic/claude-opus-4-1",
    );
  });

  it("should not render the llm model when not available", () => {
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-conversation-id",
        title: "Test Conversation",
        status: "RUNNING",
      },
    });

    renderConversationNameWithRouter();

    expect(
      screen.queryByTestId("conversation-name-llm-model"),
    ).not.toBeInTheDocument();
  });

  it("should focus input when entering edit mode", async () => {
    const user = userEvent.setup();
    renderConversationNameWithRouter();

    const titleElement = screen.getByTestId("conversation-name-title");
    await user.dblClick(titleElement);

    const inputElement = screen.getByTestId("conversation-name-input");
    expect(inputElement).toHaveFocus();
  });
});

describe("ConversationNameContextMenu", () => {
  const defaultProps = {
    onClose: vi.fn(),
  };

  afterEach(() => {
    vi.resetAllMocks();
  });

  it("should render all menu options when all handlers are provided", () => {
    const handlers = {
      onRename: vi.fn(),
      onDelete: vi.fn(),
      onStop: vi.fn(),
      onDisplayCost: vi.fn(),
      onShowAgentTools: vi.fn(),
      onShowSkills: vi.fn(),
      onDownloadConversation: vi.fn(),
    };

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} {...handlers} />,
    );

    expect(screen.getByTestId("rename-button")).toBeInTheDocument();
    expect(screen.getByTestId("delete-button")).toBeInTheDocument();
    expect(screen.getByTestId("stop-button")).toBeInTheDocument();
    expect(screen.getByTestId("display-cost-button")).toBeInTheDocument();
    expect(screen.getByTestId("show-agent-tools-button")).toBeInTheDocument();
    expect(screen.getByTestId("show-skills-button")).toBeInTheDocument();
    expect(
      screen.getByTestId("download-trajectory-button"),
    ).toBeInTheDocument();
  });

  it("should not render menu options when handlers are not provided", () => {
    renderWithProviders(<ConversationNameContextMenu {...defaultProps} />);

    expect(screen.queryByTestId("rename-button")).not.toBeInTheDocument();
    expect(screen.queryByTestId("delete-button")).not.toBeInTheDocument();
    expect(screen.queryByTestId("stop-button")).not.toBeInTheDocument();
    expect(screen.queryByTestId("display-cost-button")).not.toBeInTheDocument();
    expect(
      screen.queryByTestId("show-agent-tools-button"),
    ).not.toBeInTheDocument();
    expect(screen.queryByTestId("show-skills-button")).not.toBeInTheDocument();
  });

  it("should call rename handler when rename button is clicked", async () => {
    const user = userEvent.setup();
    const onRename = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} onRename={onRename} />,
    );

    const renameButton = screen.getByTestId("rename-button");
    await user.click(renameButton);

    expect(onRename).toHaveBeenCalledTimes(1);
  });

  it("should call delete handler when delete button is clicked", async () => {
    const user = userEvent.setup();
    const onDelete = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} onDelete={onDelete} />,
    );

    const deleteButton = screen.getByTestId("delete-button");
    await user.click(deleteButton);

    expect(onDelete).toHaveBeenCalledTimes(1);
  });

  it("should call stop handler when stop button is clicked", async () => {
    const user = userEvent.setup();
    const onStop = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} onStop={onStop} />,
    );

    const stopButton = screen.getByTestId("stop-button");
    await user.click(stopButton);

    expect(onStop).toHaveBeenCalledTimes(1);
  });

  it("should call display cost handler when display cost button is clicked", async () => {
    const user = userEvent.setup();
    const onDisplayCost = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        onDisplayCost={onDisplayCost}
      />,
    );

    const displayCostButton = screen.getByTestId("display-cost-button");
    await user.click(displayCostButton);

    expect(onDisplayCost).toHaveBeenCalledTimes(1);
  });

  it("should call show agent tools handler when show agent tools button is clicked", async () => {
    const user = userEvent.setup();
    const onShowAgentTools = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        onShowAgentTools={onShowAgentTools}
      />,
    );

    const showAgentToolsButton = screen.getByTestId("show-agent-tools-button");
    await user.click(showAgentToolsButton);

    expect(onShowAgentTools).toHaveBeenCalledTimes(1);
  });

  it("should call show microagents handler when show microagents button is clicked", async () => {
    const user = userEvent.setup();
    const onShowSkills = vi.fn();

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        onShowSkills={onShowSkills}
      />,
    );

    const showMicroagentsButton = screen.getByTestId("show-skills-button");
    await user.click(showMicroagentsButton);

    expect(onShowSkills).toHaveBeenCalledTimes(1);
  });

  it("should apply correct positioning class when position is top", () => {
    const handlers = {
      onRename: vi.fn(),
    };

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        {...handlers}
        position="top"
      />,
    );

    const contextMenu = screen.getByTestId("conversation-name-context-menu");
    expect(contextMenu).toHaveClass("bottom-full");
  });

  it("should apply correct positioning class when position is bottom", () => {
    const handlers = {
      onRename: vi.fn(),
    };

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        {...handlers}
        position="bottom"
      />,
    );

    const contextMenu = screen.getByTestId("conversation-name-context-menu");
    expect(contextMenu).toHaveClass("top-full");
  });

  it("should render correct text content for each menu option", () => {
    const handlers = {
      onRename: vi.fn(),
      onDelete: vi.fn(),
      onStop: vi.fn(),
      onDisplayCost: vi.fn(),
      onShowAgentTools: vi.fn(),
      onShowSkills: vi.fn(),
      onDownloadConversation: vi.fn(),
    };

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} {...handlers} />,
    );

    expect(screen.getByTestId("rename-button")).toHaveTextContent("Rename");
    expect(screen.getByTestId("delete-button")).toHaveTextContent(
      "Delete Conversation",
    );
    expect(screen.getByTestId("stop-button")).toHaveTextContent(
      "Close Conversation (Stop Sandbox)",
    );
    expect(screen.getByTestId("display-cost-button")).toHaveTextContent(
      "Display Cost",
    );
    expect(screen.getByTestId("show-agent-tools-button")).toHaveTextContent(
      "Show Agent Tools",
    );
    expect(screen.getByTestId("show-skills-button")).toHaveTextContent(
      "Show Skills",
    );
    expect(screen.getByTestId("download-trajectory-button")).toHaveTextContent(
      "Export Conversation",
    );
  });

  it("should call onClose when context menu is closed", () => {
    const onClose = vi.fn();
    const handlers = {
      onRename: vi.fn(),
    };

    renderWithProviders(
      <ConversationNameContextMenu
        {...defaultProps}
        onClose={onClose}
        {...handlers}
      />,
    );

    // The onClose is typically called by the parent component when clicking outside
    // This test verifies the prop is properly passed
    expect(onClose).toBeDefined();
  });
});

describe("ConversationNameContextMenu - Share Link Functionality", () => {
  const mockWriteText = vi.fn().mockResolvedValue(undefined);

  const mockOnCopyShareLink = vi.fn();
  const mockOnTogglePublic = vi.fn();
  const mockOnClose = vi.fn();

  const defaultProps = {
    onClose: mockOnClose,
    onTogglePublic: mockOnTogglePublic,
    onCopyShareLink: mockOnCopyShareLink,
    shareUrl: "https://example.com/shared/conversations/test-id",
  };

  vi.mock("#/hooks/mutation/use-update-conversation-public-flag", () => ({
    useUpdateConversationPublicFlag: () => ({
      mutate: vi.fn(),
    }),
  }));

  beforeAll(() => {
    // Mock navigator.clipboard
    Object.defineProperty(navigator, "clipboard", {
      value: {
        writeText: mockWriteText,
        readText: vi.fn(),
      },
      writable: true,
      configurable: true,
    });
  });

  beforeEach(() => {
    mockWriteText.mockClear();
    mockDisplaySuccessToast.mockClear();
  });

  afterEach(() => {
    vi.resetAllMocks();
  });

  it("should display copy and open buttons when conversation is public", () => {
    // Arrange
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-id",
        title: "Test Conversation",
        status: "STOPPED",
        conversation_version: "V1" as const,
        public: true,
      } as Conversation,
    });

    useConfigMock.mockReturnValue({
      data: {
        app_mode: "saas",
      },
    });

    // Act
    renderWithProviders(<ConversationNameContextMenu {...defaultProps} />);

    // Assert
    expect(screen.getByTestId("copy-share-link-button")).toBeInTheDocument();
    expect(screen.getByTestId("open-share-link-button")).toBeInTheDocument();
  });

  it("should not display share buttons when conversation is not public", () => {
    // Arrange
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-id",
        title: "Test Conversation",
        status: "STOPPED",
        conversation_version: "V1" as const,
        public: false,
      } as Conversation,
    });

    useConfigMock.mockReturnValue({
      data: {
        app_mode: "saas",
      },
    });

    // Act
    renderWithProviders(<ConversationNameContextMenu {...defaultProps} />);

    // Assert
    expect(
      screen.queryByTestId("copy-share-link-button"),
    ).not.toBeInTheDocument();
    expect(
      screen.queryByTestId("open-share-link-button"),
    ).not.toBeInTheDocument();
  });

  it("should call copy handler when copy button is clicked", async () => {
    // Arrange
    const user = userEvent.setup();
    const shareUrl = "https://example.com/shared/conversations/test-id";

    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-id",
        title: "Test Conversation",
        status: "STOPPED",
        conversation_version: "V1" as const,
        public: true,
      } as Conversation,
    });

    useConfigMock.mockReturnValue({
      data: {
        app_mode: "saas",
      },
    });

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} shareUrl={shareUrl} />,
    );

    const copyButton = screen.getByTestId("copy-share-link-button");

    // Act
    await user.click(copyButton);

    // Assert
    expect(mockOnCopyShareLink).toHaveBeenCalledTimes(1);
  });

  it("should have correct attributes for open share link button", () => {
    // Arrange
    const shareUrl = "https://example.com/shared/conversations/test-id";

    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-id",
        title: "Test Conversation",
        status: "STOPPED",
        conversation_version: "V1" as const,
        public: true,
      } as Conversation,
    });

    useConfigMock.mockReturnValue({
      data: {
        app_mode: "saas",
      },
    });

    renderWithProviders(
      <ConversationNameContextMenu {...defaultProps} shareUrl={shareUrl} />,
    );

    const openButton = screen.getByTestId("open-share-link-button");

    // Assert
    expect(openButton).toHaveAttribute("href", shareUrl);
    expect(openButton).toHaveAttribute("target", "_blank");
    expect(openButton).toHaveAttribute("rel", "noopener noreferrer");
  });

  it("should display correct tooltips for share buttons", () => {
    // Arrange
    useActiveConversationMock.mockReturnValue({
      data: {
        conversation_id: "test-id",
        title: "Test Conversation",
        status: "STOPPED",
        conversation_version: "V1" as const,
        public: true,
      } as Conversation,
    });

    useConfigMock.mockReturnValue({
      data: {
        app_mode: "saas",
      },
    });

    renderWithProviders(<ConversationNameContextMenu {...defaultProps} />);

    // Assert
    const copyButton = screen.getByTestId("copy-share-link-button");
    const openButton = screen.getByTestId("open-share-link-button");

    expect(copyButton).toHaveAttribute("title", "Copy to Clipboard");
    expect(openButton).toHaveAttribute("title", "Open in New Tab");
  });

  describe("Integration with ConversationName component", () => {
    beforeEach(() => {
      // Default mocks for public V1 conversation in SAAS mode
      useActiveConversationMock.mockReturnValue({
        data: {
          conversation_id: "test-conversation-id",
          title: "Test Conversation",
          status: "STOPPED",
          conversation_version: "V1" as const,
          public: true,
        } as Conversation,
      });

      useConfigMock.mockReturnValue({
        data: {
          app_mode: "saas",
        },
      });
    });

    it("should copy share URL to clipboard and show success toast when copy button is clicked through ConversationName", async () => {
      // Arrange
      const user = userEvent.setup();
      const expectedUrl =
        "http://localhost:3000/shared/conversations/test-conversation-id";

      // Ensure navigator.clipboard is properly mocked
      if (!navigator.clipboard) {
        Object.defineProperty(navigator, "clipboard", {
          value: {
            writeText: mockWriteText,
            readText: vi.fn(),
          },
          writable: true,
          configurable: true,
        });
      } else {
        vi.spyOn(navigator.clipboard, "writeText").mockImplementation(
          mockWriteText,
        );
      }

      renderConversationNameWithRouter();

      // Open context menu by clicking ellipsis
      const ellipsisButton = screen.getByRole("button", { hidden: true });
      await user.click(ellipsisButton);

      // Wait for context menu to appear and find share publicly button
      const sharePubliclyButton = await screen.findByTestId(
        "share-publicly-button",
      );
      expect(sharePubliclyButton).toBeInTheDocument();

      // Find copy button
      const copyButton = screen.getByTestId("copy-share-link-button");

      // Act
      await user.click(copyButton);

      // Assert - clipboard.writeText is async, so we need to wait
      await waitFor(
        () => {
          expect(mockWriteText).toHaveBeenCalledWith(expectedUrl);
          expect(mockDisplaySuccessToast).toHaveBeenCalledWith(
            "Link copied to clipboard",
          );
        },
        { timeout: 2000, container: document.body },
      );
    });

    it("should show both copy and open buttons when conversation is public", async () => {
      // Arrange
      const user = userEvent.setup();

      renderConversationNameWithRouter();

      // Act - open context menu
      const ellipsisButton = screen.getByRole("button", { hidden: true });
      await user.click(ellipsisButton);

      // Wait for context menu
      const sharePubliclyButton = await screen.findByTestId(
        "share-publicly-button",
      );

      // Assert
      expect(sharePubliclyButton).toBeInTheDocument();
      expect(screen.getByTestId("copy-share-link-button")).toBeInTheDocument();
      expect(screen.getByTestId("open-share-link-button")).toBeInTheDocument();
    });
  });
});
