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

const isDatabaseConfiguredMock = vi.fn();
const getSessionPepperMock = vi.fn();
const hashSessionTokenMock = vi.fn();
const findUniqueMock = vi.fn();
const deleteMock = vi.fn();
const upsertMock = vi.fn();
const createSessionForUserMock = vi.fn();
const setSessionCookieMock = vi.fn();

vi.mock("../../lib/server/env", () => ({
  isDatabaseConfigured: () => isDatabaseConfiguredMock(),
  getSessionPepper: () => getSessionPepperMock(),
}));

vi.mock("../../lib/server/hash", () => ({
  hashSessionToken: (...args: unknown[]) => hashSessionTokenMock(...args),
}));

vi.mock("../../lib/server/session", () => ({
  createSessionForUser: (...args: unknown[]) =>
    createSessionForUserMock(...args),
  setSessionCookie: (...args: unknown[]) => setSessionCookieMock(...args),
}));

vi.mock("../../lib/server/db", () => ({
  prisma: {
    magicLinkToken: {
      findUnique: (...args: unknown[]) => findUniqueMock(...args),
      delete: (...args: unknown[]) => deleteMock(...args),
    },
    user: {
      upsert: (...args: unknown[]) => upsertMock(...args),
    },
  },
}));

import { GET } from "../../app/api/auth/magic-link/verify/route";

beforeEach(() => {
  isDatabaseConfiguredMock.mockReset();
  getSessionPepperMock.mockReset();
  hashSessionTokenMock.mockReset();
  findUniqueMock.mockReset();
  deleteMock.mockReset();
  upsertMock.mockReset();
  createSessionForUserMock.mockReset();
  setSessionCookieMock.mockReset();

  isDatabaseConfiguredMock.mockReturnValue(true);
  getSessionPepperMock.mockReturnValue("pepper");
  hashSessionTokenMock.mockReturnValue("token-hash");
});

function getWithToken(token: string) {
  return new NextRequest(
    `https://x.test/api/auth/magic-link/verify?token=${encodeURIComponent(token)}`,
  );
}

describe("GET /api/auth/magic-link/verify", () => {
  it("redirects with invalid_link when the token is missing", async () => {
    const res = await GET(
      new NextRequest("https://x.test/api/auth/magic-link/verify"),
    );
    expect(res.status).toBe(307);
    expect(res.headers.get("location")).toContain("error=invalid_link");
  });

  it("redirects with invalid_link when no row matches", async () => {
    findUniqueMock.mockResolvedValue(null);
    const res = await GET(getWithToken("a-token-value-long-enough"));
    expect(res.status).toBe(307);
    expect(res.headers.get("location")).toContain("error=invalid_link");
  });

  it("redirects with expired_link when the row is past expiresAt", async () => {
    findUniqueMock.mockResolvedValue({
      id: "row-1",
      email: "a@b.c",
      expiresAt: new Date(Date.now() - 1000),
      nextPath: null,
      draftPayload: null,
    });
    const res = await GET(getWithToken("a-token-value-long-enough"));
    expect(res.status).toBe(307);
    expect(res.headers.get("location")).toContain("error=expired_link");
  });
});
