/**
 * Pubs module — markdown → print-ready pocket books via Typst.
 *
 * Ported from pocket-press (Next.js) to Hono routes.
 * Stateless — no database needed. Artifacts stored in /tmp.
 */

import { Hono } from "hono";
import { resolve, join } from "node:path";
import { mkdir, writeFile, readFile, readdir, stat } from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { parseMarkdown } from "./parse-document";
import { compileDocument } from "./typst-compile";
import { getFormat, FORMATS, listFormats } from "./formats";
import type { BookFormat } from "./formats";
import { renderShell } from "../../server/shell";
import { getModuleInfoList } from "../../shared/module";
import type { RSpaceModule } from "../../shared/module";
import { renderLanding } from "./landing";

const ARTIFACTS_DIR = process.env.ARTIFACTS_DIR || "/tmp/rpubs-artifacts";

// ── Types ──

interface ArtifactRequest {
	content: string;
	title?: string;
	author?: string;
	format: string;
	creator_id?: string;
	creator_name?: string;
	creator_wallet?: string;
	source_space?: string;
	license?: string;
	tags?: string[];
	description?: string;
	pricing?: {
		creator_share_pct?: number;
		community_share_pct?: number;
		creator_min?: { amount: number; currency?: string };
		suggested_retail?: { amount: number; currency?: string };
	};
}

// ── Helpers ──

function buildArtifactEnvelope(opts: {
	id: string;
	format: BookFormat;
	pageCount: number;
	pdfSizeBytes: number;
	parsedTitle: string;
	req: ArtifactRequest;
	baseUrl: string;
}) {
	const { id, format, pageCount, pdfSizeBytes, parsedTitle, req, baseUrl } = opts;

	const isBook = pageCount > 48;
	const productType = isBook ? "book" : "zine";
	const binding = isBook ? "perfect-bind" : "saddle-stitch";

	const substrates = isBook
		? ["paper-100gsm", "paper-100gsm-recycled", "cover-250gsm"]
		: ["paper-100gsm-recycled", "paper-100gsm", "paper-80gsm"];

	const requiredCapabilities = isBook
		? ["laser-print", "perfect-bind"]
		: ["laser-print", "saddle-stitch"];

	const pdfUrl = `${baseUrl}/api/artifact/${id}/pdf`;

	return {
		id,
		schema_version: "1.0" as const,
		type: "print-ready" as const,
		origin: "rpubs.online",
		source_space: req.source_space || null,
		creator: {
			id: req.creator_id || "anonymous",
			...(req.creator_name ? { name: req.creator_name } : {}),
			...(req.creator_wallet ? { wallet: req.creator_wallet } : {}),
		},
		created_at: new Date().toISOString(),
		...(req.license ? { license: req.license } : {}),
		payload: {
			title: parsedTitle,
			...(req.description
				? { description: req.description }
				: { description: `A ${pageCount}-page ${productType} in ${format.name} format, generated by rPubs.` }),
			...(req.tags && req.tags.length > 0 ? { tags: req.tags } : {}),
		},
		spec: {
			product_type: productType,
			dimensions: {
				width_mm: format.widthMm,
				height_mm: format.heightMm,
				bleed_mm: 3,
			},
			pages: pageCount,
			color_space: "grayscale",
			dpi: 300,
			binding,
			finish: "uncoated",
			substrates,
			required_capabilities: requiredCapabilities,
		},
		render_targets: {
			[format.id]: {
				url: pdfUrl,
				format: "pdf",
				dpi: 300,
				file_size_bytes: pdfSizeBytes,
				notes: `${format.name} format, grayscale, ${binding}. ${pageCount} pages.`,
			},
		},
		pricing: {
			creator_share_pct: req.pricing?.creator_share_pct ?? 30,
			community_share_pct: req.pricing?.community_share_pct ?? 10,
			...(req.pricing?.creator_min
				? { creator_min: { amount: req.pricing.creator_min.amount, currency: req.pricing.creator_min.currency || "USD" } }
				: {}),
			...(req.pricing?.suggested_retail
				? { suggested_retail: { amount: req.pricing.suggested_retail.amount, currency: req.pricing.suggested_retail.currency || "USD" } }
				: {}),
		},
		next_actions: [
			{ tool: "rcart.online", action: "list-for-sale", label: "Sell in community shop", endpoint: "/api/catalog/ingest", method: "POST" },
			{ tool: "rpubs.online", action: "reformat", label: "Generate another format", endpoint: "/api/reformat", method: "POST" },
			{ tool: "rfiles.online", action: "archive", label: "Save to files", endpoint: "/api/v1/files/import", method: "POST" },
		],
	};
}

