import { describe, it, expect, vi, beforeEach } from "vitest";

const { mockListFindFirst, mockMemberFindFirst } = vi.hoisted(() => ({
  mockListFindFirst: vi.fn(),
  mockMemberFindFirst: vi.fn(),
}));

vi.mock("@epicure/db", () => ({
  db: {
    query: {
      shoppingLists: { findFirst: mockListFindFirst },
      shoppingListMembers: { findFirst: mockMemberFindFirst },
    },
  },
  shoppingLists: { id: "id", userId: "user_id" },
  shoppingListMembers: { listId: "list_id", userId: "user_id" },
  eq: vi.fn((a, b) => ({ a, b, op: "eq" })),
  and: vi.fn((...args) => ({ args, op: "and" })),
}));

const { getShoppingListAccess, canWriteShoppingList } = await import("../shopping-list-access");

beforeEach(() => {
  vi.clearAllMocks();
});

describe("getShoppingListAccess", () => {
  it("returns null when the list doesn't exist", async () => {
    mockListFindFirst.mockResolvedValue(undefined);
    expect(await getShoppingListAccess("list-1", "user-1")).toBeNull();
  });

  it("grants owner role to the list's userId", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-1" });
    const access = await getShoppingListAccess("list-1", "user-1");
    expect(access?.role).toBe("owner");
  });

  it("grants the member's assigned role", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-owner" });
    mockMemberFindFirst.mockResolvedValue({ role: "editor" });
    const access = await getShoppingListAccess("list-1", "user-2");
    expect(access?.role).toBe("editor");
  });

  it("returns null when the user is neither owner nor a member", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-owner" });
    mockMemberFindFirst.mockResolvedValue(undefined);
    expect(await getShoppingListAccess("list-1", "user-2")).toBeNull();
  });

  it("grants anonymous editor access via a public-editable link", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-owner", isPublic: true, publicEditable: true });
    const access = await getShoppingListAccess("list-1", null);
    expect(access?.role).toBe("editor");
  });

  it("grants anonymous viewer access via a public read-only link", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-owner", isPublic: true, publicEditable: false });
    const access = await getShoppingListAccess("list-1", null);
    expect(access?.role).toBe("viewer");
  });

  it("denies anonymous access when the list isn't public", async () => {
    mockListFindFirst.mockResolvedValue({ id: "list-1", userId: "user-owner", isPublic: false, publicEditable: false });
    expect(await getShoppingListAccess("list-1", null)).toBeNull();
  });
});

describe("canWriteShoppingList", () => {
  it("allows owner and editor, denies viewer", () => {
    expect(canWriteShoppingList("owner")).toBe(true);
    expect(canWriteShoppingList("editor")).toBe(true);
    expect(canWriteShoppingList("viewer")).toBe(false);
  });
});
