import { describe, test, expect } from "bun:test";
import {
	composeVerseWindow,
	formatWindowReference,
	getChapterCount,
	getVerseCount,
	extractVerses,
} from "$lib/server/xml-bible";
import { bookIdToNumber, getBookById } from "$lib/server/bible";

describe("getChapterCount / getVerseCount", () => {
	test("Genesis has 50 chapters", () => {
		expect(getChapterCount(bookIdToNumber["GEN"])).toBe(50);
	});

	test("Jude has 1 chapter", () => {
		expect(getChapterCount(bookIdToNumber["JUD"])).toBe(1);
	});

	test("Genesis 1 has 31 verses", () => {
		expect(getVerseCount(bookIdToNumber["GEN"], 1)).toBe(31);
	});
});

describe("composeVerseWindow — default [v-2, v-1, v]", () => {
	test("Genesis 1:3 → window is 1:1-3 (same chapter, hyphen)", () => {
		const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 3);
		expect(w).not.toBeNull();
		expect(w!.startChapter).toBe(1);
		expect(w!.startVerse).toBe(1);
		expect(w!.endChapter).toBe(1);
		expect(w!.endVerse).toBe(3);
		expect(w!.verses).toHaveLength(3);
		expect(formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse))
			.toBe("Genesis 1:1-3");
	});

	test("selected verse is the last in the default window", () => {
		const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 10);
		expect(w!.selectedVerse).toBe(10);
		// verses[2] should equal Genesis 1:10's text
		const direct = extractVerses(bookIdToNumber["GEN"], 1, 10, 1);
		expect(w!.verses[2]).toBe(direct[0]);
	});
});

describe("composeVerseWindow — fall-forward at book start", () => {
	test("Genesis 1:1 → fall forward to [1,2,3]", () => {
		const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 1);
		expect(w!.startChapter).toBe(1);
		expect(w!.startVerse).toBe(1);
		expect(w!.endChapter).toBe(1);
		expect(w!.endVerse).toBe(3);
		expect(w!.verses).toHaveLength(3);
		expect(formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse))
			.toBe("Genesis 1:1-3");
	});

	test("Genesis 1:2 → fall forward to [2,3,4] (v-2 < 1)", () => {
		const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 2);
		expect(w!.startVerse).toBe(2);
		expect(w!.endVerse).toBe(4);
		expect(w!.verses).toHaveLength(3);
	});

	test("John 1:1 → fall forward", () => {
		const w = composeVerseWindow(bookIdToNumber["JHN"], 1, 1);
		expect(w!.bookId).toBe("JHN");
		expect(w!.startVerse).toBe(1);
		expect(w!.endVerse).toBe(3);
	});
});

describe("composeVerseWindow — cross-chapter within a book", () => {
	test("Genesis 2:1 → window spans 1:30-2:1 (en-dash, two chapters)", () => {
		const w = composeVerseWindow(bookIdToNumber["GEN"], 2, 1);
		expect(w!.startChapter).toBe(1);
		expect(w!.endChapter).toBe(2);
		// Genesis 1 has 31 verses; selected index for 2:1 = 31 + 1 = 32; window = [30,31,32]
		expect(w!.startVerse).toBe(30);
		expect(w!.startChapter === 1 && w!.endChapter === 2).toBe(true);
		expect(w!.verses).toHaveLength(3);
		const ref = formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse);
		expect(ref).toBe("Genesis 1:30\u20132:1");
	});

	test("window never crosses a book boundary", () => {
		// Every book has >= 3 verses, and windows are constrained in-book by construction.
		// Spot-check the last verse of Malachi 4 (end of OT) still resolves in-book.
		const mal = bookIdToNumber["MAL"];
		const lastChapter = getChapterCount(mal);
		const lastVerse = getVerseCount(mal, lastChapter);
		const w = composeVerseWindow(mal, lastChapter, lastVerse);
		expect(w).not.toBeNull();
		expect(w!.bookId).toBe("MAL");
		expect(w!.endChapter).toBe(lastChapter);
	});
});

describe("composeVerseWindow — invalid input", () => {
	test("unknown book number returns null", () => {
		expect(composeVerseWindow(999, 1, 1)).toBeNull();
	});

	test("chapter out of range returns null", () => {
		expect(composeVerseWindow(bookIdToNumber["GEN"], 999, 1)).toBeNull();
	});

	test("verse out of range returns null", () => {
		expect(composeVerseWindow(bookIdToNumber["GEN"], 1, 9999)).toBeNull();
	});
});

describe("formatWindowReference", () => {
	test("same chapter, range", () => {
		expect(formatWindowReference("Genesis", 1, 1, 1, 3)).toBe("Genesis 1:1-3");
	});

	test("same chapter, single verse", () => {
		expect(formatWindowReference("Jude", 1, 5, 1, 5)).toBe("Jude 1:5");
	});

	test("cross chapter uses en-dash", () => {
		expect(formatWindowReference("Genesis", 1, 31, 2, 1)).toBe("Genesis 1:31\u20132:1");
	});
});

describe("composeVerseWindow — every book's opening verse is safe", () => {
	// The spec guarantees every book has >=3 verses after its opening, so Gen 1:1-style
	// fall-forward must succeed for the first verse of every book.
	test("book 1:1 of all 66 books resolves to a 3-verse in-book window", () => {
		const ids = Object.keys(bookIdToNumber);
		for (const id of ids) {
			const num = bookIdToNumber[id];
			const w = composeVerseWindow(num, 1, 1);
			expect(w, `${getBookById(id)!.name} 1:1 should resolve`).not.toBeNull();
			expect(w!.verses).toHaveLength(3);
			expect(w!.bookId).toBe(id);
		}
	});
});
