/**
 * IPFS API Routes — Hono router mounted at /api/ipfs.
 *
 * GET  /api/ipfs/status     — IPFS node health + stats (authenticated)
 * GET  /api/ipfs/:cid       — Proxy gateway fetch (avoids CORS for clients)
 * POST /api/ipfs/pin        — Pin arbitrary blob (authenticated, 50MB limit)
 * DELETE /api/ipfs/:cid     — Unpin a CID (authenticated)
 */

import { Hono } from "hono";
import type { Context, Next } from "hono";
import { verifyToken, extractToken } from "./auth";
import type { EncryptIDClaims } from "./auth";
import {
	isIPFSEnabled,
	pinToIPFS,
	unpinFromIPFS,
	fetchFromIPFS,
	getIPFSStatus,
	ipfsGatewayUrl,
} from "./ipfs";

const MAX_PIN_SIZE = 50 * 1024 * 1024; // 50 MB

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

export const ipfsRouter = new Hono<IPFSEnv>();

/** Auth middleware for mutating endpoints. */
async function requireAuth(c: Context<IPFSEnv>, next: Next) {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	try {
		const claims = await verifyToken(token);
		c.set("userId", claims.sub);
		c.set("claims", claims);
	} catch {
		return c.json({ error: "Invalid or expired token" }, 401);
	}
	await next();
}

/** GET /status — IPFS node health + stats */
ipfsRouter.get("/status", requireAuth, async (c) => {
	if (!isIPFSEnabled()) {
		return c.json({ enabled: false }, 503);
	}
	try {
		const status = await getIPFSStatus();
		return c.json({ enabled: true, ...status });
	} catch (e: any) {
		return c.json({ enabled: true, error: e.message }, 502);
	}
});

/** POST /pin — Pin a blob to IPFS */
ipfsRouter.post("/pin", requireAuth, async (c) => {
	if (!isIPFSEnabled()) {
		return c.json({ error: "IPFS not enabled" }, 503);
	}

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

	const filename =
		c.req.header("X-Filename") || `upload-${Date.now()}.bin`;

	try {
		const cid = await pinToIPFS(new Uint8Array(blob), filename);
		return c.json({ cid, gateway: ipfsGatewayUrl(cid), size: blob.byteLength });
	} catch (e: any) {
		console.error("[ipfs/pin]", e.message);
		return c.json({ error: "IPFS pin failed" }, 502);
	}
});

/** DELETE /:cid — Unpin a CID */
ipfsRouter.delete("/:cid", requireAuth, async (c) => {
	if (!isIPFSEnabled()) {
		return c.json({ error: "IPFS not enabled" }, 503);
	}

	const cid = c.req.param("cid");
	try {
		await unpinFromIPFS(cid);
		return c.json({ ok: true, cid });
	} catch (e: any) {
		console.error("[ipfs/unpin]", e.message);
		return c.json({ error: "IPFS unpin failed" }, 502);
	}
});

/** GET /:cid — Proxy gateway fetch (public, no auth needed) */
ipfsRouter.get("/:cid", async (c) => {
	const cid = c.req.param("cid");
	try {
		const res = await fetchFromIPFS(cid);
		if (!res.ok) {
			return c.json({ error: "Not found on IPFS" }, 404);
		}
		const body = await res.arrayBuffer();
		return new Response(body, {
			headers: {
				"Content-Type":
					res.headers.get("Content-Type") || "application/octet-stream",
				"Cache-Control": "public, max-age=31536000, immutable",
			},
		});
	} catch (e: any) {
		console.error("[ipfs/proxy]", e.message);
		return c.json({ error: "IPFS fetch failed" }, 502);
	}
});