function escapeAttr(s: string): string {
	return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

// ── Routes ──

const routes = new Hono();

// ── API: List available formats ──
routes.get("/api/formats", (c) => {
	return c.json({ formats: listFormats() });
});

// ── API: Generate PDF (direct download) ──
routes.post("/api/generate", async (c) => {
	try {
		const body = await c.req.json();
		const { content, title, author, format: formatId } = body;

		if (!content || typeof content !== "string" || content.trim().length === 0) {
			return c.json({ error: "Content is required" }, 400);
		}

		if (!formatId || !getFormat(formatId)) {
			return c.json({ error: `Invalid format. Available: ${Object.keys(FORMATS).join(", ")}` }, 400);
		}

		const document = parseMarkdown(content, title, author);
		const result = await compileDocument({ document, formatId });

		const filename = `${document.title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${formatId}.pdf`;

		return new Response(new Uint8Array(result.pdf), {
			status: 200,
			headers: {
				"Content-Type": "application/pdf",
				"Content-Disposition": `attachment; filename="${filename}"`,
				"X-Page-Count": String(result.pageCount),
			},
		});
	} catch (error) {
		console.error("[Pubs] Generation error:", error);
		return c.json({ error: error instanceof Error ? error.message : "Generation failed" }, 500);
	}
});

// ── API: Create persistent artifact ──
routes.post("/api/artifact", async (c) => {
	try {
		const body: ArtifactRequest = await c.req.json();
		const { content, title, author, format: formatId } = body;

		if (!content || typeof content !== "string" || content.trim().length === 0) {
			return c.json({ error: "Content is required" }, 400);
		}

		const format = getFormat(formatId);
		if (!formatId || !format) {
			return c.json({ error: `Invalid format. Available: ${Object.keys(FORMATS).join(", ")}` }, 400);
		}

		if (body.tags && !Array.isArray(body.tags)) {
			return c.json({ error: "tags must be an array of strings" }, 400);
		}

		const document = parseMarkdown(content, title, author);
		const result = await compileDocument({ document, formatId });

		// Store artifact
		const artifactId = randomUUID();
		const artifactDir = join(ARTIFACTS_DIR, artifactId);
		await mkdir(artifactDir, { recursive: true });

		await writeFile(join(artifactDir, `${formatId}.pdf`), result.pdf);
		await writeFile(join(artifactDir, "source.md"), content);

		// Build envelope
		const proto = c.req.header("x-forwarded-proto") || "https";
		const host = c.req.header("host") || "rpubs.online";
		const baseUrl = `${proto}://${host}`;

		const artifact = buildArtifactEnvelope({
			id: artifactId,
			format,
			pageCount: result.pageCount,
			pdfSizeBytes: result.pdf.length,
			parsedTitle: document.title,
			req: body,
			baseUrl,
		});

		await writeFile(join(artifactDir, "artifact.json"), JSON.stringify(artifact, null, 2));

		return c.json(artifact, 201);
	} catch (error) {
		console.error("[Pubs] Artifact error:", error);
		return c.json({ error: error instanceof Error ? error.message : "Artifact generation failed" }, 500);
	}
});

// ── API: Get artifact (metadata or files) ──
routes.get("/api/artifact/:id", async (c) => {
	const id = c.req.param("id");

	if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
		return c.json({ error: "Invalid artifact ID" }, 400);
	}

	const artifactDir = join(ARTIFACTS_DIR, id);

	try {
		await stat(artifactDir);
	} catch {
		return c.json({ error: "Artifact not found" }, 404);
	}

	const fileType = c.req.query("file");

	if (fileType === "source") {
		try {
			const source = await readFile(join(artifactDir, "source.md"));
			return new Response(source, {
				headers: {
					"Content-Type": "text/markdown; charset=utf-8",
					"Content-Disposition": `inline; filename="source.md"`,
				},
			});
		} catch {
			return c.json({ error: "Source file not found" }, 404);
		}
	}

	if (fileType === "pdf") {
		const files = await readdir(artifactDir);
		const pdfFile = files.find((f) => f.endsWith(".pdf"));
		if (!pdfFile) return c.json({ error: "PDF not found" }, 404);

		const pdf = await readFile(join(artifactDir, pdfFile));
		return new Response(new Uint8Array(pdf), {
			headers: {
				"Content-Type": "application/pdf",
				"Content-Disposition": `inline; filename="${pdfFile}"`,
				"Content-Length": String(pdf.length),
			},
		});
	}

	// Default: return artifact JSON
	try {
		const artifactJson = await readFile(join(artifactDir, "artifact.json"), "utf-8");
		return new Response(artifactJson, {
			headers: { "Content-Type": "application/json" },
		});
	} catch {
		return c.json({ error: "Artifact metadata not found" }, 404);
	}
});

