import type { QueryContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { ForbiddenError } from "../lib/errors.generated";
import { baseSubscription } from "../testing/fixtures";
import { run } from "./listNotificationSubscriptions";

describe("listNotificationSubscriptions", () => {
  const userCtx: QueryContext = {
    actorId: "user-1",
    permissions: [],
  };

  it("returns the caller's subscriptions narrowed by (sourceType, sourceId)", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([
      { ...baseSubscription, userId: "user-1" },
      { ...baseSubscription, id: "subscription-2", userId: "user-1", sourceId: "po-1" },
    ]);

    const result = await run(mock.db, { sourceType: "PURCHASE_ORDER", sourceId: "po-1" }, userCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
    }
    // Results are always scoped to the caller.
    expect(mock.selects[0]?.parameters).toContain("user-1");
  });

  it("returns an empty list when the caller has no subscriptions for the source", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([]);

    const result = await run(
      mock.db,
      { sourceType: "PURCHASE_ORDER", sourceId: "po-empty" },
      userCtx,
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
  });

  it("filters by the requested sourceType", async () => {
    // The mock filters by where(); we assert the query was issued with the
    // requested sourceType and that returned rows reflect that filter.
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([
      { ...baseSubscription, userId: "user-1", sourceType: "RFQ", sourceId: "shared-id" },
    ]);

    const result = await run(mock.db, { sourceType: "RFQ", sourceId: "shared-id" }, userCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      for (const row of result.value.items) {
        expect(row.sourceType).toBe("RFQ");
      }
    }
  });

  it("returns the caller's subscriptions when called with userId == caller", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([
      { ...baseSubscription, userId: "user-1" },
      { ...baseSubscription, id: "sub-2", userId: "user-1", sourceId: "po-2" },
    ]);

    const result = await run(mock.db, { userId: "user-1" }, userCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
      for (const row of result.value.items) {
        expect(row.userId).toBe("user-1");
      }
    }
  });

  it("returns an empty list when the caller has no subscriptions", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([]);

    const result = await run(mock.db, {}, userCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
  });

  it("rejects calls with userId != caller with FORBIDDEN", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();

    const result = await run(mock.db, { userId: "user-other" }, userCtx);

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(ForbiddenError);
    }
  });

  it("returns rows with subscribedAt populated", async () => {
    const mock = createKyselyMock<Namespace[keyof Namespace]>();
    mock.enqueueResults([{ ...baseSubscription, userId: "user-1" }]);

    const result = await run(mock.db, { sourceType: "PURCHASE_ORDER", sourceId: "po-1" }, userCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0]?.subscribedAt).toBeDefined();
    }
  });
});
