import { convexTest } from "convex-test";
import { describe, it, expect } from "vitest";
import { api } from "./_generated/api";
import schema from "./schema";

const modules = import.meta.glob("./**/*.ts");

describe("users", () => {
  it("stores a new user on first login", async () => {
    const t = convexTest(schema, modules);
    const asSarah = t.withIdentity({
      name: "Sarah",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });
    const userId = await asSarah.mutation(api.users.store, {});
    expect(userId).toBeTruthy();

    const user = await asSarah.query(api.users.current, {});
    expect(user).toMatchObject({
      name: "Sarah",
      email: "sarah@example.com",
      role: "customer",
    });
  });

  it("returns same user ID on subsequent logins", async () => {
    const t = convexTest(schema, modules);
    const asSarah = t.withIdentity({
      name: "Sarah",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });

    const id1 = await asSarah.mutation(api.users.store, {});
    const id2 = await asSarah.mutation(api.users.store, {});
    expect(id1).toEqual(id2);
  });

  it("updates name if it changed", async () => {
    const t = convexTest(schema, modules);
    const asSarah = t.withIdentity({
      name: "Sarah",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });
    await asSarah.mutation(api.users.store, {});

    const asSarahRenamed = t.withIdentity({
      name: "Sarah Connor",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });
    await asSarahRenamed.mutation(api.users.store, {});

    const user = await asSarahRenamed.query(api.users.current, {});
    expect(user?.name).toBe("Sarah Connor");
  });

  it("returns null for unauthenticated user", async () => {
    const t = convexTest(schema, modules);
    const result = await t.query(api.users.current, {});
    expect(result).toBeNull();
  });

  it("returns user for authenticated user", async () => {
    const t = convexTest(schema, modules);
    const asSarah = t.withIdentity({
      name: "Sarah",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });
    await asSarah.mutation(api.users.store, {});

    const user = await asSarah.query(api.users.current, {});
    expect(user).not.toBeNull();
    expect(user?.email).toBe("sarah@example.com");
  });

  it("updateProfile updates only name, phone, avatarUrl for current user", async () => {
    const t = convexTest(schema, modules);
    const asSarah = t.withIdentity({
      name: "Sarah",
      email: "sarah@example.com",
      subject: "clerk_sarah_123",
    });
    await asSarah.mutation(api.users.store, {});

    await asSarah.mutation(api.users.updateProfile, {
      name: "Sarah Jane",
      phone: "555-0000",
      avatarUrl: "https://example.com/avatar.png",
    });

    const user = await asSarah.query(api.users.current, {});
    expect(user?.name).toBe("Sarah Jane");
    expect(user?.phone).toBe("555-0000");
    expect(user?.avatarUrl).toBe("https://example.com/avatar.png");
    expect(user?.email).toBe("sarah@example.com");
  });

  it("listCustomers returns only customers and is admin-only", async () => {
    const t = convexTest(schema, modules);
    const asCustomer = t.withIdentity({
      name: "Customer",
      email: "customer@example.com",
      subject: "clerk_customer_789",
    });
    const asAdmin = t.withIdentity({
      name: "Admin",
      email: "admin@example.com",
      subject: "clerk_admin_456",
    });
    await asCustomer.mutation(api.users.store, {});
    const adminId = await asAdmin.mutation(api.users.store, {});
    await t.run(async (ctx) => {
      await ctx.db.patch(adminId, { role: "admin" });
    });

    await expect(
      asCustomer.query(api.users.listCustomers, {
        paginationOpts: { numItems: 10, cursor: null },
      }),
    ).rejects.toThrow(/Unauthorized|admin/);

    const result = await asAdmin.query(api.users.listCustomers, {
      paginationOpts: { numItems: 10, cursor: null },
    });
    expect(result.page.length).toBeGreaterThanOrEqual(1);
    expect(result.page.every((u: { role: string }) => u.role === "customer")).toBe(
      true,
    );
  });
});
