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");

async function setupAdminUser(t: ReturnType<typeof convexTest>) {
  const asAdmin = t.withIdentity({
    name: "Admin",
    email: "admin@example.com",
    subject: "clerk_admin_123",
  });
  const userId = await asAdmin.mutation(api.users.store, {});
  await t.run(async (ctx) => {
    await ctx.db.patch(userId, { role: "admin" });
  });
  return asAdmin;
}

async function setupCategory(t: ReturnType<typeof convexTest>) {
  let categoryId: import("./_generated/dataModel").Id<"categories">;
  await t.run(async (ctx) => {
    categoryId = await ctx.db.insert("categories", {
      name: "Pet Food",
      slug: "pet-food",
    });
  });
  return categoryId!;
}

async function setupProduct(
  t: ReturnType<typeof convexTest>,
  asAdmin: ReturnType<ReturnType<typeof convexTest>["withIdentity"]>,
) {
  const categoryId = await setupCategory(t);
  return await asAdmin.mutation(api.products.create, {
    name: "Wishlist Product",
    slug: "wishlist-product",
    status: "active",
    categoryId,
    tags: [],
  });
}

describe("wishlists", () => {
  it("toggle adds then removes and list is empty after remove", async () => {
    const t = convexTest(schema, modules);
    const asAdmin = await setupAdminUser(t);
    const productId = await setupProduct(t, asAdmin);

    const asA = t.withIdentity({
      name: "Alice",
      email: "alice@example.com",
      subject: "clerk_alice_123",
    });
    await asA.mutation(api.users.store, {});

    const addResult = await asA.mutation(api.wishlists.toggle, { productId });
    expect(addResult).toEqual({ added: true, id: expect.anything() });

    const listAfterAdd = await asA.query(api.wishlists.list, {});
    expect(listAfterAdd).toHaveLength(1);

    const removeResult = await asA.mutation(api.wishlists.toggle, { productId });
    expect(removeResult).toEqual({ removed: true });

    const listAfterRemove = await asA.query(api.wishlists.list, {});
    expect(listAfterRemove).toHaveLength(0);
  });

  it("isWishlisted is true after add and false after remove", async () => {
    const t = convexTest(schema, modules);
    const asAdmin = await setupAdminUser(t);
    const productId = await setupProduct(t, asAdmin);

    const asA = t.withIdentity({
      name: "Alice",
      email: "alice@example.com",
      subject: "clerk_alice_123",
    });
    await asA.mutation(api.users.store, {});

    expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe(
      false,
    );

    await asA.mutation(api.wishlists.toggle, { productId });
    expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe(
      true,
    );

    await asA.mutation(api.wishlists.toggle, { productId });
    expect(await asA.query(api.wishlists.isWishlisted, { productId })).toBe(
      false,
    );
  });

  it("count returns 0 for unauthenticated user", async () => {
    const t = convexTest(schema, modules);
    const count = await t.query(api.wishlists.count, {});
    expect(count).toBe(0);
  });

  it("count returns correct number after add and toggle", async () => {
    const t = convexTest(schema, modules);
    const asAdmin = await setupAdminUser(t);
    const productId1 = await setupProduct(t, asAdmin);
    const categoryId = await setupCategory(t);
    const productId2 = await asAdmin.mutation(api.products.create, {
      name: "Wishlist Product 2",
      slug: "wishlist-product-2",
      status: "active",
      categoryId,
      tags: [],
    });

    const asA = t.withIdentity({
      name: "Alice",
      email: "alice@example.com",
      subject: "clerk_alice_123",
    });
    await asA.mutation(api.users.store, {});

    expect(await asA.query(api.wishlists.count, {})).toBe(0);

    await asA.mutation(api.wishlists.toggle, { productId: productId1 });
    await asA.mutation(api.wishlists.toggle, { productId: productId2 });
    expect(await asA.query(api.wishlists.count, {})).toBe(2);

    await asA.mutation(api.wishlists.toggle, { productId: productId1 });
    expect(await asA.query(api.wishlists.count, {})).toBe(1);
  });

  it("remove throws if user does not own the wishlist item", async () => {
    const t = convexTest(schema, modules);
    const asAdmin = await setupAdminUser(t);
    const productId = await setupProduct(t, asAdmin);

    const asA = t.withIdentity({
      name: "Alice",
      email: "alice@example.com",
      subject: "clerk_alice_123",
    });
    const asB = t.withIdentity({
      name: "Bob",
      email: "bob@example.com",
      subject: "clerk_bob_456",
    });
    await asA.mutation(api.users.store, {});
    await asB.mutation(api.users.store, {});

    await asA.mutation(api.wishlists.add, { productId });
    const listA = await asA.query(api.wishlists.list, {});
    expect(listA).toHaveLength(1);
    const wishlistId = listA[0]._id;

    await expect(
      asB.mutation(api.wishlists.remove, { id: wishlistId }),
    ).rejects.toThrow(/Unauthorized|does not belong/);

    await asA.mutation(api.wishlists.remove, { id: wishlistId });
    const listAfter = await asA.query(api.wishlists.list, {});
    expect(listAfter).toHaveLength(0);
  });
});
