import { XMLParser } from 'fast-xml-parser';
import { readFileSync } from 'fs';
import { join } from 'path';
import { bookNumberToId, getBookByNumber } from './bible';

// XML parser configuration
const parser = new XMLParser({
	ignoreAttributes: false,
	attributeNamePrefix: '',
	textNodeName: '_text',
	parseAttributeValue: true,
	trimValues: true,
	isArray: (name) => ['chapter', 'verse'].includes(name)
});

// Cache for parsed Bible data to avoid re-reading the file
let cachedBible: any = null;
let cachedGreekBible: any = null;

interface VerseData {
	number: number;
	_text: string;
}

interface ChapterData {
	number: number;
	verse: VerseData[];
}

interface BookData {
	number: number;
	chapter: ChapterData[];
}

interface TestamentData {
	name: string;
	book: BookData[];
}

interface BibleData {
	bible: {
		testament: TestamentData[];
	};
}

/**
 * Load and parse the Bible XML file
 */
function loadBibleXml(): BibleData {
	if (cachedBible) {
		return cachedBible;
	}

	const xmlPath = join(process.cwd(), 'EnglishNKJBible.xml');
	const xmlContent = readFileSync(xmlPath, 'utf-8');
	cachedBible = parser.parse(xmlContent) as BibleData;
	return cachedBible;
}

/**
	* Load and parse the Greek Bible XML file
	*/
function loadGreekBibleXml(): BibleData {
	if (cachedGreekBible) {
		return cachedGreekBible;
	}

	const xmlPath = join(process.cwd(), 'GreekModern1904Bible.xml');
	const xmlContent = readFileSync(xmlPath, 'utf-8');
	cachedGreekBible = parser.parse(xmlContent) as BibleData;
	return cachedGreekBible;
}
/**
 * Get a specific book from the Bible XML
 */
function getBook(bookNumber: number): BookData | null {
	const bible = loadBibleXml();

	// Old Testament books are 1-39, New Testament are 40-66
	const testamentIndex = bookNumber <= 39 ? 0 : 1;
	const testament = bible.bible.testament[testamentIndex];

	if (!testament) {
		return null;
	}

	// Find the book by number within the testament
	const bookIndex = bookNumber <= 39 ? bookNumber - 1 : bookNumber - 40;
	return testament.book[bookIndex] || null;
}

/**
	* Get a specific book from the Greek Bible XML
	*/
function getGreekBook(bookNumber: number): BookData | null {
	const bible = loadGreekBibleXml();

	// Old Testament books are 1-39, New Testament are 40-66
	const testamentIndex = bookNumber <= 39 ? 0 : 1;
	const testament = bible.bible.testament[testamentIndex];

	if (!testament) {
		return null;
	}

	// Find the book by number within the testament
	const bookIndex = bookNumber <= 39 ? bookNumber - 1 : bookNumber - 40;
	return testament.book[bookIndex] || null;
}

/**
 * Get a specific chapter from a book
 */
function getChapter(bookNumber: number, chapterNumber: number): ChapterData | null {
	const book = getBook(bookNumber);
	if (!book) {
		return null;
	}

	return book.chapter.find((ch) => ch.number === chapterNumber) || null;
}

/**
	* Get a specific chapter from the Greek Bible
	*/
function getGreekChapter(bookNumber: number, chapterNumber: number): ChapterData | null {
	const book = getGreekBook(bookNumber);
	if (!book) {
		return null;
	}

	return book.chapter.find((ch) => ch.number === chapterNumber) || null;
}

/**
 * Get the number of verses in a specific chapter
 */
export function getVerseCount(bookNumber: number, chapterNumber: number): number {
	const chapter = getChapter(bookNumber, chapterNumber);
	return chapter ? chapter.verse.length : 0;
}

/**
	* Get the number of verses in a specific Greek chapter
	*/
function getGreekVerseCount(bookNumber: number, chapterNumber: number): number {
	const chapter = getGreekChapter(bookNumber, chapterNumber);
	return chapter ? chapter.verse.length : 0;
}

