/**
 * Tests for waitlists API routes
 * GET/POST /api/waitlists
 */

jest.mock("@/lib/auth", () => ({ auth: jest.fn() }));
jest.mock("@/lib/utils/auth-helpers.util", () => ({
  getAuthenticatedUser: jest.fn(),
}));
jest.mock("@/lib/db");
jest.mock("@/lib/dual-database", () => ({
  saveWaitlistToBoth: jest.fn().mockResolvedValue(undefined),
  deleteWaitlistFromBoth: jest.fn().mockResolvedValue(undefined),
  saveWaitlistSignupToBoth: jest.fn().mockResolvedValue(undefined),
  deleteWaitlistSignupFromBoth: jest.fn().mockResolvedValue(undefined),
}));
jest.mock("@/lib/config/constants", () => ({
  clampPaginationLimit: jest.fn(() => 50),
}));
jest.mock("@/lib/csrf", () => ({
  withCSRF: (handler: Function) => handler,
}));
jest.mock("@/lib/validators/api-schemas", () => ({
  createWaitlistSchema: {},
  validateBody: jest.fn(),
}));
jest.mock("@/lib/logger", () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
  },
}));
jest.mock("@paralleldrive/cuid2", () => ({
  createId: jest.fn(() => "cuid-wl-123"),
}));

import { NextRequest } from "next/server";
import { getAuthenticatedUser } from "@/lib/utils/auth-helpers.util";
import { prisma } from "@/lib/db";
import { validateBody } from "@/lib/validators/api-schemas";

const mockUser = { userId: "user-1", email: "test@test.com" };

function makeReq(url: string, method = "GET", body?: object) {
  const opts: RequestInit = { method };
  if (body) {
    opts.body = JSON.stringify(body);
    opts.headers = { "Content-Type": "application/json" };
  }
  return new NextRequest(`http://localhost:9000${url}`, opts);
}

describe("GET /api/waitlists", () => {
  let mod: typeof import("@/app/api/waitlists/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/route");
  });
  beforeEach(() => jest.clearAllMocks());

  it("returns public waitlists without auth", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    (prisma.waitlists.findMany as jest.Mock).mockResolvedValue([
      {
        id: "wl-1",
        title: "Test Waitlist",
        titleAr: null,
        description: "desc",
        descriptionAr: null,
        category: "general",
        createdAt: new Date(),
        _count: { signups: 5 },
      },
    ]);

    const res = await mod.GET(makeReq("/api/waitlists"));
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.waitlists).toHaveLength(1);
    expect(data.waitlists[0].signupCount).toBe(5);
  });

  it("returns 401 for mine=true without auth", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await mod.GET(makeReq("/api/waitlists?mine=true"));
    const data = await res.json();
    expect(res.status).toBe(401);
    expect(data.error).toContain("Authentication required");
  });

  it("returns user waitlists with mine=true and auth", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findMany as jest.Mock).mockResolvedValue([
      {
        id: "wl-2",
        title: "My WL",
        titleAr: null,
        description: null,
        descriptionAr: null,
        category: "general",
        isActive: true,
        isPublic: false,
        createdAt: new Date(),
        updatedAt: new Date(),
        _count: { signups: 3 },
      },
    ]);

    const res = await mod.GET(makeReq("/api/waitlists?mine=true"));
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.waitlists).toHaveLength(1);
    expect(data.waitlists[0].isOwner).toBe(true);
  });

  it("includes isSignedUp for authenticated users on public list", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findMany as jest.Mock).mockResolvedValue([
      {
        id: "wl-1",
        title: "Public WL",
        titleAr: null,
        description: null,
        descriptionAr: null,
        category: "general",
        createdAt: new Date(),
        _count: { signups: 10 },
      },
    ]);
    (prisma.waitlist_signups.findMany as jest.Mock).mockResolvedValue([
      { waitlistId: "wl-1" },
    ]);

    const res = await mod.GET(makeReq("/api/waitlists"));
    const data = await res.json();
    expect(data.waitlists[0].isSignedUp).toBe(true);
  });

  it("returns 500 on service error", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    (prisma.waitlists.findMany as jest.Mock).mockRejectedValue(
      new Error("DB fail"),
    );
    const res = await mod.GET(makeReq("/api/waitlists"));
    expect(res.status).toBe(500);
  });
});

