import { buzz } from '$lib/lexicons';
import type { Main as BookRecord } from '$lib/lexicons/buzz/bookhive/book';
import type { AtUriString } from '@atproto/lex';
import type { CurrentlyReading, FinishedBook } from '$lib/types';
import { cache } from '$lib/server/cache';
import { blobUrl, createClient, getRepoInfo, repoId, type RepoInfo } from './client';

type BookEntry = { value: BookRecord; uri: AtUriString };

type BookHiveBookView = {
	status?: string;
	title: string;
	authors: string;
	hiveId: string;
	cover: string | null;
	startedAt: string | null;
	finishedAt: string | null;
	createdAt: string | null;
	stars: number | null;
};

const READING = 'buzz.bookhive.defs#reading';
const FINISHED = 'buzz.bookhive.defs#finished';

/**
 * The currently-reading and finished views both come from the same bookhive
 * collection, so we list it once and cache the normalized records. Cached
 * under the default ttl 1 day.
 */
async function listBooks(repo: RepoInfo): Promise<BookHiveBookView[]> {
	const cached = cache.get('books') as BookHiveBookView[] | null;
	if (cached) return cached;

	const client = createClient(repo.pds);
	const records: BookEntry[] = [];
	let cursor: string | undefined;
	let pages = 0;
	// Walk the cursor until the collection is exhausted (or we hit the safety
	// cap). The full set is cached, so this only runs on a cold cache.
	do {
		const res = await client.list(buzz.bookhive.book, {
			repo: repoId(repo.did),
			limit: 100,
			cursor
		});
		records.push(...(res.records as ReadonlyArray<BookEntry>));
		cursor = res.cursor;
	} while (cursor && ++pages < 50);
	const books = records.map(({ value: v }) => ({
		status: v.status,
		title: v.title,
		authors: v.authors.split('\t').join(', '),
		hiveId: v.hiveId,
		cover: v.cover ? blobUrl(repo.pds, repo.did, v.cover) : null,
		finishedAt: v.finishedAt ?? null,
		startedAt: v.startedAt ?? null,
		createdAt: v.createdAt ?? null,
		stars: v.stars ?? null
	}));
	cache.set('books', books);
	return books;
}

export async function fetchCurrentlyReading(): Promise<CurrentlyReading | null> {
	const repo = getRepoInfo('Currently reading');
	if (!repo) return null;

	try {
		const reading = (await listBooks(repo)).find((b) => b.status === READING);
		if (!reading) return null;

		return {
			title: reading.title,
			bookUrl: `https://bookhive.buzz/books/${reading.hiveId}`,
			authors: reading.authors,
			cover: reading.cover
		};
	} catch (error) {
		console.warn('Failed to fetch currently reading book:', error);
		return null;
	}
}

export async function fetchFinishedBooks(): Promise<FinishedBook[]> {
	const repo = getRepoInfo('Books history');
	if (!repo) return [];

	try {
		return (await listBooks(repo))
			.filter((b) => b.status === FINISHED)
			.map((b) => ({
				title: b.title,
				authors: b.authors,
				bookUrl: `https://bookhive.buzz/books/${b.hiveId}`,
				cover: b.cover,
				startedAt: b.startedAt ?? null,
				finishedAt: b.finishedAt ?? null,
				stars: b.stars ?? null
			}))
			.sort((a, b) => (b.finishedAt ?? '').localeCompare(a.finishedAt ?? ''));
	} catch (error) {
		console.warn('Failed to fetch finished books:', error);
		return [];
	}
}