/**
 * Get the number of chapters in a specific book
 */
export function getChapterCount(bookNumber: number): number {
	const book = getBook(bookNumber);
	return book ? book.chapter.length : 0;
}

/**
	* Get the number of chapters in a specific Greek book
	*/
function getGreekChapterCount(bookNumber: number): number {
	const book = getGreekBook(bookNumber);
	return book ? book.chapter.length : 0;
}

/**
 * Extract consecutive verses from a specific location
 */
export function extractVerses(
	bookNumber: number,
	chapterNumber: number,
	startVerse: number,
	count: number
): string[] {
	const chapter = getChapter(bookNumber, chapterNumber);
	if (!chapter) {
		return [];
	}

	const verses: string[] = [];
	for (let i = 0; i < count; i++) {
		const verseIndex = startVerse - 1 + i; // Convert to 0-based index
		if (verseIndex >= chapter.verse.length) {
			break;
		}
		verses.push(chapter.verse[verseIndex]._text);
	}

	return verses;
}

/**
	* Extract consecutive verses from a specific Greek location
	*/
function extractGreekVerses(
	bookNumber: number,
	chapterNumber: number,
	startVerse: number,
	count: number
): string[] {
	const chapter = getGreekChapter(bookNumber, chapterNumber);
	if (!chapter) {
		return [];
	}

	const verses: string[] = [];
	for (let i = 0; i < count; i++) {
		const verseIndex = startVerse - 1 + i; // Convert to 0-based index
		if (verseIndex >= chapter.verse.length) {
			break;
		}
		verses.push(chapter.verse[verseIndex]._text);
	}

	return verses;
}

/**
 * Get a random book number (1-66)
 */
function getRandomBookNumber(): number {
	return Math.floor(Math.random() * 66) + 1;
}

/**
 * Get a random chapter number for a specific book
 */
function getRandomChapterNumber(bookNumber: number): number {
	const chapterCount = getChapterCount(bookNumber);
	return Math.floor(Math.random() * chapterCount) + 1;
}

/**
 * Get a random starting verse number for a specific chapter
 * Ensures there are enough verses for the requested count
 */
function getRandomStartVerse(bookNumber: number, chapterNumber: number, verseCount: number): number {
	const totalVerses = getVerseCount(bookNumber, chapterNumber);
	const maxStartVerse = Math.max(1, totalVerses - verseCount + 1);
	return Math.floor(Math.random() * maxStartVerse) + 1;
}

/**
	* Get a random chapter number for a specific Greek book
	*/
function getRandomGreekChapterNumber(bookNumber: number): number {
	const chapterCount = getGreekChapterCount(bookNumber);
	return Math.floor(Math.random() * chapterCount) + 1;
}

/**
	* Get a random starting verse number for a specific Greek chapter
	* Ensures there are enough verses for the requested count
	*/
function getRandomGreekStartVerse(bookNumber: number, chapterNumber: number, verseCount: number): number {
	const totalVerses = getGreekVerseCount(bookNumber, chapterNumber);
	const maxStartVerse = Math.max(1, totalVerses - verseCount + 1);
	return Math.floor(Math.random() * maxStartVerse) + 1;
}

/**
 * Get a random set of verses from the Bible
 * Returns 3 consecutive verses by default
 */
export function getRandomVerses(count: number = 3): {
	bookId: string;
	bookName: string;
	chapter: number;
	startVerse: number;
	endVerse: number;
	verses: string[];
} | null {
	// Try up to 10 times to find a valid passage
	for (let attempt = 0; attempt < 10; attempt++) {
		const bookNumber = getRandomBookNumber();
		const book = getBookByNumber(bookNumber);

		if (!book) {
			continue;
		}

		const chapterNumber = getRandomChapterNumber(bookNumber);
		const verseCount = getVerseCount(bookNumber, chapterNumber);

		// Skip chapters that don't have enough verses
		if (verseCount < count) {
			continue;
		}

		const startVerse = getRandomStartVerse(bookNumber, chapterNumber, count);
		const verses = extractVerses(bookNumber, chapterNumber, startVerse, count);

		if (verses.length === count) {
			return {
				bookId: book.id,
				bookName: book.name,
				chapter: chapterNumber,
				startVerse,
				endVerse: startVerse + count - 1,
				verses
			};
		}
	}

	return null;
}

