import { beforeEach, describe, expect, it } from "bun:test";
import { items } from "../../src/db/schema.ts";
import {
	exportItemsCsv,
	importItemsCsv,
} from "../../src/server/services/csv.service.ts";
import { createItem } from "../../src/server/services/item.service.ts";
import { createTestDb } from "../helpers/db.ts";

describe("CSV Service", () => {
	let db: any;
	let userId: number;

	beforeEach(async () => {
		({ db, userId } = await createTestDb());
	});

	// ── Export ────────────────────────────────────────────────────────────────

	describe("exportItemsCsv", () => {
		it("returns correct headers on empty collection", async () => {
			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			expect(lines[0]).toBe(
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
			);
			expect(lines).toHaveLength(1);
		});

		it("exports items with correct values", async () => {
			await createItem(db, userId, {
				name: "Tent",
				weightGrams: 1200,
				priceCents: 35000,
				categoryId: 1,
				notes: "Ultralight",
				productUrl: "https://example.com/tent",
			});

			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			expect(lines).toHaveLength(2);
			expect(lines[1]).toContain("Tent");
			expect(lines[1]).toContain("1200");
			expect(lines[1]).toContain("35000");
			expect(lines[1]).toContain("Uncategorized");
			expect(lines[1]).toContain("Ultralight");
			expect(lines[1]).toContain("https://example.com/tent");
		});

		it("properly escapes fields with commas", async () => {
			await createItem(db, userId, {
				name: "Tent, Ultralight",
				categoryId: 1,
			});

			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			expect(lines[1]).toContain('"Tent, Ultralight"');
		});

		it("properly escapes fields with double quotes", async () => {
			await createItem(db, userId, {
				name: 'He said "great tent"',
				categoryId: 1,
			});

			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			expect(lines[1]).toContain('"He said ""great tent"""');
		});

		it("exports multiple items", async () => {
			await createItem(db, userId, { name: "Tent", categoryId: 1 });
			await createItem(db, userId, {
				name: "Sleeping Bag",
				categoryId: 1,
			});

			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			expect(lines).toHaveLength(3); // header + 2 items
		});

		it("exports quantity correctly", async () => {
			// Insert directly to set quantity > 1 (createItem service defaults to 1)
			await db
				.insert(items)
				.values({ name: "Bolt", categoryId: 1, quantity: 4, userId });

			const csv = await exportItemsCsv(db, userId);
			const lines = csv.split("\n");
			const fields = lines[1].split(",");
			// quantity is second field
			expect(fields[1]).toBe("4");
		});
	});

	// ── Import ────────────────────────────────────────────────────────────────

	describe("importItemsCsv", () => {
		it("parses a valid CSV and creates items", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				"Tent,1,1200,35000,Camping,Ultralight,https://example.com/tent",
				"Sleeping Bag,1,800,25000,Camping,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(2);
			expect(result.errors).toHaveLength(0);
		});

		it("creates missing category and reports it", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				"Helmet,1,350,12000,Cycling,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.createdCategories).toContain("Cycling");
			expect(result.errors).toHaveLength(0);
		});

		it("uses existing category (case-insensitive) without creating a duplicate", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				// "uncategorized" should match the seeded "Uncategorized"
				"Spork,1,,,uncategorized,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.createdCategories).toHaveLength(0);
		});

		it("skips rows with no name and records an error", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				",1,200,,,",
				"Tent,1,1200,,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.errors).toHaveLength(1);
			expect(result.errors[0]).toMatch(/missing required field "name"/);
		});

		it("defaults quantity to 1 when not provided", async () => {
			const csv = [
				"name,weightGrams,priceCents,category,notes,productUrl",
				"Tent,1200,35000,Camping,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.errors).toHaveLength(0);
		});

		it("handles optional fields being empty", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				"Tent,,,,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.errors).toHaveLength(0);
		});

		it("handles quoted fields containing commas", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				'"Tent, Ultralight",1,1200,,,',
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.errors).toHaveLength(0);
		});

		it("returns zero imported on empty CSV", async () => {
			const result = await importItemsCsv(db, userId, "");
			expect(result.imported).toBe(0);
			expect(result.errors).toHaveLength(0);
		});

		it("uses Uncategorized when category column is empty", async () => {
			const csv = [
				"name,quantity,weightGrams,priceCents,category,notes,productUrl",
				"Tent,1,,,,",
			].join("\n");

			const result = await importItemsCsv(db, userId, csv);
			expect(result.imported).toBe(1);
			expect(result.createdCategories).toHaveLength(0);
		});
	});
});
