import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";

const mockSession = { user: { id: "user-1" } };

vi.mock("@/lib/api-auth", () => ({
  requireDeveloper: vi.fn(),
}));

vi.mock("@/lib/validate-webhook-url", () => ({
  validateWebhookUrl: vi.fn().mockResolvedValue(null),
}));

const { mockSelectChain, mockInsertValues } = vi.hoisted(() => {
  const mockSelectChain = {
    from: vi.fn().mockReturnThis(),
    where: vi.fn().mockResolvedValue([]),
  };
  return { mockSelectChain, mockInsertValues: vi.fn().mockResolvedValue(undefined) };
});

vi.mock("@epicure/db", () => ({
  db: {
    select: vi.fn(() => mockSelectChain),
    insert: vi.fn(() => ({ values: mockInsertValues })),
  },
  webhooks: { id: "id", userId: "user_id", url: "url", events: "events", active: "active", createdAt: "created_at" },
  eq: vi.fn((a, b) => ({ a, b, op: "eq" })),
}));

const { requireDeveloper } = await import("@/lib/api-auth");
const { validateWebhookUrl } = await import("@/lib/validate-webhook-url");
import { GET, POST } from "../route";

function makeRequest(method: string, body?: unknown) {
  return new NextRequest("http://localhost/api/v1/webhooks", {
    method,
    headers: { "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
}

beforeEach(() => {
  vi.clearAllMocks();
  vi.mocked(requireDeveloper).mockResolvedValue({ session: mockSession as never, response: null });
  vi.mocked(validateWebhookUrl).mockResolvedValue(null);
  mockSelectChain.where.mockResolvedValue([]);
});

describe("GET /api/v1/webhooks", () => {
  it("returns 401 when not authenticated", async () => {
    vi.mocked(requireDeveloper).mockResolvedValue({
      session: null,
      response: new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 }),
    } as never);

    const res = await GET();
    expect(res.status).toBe(401);
  });

  it("returns 200 with the user's webhooks", async () => {
    mockSelectChain.where.mockResolvedValue([{ id: "wh-1", userId: "user-1" }]);
    const res = await GET();
    expect(res.status).toBe(200);
    const body = await res.json() as unknown[];
    expect(body).toHaveLength(1);
  });
});

describe("POST /api/v1/webhooks", () => {
  const validBody = { url: "https://example.com/hook", events: ["recipe.created"] };

  it("returns 400 on validation error", async () => {
    const res = await POST(makeRequest("POST", { url: "" }));
    expect(res.status).toBe(400);
  });

  it("returns 400 when the URL fails SSRF validation", async () => {
    vi.mocked(validateWebhookUrl).mockResolvedValue("Webhook URL must not point to a private or reserved address");
    const res = await POST(makeRequest("POST", validBody));
    expect(res.status).toBe(400);
  });

  it("returns 401 when not authenticated", async () => {
    vi.mocked(requireDeveloper).mockResolvedValue({
      session: null,
      response: new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 }),
    } as never);

    const res = await POST(makeRequest("POST", validBody));
    expect(res.status).toBe(401);
  });

  it("returns 201 and creates the webhook", async () => {
    const res = await POST(makeRequest("POST", validBody));
    expect(res.status).toBe(201);
    const body = await res.json() as { url: string; secret: string };
    expect(body.url).toBe(validBody.url);
    expect(body.secret).toBeTruthy();
    expect(mockInsertValues).toHaveBeenCalled();
  });
});
