import { TRPCError } from "@trpc/server";
import { describe, expect, it, vi } from "vitest";
import {
  getNotificationErrorCandidates,
  rethrowNotificationReferenceError,
  resolveUserId,
} from "../router/notification-procedure-support.js";

describe("notification procedure support", () => {
  it("resolves the current user id from the session email", async () => {
    const findUnique = vi.fn().mockResolvedValue({ id: "user_1" });

    await expect(resolveUserId({
      db: {
        user: {
          findUnique,
        },
      },
      session: {
        user: { email: "person@example.com" },
      },
    })).resolves.toBe("user_1");

    expect(findUnique).toHaveBeenCalledWith({
      where: { email: "person@example.com" },
      select: { id: true },
    });
  });

  it("rejects when no session email is available", async () => {
    await expect(resolveUserId({
      db: {
        user: {
          findUnique: vi.fn(),
        },
      },
      session: {
        user: null,
      },
    })).rejects.toEqual(expect.objectContaining<Partial<TRPCError>>({
      code: "UNAUTHORIZED",
    }));
  });

  it("collects nested notification error candidates from causes", () => {
    const nested = {
      code: "P2003",
      meta: { field_name: "senderId" },
    };

    const candidates = getNotificationErrorCandidates({
      cause: {
        shape: {
          data: {
            cause: nested,
          },
        },
      },
    });

    expect(candidates).toContainEqual(expect.objectContaining({
      code: "P2003",
      meta: expect.objectContaining({ field_name: "senderId" }),
    }));
  });

  it("rewrites sender foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "senderId" },
    };

    try {
      rethrowNotificationReferenceError(error);
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Sender user not found",
      });
    }
  });

  it("rewrites assignee foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "Notification_assigneeId_fkey" },
    };

    try {
      rethrowNotificationReferenceError(error);
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Assignee user not found",
      });
    }
  });

  it("rewrites broadcast source foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "Notification_sourceId_fkey" },
    };

    try {
      rethrowNotificationReferenceError(error);
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Notification broadcast not found",
      });
    }
  });

  it("rewrites notification recipient foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "Notification_userId_fkey" },
    };

    try {
      rethrowNotificationReferenceError(error);
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Notification recipient user not found",
      });
    }
  });

  it("rewrites task recipient foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "Notification_userId_fkey" },
    };

    try {
      rethrowNotificationReferenceError(error, "task");
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Task recipient user not found",
      });
    }
  });

  it("rewrites broadcast recipient foreign-key errors to a not found TRPC error", () => {
    const error = {
      code: "P2003",
      meta: { field_name: "Notification_userId_fkey" },
    };

    try {
      rethrowNotificationReferenceError(error, "broadcast");
      throw new Error("expected notification reference error");
    } catch (caught) {
      expect(caught).toBeInstanceOf(TRPCError);
      expect(caught).toMatchObject<Partial<TRPCError>>({
        code: "NOT_FOUND",
        message: "Broadcast recipient user not found",
      });
    }
  });

  it("rethrows unrelated errors unchanged", () => {
    const error = new Error("boom");

    expect(() => rethrowNotificationReferenceError(error)).toThrow(error);
  });
});
