/**
 * Maps module — real-time collaborative location sharing.
 *
 * Port of rmaps-online. Rooms are ephemeral (no DB). Proxies to
 * the sync server for WebSocket-based location sharing. Embeds
 * MapLibre GL for outdoor maps and c3nav for indoor maps.
 */

import { Hono } from "hono";
import { renderShell } from "../../server/shell";
import { getModuleInfoList } from "../../shared/module";
import type { RSpaceModule } from "../../shared/module";
import { renderLanding } from "./landing";

const routes = new Hono();

const SYNC_SERVER = process.env.MAPS_SYNC_URL || "http://localhost:3001";

// ── Sync URL for client-side WebSocket connection ──
routes.get("/api/sync-url", (c) => {
	const wsUrl = process.env.MAPS_SYNC_URL || "wss://sync.rmaps.online";
	return c.json({ syncUrl: wsUrl });
});

// ── Proxy: sync server health ──
routes.get("/api/health", async (c) => {
	try {
		const res = await fetch(`${SYNC_SERVER}/health`, { signal: AbortSignal.timeout(3000) });
		if (res.ok) return c.json(await res.json());
		return c.json({ status: "degraded", sync: false });
	} catch {
		return c.json({ status: "degraded", sync: false });
	}
});

// ── Proxy: room stats ──
routes.get("/api/stats", async (c) => {
	try {
		const res = await fetch(`${SYNC_SERVER}/stats`, { signal: AbortSignal.timeout(3000) });
		if (res.ok) return c.json(await res.json());
	} catch {}
	return c.json({ rooms: {} });
});

// ── Proxy: push notification VAPID key ──
routes.get("/api/push/vapid-public-key", async (c) => {
	try {
		const res = await fetch(`${SYNC_SERVER}/push/vapid-public-key`, { signal: AbortSignal.timeout(3000) });
		if (res.ok) return c.json(await res.json());
	} catch {}
	return c.json({ error: "Push not available" }, 503);
});

// ── Proxy: push subscribe/unsubscribe ──
routes.post("/api/push/subscribe", async (c) => {
	const body = await c.req.json();
	const res = await fetch(`${SYNC_SERVER}/push/subscribe`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify(body),
	});
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/push/unsubscribe", async (c) => {
	const body = await c.req.json();
	const res = await fetch(`${SYNC_SERVER}/push/unsubscribe`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify(body),
	});
	return c.json(await res.json(), res.status as any);
});

// ── Proxy: request location (ping) ──
routes.post("/api/push/request-location", async (c) => {
	const body = await c.req.json();
	const res = await fetch(`${SYNC_SERVER}/push/request-location`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify(body),
	});
	return c.json(await res.json(), res.status as any);
});

// ── Proxy: routing (OSRM + c3nav) ──
routes.post("/api/routing", async (c) => {
	const body = await c.req.json();
	const { from, to, mode = "walking" } = body;

	if (!from?.lat || !from?.lng || !to?.lat || !to?.lng) {
		return c.json({ error: "from and to with lat/lng required" }, 400);
	}

	// Use OSRM for outdoor routing
	const profile = mode === "driving" ? "car" : "foot";
	try {
		const res = await fetch(
			`https://router.project-osrm.org/route/v1/${profile}/${from.lng},${from.lat};${to.lng},${to.lat}?overview=full&geometries=geojson&steps=true`,
			{ signal: AbortSignal.timeout(10000) }
		);
		if (res.ok) {
			const data = await res.json();
			return c.json(data);
		}
	} catch {}
	return c.json({ error: "Routing failed" }, 502);
});

// ── Proxy: c3nav API ──
const VALID_C3NAV_EVENTS = ["39c3", "38c3", "37c3", "eh22", "eh2025", "camp2023"];
const ALLOWED_C3NAV_ENDPOINTS = ["map/settings", "map/bounds", "map/locations", "map/locations/full", "map/projection"];

routes.get("/api/c3nav/:event", async (c) => {
	const event = c.req.param("event");
	const endpoint = c.req.query("endpoint") || "map/bounds";

	if (!VALID_C3NAV_EVENTS.includes(event)) return c.json({ error: "Invalid event" }, 400);
	const isAllowed = ALLOWED_C3NAV_ENDPOINTS.some((a) => endpoint === a || endpoint.startsWith(a + "/"));
	if (!isAllowed && !endpoint.startsWith("map/locations/")) return c.json({ error: "Endpoint not allowed" }, 403);

	try {
		const res = await fetch(`https://${event}.c3nav.de/api/v2/${endpoint}/`, {
			headers: { "X-API-Key": "anonymous", Accept: "application/json", "User-Agent": "rMaps/1.0" },
			signal: AbortSignal.timeout(5000),
		});
		if (res.ok) return c.json(await res.json());
		return c.json({ error: "c3nav API error" }, res.status as any);
	} catch {
		return c.json({ error: "c3nav unreachable" }, 502);
	}
});

// ── Page route ──
routes.get("/", (c) => {
	const space = c.req.param("space") || "demo";
	const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
	return c.html(renderShell({
		title: `${space} — Maps | rSpace`,
		moduleId: "rmaps",
		spaceSlug: space,
		modules: getModuleInfoList(),
		body: `<folk-map-viewer space="${space}"></folk-map-viewer>`,
		scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
		styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
	}));
});

// Room-specific page
routes.get("/:room", (c) => {
	const space = c.req.param("space") || "demo";
	const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
	const room = c.req.param("room");
	return c.html(renderShell({
		title: `${room} — Maps | rSpace`,
		moduleId: "rmaps",
		spaceSlug: space,
		modules: getModuleInfoList(),
		styles: `<link rel="stylesheet" href="/modules/rmaps/maps.css">`,
		body: `<folk-map-viewer space="${space}" room="${room}"></folk-map-viewer>`,
		scripts: `<script type="module" src="/modules/rmaps/folk-map-viewer.js"></script>`,
	}));
});

export const mapsModule: RSpaceModule = {
	id: "rmaps",
	name: "rMaps",
	icon: "🗺",
	description: "Real-time collaborative location sharing and indoor/outdoor maps",
	scoping: { defaultScope: 'global', userConfigurable: false },
	routes,
	landingPage: renderLanding,
	standaloneDomain: "rmaps.online",
	feeds: [
		{
			id: "locations",
			name: "Locations",
			kind: "data",
			description: "Shared location pins, points of interest, and room positions",
			filterable: true,
		},
		{
			id: "routes",
			name: "Routes",
			kind: "data",
			description: "Calculated routes and navigation paths between locations",
		},
	],
	acceptsFeeds: ["data"],
	outputPaths: [
		{ path: "routes", name: "Routes", icon: "🛤️", description: "Saved navigation routes" },
		{ path: "places", name: "Places", icon: "📍", description: "Saved locations and points of interest" },
	],
};
