/* eslint-disable @next/next/no-img-element */
import "@testing-library/jest-dom";
import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import { AgentTurn } from "./AgentTurn";

jest.mock("next/image", () => ({
  __esModule: true,
  default: (props: React.ImgHTMLAttributes<HTMLImageElement>) => (
    <img {...props} alt={props.alt ?? ""} />
  ),
}));

jest.mock("framer-motion", () => ({
  AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  motion: {
    div: ({
      children,
      animate: _animate,
      exit: _exit,
      initial: _initial,
      transition: _transition,
      ...props
    }: React.HTMLAttributes<HTMLDivElement> & Record<string, unknown>) => (
      <div {...props}>{children}</div>
    ),
    span: ({
      children,
      animate: _animate,
      transition: _transition,
      ...props
    }: React.HTMLAttributes<HTMLSpanElement> & Record<string, unknown>) => (
      <span {...props}>{children}</span>
    ),
  },
}));

jest.mock("./AgentMarkdownBlock", () => ({
  MarkdownBlock: ({ children }: { children: string }) => (
    <div>{children.split(/\n+/u).map((line) => <p key={line}>{line}</p>)}</div>
  ),
  normalizeClipboardText: (value: string) => value.replace(/\s+$/u, ""),
}));

describe("AgentTurn speech selection", () => {
  it("shows a floating action and reads from the selected text", async () => {
    const content = "第一段内容。\n\n第二段内容。";
    const speechText = "第一段内容。\n第二段内容。";
    const onSpeak = jest.fn();
    const removeAllRanges = jest.fn();

    render(
      <AgentTurn
        message={{ id: "assistant-1", role: "assistant", content }}
        isStreaming={false}
        messageSpeechState="idle"
        onSpeak={onSpeak}
        onPause={jest.fn()}
        onResume={jest.fn()}
        onStopSpeech={jest.fn()}
        isTtsSupported
        onCreateBranch={jest.fn()}
        onReplyPermission={jest.fn()}
        onReplyQuestion={jest.fn()}
        onRejectQuestion={jest.fn()}
      />,
    );

    const selectedParagraph = screen.getByText("第二段内容。");
    const selectedTextNode = selectedParagraph.firstChild as Text;
    const range = {
      commonAncestorContainer: selectedTextNode,
      getBoundingClientRect: () => ({
        width: 72,
        height: 20,
        top: 120,
        right: 172,
        bottom: 140,
        left: 100,
        x: 100,
        y: 120,
        toJSON: () => ({}),
      }),
    } as unknown as Range;
    const selection = {
      rangeCount: 1,
      isCollapsed: false,
      getRangeAt: () => range,
      toString: () => "第二段",
      removeAllRanges,
    } as unknown as Selection;

    jest.spyOn(window, "getSelection").mockReturnValue(selection);
    jest.spyOn(window, "requestAnimationFrame").mockImplementation((callback) => {
      callback(0);
      return 1;
    });

    fireEvent.pointerUp(selectedParagraph);

    const speechAction = await screen.findByRole("button", { name: "从这里开始朗读" });
    fireEvent.click(speechAction);

    await waitFor(() => {
      expect(onSpeak).toHaveBeenCalledWith("assistant-1", speechText, {
        startOffset: speechText.indexOf("第二段"),
      });
    });
    expect(removeAllRanges).toHaveBeenCalledTimes(1);
    await waitFor(() => {
      expect(screen.queryByRole("button", { name: "从这里开始朗读" })).not.toBeInTheDocument();
    });
  });
});
