import { beforeEach, describe, expect, it } from "bun:test";
import { Hono } from "hono";
import {
	globalItems,
	globalItemTags,
	items,
	tags,
} from "../../src/db/schema.ts";
import { globalItemRoutes } from "../../src/server/routes/global-items.ts";
import { createTestDb } from "../helpers/db.ts";

type TestDb = Awaited<ReturnType<typeof createTestDb>>;

async function createTestApp() {
	const { db, userId } = await createTestDb();
	const app = new Hono();

	app.use("*", async (c, next) => {
		c.set("db", db);
		c.set("userId", userId);
		await next();
	});

	app.route("/api/global-items", globalItemRoutes);
	return { app, db, userId };
}

async function insertGlobalItem(
	db: TestDb["db"],
	brand: string,
	model: string,
) {
	const [row] = await db
		.insert(globalItems)
		.values({ brand, model, category: "bags" })
		.returning();
	return row;
}

async function insertItem(
	db: TestDb["db"],
	name: string,
	userId: number,
	opts?: { globalItemId?: number },
) {
	const [row] = await db
		.insert(items)
		.values({ name, categoryId: 1, userId, globalItemId: opts?.globalItemId })
		.returning();
	return row;
}

describe("Global Item Routes", () => {
	let app: Hono;
	let db: TestDb["db"];
	let userId: number;

	beforeEach(async () => {
		const testApp = await createTestApp();
		app = testApp.app;
		db = testApp.db;
		userId = testApp.userId;
	});

	describe("GET /api/global-items", () => {
		it("returns 200 with all global items", async () => {
			await insertGlobalItem(db, "Revelate Designs", "Terrapin System");
			await insertGlobalItem(db, "Apidura", "Handlebar Pack");

			const res = await app.request("/api/global-items");
			expect(res.status).toBe(200);

			const body = await res.json();
			expect(body).toHaveLength(2);
		});

		it("filters results by query parameter", async () => {
			await insertGlobalItem(db, "Revelate Designs", "Terrapin System");
			await insertGlobalItem(db, "Apidura", "Handlebar Pack");

			const res = await app.request("/api/global-items?q=revelate");
			expect(res.status).toBe(200);

			const body = await res.json();
			expect(body).toHaveLength(1);
			expect(body[0].brand).toBe("Revelate Designs");
		});

		it("filters results by tags parameter", async () => {
			const gi1 = await insertGlobalItem(
				db,
				"Revelate Designs",
				"Terrapin System",
			);
			await insertGlobalItem(db, "Apidura", "Handlebar Pack");

			const [tag] = await db
				.insert(tags)
				.values({ name: "ultralight" })
				.returning();
			await db
				.insert(globalItemTags)
				.values({ globalItemId: gi1.id, tagId: tag.id });

			const res = await app.request("/api/global-items?tags=ultralight");
			expect(res.status).toBe(200);

			const body = await res.json();
			expect(body).toHaveLength(1);
			expect(body[0].brand).toBe("Revelate Designs");
		});
	});

	describe("GET /api/global-items/:id", () => {
		it("returns item with ownerCount", async () => {
			const gi = await insertGlobalItem(db, "MSR", "PocketRocket 2");

			const res = await app.request(`/api/global-items/${gi.id}`);
			expect(res.status).toBe(200);

			const body = await res.json();
			expect(body.brand).toBe("MSR");
			expect(body.ownerCount).toBe(0);
		});

		it("returns ownerCount from items.globalItemId", async () => {
			const gi = await insertGlobalItem(db, "MSR", "PocketRocket 2");
			await insertItem(db, "My Stove", userId, {
				globalItemId: gi.id,
			});

			const res = await app.request(`/api/global-items/${gi.id}`);
			expect(res.status).toBe(200);

			const body = await res.json();
			expect(body.ownerCount).toBe(1);
		});

		it("returns 404 for non-existent id", async () => {
			const res = await app.request("/api/global-items/999");
			expect(res.status).toBe(404);
		});

		it("returns 400 for invalid id", async () => {
			const res = await app.request("/api/global-items/abc");
			expect(res.status).toBe(400);
		});
	});
});
