import { act, useEffect } from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createRoot, type Root } from "react-dom/client";
import { useSidebarData } from "./use-sidebar-data";
import type { SidebarInitialData } from "@/components/sidebar/types";
import { buildKernelFileTreeProjection } from "@/lib/kernel-file-tree";

const mockUseConvexSidebarData = vi.fn();
const mockUseQuery = vi.fn();

vi.mock("@/hooks/use-convex-sidebar-data", () => ({
  useConvexSidebarData: (...args: unknown[]) => mockUseConvexSidebarData(...args),
}));

vi.mock("@tanstack/react-query", () => ({
  useQuery: (...args: unknown[]) => mockUseQuery(...args),
}));

function buildInitialData(): SidebarInitialData {
  const kernelFileTreeProjection = buildKernelFileTreeProjection({
    documents: [],
    mediaAssets: [],
    mindmapAssets: [],
    tableAssets: [],
    mindmapAssetChildren: {},
  });
  return {
    activeWorkspaceId: "ws_1",
    workspaces: [],
    documents: [],
    kernelFileTreeProjection,
    kernelSidebarProjection: {
      projectionId: "kernel_projection:sidebar_tree:workspace_root",
      projection: "sidebar_tree",
      rootNodeId: null,
      items: [],
      edges: [],
    },
    kernelSidebarTree: [],
    trashedDocuments: [],
    trashedMediaAssets: [],
    trashedMindmapAssets: [],
    trashedTableAssets: [],
    tableAssets: [],
    mindmapDocs: [],
    mindmapAssets: [],
    mindmapAssetChildren: {},
    mediaAssets: [],
  };
}

function Harness({
  initialData,
  onState,
}: {
  initialData: SidebarInitialData;
  onState: (state: ReturnType<typeof useSidebarData>) => void;
}) {
  const state = useSidebarData(initialData);

  useEffect(() => {
    onState(state);
  }, [onState, state]);

  return null;
}

describe("useSidebarData", () => {
  let container: HTMLDivElement;
  let root: Root;
  const onState = vi.fn();
  const stableRefetch = vi.fn(async () => undefined);
  const originalFetch = global.fetch;

  beforeEach(() => {
    (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT =
      true;
    container = document.createElement("div");
    document.body.appendChild(container);
    root = createRoot(container);
    onState.mockClear();
    mockUseConvexSidebarData.mockImplementation(() => ({
      data: buildInitialData(),
      isLoading: false,
      isAuthLoading: false,
      isAuthenticated: true,
      hasLiveSubscription: true,
      canUseHttpFallback: false,
      error: null,
      refetch: stableRefetch,
    }));
    mockUseQuery.mockImplementation(() => ({
      data: buildInitialData(),
      isLoading: false,
      error: null,
      refetch: stableRefetch,
    }));
    global.fetch = vi.fn();
  });

  afterEach(() => {
    act(() => {
      root.unmount();
    });
    container.remove();
    global.fetch = originalFetch;
  });

  it("底层 hook 语义未变时应保持返回对象稳定", async () => {
    const initialData = buildInitialData();

    await act(async () => {
      root.render(<Harness initialData={initialData} onState={onState} />);
    });
    const firstState = onState.mock.lastCall?.[0];

    await act(async () => {
      root.render(<Harness initialData={initialData} onState={onState} />);
    });
    const secondState = onState.mock.lastCall?.[0];

    expect(firstState).toBeDefined();
    expect(secondState).toBe(firstState);
  });

  it("Convex live 模式下手动 refetch 应强制刷新一份 HTTP sidebar snapshot", async () => {
    const initialData = buildInitialData();
    const refreshedData: SidebarInitialData = {
      ...buildInitialData(),
      documents: [
        {
          access_scope: "private",
          id: "doc-1",
          workspace_id: "ws_1",
          title: "新标题",
          parent_id: null,
          sort_order: 0,
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:01.000Z",
        },
      ],
      kernelSidebarTree: [
        {
          id: "doc-1",
          workspace_id: "ws_1",
          title: "新标题",
          parent_id: null,
          sort_order: 0,
          access_scope: "private",
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:01.000Z",
          children: [],
        },
      ],
    };
    (global.fetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
      ok: true,
      json: async () => refreshedData,
    });

    await act(async () => {
      root.render(<Harness initialData={initialData} onState={onState} />);
    });

    const state = onState.mock.lastCall?.[0] as ReturnType<typeof useSidebarData>;
    await act(async () => {
      await state.refetch();
    });

    expect(global.fetch).toHaveBeenCalledWith("/api/sidebar?workspaceId=ws_1", {
      method: "GET",
      credentials: "include",
    });
    const refreshedState = onState.mock.lastCall?.[0] as ReturnType<typeof useSidebarData>;
    expect(refreshedState.data.documents[0]?.title).toBe("新标题");
  });

  it("手动 refetch 只应临时覆盖主链，底层 live 数据变化后应回到新的 live snapshot", async () => {
    const initialData = buildInitialData();
    const manualSnapshot: SidebarInitialData = {
      ...buildInitialData(),
      documents: [
        {
          access_scope: "private",
          id: "doc-1",
          workspace_id: "ws_1",
          title: "HTTP 标题",
          parent_id: null,
          sort_order: 0,
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:02.000Z",
        },
      ],
      kernelSidebarTree: [
        {
          id: "doc-1",
          workspace_id: "ws_1",
          title: "HTTP 标题",
          parent_id: null,
          sort_order: 0,
          access_scope: "private",
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:02.000Z",
          children: [],
        },
      ],
    };
    const nextLiveData: SidebarInitialData = {
      ...buildInitialData(),
      documents: [
        {
          access_scope: "private",
          id: "doc-1",
          workspace_id: "ws_1",
          title: "Live 标题",
          parent_id: null,
          sort_order: 0,
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:00.000Z",
        },
      ],
      kernelSidebarTree: [
        {
          id: "doc-1",
          workspace_id: "ws_1",
          title: "Live 标题",
          parent_id: null,
          sort_order: 0,
          access_scope: "private",
          is_starred: false,
          is_template: false,
          created_at: "2026-04-21T00:00:00.000Z",
          updated_at: "2026-04-21T00:00:00.000Z",
          children: [],
        },
      ],
    };
    let liveData = initialData;
    mockUseConvexSidebarData.mockImplementation(() => ({
      data: liveData,
      isLoading: false,
      isAuthLoading: false,
      isAuthenticated: true,
      hasLiveSubscription: true,
      canUseHttpFallback: false,
      error: null,
      refetch: stableRefetch,
    }));
    (global.fetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
      ok: true,
      json: async () => manualSnapshot,
    });

    await act(async () => {
      root.render(<Harness initialData={initialData} onState={onState} />);
    });

    const state = onState.mock.lastCall?.[0] as ReturnType<typeof useSidebarData>;
    await act(async () => {
      await state.refetch();
    });
    expect((onState.mock.lastCall?.[0] as ReturnType<typeof useSidebarData>).data.documents[0]?.title).toBe("HTTP 标题");

    liveData = nextLiveData;
    await act(async () => {
      root.render(<Harness initialData={initialData} onState={onState} />);
    });

    expect((onState.mock.lastCall?.[0] as ReturnType<typeof useSidebarData>).data.documents[0]?.title).toBe("Live 标题");
  });
});