describe("POST /api/waitlists", () => {
  let mod: typeof import("@/app/api/waitlists/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/route");
  });
  beforeEach(() => jest.clearAllMocks());

  it("returns 401 when unauthenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists", "POST", { title: "Test" }),
    );
    const data = await res.json();
    expect(res.status).toBe(401);
    expect(data.error).toContain("Authentication required");
  });

  it("returns 400 for validation failure", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (validateBody as jest.Mock).mockResolvedValue({
      success: false,
      error: "Title is required",
    });

    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists", "POST", {}),
    );
    const data = await res.json();
    expect(res.status).toBe(400);
    expect(data.error).toBe("Title is required");
  });

  it("creates waitlist successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (validateBody as jest.Mock).mockResolvedValue({
      success: true,
      data: {
        title: "New Waitlist",
        category: "general",
        isActive: true,
        isPublic: true,
      },
    });
    (prisma.waitlists.create as jest.Mock).mockResolvedValue({
      id: "cuid-wl-123",
      title: "New Waitlist",
      titleAr: null,
      description: null,
      descriptionAr: null,
      category: "general",
      isActive: true,
      isPublic: true,
      createdAt: new Date(),
      createdBy: "user-1",
    });

    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists", "POST", { title: "New Waitlist" }),
    );
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.waitlist.title).toBe("New Waitlist");
    expect(data.waitlist.signupCount).toBe(0);
  });

  it("returns 500 on service error", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (validateBody as jest.Mock).mockResolvedValue({
      success: true,
      data: {
        title: "Test",
        category: "general",
        isActive: true,
        isPublic: true,
      },
    });
    (prisma.waitlists.create as jest.Mock).mockRejectedValue(
      new Error("DB fail"),
    );

    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists", "POST", { title: "Test" }),
    );
    expect(res.status).toBe(500);
  });
});

// --- GET /api/waitlists/[id] ---

describe("GET /api/waitlists/[id]", () => {
  let mod: typeof import("@/app/api/waitlists/[id]/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/[id]/route");
  });
  beforeEach(() => jest.clearAllMocks());

  const ctx = { params: { id: "w1" } };

  it("returns 401 when unauthenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await mod.GET(makeReq("/api/waitlists/w1"), ctx as never);
    expect(res.status).toBe(401);
  });

  it("returns 404 when not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue(null);
    const res = await mod.GET(makeReq("/api/waitlists/w1"), ctx as never);
    expect(res.status).toBe(404);
  });

  it("returns 403 when not owner", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "other-user",
      _count: { signups: 0 },
      signups: [],
    });
    const res = await mod.GET(makeReq("/api/waitlists/w1"), ctx as never);
    expect(res.status).toBe(403);
  });

  it("returns waitlist detail for owner", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "user-1",
      title: "My Waitlist",
      titleAr: null,
      description: null,
      descriptionAr: null,
      category: "general",
      isActive: true,
      isPublic: false,
      createdAt: new Date(),
      updatedAt: new Date(),
      _count: { signups: 2 },
      signups: [{ id: "s1" }, { id: "s2" }],
    });
    const res = await mod.GET(makeReq("/api/waitlists/w1"), ctx as never);
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.waitlist.id).toBe("w1");
    expect(data.waitlist.signupCount).toBe(2);
  });
});

// --- PATCH /api/waitlists/[id] ---

describe("PATCH /api/waitlists/[id]", () => {
  let mod: typeof import("@/app/api/waitlists/[id]/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/[id]/route");
  });
  beforeEach(() => jest.clearAllMocks());

  const ctx = { params: { id: "w1" } };

  it("returns 401 when unauthenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await (mod.PATCH as Function)(
      makeReq("/api/waitlists/w1", "PATCH", { title: "Updated" }),
      ctx,
    );
    expect(res.status).toBe(401);
  });

  it("returns 404 when not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue(null);
    const res = await (mod.PATCH as Function)(
      makeReq("/api/waitlists/w1", "PATCH", { title: "Updated" }),
      ctx,
    );
    expect(res.status).toBe(404);
  });

  it("updates waitlist successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "user-1",
    });
    (prisma.waitlists.update as jest.Mock).mockResolvedValue({
      id: "w1",
      title: "Updated",
      titleAr: null,
      description: null,
      descriptionAr: null,
      category: "general",
      isActive: true,
      isPublic: false,
      createdBy: "user-1",
      updatedAt: new Date(),
    });
    const res = await (mod.PATCH as Function)(
      makeReq("/api/waitlists/w1", "PATCH", { title: "Updated" }),
      ctx,
    );
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.success).toBe(true);
  });
});

// --- DELETE /api/waitlists/[id] ---

