import type { RequestHandler } from './$types';
import { db } from '$lib/server/db';
import { dailyVerses, verseSubmissions, user } from '$lib/server/db/schema';
import { sql } from 'drizzle-orm';
import { json } from '@sveltejs/kit';
import { bibleBooks } from '$lib/types/bible';
import {
	getChapterCount,
	getVerseCount,
	composeVerseWindow,
	formatWindowReference
} from '$lib/server/xml-bible';

const DEV_HOSTS = ['localhost:5173', 'test.bibdle.com'];
const MAX_SCAN_RETRIES = 5;

// Dev-only: insert a verse_submissions row (from a random existing account) +
// its corresponding daily_verses row, scheduled on the earliest valid future
// date per the spec's scheduling rules. Lets you populate /scheduled-verses to
// verify the admin view without going through the (Step 5+) submit API.
export const POST: RequestHandler = async ({ request }) => {
	const host = request.headers.get('host') ?? '';
	if (!DEV_HOSTS.includes(host)) {
		return json({ error: 'Not allowed in production' }, { status: 403 });
	}

	// Pick a random existing user.
	const users = await db.select().from(user);
	if (users.length === 0) {
		return json(
			{ error: 'No users exist yet. Sign up at least one account first.' },
			{ status: 400 }
		);
	}
	const submitter = users[Math.floor(Math.random() * users.length)];

	// Pick a random valid (book, chapter, verse).
	const book = bibleBooks[Math.floor(Math.random() * bibleBooks.length)];
	const chapterCount = getChapterCount(book.order);
	const chapter = Math.floor(Math.random() * chapterCount) + 1;
	const verseCount = getVerseCount(book.order, chapter);
	const verse = Math.floor(Math.random() * verseCount) + 1;

	const window = composeVerseWindow(book.order, chapter, verse);
	if (!window) {
		return json({ error: 'Failed to compose verse window' }, { status: 500 });
	}
	const reference = formatWindowReference(
		window.bookName,
		window.startChapter,
		window.startVerse,
		window.endChapter,
		window.endVerse
	);
	const verseText = window.verses.join(' ');

	const todayUtc = new Date().toISOString().slice(0, 10);
	const tomorrowMs = new Date(todayUtc + 'T00:00:00Z').getTime() + 86400000;

	function addDays(ms: number, n: number): string {
		return new Date(ms + n * 86400000).toISOString().slice(0, 10);
	}

	// Fetch all future daily_verses into memory (date → bookId, date → reference).
	const future = await db.select().from(dailyVerses).where(sql`date > ${todayUtc}`);
	const byDate = new Map<string, { bookId: string; reference: string }>();
	for (const r of future) byDate.set(r.date, { bookId: r.bookId, reference: r.reference });

	// For the 60-day repeat rule, also need a bounded historical lookup per
	// candidate date. Build a set of all existing references globally to do a
	// cheap pre-check, then a precise range query when we settle on a date.
	const allRows = await db.select().from(dailyVerses);
	const refDates = new Map<string, string[]>(); // reference → list of dates
	for (const r of allRows) {
		const arr = refDates.get(r.reference) ?? [];
		arr.push(r.date);
		refDates.set(r.reference, arr);
	}

	let scheduledDate: string | null = null;

	for (let attempt = 0; attempt < MAX_SCAN_RETRIES && !scheduledDate; attempt++) {
		// Walk day-by-day from tomorrow.
		for (let offset = 0; offset < 10000; offset++) {
			const D = addDays(tomorrowMs, offset);

			// Rule 1: empty (no committed daily_verses row).
			if (byDate.has(D)) continue;

			// Rule 2: no back-to-back with prior neighbor.
			const prev = byDate.get(addDays(tomorrowMs, offset - 1));
			if (prev && prev.bookId === book.id) continue;

			// Rule 3: no back-to-back with next committed neighbor.
			const next = byDate.get(addDays(tomorrowMs, offset + 1));
			if (next && next.bookId === book.id) continue;

			// Rule 4: 60-day repeat distance — no same reference within ±60 days.
			const sameRefDates = refDates.get(reference) ?? [];
			const dMs = new Date(D + 'T00:00:00Z').getTime();
			const tooClose = sameRefDates.some((d) => {
				const diff = Math.abs(new Date(d + 'T00:00:00Z').getTime() - dMs);
				return diff <= 60 * 86400000;
			});
			if (tooClose) continue;

			scheduledDate = D;
			break;
		}
		if (!scheduledDate) break; // exhausted retries
	}

	if (!scheduledDate) {
		return json({ error: 'Could not find a valid future date' }, { status: 500 });
	}

	// Insert both rows in a transaction. On a unique conflict (concurrent
	// submission grabbed the same date), bubble up so the caller can retry.
	const now = Date.now();
	const verseId = Bun.randomUUIDv7();
	const submissionId = Bun.randomUUIDv7();

	try {
		db.transaction((tx) => {
			tx.insert(dailyVerses)
				.values({
					id: verseId,
					date: scheduledDate!,
					bookId: book.id,
					verseText,
					reference,
					createdAt: sql`${Math.floor(now / 1000)}`
				})
				.run();
			tx.insert(verseSubmissions)
				.values({
					id: submissionId,
					userId: submitter.id,
					scheduledDate: scheduledDate!,
					selectedBookId: book.id,
					selectedChapter: chapter,
					selectedVerse: verse,
					submittedAt: now
				})
				.run();
		});
	} catch (err: any) {
		if (err?.code === 'SQLITE_CONSTRAINT_UNIQUE' || err?.message?.includes('UNIQUE')) {
			return json(
				{ error: 'Date collision during seed; retry the request', detail: String(err?.message ?? err) },
				{ status: 409 }
			);
		}
		throw err;
	}

	return json({
		ok: true,
		submitter: { id: submitter.id, email: submitter.email },
		selected: {
			bookId: book.id,
			bookName: book.name,
			chapter,
			verse
		},
		scheduledDate,
		reference,
		verseText
	});
};

// Convenience: GET returns the current count of submissions + future scheduled
// verses, useful as a quick dev sanity check without mutating state.
export const GET: RequestHandler = async ({ request }) => {
	const host = request.headers.get('host') ?? '';
	if (!DEV_HOSTS.includes(host)) {
		return json({ error: 'Not allowed in production' }, { status: 403 });
	}
	const subs = await db.select().from(verseSubmissions);
	const future = await db
		.select()
		.from(dailyVerses)
		.where(sql`date > ${new Date().toISOString().slice(0, 10)}`);
	return json({
		totalSubmissions: subs.length,
		futureScheduledVerses: future.length
	});
};

