/**
 * Publications store — filesystem-backed persistent publications per space.
 *
 * Layout:
 *   {PUBS_DIR}/{space}/{slug}/publication.json
 *   {PUBS_DIR}/{space}/{slug}/source.pdf
 *   {PUBS_DIR}/{space}/{slug}/source.md
 *   {PUBS_DIR}/{space}/{slug}/reflowable.epub
 *   {PUBS_DIR}/{space}/{slug}/fixed.epub
 *
 * The reflowable EPUB is always built. The fixed EPUB is optional (costlier
 * to generate — we only build it if asked).
 */

import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";

export const PUBS_DIR = process.env.PUBS_DIR || "/data/rpubs-publications";

export interface PublicationRecord {
	id: string;
	space: string;
	slug: string;
	title: string;
	author: string;
	description?: string;
	formatId: string;
	formatName: string;
	pageCount: number;
	pdfBytes: number;
	reflowableEpubBytes: number;
	fixedEpubBytes?: number;
	createdAt: number;
	updatedAt: number;
}

export function slugify(text: string): string {
	return text
		.toLowerCase()
		.replace(/[^a-z0-9]+/g, "-")
		.replace(/^-|-$/g, "")
		.slice(0, 80) || "publication";
}

export function safeSpace(space: string): string {
	// Limit to safe chars — we're using this as a directory name.
	return space.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 60) || "personal";
}

function spaceDir(space: string): string {
	return resolve(PUBS_DIR, safeSpace(space));
}

function pubDir(space: string, slug: string): string {
	return resolve(spaceDir(space), slugify(slug));
}

export async function listPublications(space: string): Promise<PublicationRecord[]> {
	const dir = spaceDir(space);
	try {
		await stat(dir);
	} catch {
		return [];
	}

	const slugs = await readdir(dir);
	const records: PublicationRecord[] = [];
	for (const slug of slugs) {
		try {
			const meta = await readFile(join(dir, slug, "publication.json"), "utf-8");
			records.push(JSON.parse(meta));
		} catch {
			// skip malformed entries
		}
	}
	return records.sort((a, b) => b.updatedAt - a.updatedAt);
}

export async function getPublication(
	space: string,
	slug: string,
): Promise<PublicationRecord | null> {
	try {
		const meta = await readFile(
			join(pubDir(space, slug), "publication.json"),
			"utf-8",
		);
		return JSON.parse(meta);
	} catch {
		return null;
	}
}

export async function getPublicationFile(
	space: string,
	slug: string,
	file: "pdf" | "reflowable.epub" | "fixed.epub" | "source.md",
): Promise<Buffer | null> {
	const name =
		file === "pdf" ? "source.pdf"
		: file === "reflowable.epub" ? "reflowable.epub"
		: file === "fixed.epub" ? "fixed.epub"
		: "source.md";

	try {
		return Buffer.from(await readFile(join(pubDir(space, slug), name)));
	} catch {
		return null;
	}
}

/** Ensure the slug is unique within the space — if not, append a short suffix. */
async function uniqueSlug(space: string, desired: string): Promise<string> {
	let slug = slugify(desired);
	const existing = new Set(
		(await listPublications(space)).map((p) => p.slug),
	);
	if (!existing.has(slug)) return slug;

	const suffix = Math.random().toString(36).slice(2, 7);
	return `${slug}-${suffix}`;
}

export interface SavePublicationInput {
	space: string;
	title: string;
	author: string;
	description?: string;
	formatId: string;
	formatName: string;
	pageCount: number;
	sourceMarkdown: string;
	pdf: Buffer;
	reflowableEpub: Buffer;
	fixedEpub?: Buffer;
}

export async function savePublication(
	input: SavePublicationInput,
): Promise<PublicationRecord> {
	const space = safeSpace(input.space);
	const slug = await uniqueSlug(space, input.title);
	const dir = pubDir(space, slug);
	await mkdir(dir, { recursive: true });

	await Promise.all([
		writeFile(join(dir, "source.md"), input.sourceMarkdown, "utf-8"),
		writeFile(join(dir, "source.pdf"), input.pdf),
		writeFile(join(dir, "reflowable.epub"), input.reflowableEpub),
		input.fixedEpub
			? writeFile(join(dir, "fixed.epub"), input.fixedEpub)
			: Promise.resolve(),
	]);

	const now = Date.now();
	const record: PublicationRecord = {
		id: crypto.randomUUID(),
		space,
		slug,
		title: input.title,
		author: input.author,
		description: input.description,
		formatId: input.formatId,
		formatName: input.formatName,
		pageCount: input.pageCount,
		pdfBytes: input.pdf.length,
		reflowableEpubBytes: input.reflowableEpub.length,
		fixedEpubBytes: input.fixedEpub?.length,
		createdAt: now,
		updatedAt: now,
	};

	await writeFile(
		join(dir, "publication.json"),
		JSON.stringify(record, null, 2),
		"utf-8",
	);

	return record;
}
