import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { renderHook, act, waitFor } from "@testing-library/react";

const {
  mockRequestJson,
  mockRouter,
  mockGetCachedProfile,
  mockIsRegistered,
  mockToastShow,
  mockShare,
  mockCopyToClipboard,
} = vi.hoisted(() => ({
  mockRequestJson: vi.fn(),
  mockRouter: {
    push: vi.fn(),
    refresh: vi.fn(),
    replace: vi.fn(),
  },
  mockGetCachedProfile: vi.fn(),
  mockIsRegistered: vi.fn(),
  mockToastShow: vi.fn(),
  mockShare: vi.fn(),
  mockCopyToClipboard: vi.fn(),
}));

vi.mock("next/navigation", () => ({
  useRouter: () => mockRouter,
}));

vi.mock("@/lib/fetcher", () => ({
  requestJson: mockRequestJson,
}));

vi.mock("@/lib/userId", () => ({
  getCachedProfile: mockGetCachedProfile,
  isRegistered: mockIsRegistered,
}));

vi.mock("@/hooks/useToast", () => ({
  useToast: () => ({ show: mockToastShow }),
}));

vi.mock("@/hooks/useShare", () => ({
  useShare: () => ({ share: mockShare, copyToClipboard: mockCopyToClipboard }),
}));

import { useBlindboxRoom } from "./useBlindboxRoom";

function buildRoom(memberIds: string[] = ["user-1"]) {
  return {
    id: "room-1",
    code: "ABC123",
    name: "周末契约",
    creatorId: "user-1",
    city: "上海",
    address: "黄浦区",
    lat: 31.23,
    lng: 121.47,
    poolCount: 3,
    members: memberIds.map((id) => ({ id, username: id, avatar: "🐱" })),
  };
}

describe("useBlindboxRoom", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockIsRegistered.mockReturnValue(true);
    mockGetCachedProfile.mockReturnValue({
      id: "user-1",
      username: "alice",
      avatar: "🐱",
    });
    mockRequestJson.mockImplementation(async (url: string, init?: { method?: string }) => {
      if (url === "/api/blindbox/room/ABC123" && !init) {
        return buildRoom(["user-1", "user-2"]);
      }
      if (url === "/api/blindbox/room/ABC123" && init?.method === "DELETE") {
        return { action: "left" };
      }
      if (url === "/api/blindbox/room/join") {
        return { id: "room-1", code: "ABC123", name: "周末契约" };
      }
      throw new Error(`unexpected request: ${url}`);
    });
  });

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

  it("loads room data and marks current user as member", async () => {
    const { result } = renderHook(() => useBlindboxRoom("ABC123"));

    await waitFor(() => {
      expect(result.current.room?.id).toBe("room-1");
    });

    expect(result.current.isMember).toBe(true);
    expect(mockRequestJson).toHaveBeenCalledWith("/api/blindbox/room/ABC123");
  });

  it("joins room with code-only payload and refreshes room info", async () => {
    let joined = false;
    mockRequestJson.mockImplementation(async (url: string, init?: { method?: string; body?: unknown }) => {
      if (url === "/api/blindbox/room/ABC123" && !init) {
        return buildRoom(joined ? ["user-1", "user-2"] : ["user-2"]);
      }
      if (url === "/api/blindbox/room/join") {
        joined = true;
        return { id: "room-1", code: "ABC123", name: "周末契约" };
      }
      throw new Error(`unexpected request: ${url}`);
    });

    const { result } = renderHook(() => useBlindboxRoom("ABC123"));

    await waitFor(() => {
      expect(result.current.room?.id).toBe("room-1");
      expect(result.current.isMember).toBe(false);
    });

    await act(async () => {
      await result.current.handleJoinRoom();
    });

    expect(mockRequestJson).toHaveBeenCalledWith("/api/blindbox/room/join", {
      method: "POST",
      body: { code: "ABC123" },
    });
    await waitFor(() => {
      expect(result.current.isMember).toBe(true);
    });
  });

  it("requires confirmation before leave/delete and sends delete without body", async () => {
    const { result } = renderHook(() => useBlindboxRoom("ABC123"));

    await waitFor(() => {
      expect(result.current.room?.id).toBe("room-1");
    });

    await act(async () => {
      await result.current.handleLeaveOrDelete();
    });
    expect(result.current.confirmLeave).toBe(true);

    await act(async () => {
      await result.current.handleLeaveOrDelete();
    });

    expect(mockRequestJson).toHaveBeenCalledWith("/api/blindbox/room/ABC123", {
      method: "DELETE",
    });
    expect(mockRouter.push).toHaveBeenCalledWith("/blindbox");
    expect(mockRouter.refresh).toHaveBeenCalled();
  });
});
