/**
 * Backup API Routes — Hono router for encrypted backup operations.
 *
 * All endpoints require EncryptID JWT authentication.
 * The server stores opaque ciphertext blobs it cannot decrypt.
 */

import { Hono } from "hono";
import type { Context, Next } from "hono";
import { verifyToken, extractToken } from "../auth";
import type { EncryptIDClaims } from "../auth";
import {
	putBackup,
	getBackup,
	listBackups,
	deleteBackup,
	deleteAllBackups,
	getUsage,
} from "./backup-store";
import { ipfsGatewayUrl } from "../ipfs";

const MAX_BLOB_SIZE = 10 * 1024 * 1024; // 10 MB per blob

type BackupEnv = {
	Variables: {
		userId: string;
		claims: EncryptIDClaims;
	};
};

const backupRouter = new Hono<BackupEnv>();

/** Auth middleware — extracts and verifies JWT, sets userId. */
backupRouter.use("*", async (c: Context<BackupEnv>, next: Next) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) {
		return c.json({ error: "Authentication required" }, 401);
	}
	let claims: EncryptIDClaims;
	try {
		claims = await verifyToken(token);
	} catch {
		return c.json({ error: "Invalid or expired token" }, 401);
	}
	c.set("userId", claims.sub);
	c.set("claims", claims);
	await next();
});

/** PUT /api/backup/:space/:docId — upload encrypted blob */
backupRouter.put("/:space/:docId", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");
	const docId = decodeURIComponent(c.req.param("docId"));

	const blob = await c.req.arrayBuffer();
	if (blob.byteLength > MAX_BLOB_SIZE) {
		return c.json({ error: `Blob too large (max ${MAX_BLOB_SIZE} bytes)` }, 413);
	}
	if (blob.byteLength === 0) {
		return c.json({ error: "Empty blob" }, 400);
	}

	await putBackup(userId, space, docId, new Uint8Array(blob));
	return c.json({ ok: true, size: blob.byteLength });
});

/** GET /api/backup/:space/:docId — download encrypted blob */
backupRouter.get("/:space/:docId", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");
	const docId = decodeURIComponent(c.req.param("docId"));

	const blob = await getBackup(userId, space, docId);
	if (!blob) {
		return c.json({ error: "Not found" }, 404);
	}

	const body = new Uint8Array(blob).buffer as ArrayBuffer;
	return new Response(body, {
		headers: {
			"Content-Type": "application/octet-stream",
			"Content-Length": blob.byteLength.toString(),
		},
	});
});

/** GET /api/backup/:space — list manifest for a space */
backupRouter.get("/:space", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");

	const manifest = await listBackups(userId, space);
	return c.json(manifest);
});

/** GET /api/backup/:space/:docId/ipfs — IPFS gateway URL for a backup blob */
backupRouter.get("/:space/:docId/ipfs", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");
	const docId = decodeURIComponent(c.req.param("docId"));

	const manifest = await listBackups(userId, space);
	const entry = manifest.entries.find((e) => e.docId === docId);
	if (!entry) return c.json({ error: "Not found" }, 404);
	if (!entry.cid) return c.json({ error: "Not pinned to IPFS" }, 404);

	return c.json({ cid: entry.cid, gateway: ipfsGatewayUrl(entry.cid) });
});

/** DELETE /api/backup/:space/:docId — delete specific backup */
backupRouter.delete("/:space/:docId", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");
	const docId = decodeURIComponent(c.req.param("docId"));

	const ok = await deleteBackup(userId, space, docId);
	if (!ok) {
		return c.json({ error: "Not found or delete failed" }, 404);
	}
	return c.json({ ok: true });
});

/** DELETE /api/backup/:space — delete all backups for a space */
backupRouter.delete("/:space", async (c) => {
	const userId = c.get("userId");
	const space = c.req.param("space");

	await deleteAllBackups(userId, space);
	return c.json({ ok: true });
});

/** GET /api/backup/status — overall backup status for authenticated user */
backupRouter.get("/", async (c) => {
	const userId = c.get("userId");
	const usage = await getUsage(userId);
	return c.json(usage);
});

export { backupRouter };
