import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { act } from "react";
import { ReactFlowProvider } from "@xyflow/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { SmartTextNode } from "@/components/nodes/SmartTextNode";
import { REQUEST_SMART_TEXT_EDIT_EVENT } from "@/utils/smartTextEditRequest";

const mockUpdateNodeData = vi.fn();

vi.mock("@/store/workflowStore", () => ({
  useWorkflowStore: vi.fn((selector) => {
    const state = {
      updateNodeData: mockUpdateNodeData,
      edges: [],
      currentNodeIds: [],
      groups: {},
      nodes: [],
      getConnectedInputs: vi.fn(() => ({ images: [], videos: [], text: null, dynamicInputs: {} })),
      getNodesWithComments: vi.fn(() => []),
      markCommentViewed: vi.fn(),
      setNavigationTarget: vi.fn(),
      setHoveredNodeId: vi.fn(),
    };
    return selector(state);
  }),
}));

vi.mock("@/hooks/useSelectedNodeCount", () => ({
  useSelectedNodeCount: () => 1,
}));

function TestWrapper({ children }: { children: React.ReactNode }) {
  return (
    <ReactFlowProvider>
      {children}
    </ReactFlowProvider>
  );
}

const defaultProps = {
  id: "text-1",
  type: "smartText" as const,
  data: {
    manualLocked: true,
    manualText: "First line\nLast character",
    inputPrompt: "",
    inputImages: [],
    outputText: null,
    provider: "openai" as const,
    model: "gpt-4o-mini",
    temperature: 0.7,
    maxTokens: 1000,
    status: "idle" as const,
    error: null,
  },
  selected: true,
  isConnectable: true,
  positionAbsoluteX: 0,
  positionAbsoluteY: 0,
  zIndex: 0,
  draggable: true,
  dragging: false,
  deletable: true,
  selectable: true,
  parentId: undefined,
  dragHandle: undefined,
};

describe("SmartTextNode", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("enters editing when an external smart text edit request targets the node", async () => {
    render(
      <TestWrapper>
        <SmartTextNode {...defaultProps} />
      </TestWrapper>
    );

    const renderedText = document.querySelector("p");
    expect(renderedText?.textContent).toContain("Last character");

    act(() => {
      window.dispatchEvent(new CustomEvent(REQUEST_SMART_TEXT_EDIT_EVENT, {
        detail: { nodeId: "text-1" },
      }));
    });

    const editor = await screen.findByRole("textbox", { name: /Write text content|编写文本内容|編寫文字內容|テキスト内容を書く/ });
    await waitFor(() => expect(document.activeElement).toBe(editor));

    const selection = window.getSelection();
    expect(selection?.rangeCount).toBe(1);
    expect(selection?.getRangeAt(0).collapsed).toBe(true);
  });

  it("prevents native text selection when double-clicking preview text", () => {
    render(
      <TestWrapper>
        <SmartTextNode {...defaultProps} />
      </TestWrapper>
    );

    const renderedText = document.querySelector("p");
    expect(renderedText?.textContent).toContain("Last character");

    const mouseDown = new MouseEvent("mousedown", {
      bubbles: true,
      cancelable: true,
      detail: 2,
    });

    renderedText!.dispatchEvent(mouseDown);

    expect(mouseDown.defaultPrevented).toBe(true);
  });

  it("ignores external smart text edit requests for other nodes", () => {
    render(
      <TestWrapper>
        <SmartTextNode {...defaultProps} />
      </TestWrapper>
    );

    act(() => {
      window.dispatchEvent(new CustomEvent(REQUEST_SMART_TEXT_EDIT_EVENT, {
        detail: { nodeId: "other-text-node" },
      }));
    });

    expect(screen.queryByRole("textbox", { name: /Write text content|编写文本内容|編寫文字內容|テキスト内容を書く/ })).not.toBeInTheDocument();
  });

  it("keeps the editable surface isolated from canvas drag and pan interactions", async () => {
    render(
      <TestWrapper>
        <SmartTextNode {...defaultProps} />
      </TestWrapper>
    );

    act(() => {
      window.dispatchEvent(new CustomEvent(REQUEST_SMART_TEXT_EDIT_EVENT, {
        detail: { nodeId: "text-1" },
      }));
    });

    const editor = await screen.findByRole("textbox", { name: /Write text content|编写文本内容|編寫文字內容|テキスト内容を書く/ });
    expect(editor).toHaveClass("nodrag");
    expect(editor).toHaveClass("nopan");
    expect(editor).toHaveClass("nowheel");
  });

  it("commits edits on native blur", async () => {
    render(
      <TestWrapper>
        <SmartTextNode {...defaultProps} />
      </TestWrapper>
    );

    act(() => {
      window.dispatchEvent(new CustomEvent(REQUEST_SMART_TEXT_EDIT_EVENT, {
        detail: { nodeId: "text-1" },
      }));
    });
    const editor = await screen.findByRole("textbox", { name: /Write text content|编写文本内容|編寫文字內容|テキスト内容を書く/ });

    editor.innerHTML = "Updated text";
    fireEvent.input(editor);
    fireEvent.blur(editor);

    await waitFor(() => {
      expect(mockUpdateNodeData).toHaveBeenCalledWith("text-1", {
        manualText: "Updated text",
      });
    });
  });
});