/**
	* Get a random set of verses from the Greek Bible
	* Returns 3 consecutive verses by default
	*/
export function getRandomGreekVerses(count: number = 3): {
	bookId: string;
	bookName: string;
	chapter: number;
	startVerse: number;
	endVerse: number;
	verses: string[];
} | null {
	// Try up to 10 times to find a valid passage
	for (let attempt = 0; attempt < 10; attempt++) {
		const bookNumber = getRandomBookNumber();
		const book = getBookByNumber(bookNumber);

		if (!book) {
			continue;
		}

		const chapterNumber = getRandomGreekChapterNumber(bookNumber);
		const verseCount = getGreekVerseCount(bookNumber, chapterNumber);

		// Skip chapters that don't have enough verses
		if (verseCount < count) {
			continue;
		}

		const startVerse = getRandomGreekStartVerse(bookNumber, chapterNumber, count);
		const verses = extractGreekVerses(bookNumber, chapterNumber, startVerse, count);

		if (verses.length === count) {
			return {
				bookId: book.id,
				bookName: book.name,
				chapter: chapterNumber,
				startVerse,
				endVerse: startVerse + count - 1,
				verses
			};
		}
	}

	return null;
}

/**
	* Get a random set of verses from a specific book
	* Returns `count` consecutive verses by default
	*/
export function getRandomVersesFromBook(
	bookNumber: number,
	count: number = 1
): {
	bookId: string;
	bookName: string;
	chapter: number;
	startVerse: number;
	endVerse: number;
	verses: string[];
} | null {
	const book = getBookByNumber(bookNumber);
	if (!book) {
		return null;
	}

	// Try up to 10 times to find a valid passage
	for (let attempt = 0; attempt < 10; attempt++) {
		const chapterNumber = getRandomChapterNumber(bookNumber);
		const verseCount = getVerseCount(bookNumber, chapterNumber);

		// Skip chapters that don't have enough verses
		if (verseCount < count) {
			continue;
		}

		const startVerse = getRandomStartVerse(bookNumber, chapterNumber, count);
		const verses = extractVerses(bookNumber, chapterNumber, startVerse, count);

		if (verses.length === count) {
			return {
				bookId: book.id,
				bookName: book.name,
				chapter: chapterNumber,
				startVerse,
				endVerse: startVerse + count - 1,
				verses
			};
		}
	}

	return null;
}

/**
 * Format a reference string from verse data
 */
export function formatReference(bookName: string, chapter: number, startVerse: number, endVerse: number): string {
	if (startVerse === endVerse) {
		return `${bookName} ${chapter}:${startVerse}`;
	}
	return `${bookName} ${chapter}:${startVerse}-${endVerse}`;
}

/**
 * Get all NKJV verses with metadata for embeddings
 */
export function getAllNKJVVerses(): Array<{ text: string; book: string; chapter: number; verse: number }> {
	const bible = loadBibleXml();
	const verses: Array<{ text: string; book: string; chapter: number; verse: number }> = [];

	for (const testament of bible.bible.testament) {
		for (const bookData of testament.book) {
			const bookNumber = +bookData.number;
			const bookInfo = getBookByNumber(bookNumber);
			const bookName = bookInfo ? bookInfo.name : `Book ${bookNumber}`;
			for (const chapterData of bookData.chapter) {
				const chapterNum = +chapterData.number;
				for (const verseData of chapterData.verse) {
					const verseNum = +verseData.number;
					verses.push({
						text: verseData._text || '',
						book: bookName,
						chapter: chapterNum,
						verse: verseNum
					});
				}
			}
		}
	}
	return verses;
}

