/* @vitest-environment node */

import { describe, it, expect } from "vitest";
import { ApiError } from "@/lib/api/errors";
import { encodeCursor, decodeCursor } from "./cursor.js";

describe("lib/search/cursor", () => {
	it("decodeCursor(null) returns the default cursor", () => {
		expect(decodeCursor(null)).toEqual({
			v: 1,
			mode: "sync",
			offset: 0,
			contextId: null,
		});
	});

	it('decodeCursor("") treats empty string like missing cursor (default)', () => {
		expect(decodeCursor("")).toEqual({
			v: 1,
			mode: "sync",
			offset: 0,
			contextId: null,
		});
	});

	it("encodeCursor + decodeCursor roundtrip works", () => {
		const encoded = encodeCursor({ v: 1, mode: "sync", offset: 10 });
		expect(typeof encoded).toBe("string");
		expect(encoded.length).toBeGreaterThan(5);

		const decoded = decodeCursor(encoded);
		expect(decoded).toEqual({
			v: 1,
			mode: "sync",
			offset: 10,
			contextId: null,
		});
	});

	it("keeps contextId when provided", () => {
		const encoded = encodeCursor({
			v: 1,
			mode: "sync",
			offset: 5,
			contextId: "ctx-123",
		});

		expect(decodeCursor(encoded)).toEqual({
			v: 1,
			mode: "sync",
			offset: 5,
			contextId: "ctx-123",
		});
	});

	it("encodeCursor rejects invalid payloads", () => {
		expect(() => encodeCursor(null)).toThrow(ApiError);
		expect(() => encodeCursor({})).toThrow(ApiError);
		expect(() => encodeCursor({ offset: -1 })).toThrow(ApiError);
	});

	it("decodeCursor rejects whitespace-only cursor", () => {
		expect(() => decodeCursor("   ")).toThrow(ApiError);
	});

	it("decodeCursor rejects non-JSON payloads (deterministic)", () => {
		// Using a valid base64url string that decodes to non-JSON ensures deterministic failure:
		// JSON.parse("not-json") must throw => decodeCursor must throw ApiError.
		const bad = Buffer.from("not-json", "utf8").toString("base64url");
		expect(() => decodeCursor(bad)).toThrow(ApiError);
	});

	it("decodeCursor rejects wrong version", () => {
		const bad = Buffer.from(
			JSON.stringify({ v: 2, mode: "sync", offset: 0 }),
			"utf8"
		).toString("base64url");

		expect(() => decodeCursor(bad)).toThrow(ApiError);
	});

	it("decodeCursor rejects negative offsets", () => {
		const bad = Buffer.from(
			JSON.stringify({ v: 1, mode: "sync", offset: -1 }),
			"utf8"
		).toString("base64url");

		expect(() => decodeCursor(bad)).toThrow(ApiError);
	});
});
