import { describe, test, expect } from "bun:test";
import {
	getBookById,
	getBookByNumber,
	getBooksByTestament,
	getBooksBySection,
	isAdjacent,
	bookNumberToId,
	bookIdToNumber,
	bibleBooks,
} from "$lib/server/bible";

describe("bibleBooks data integrity", () => {
	test("contains exactly 66 books", () => {
		expect(bibleBooks).toHaveLength(66);
	});

	test("order numbers are 1 through 66 with no gaps or duplicates", () => {
		const orders = bibleBooks.map((b) => b.order).sort((a, b) => a - b);
		for (let i = 0; i < 66; i++) {
			expect(orders[i]).toBe(i + 1);
		}
	});

	test("book IDs are unique", () => {
		const ids = bibleBooks.map((b) => b.id);
		const unique = new Set(ids);
		expect(unique.size).toBe(66);
	});

	test("every book has a non-empty name", () => {
		for (const book of bibleBooks) {
			expect(book.name.length).toBeGreaterThan(0);
		}
	});

	test("Old Testament has 39 books", () => {
		expect(bibleBooks.filter((b) => b.testament === "old")).toHaveLength(39);
	});

	test("New Testament has 27 books", () => {
		expect(bibleBooks.filter((b) => b.testament === "new")).toHaveLength(27);
	});

	test("Genesis is first and Revelation is last", () => {
		const sorted = [...bibleBooks].sort((a, b) => a.order - b.order);
		expect(sorted[0].id).toBe("GEN");
		expect(sorted[65].id).toBe("REV");
	});

	test("Matthew is the first New Testament book", () => {
		const nt = bibleBooks
			.filter((b) => b.testament === "new")
			.sort((a, b) => a.order - b.order);
		expect(nt[0].id).toBe("MAT");
	});
});

describe("section counts", () => {
	test("Law: 5 books", () => {
		expect(getBooksBySection("Law")).toHaveLength(5);
	});

	test("History: 13 books (12 OT + Acts)", () => {
		expect(getBooksBySection("History")).toHaveLength(13);
	});

	test("Wisdom: 5 books", () => {
		expect(getBooksBySection("Wisdom")).toHaveLength(5);
	});

	test("Major Prophets: 5 books", () => {
		expect(getBooksBySection("Major Prophets")).toHaveLength(5);
	});

	test("Minor Prophets: 12 books", () => {
		expect(getBooksBySection("Minor Prophets")).toHaveLength(12);
	});

	test("Gospels: 4 books", () => {
		expect(getBooksBySection("Gospels")).toHaveLength(4);
	});

	test("Pauline Epistles: 13 books", () => {
		expect(getBooksBySection("Pauline Epistles")).toHaveLength(13);
	});

	test("General Epistles: 8 books", () => {
		expect(getBooksBySection("General Epistles")).toHaveLength(8);
	});

	test("Apocalyptic: 1 book (Revelation)", () => {
		const books = getBooksBySection("Apocalyptic");
		expect(books).toHaveLength(1);
		expect(books[0].id).toBe("REV");
	});

	test("all sections sum to 66", () => {
		const total =
			getBooksBySection("Law").length +
			getBooksBySection("History").length +
			getBooksBySection("Wisdom").length +
			getBooksBySection("Major Prophets").length +
			getBooksBySection("Minor Prophets").length +
			getBooksBySection("Gospels").length +
			getBooksBySection("Pauline Epistles").length +
			getBooksBySection("General Epistles").length +
			getBooksBySection("Apocalyptic").length;
		expect(total).toBe(66);
	});
});

describe("getBookById", () => {
	test("returns Genesis for GEN", () => {
		const book = getBookById("GEN");
		expect(book).toBeDefined();
		expect(book!.name).toBe("Genesis");
	});

	test("returns Revelation for REV", () => {
		const book = getBookById("REV");
		expect(book!.name).toBe("Revelation");
	});

	test("returns undefined for unknown ID", () => {
		expect(getBookById("UNKNOWN")).toBeUndefined();
	});
});

describe("getBookByNumber", () => {
	test("1 → Genesis", () => {
		expect(getBookByNumber(1)!.id).toBe("GEN");
	});

	test("66 → Revelation", () => {
		expect(getBookByNumber(66)!.id).toBe("REV");
	});

	test("40 → Matthew (first NT book)", () => {
		expect(getBookByNumber(40)!.id).toBe("MAT");
	});

	test("0 → undefined", () => {
		expect(getBookByNumber(0)).toBeUndefined();
	});

	test("67 → undefined", () => {
		expect(getBookByNumber(67)).toBeUndefined();
	});
});

describe("getBooksByTestament", () => {
	test("old returns 39 books", () => {
		expect(getBooksByTestament("old")).toHaveLength(39);
	});

	test("new returns 27 books", () => {
		expect(getBooksByTestament("new")).toHaveLength(27);
	});

	test("all OT books have testament = old", () => {
		for (const book of getBooksByTestament("old")) {
			expect(book.testament).toBe("old");
		}
	});

	test("all NT books have testament = new", () => {
		for (const book of getBooksByTestament("new")) {
			expect(book.testament).toBe("new");
		}
	});
});

describe("isAdjacent", () => {
	test("Genesis and Exodus are adjacent", () => {
		expect(isAdjacent("GEN", "EXO")).toBe(true);
	});

	test("adjacency is symmetric", () => {
		expect(isAdjacent("EXO", "GEN")).toBe(true);
	});

	test("Malachi and Matthew are adjacent across testament boundary", () => {
		expect(isAdjacent("MAL", "MAT")).toBe(true); // 39, 40
	});

	test("Jude and Revelation are adjacent", () => {
		expect(isAdjacent("JUD", "REV")).toBe(true); // 65, 66
	});

	test("same book is not adjacent to itself", () => {
		expect(isAdjacent("GEN", "GEN")).toBe(false);
	});

	test("books two apart are not adjacent", () => {
		expect(isAdjacent("GEN", "LEV")).toBe(false); // 1, 3
	});

	test("returns false for invalid IDs", () => {
		expect(isAdjacent("FAKE", "GEN")).toBe(false);
	});
});

describe("bookNumberToId / bookIdToNumber lookup tables", () => {
	test("bookNumberToId[1] is GEN", () => {
		expect(bookNumberToId[1]).toBe("GEN");
	});

	test("bookNumberToId[66] is REV", () => {
		expect(bookNumberToId[66]).toBe("REV");
	});

	test("bookIdToNumber['GEN'] is 1", () => {
		expect(bookIdToNumber["GEN"]).toBe(1);
	});

	test("bookIdToNumber['REV'] is 66", () => {
		expect(bookIdToNumber["REV"]).toBe(66);
	});

	test("round-trip: number → ID → number", () => {
		for (let i = 1; i <= 66; i++) {
			const id = bookNumberToId[i];
			expect(bookIdToNumber[id]).toBe(i);
		}
	});

	test("round-trip: ID → number → ID", () => {
		for (const book of bibleBooks) {
			const num = bookIdToNumber[book.id];
			expect(bookNumberToId[num]).toBe(book.id);
		}
	});
});
