import { describe, it, expect, vi } from "vitest";
import { initTRPC, TRPCError } from "@trpc/server";

vi.mock("~/server/db", () => ({
  db: {},
}));

vi.mock("~/server/auth/session", () => ({
  validateSession: vi.fn(),
}));

vi.mock("~/server/auth/jwt", () => ({
  verifyJWT: vi.fn(),
}));

describe("createTRPCContext", () => {
  it("should export createTRPCContext function", async () => {
    const mod = await import("./trpc");
    expect(mod.createTRPCContext).toBeInstanceOf(Function);
  });

  it("should return anonymous context for unauthenticated requests", async () => {
    const { createTRPCContext } = await import("./trpc");
    const ctx = await createTRPCContext({
      req: new Request("http://localhost:3000/api/trpc"),
    });
    expect(ctx.user).toBeNull();
    expect(ctx.apiKey).toBeNull();
    expect(ctx.db).toBeDefined();
  });
});

describe("tRPC middleware", () => {
  type TestCtx = { user?: { id: string; role: string }; db: object };

  it("publicProcedure should allow unauthenticated access", async () => {
    const { publicProcedure } = await import("./utils");
    const t = initTRPC.context<TestCtx>().create();
    const testRouter = t.router({
      test: publicProcedure.query(() => "ok"),
    });
    const caller = t.createCallerFactory(testRouter);
    const result = await caller({ db: {} }).test();
    expect(result).toBe("ok");
  });

  it("protectedProcedure should reject unauthenticated requests", async () => {
    const { protectedProcedure } = await import("./utils");
    const t = initTRPC.context<TestCtx>().create();
    const testRouter = t.router({
      test: protectedProcedure.query(() => "ok"),
    });
    const caller = t.createCallerFactory(testRouter);
    await expect(caller({ db: {} }).test()).rejects.toThrow(TRPCError);
  });

  it("protectedProcedure should allow authenticated requests", async () => {
    const { protectedProcedure } = await import("./utils");
    const t = initTRPC.context<TestCtx>().create();
    const testRouter = t.router({
      test: protectedProcedure.query(({ ctx }) => ctx.user?.id),
    });
    const caller = t.createCallerFactory(testRouter);
    const result = await caller({
      db: {},
      user: { id: "user-1", role: "user" },
    }).test();
    expect(result).toBe("user-1");
  });

  it("adminProcedure should reject non-admin users with FORBIDDEN", async () => {
    const { adminProcedure } = await import("./utils");
    const t = initTRPC.context<TestCtx>().create();
    const testRouter = t.router({
      test: adminProcedure.query(() => "ok"),
    });
    const caller = t.createCallerFactory(testRouter);
    await expect(
      caller({ db: {}, user: { id: "user-1", role: "user" } }).test(),
    ).rejects.toThrow(TRPCError);
  });

  it("adminProcedure should reject unauthenticated with UNAUTHORIZED", async () => {
    const { adminProcedure } = await import("./utils");
    const t = initTRPC.context<TestCtx>().create();
    const testRouter = t.router({
      test: adminProcedure.query(() => "ok"),
    });
    const caller = t.createCallerFactory(testRouter);
    await expect(caller({ db: {} }).test()).rejects.toThrow(TRPCError);
  });
});