describe("DELETE /api/waitlists/[id]", () => {
  let mod: typeof import("@/app/api/waitlists/[id]/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/[id]/route");
  });
  beforeEach(() => jest.clearAllMocks());

  const ctx = { params: { id: "w1" } };

  it("deletes waitlist successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "user-1",
    });
    (prisma.waitlists.delete as jest.Mock).mockResolvedValue({});
    const res = await (mod.DELETE as Function)(
      makeReq("/api/waitlists/w1", "DELETE"),
      ctx,
    );
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.success).toBe(true);
  });

  it("returns 404 when not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue(null);
    const res = await (mod.DELETE as Function)(
      makeReq("/api/waitlists/w1", "DELETE"),
      ctx,
    );
    expect(res.status).toBe(404);
  });
});

// --- POST /api/waitlists/[id]/signup ---

describe("POST /api/waitlists/[id]/signup", () => {
  let mod: typeof import("@/app/api/waitlists/[id]/signup/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/[id]/signup/route");
  });
  beforeEach(() => {
    jest.clearAllMocks();
    (validateBody as jest.Mock).mockResolvedValue({
      success: true,
      data: { name: "Alice", email: "alice@test.com", mobile: "+974123456" },
    });
  });

  const ctx = { params: { id: "w1" } };

  it("returns 400 for validation failure", async () => {
    (validateBody as jest.Mock).mockResolvedValueOnce({
      success: false,
      error: "Name is required",
    });
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists/w1/signup", "POST", {}),
      ctx,
    );
    expect(res.status).toBe(400);
  });

  const validSignupData = {
    name: "Alice",
    email: "alice@test.com",
    mobile: "+974123456",
  };

  it("returns 404 when waitlist not found", async () => {
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue(null);
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists/w1/signup", "POST", validSignupData),
      ctx,
    );
    expect(res.status).toBe(404);
  });

  it("returns 400 when waitlist inactive", async () => {
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      isActive: false,
    });
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists/w1/signup", "POST", validSignupData),
      ctx,
    );
    expect(res.status).toBe(400);
  });

  it("returns 409 for duplicate signup", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      isActive: true,
    });
    (prisma.waitlist_signups.findUnique as jest.Mock).mockResolvedValue({
      id: "existing",
    });
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists/w1/signup", "POST", validSignupData),
      ctx,
    );
    expect(res.status).toBe(409);
  });

  it("creates signup successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      isActive: true,
    });
    (prisma.waitlist_signups.findUnique as jest.Mock).mockResolvedValue(null);
    (prisma.waitlist_signups.create as jest.Mock).mockResolvedValue({
      id: "cuid-wl-123",
      waitlistId: "w1",
      userId: null,
      name: "Alice",
      email: "alice@test.com",
      mobile: "+974123456",
      createdAt: new Date(),
    });
    const res = await (mod.POST as Function)(
      makeReq("/api/waitlists/w1/signup", "POST", validSignupData),
      ctx,
    );
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.success).toBe(true);
  });
});

// --- GET /api/waitlists/[id]/signups ---

describe("GET /api/waitlists/[id]/signups", () => {
  let mod: typeof import("@/app/api/waitlists/[id]/signups/route");
  beforeAll(async () => {
    mod = await import("@/app/api/waitlists/[id]/signups/route");
  });
  beforeEach(() => jest.clearAllMocks());

  const ctx = { params: { id: "w1" } };

  it("returns 401 when unauthenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await mod.GET(
      makeReq("/api/waitlists/w1/signups"),
      ctx as never,
    );
    expect(res.status).toBe(401);
  });

  it("returns 404 when waitlist not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue(null);
    const res = await mod.GET(
      makeReq("/api/waitlists/w1/signups"),
      ctx as never,
    );
    expect(res.status).toBe(404);
  });

  it("returns 403 when not owner", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "other-user",
    });
    const res = await mod.GET(
      makeReq("/api/waitlists/w1/signups"),
      ctx as never,
    );
    expect(res.status).toBe(403);
  });

  it("returns signups list", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (prisma.waitlists.findUnique as jest.Mock).mockResolvedValue({
      id: "w1",
      userId: "user-1",
    });
    (prisma.waitlist_signups.findMany as jest.Mock).mockResolvedValue([
      { id: "s1", name: "Alice" },
    ]);
    (prisma.waitlist_signups.count as jest.Mock).mockResolvedValue(1);
    const res = await mod.GET(
      makeReq("/api/waitlists/w1/signups"),
      ctx as never,
    );
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.signups).toHaveLength(1);
  });
});
