import type { LatestPost, Publication } from '$lib/types';
import { cache, HOUR_MS } from '$lib/server/cache';
import { getRepoInfo } from './client';
import { fetchPublications } from './publications';
import { countDistinctDids } from './interactions';

/** Minimal view of a `site.standard.document` record — metadata only, no content. */
type DocumentRecord = {
	uri: string;
	value: {
		site: string;
		title: string;
		path: string;
		description?: string;
		publishedAt?: string;
		coverImage?: { ref: { $link: string }; mimeType?: string };
	};
};

/** Joins a publication base url with a document path, tolerating leading/trailing slashes. */
function buildPostUrl(publicationUrl: string, path: string): string {
	return `${publicationUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
}

/**
 * Walks the `site.standard.document` records on the PDS and returns the most recent
 * post for each publication. A document maps to its publication via its `site` field
 * (the publication record's at-uri). Cached for 1 hour.
 */
export async function fetchLatestPosts(): Promise<LatestPost[]> {
	const cached = cache.get('latestPosts');
	if (cached) {
		return cached as LatestPost[];
	}

	const repo = getRepoInfo('Latest posts');
	if (!repo) return [];

	try {
		// Reuse the cached, blento-filtered publications and key them by their at-uri.
		const publications = await fetchPublications();
		const byUri = new Map<string, Publication>(publications.map((p) => [p.uri, p]));

		const url =
			`${repo.pds}/xrpc/com.atproto.repo.listRecords` +
			`?repo=${encodeURIComponent(repo.did)}` +
			`&collection=site.standard.document&limit=100`;
		const res = await fetch(url);
		if (!res.ok) {
			console.warn(`Failed to list documents: ${res.status} ${res.statusText}`);
			return [];
		}
		const { records } = (await res.json()) as { records: DocumentRecord[] };

		// Most recent document per publication, by publishedAt.
		const newestByPublication = new Map<string, DocumentRecord>();
		for (const doc of records) {
			const pub = byUri.get(doc.value.site);
			if (!pub) continue; // not one of the publications we surface
			const current = newestByPublication.get(doc.value.site);
			if (!current || (doc.value.publishedAt ?? '') > (current.value.publishedAt ?? '')) {
				newestByPublication.set(doc.value.site, doc);
			}
		}

		const posts: LatestPost[] = await Promise.all(
			[...newestByPublication].map(async ([siteUri, doc]) => {
				const pub = byUri.get(siteUri)!;
				const cid = doc.value.coverImage?.ref.$link;
				return {
					uri: doc.uri,
					title: doc.value.title,
					description: doc.value.description ?? '',
					publishedAt: doc.value.publishedAt ?? null,
					postUrl: buildPostUrl(pub.href, doc.value.path),
					coverImage: cid
						? `${repo.pds}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(repo.did)}&cid=${cid}`
						: null,
					publicationName: pub.name,
					recommendCount: await countDistinctDids(
						doc.uri,
						'site.standard.graph.recommend',
						'.document'
					)
				};
			})
		);

		posts.sort((a, b) => (b.publishedAt ?? '').localeCompare(a.publishedAt ?? ''));

		cache.setWithExpiry('latestPosts', posts, HOUR_MS);
		return posts;
	} catch (error) {
		console.warn('Failed to fetch latest posts:', error);
		return [];
	}
}