// ── API: Direct PDF access ──
routes.get("/api/artifact/:id/pdf", async (c) => {
	const id = c.req.param("id");

	if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
		return c.json({ error: "Invalid artifact ID" }, 400);
	}

	const artifactDir = join(ARTIFACTS_DIR, id);

	try {
		await stat(artifactDir);
	} catch {
		return c.json({ error: "Artifact not found" }, 404);
	}

	const files = await readdir(artifactDir);
	const pdfFile = files.find((f) => f.endsWith(".pdf"));
	if (!pdfFile) return c.json({ error: "PDF not found" }, 404);

	const pdf = await readFile(join(artifactDir, pdfFile));
	return new Response(new Uint8Array(pdf), {
		headers: {
			"Content-Type": "application/pdf",
			"Content-Disposition": `inline; filename="${pdfFile}"`,
			"Content-Length": String(pdf.length),
		},
	});
});

// ── Page: Editor ──
routes.get("/", (c) => {
	const spaceSlug = c.req.param("space") || "personal";
	return c.html(renderShell({
		title: `${spaceSlug} — rPubs Editor | rSpace`,
		moduleId: "rpubs",
		spaceSlug,
		modules: getModuleInfoList(),
		theme: "dark",
		body: `<folk-pubs-editor space="${spaceSlug}"></folk-pubs-editor>`,
		scripts: `<script type="module" src="/modules/rpubs/folk-pubs-editor.js"></script>`,
		styles: `<link rel="stylesheet" href="/modules/rpubs/pubs.css">`,
	}));
});

// ── Module export ──

export const pubsModule: RSpaceModule = {
	id: "rpubs",
	name: "rPubs",
	icon: "📖",
	description: "Drop in a document, get a pocket book",
	scoping: { defaultScope: 'global', userConfigurable: true },
	routes,
	standaloneDomain: "rpubs.online",
	landingPage: renderLanding,
	feeds: [
		{
			id: "publications",
			name: "Publications",
			kind: "data",
			description: "Print-ready artifacts generated from markdown documents",
			filterable: true,
		},
		{
			id: "citations",
			name: "Citations",
			kind: "data",
			description: "Citation and reference data extracted from documents",
		},
	],
	acceptsFeeds: ["data"],
	outputPaths: [
		{ path: "publications", name: "Publications", icon: "📖", description: "Published pocket books and zines" },
		{ path: "drafts", name: "Drafts", icon: "📝", description: "Work-in-progress documents" },
	],
};
