import { describe, it, expect, vi, beforeEach } from "vitest";

const pipelineChain = vi.hoisted(() => ({
  zadd: vi.fn().mockReturnThis(),
  zremrangebyscore: vi.fn().mockReturnThis(),
  zcard: vi.fn().mockReturnThis(),
  expire: vi.fn().mockReturnThis(),
  exec: vi.fn(),
}));

vi.mock("../redis", () => ({
  getRedis: vi.fn(() => ({ pipeline: vi.fn(() => pipelineChain) })),
}));

vi.mock("next/server", () => ({
  NextResponse: {
    json: vi.fn((body: unknown, init?: { status?: number }) => ({ body, init, isNextResponse: true })),
  },
}));

import { NextResponse } from "next/server";
const { applyRateLimit } = await import("../rate-limit");

beforeEach(() => {
  vi.clearAllMocks();
});

describe("applyRateLimit", () => {
  it("returns null when under the limit", async () => {
    pipelineChain.exec.mockResolvedValue([null, null, [null, 3], null]);
    const result = await applyRateLimit("test-key", 10, 60);
    expect(result).toBeNull();
  });

  it("returns 429 response when over the limit", async () => {
    pipelineChain.exec.mockResolvedValue([null, null, [null, 11], null]);
    const result = await applyRateLimit("test-key", 10, 60);
    expect(result).not.toBeNull();
    expect(NextResponse.json).toHaveBeenCalledWith(
      expect.objectContaining({ error: expect.any(String) }),
      expect.objectContaining({ status: 429 })
    );
  });

  it("returns null when exactly at limit (count === limit is allowed)", async () => {
    pipelineChain.exec.mockResolvedValue([null, null, [null, 10], null]);
    const result = await applyRateLimit("test-key", 10, 60);
    expect(result).toBeNull();
  });

  it("calls pipeline with zadd, zremrangebyscore, zcard, expire", async () => {
    pipelineChain.exec.mockResolvedValue([null, null, [null, 1], null]);
    await applyRateLimit("my-key", 5, 30);
    expect(pipelineChain.zadd).toHaveBeenCalled();
    expect(pipelineChain.zremrangebyscore).toHaveBeenCalled();
    expect(pipelineChain.zcard).toHaveBeenCalled();
    expect(pipelineChain.expire).toHaveBeenCalled();
  });
});
