import type { ReactElement, ReactNode } from "react";

import { WorkspaceMemberRole } from "@mosaic/shared";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";

import {
  deactivateUser,
  fetchAdminUsers,
  inviteUser,
  updateUser,
  type AdminUsersResponse,
} from "@/lib/api/admin";
import { fetchUserWorkspaces, updateWorkspaceMemberRole } from "@/lib/api/workspaces";
import UsersSettingsPage from "./page";

vi.mock("next/link", () => ({
  default: function LinkMock({
    children,
    href,
  }: {
    children: ReactNode;
    href: string;
  }): ReactElement {
    return <a href={href}>{children}</a>;
  },
}));

vi.mock("@/lib/api/admin", () => ({
  fetchAdminUsers: vi.fn(),
  inviteUser: vi.fn(),
  updateUser: vi.fn(),
  deactivateUser: vi.fn(),
}));

vi.mock("@/lib/api/workspaces", () => ({
  fetchUserWorkspaces: vi.fn(),
  updateWorkspaceMemberRole: vi.fn(),
}));

const fetchAdminUsersMock = vi.mocked(fetchAdminUsers);
const inviteUserMock = vi.mocked(inviteUser);
const updateUserMock = vi.mocked(updateUser);
const deactivateUserMock = vi.mocked(deactivateUser);
const fetchUserWorkspacesMock = vi.mocked(fetchUserWorkspaces);
const updateWorkspaceMemberRoleMock = vi.mocked(updateWorkspaceMemberRole);

const adminUsersResponse: AdminUsersResponse = {
  data: [
    {
      id: "user-1",
      name: "Alice",
      email: "alice@example.com",
      emailVerified: true,
      image: null,
      createdAt: "2026-01-01T00:00:00.000Z",
      deactivatedAt: null,
      isLocalAuth: false,
      invitedAt: null,
      invitedBy: null,
      workspaceMemberships: [
        {
          workspaceId: "workspace-1",
          workspaceName: "Personal Workspace",
          role: WorkspaceMemberRole.ADMIN,
          joinedAt: "2026-01-01T00:00:00.000Z",
        },
      ],
    },
  ],
  meta: {
    total: 1,
    page: 1,
    limit: 50,
    totalPages: 1,
  },
};

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

    fetchAdminUsersMock.mockResolvedValue(adminUsersResponse);
    fetchUserWorkspacesMock.mockResolvedValue([
      {
        id: "workspace-1",
        name: "Personal Workspace",
        ownerId: "owner-1",
        role: WorkspaceMemberRole.OWNER,
        createdAt: "2026-01-01T00:00:00.000Z",
      },
    ]);
    inviteUserMock.mockResolvedValue({
      userId: "user-2",
      invitationToken: "token-1",
      email: "new@example.com",
      invitedAt: "2026-01-02T00:00:00.000Z",
    });
    const firstUser = adminUsersResponse.data[0];
    if (!firstUser) {
      throw new Error("Expected at least one admin user in test fixtures");
    }

    updateUserMock.mockResolvedValue(firstUser);
    deactivateUserMock.mockResolvedValue(firstUser);
    updateWorkspaceMemberRoleMock.mockResolvedValue({
      workspaceId: "workspace-1",
      userId: "user-1",
      role: WorkspaceMemberRole.ADMIN,
      joinedAt: "2026-01-01T00:00:00.000Z",
      user: {
        id: "user-1",
        email: "alice@example.com",
        name: "Alice",
        image: null,
      },
    });
  });

  it("invites a user with email and role from the dialog", async () => {
    const user = userEvent.setup();
    render(<UsersSettingsPage />);

    expect(await screen.findByText("User Directory")).toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: "Invite User" }));
    await user.type(screen.getByLabelText("Email"), "new@example.com");
    await user.click(screen.getByRole("button", { name: "Send Invite" }));

    await waitFor(() => {
      expect(inviteUserMock).toHaveBeenCalledWith({
        email: "new@example.com",
        role: WorkspaceMemberRole.MEMBER,
        workspaceId: "workspace-1",
      });
    });
  });

  it("opens user detail dialog from row click and saves edited profile fields", async () => {
    const user = userEvent.setup();
    render(<UsersSettingsPage />);

    expect(await screen.findByText("alice@example.com")).toBeInTheDocument();

    await user.click(screen.getByText("Alice"));

    const nameInput = await screen.findByLabelText("Name");
    await user.clear(nameInput);
    await user.type(nameInput, "Alice Updated");

    await user.click(screen.getByRole("button", { name: "Save Changes" }));

    await waitFor(() => {
      expect(updateUserMock).toHaveBeenCalledWith("user-1", { name: "Alice Updated" });
    });

    expect(updateWorkspaceMemberRoleMock).not.toHaveBeenCalled();
  });
});
