import { beforeEach, describe, expect, it } from "bun:test";
import { Hono } from "hono";
import { categoryRoutes } from "../../src/server/routes/categories.ts";
import { itemRoutes } from "../../src/server/routes/items.ts";
import { createTestDb } from "../helpers/db.ts";

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

	// Inject test DB and userId into context for all routes
	app.use("*", async (c, next) => {
		c.set("db", db);
		c.set("userId", userId);
		await next();
	});

	app.route("/api/items", itemRoutes);
	app.route("/api/categories", categoryRoutes);
	return { app, db, userId };
}

describe("Item Routes", () => {
	let app: Hono;

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

	it("POST /api/items with valid data returns 201", async () => {
		const res = await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				name: "Tent",
				weightGrams: 1200,
				priceCents: 35000,
				categoryId: 1,
			}),
		});

		expect(res.status).toBe(201);
		const body = await res.json();
		expect(body.name).toBe("Tent");
		expect(body.id).toBeGreaterThan(0);
	});

	it("POST /api/items with missing name returns 400", async () => {
		const res = await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ categoryId: 1 }),
		});

		expect(res.status).toBe(400);
	});

	it("GET /api/items returns array", async () => {
		// Create an item first
		await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ name: "Tent", categoryId: 1 }),
		});

		const res = await app.request("/api/items");
		expect(res.status).toBe(200);
		const body = await res.json();
		expect(Array.isArray(body)).toBe(true);
		expect(body.length).toBeGreaterThanOrEqual(1);
	});

	it("PUT /api/items/:id updates fields", async () => {
		// Create first
		const createRes = await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				name: "Tent",
				weightGrams: 1200,
				categoryId: 1,
			}),
		});
		const created = await createRes.json();

		// Update
		const res = await app.request(`/api/items/${created.id}`, {
			method: "PUT",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ name: "Big Agnes Tent", weightGrams: 1100 }),
		});

		expect(res.status).toBe(200);
		const body = await res.json();
		expect(body.name).toBe("Big Agnes Tent");
		expect(body.weightGrams).toBe(1100);
	});

	it("DELETE /api/items/:id returns success", async () => {
		// Create first
		const createRes = await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ name: "Tent", categoryId: 1 }),
		});
		const created = await createRes.json();

		const res = await app.request(`/api/items/${created.id}`, {
			method: "DELETE",
		});

		expect(res.status).toBe(200);
		const body = await res.json();
		expect(body.success).toBe(true);
	});

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

	it("POST /api/items/:id/duplicate returns 201 with the copy", async () => {
		const createRes = await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ name: "Tent", categoryId: 1, weightGrams: 1200 }),
		});
		const created = await createRes.json();

		const res = await app.request(`/api/items/${created.id}/duplicate`, {
			method: "POST",
		});

		expect(res.status).toBe(201);
		const body = await res.json();
		expect(body.name).toBe("Tent (copy)");
		expect(body.weightGrams).toBe(1200);
		expect(body.id).not.toBe(created.id);
	});

	it("POST /api/items/999/duplicate returns 404", async () => {
		const res = await app.request("/api/items/999/duplicate", {
			method: "POST",
		});
		expect(res.status).toBe(404);
	});

	it("GET /api/items/export returns CSV with correct content-type", async () => {
		// Create an item first
		await app.request("/api/items", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				name: "Tent",
				weightGrams: 1200,
				priceCents: 35000,
				categoryId: 1,
			}),
		});

		const res = await app.request("/api/items/export");
		expect(res.status).toBe(200);
		expect(res.headers.get("Content-Type")).toContain("text/csv");

		const text = await res.text();
		const lines = text.split("\n");
		expect(lines[0]).toBe(
			"name,quantity,weightGrams,priceCents,category,notes,productUrl",
		);
		expect(lines.length).toBeGreaterThanOrEqual(2);
		expect(lines[1]).toContain("Tent");
	});

	it("POST /api/items/import with CSV file creates items", async () => {
		const csvContent = [
			"name,quantity,weightGrams,priceCents,category,notes,productUrl",
			"Sleeping Bag,1,800,25000,Camping,,",
		].join("\n");

		const formData = new FormData();
		formData.append(
			"file",
			new Blob([csvContent], { type: "text/csv" }),
			"import.csv",
		);

		const res = await app.request("/api/items/import", {
			method: "POST",
			body: formData,
		});

		expect(res.status).toBe(200);
		const body = await res.json();
		expect(body.imported).toBe(1);
		expect(body.errors).toHaveLength(0);
	});

	it("POST /api/items/import with no file returns 400", async () => {
		const res = await app.request("/api/items/import", {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({}),
		});

		expect(res.status).toBe(400);
	});
});