/**
 * Resolve a 1-based book-wide verse index to { chapter, verse }.
 * The index is contiguous across all chapters of the book.
 */
function resolveBookVerseIndex(
	bookNumber: number,
	index: number
): { chapter: number; verse: number } | null {
	const chapterCount = getChapterCount(bookNumber);
	let cumulative = 0;
	for (let c = 1; c <= chapterCount; c++) {
		const vCount = getVerseCount(bookNumber, c);
		if (index <= cumulative + vCount) {
			return { chapter: c, verse: index - cumulative };
		}
		cumulative += vCount;
	}
	return null;
}

export interface ComposedWindow {
	bookId: string;
	bookName: string;
	verses: string[]; // the 3 verse texts in order
	startChapter: number;
	startVerse: number;
	endChapter: number;
	endVerse: number;
	selectedVerse: number; // the anchor verse number within its chapter
}

/**
 * Compose a 3-verse window anchored on (book, chapter, verse), constrained to
 * a single book. Default window is [v-2, v-1, v] in book-wide verse indices;
 * if that would run before the book's start (v is 1 or 2), fall forward to
 * [v, v+1, v+2]. Cross-chapter windows within the same book are allowed.
 *
 * Mirrors the spec's "Verse Windowing" section.
 */
export function composeVerseWindow(
	bookNumber: number,
	chapter: number,
	verse: number
): ComposedWindow | null {
	const book = getBookByNumber(bookNumber);
	if (!book) return null;

	const chapterCount = getChapterCount(bookNumber);
	if (chapter < 1 || chapter > chapterCount) return null;
	const verseCount = getVerseCount(bookNumber, chapter);
	if (verse < 1 || verse > verseCount) return null;

	// Book-wide 1-based index of the selected verse.
	let selectedIndex = 0;
	for (let c = 1; c < chapter; c++) selectedIndex += getVerseCount(bookNumber, c);
	selectedIndex += verse;

	let startIdx: number;
	let endIdx: number;
	if (selectedIndex - 2 < 1) {
		// Fall forward: [v, v+1, v+2]
		startIdx = selectedIndex;
		endIdx = selectedIndex + 2;
	} else {
		startIdx = selectedIndex - 2;
		endIdx = selectedIndex;
	}

	const start = resolveBookVerseIndex(bookNumber, startIdx);
	const end = resolveBookVerseIndex(bookNumber, endIdx);
	if (!start || !end) return null;

	// Extract the contiguous window, crossing chapter boundaries within the book as needed.
	const verses: string[] = [];
	let curChapter = start.chapter;
	let curVerse = start.verse;
	while (verses.length < 3 && curChapter <= chapterCount) {
		const vCount = getVerseCount(bookNumber, curChapter);
		while (curVerse <= vCount && verses.length < 3) {
			const ex = extractVerses(bookNumber, curChapter, curVerse, 1);
			if (ex.length === 1) {
				verses.push(ex[0]);
			} else {
				break;
			}
			curVerse++;
		}
		curChapter++;
		curVerse = 1;
	}

	if (verses.length < 3) return null;

	return {
		bookId: book.id,
		bookName: book.name,
		verses,
		startChapter: start.chapter,
		startVerse: start.verse,
		endChapter: end.chapter,
		endVerse: end.verse,
		selectedVerse: verse
	};
}

/**
 * Format a reference for a window that may span chapters within a book.
 * Same-chapter:  "Genesis 1:1-3"  (hyphen)
 * Cross-chapter: "Genesis 1:31–2:1" (en-dash)
 */
export function formatWindowReference(
	bookName: string,
	startChapter: number,
	startVerse: number,
	endChapter: number,
	endVerse: number
): string {
	if (startChapter === endChapter) {
		if (startVerse === endVerse) return `${bookName} ${startChapter}:${startVerse}`;
		return `${bookName} ${startChapter}:${startVerse}-${endVerse}`;
	}
	return `${bookName} ${startChapter}:${startVerse}\u2013${endChapter}:${endVerse}`;
}
