/* @vitest-environment node */

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

vi.mock("@/lib/auth/session", () => ({
	getSession: vi.fn(),
}));

vi.mock("@/lib/search", () => ({
	search: vi.fn(),
}));

import { getSession } from "@/lib/auth/session";
import { search as searchBackend } from "@/lib/search";
import { GET, dynamic } from "./route.js";

describe("GET /api/search", () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('exports dynamic="force-dynamic"', () => {
		expect(dynamic).toBe("force-dynamic");
	});

	it("returns 401 when unauthenticated", async () => {
		getSession.mockResolvedValue(null);

		const req = new Request("http://localhost/api/search?q=test&branch=NL01");
		const res = await GET(req);

		expect(res.status).toBe(401);
		expect(await res.json()).toEqual({
			error: { message: "Unauthorized", code: "AUTH_UNAUTHENTICATED" },
		});
	});

	it("returns 403 when branch user tries to access a different branch", async () => {
		getSession.mockResolvedValue({
			userId: "u1",
			role: "branch",
			branchId: "NL01",
		});

		const req = new Request("http://localhost/api/search?q=test&branch=NL02");
		const res = await GET(req);

		expect(res.status).toBe(403);
		expect(await res.json()).toEqual({
			error: { message: "Forbidden", code: "AUTH_FORBIDDEN_BRANCH" },
		});
	});

	it("returns 400 when missing both q and date range", async () => {
		getSession.mockResolvedValue({
			userId: "u2",
			role: "admin",
			branchId: null,
		});

		const req = new Request("http://localhost/api/search?branch=NL01");
		const res = await GET(req);

		expect(res.status).toBe(400);
		expect(await res.json()).toMatchObject({
			error: {
				code: "VALIDATION_SEARCH_MISSING_FILTER",
			},
		});
	});

	it("returns 400 for invalid date format", async () => {
		getSession.mockResolvedValue({
			userId: "u2",
			role: "admin",
			branchId: null,
		});

		const req = new Request(
			"http://localhost/api/search?branch=NL01&q=x&from=2025/01/01"
		);
		const res = await GET(req);

		expect(res.status).toBe(400);
		expect(await res.json()).toMatchObject({
			error: {
				code: "VALIDATION_SEARCH_DATE",
			},
		});
	});

	it("returns 200 and passes through items + nextCursor", async () => {
		getSession.mockResolvedValue({
			userId: "u2",
			role: "admin",
			branchId: null,
		});

		searchBackend.mockResolvedValue({
			items: [
				{
					branch: "NL20",
					date: "2025-12-18",
					year: "2025",
					month: "12",
					day: "18",
					filename: "x.pdf",
					relativePath: "NL20/2025/12/18/x.pdf",
				},
			],
			nextCursor: "abc",
			total: 1,
		});

		const req = new Request(
			"http://localhost/api/search?branch=NL20&q=bridgestone&limit=100"
		);
		const res = await GET(req);

		expect(res.status).toBe(200);
		expect(await res.json()).toEqual({
			items: [
				{
					branch: "NL20",
					date: "2025-12-18",
					year: "2025",
					month: "12",
					day: "18",
					filename: "x.pdf",
					relativePath: "NL20/2025/12/18/x.pdf",
				},
			],
			nextCursor: "abc",
			total: 1,
		});

		expect(searchBackend).toHaveBeenCalledTimes(1);
	});
});
