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

const makeMember = (
  overrides: Partial<WorkspaceMemberWithUser> & { userId: string }
): WorkspaceMemberWithUser => ({
  workspaceId: overrides.workspaceId ?? "ws-1",
  userId: overrides.userId,
  role: overrides.role ?? WorkspaceMemberRole.MEMBER,
  joinedAt: overrides.joinedAt ?? new Date("2025-01-01"),
  user: overrides.user ?? {
    id: overrides.userId,
    name: `User ${overrides.userId}`,
    email: `${overrides.userId}@example.com`,
    emailVerified: true,
    image: null,
    authProviderId: `auth-${overrides.userId}`,
    preferences: {},
    deactivatedAt: null,
    isLocalAuth: false,
    passwordHash: null,
    invitedBy: null,
    invitationToken: null,
    invitedAt: null,
    createdAt: new Date("2025-01-01"),
    updatedAt: new Date("2025-01-01"),
  },
});

describe("MemberList", (): void => {
  const mockOnRoleChange = vi.fn<(userId: string, newRole: WorkspaceMemberRole) => Promise<void>>();
  const mockOnRemove = vi.fn<(userId: string) => Promise<void>>();

  const defaultProps = {
    currentUserId: "user-1",
    currentUserRole: WorkspaceMemberRole.ADMIN,
    workspaceOwnerId: "owner-1",
    onRoleChange: mockOnRoleChange,
    onRemove: mockOnRemove,
  };

  beforeEach((): void => {
    mockOnRoleChange.mockReset();
    mockOnRoleChange.mockResolvedValue(undefined);
    mockOnRemove.mockReset();
    mockOnRemove.mockResolvedValue(undefined);
  });

  it("should render member list with correct count", (): void => {
    const members = [makeMember({ userId: "user-1" }), makeMember({ userId: "user-2" })];
    render(<MemberList {...defaultProps} members={members} />);
    expect(screen.getByText("Members (2)")).toBeInTheDocument();
  });

  it("should display member name and email", (): void => {
    const members = [
      makeMember({
        userId: "user-2",
        user: {
          id: "user-2",
          name: "Jane Doe",
          email: "jane@example.com",
          emailVerified: true,
          image: null,
          authProviderId: "auth-2",
          preferences: {},
          deactivatedAt: null,
          isLocalAuth: false,
          passwordHash: null,
          invitedBy: null,
          invitationToken: null,
          invitedAt: null,
          createdAt: new Date("2025-01-01"),
          updatedAt: new Date("2025-01-01"),
        },
      }),
    ];
    render(<MemberList {...defaultProps} members={members} />);
    expect(screen.getByText("Jane Doe")).toBeInTheDocument();
    expect(screen.getByText("jane@example.com")).toBeInTheDocument();
  });

  it("should show (you) indicator for current user", (): void => {
    const members = [makeMember({ userId: "user-1" })];
    render(<MemberList {...defaultProps} members={members} />);
    expect(screen.getByText("(you)")).toBeInTheDocument();
  });

  it("should call onRoleChange with validated role when admin changes a member role", async (): Promise<void> => {
    const user = userEvent.setup();
    const members = [
      makeMember({ userId: "user-1" }),
      makeMember({ userId: "user-2", role: WorkspaceMemberRole.MEMBER }),
    ];
    render(<MemberList {...defaultProps} members={members} />);

    const roleSelect = screen.getByDisplayValue("Member");
    await user.selectOptions(roleSelect, WorkspaceMemberRole.GUEST);

    expect(mockOnRoleChange).toHaveBeenCalledWith("user-2", WorkspaceMemberRole.GUEST);
  });

  it("should not show role select for the workspace owner", (): void => {
    const members = [
      makeMember({ userId: "owner-1", role: WorkspaceMemberRole.OWNER }),
      makeMember({ userId: "user-1", role: WorkspaceMemberRole.ADMIN }),
    ];
    render(<MemberList {...defaultProps} members={members} />);
    expect(screen.getByText("OWNER")).toBeInTheDocument();
  });

  it("should not show remove button for the workspace owner", (): void => {
    const members = [makeMember({ userId: "owner-1", role: WorkspaceMemberRole.OWNER })];
    render(<MemberList {...defaultProps} members={members} />);
    expect(screen.queryByLabelText("Remove member")).not.toBeInTheDocument();
  });
});
