/**
 * Data module — privacy-first analytics dashboard.
 *
 * Lightweight module that shows analytics stats from the
 * self-hosted Umami instance. No database — proxies to Umami API.
 */

import { Hono } from "hono";
import { renderShell } from "../../server/shell";
import { getAllModules, getModuleInfoList } from "../../shared/module";
import type { RSpaceModule } from "../../shared/module";
import { syncServer } from "../../server/sync-instance";
import { renderLanding } from "./landing";

const routes = new Hono();

const UMAMI_URL = process.env.UMAMI_URL || "https://analytics.rspace.online";
const UMAMI_WEBSITE_ID = process.env.UMAMI_WEBSITE_ID || "292f6ac6-79f8-497b-ba6a-7a51e3b87b9f";

const TRACKED_APPS = [
	"rSpace", "rNotes", "rVote", "rFlows", "rCart", "rWallet",
	"rPubs", "rChoices", "rCal", "rForum", "rInbox", "rFiles",
	"rTrips", "rTube", "rTasks", "rNetwork", "rData",
];

// ── API routes ──

// GET /api/info — module info
routes.get("/api/info", (c) => {
	return c.json({
		module: "data",
		name: "rData",
		umamiUrl: UMAMI_URL,
		umamiConfigured: !!UMAMI_URL,
		features: ["privacy-first", "cookieless", "self-hosted"],
		trackedApps: TRACKED_APPS.length,
	});
});

// GET /api/health
routes.get("/api/health", (c) => c.json({ ok: true }));

// GET /api/stats — proxy to Umami stats API
routes.get("/api/stats", async (c) => {
	const startAt = c.req.query("startAt") || String(Date.now() - 24 * 3600_000);
	const endAt = c.req.query("endAt") || String(Date.now());

	try {
		const res = await fetch(
			`${UMAMI_URL}/api/websites/${UMAMI_WEBSITE_ID}/stats?startAt=${startAt}&endAt=${endAt}`,
			{ signal: AbortSignal.timeout(5000) }
		);
		if (res.ok) {
			const data = await res.json();
			return c.json({
				...data as Record<string, unknown>,
				trackedApps: TRACKED_APPS.length,
				apps: TRACKED_APPS,
				selfHosted: true,
				dashboardUrl: UMAMI_URL,
			});
		}
	} catch {}

	// Fallback when Umami unreachable
	return c.json({
		trackedApps: TRACKED_APPS.length,
		cookiesSet: 0,
		scriptSize: "~2KB",
		selfHosted: true,
		dashboardUrl: UMAMI_URL,
		apps: TRACKED_APPS,
	});
});

// GET /api/active — proxy to Umami active visitors
routes.get("/api/active", async (c) => {
	try {
		const res = await fetch(
			`${UMAMI_URL}/api/websites/${UMAMI_WEBSITE_ID}/active`,
			{ signal: AbortSignal.timeout(5000) }
		);
		if (res.ok) return c.json(await res.json());
	} catch {}
	return c.json({ x: 0 });
});

// GET /collect.js — proxy Umami tracker script
routes.get("/collect.js", async (c) => {
	try {
		const res = await fetch(`${UMAMI_URL}/script.js`, { signal: AbortSignal.timeout(5000) });
		if (res.ok) {
			const script = await res.text();
			return new Response(script, {
				headers: {
					"Content-Type": "application/javascript",
					"Cache-Control": "public, max-age=3600",
				},
			});
		}
	} catch {}
	return new Response("/* umami unavailable */", {
		headers: { "Content-Type": "application/javascript" },
	});
});

// POST /api/collect — proxy Umami event collection
routes.post("/api/collect", async (c) => {
	try {
		const body = await c.req.text();
		const res = await fetch(`${UMAMI_URL}/api/send`, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body,
			signal: AbortSignal.timeout(5000),
		});
		if (res.ok) return c.json(await res.json());
	} catch {}
	return c.json({ ok: true });
});

// ── Content Tree API ──

/** Build a module icon/name lookup from registered modules */
function getModuleMeta(): Map<string, { name: string; icon: string }> {
	const map = new Map<string, { name: string; icon: string }>();
	for (const m of getAllModules()) {
		// Module IDs in doc keys omit the 'r' prefix and are lowercase, e.g. "notes" for rNotes
		map.set(m.id.replace(/^r/, ""), { name: m.name, icon: m.icon });
		// Also map the full ID for direct matches
		map.set(m.id, { name: m.name, icon: m.icon });
	}
	return map;
}

/** Extract a human-readable title from an Automerge doc */
function extractTitle(doc: any): string | null {
	if (!doc) return null;
	if (doc.meta?.title) return doc.meta.title;
	if (doc.title) return doc.title;
	if (doc.name) return doc.name;
	if (doc.spaceConfig?.title) return doc.spaceConfig.title;
	return null;
}

/** Extract tags from an Automerge doc */
function extractTags(doc: any): string[] {
	if (!doc) return [];
	if (Array.isArray(doc.tags)) return doc.tags.map(String);
	if (Array.isArray(doc.meta?.tags)) return doc.meta.tags.map(String);
	return [];
}

/** Count items in a doc (for collection-level docs with Record-like data) */
function countItems(doc: any): number | null {
	if (!doc) return null;
	// Check common collection patterns
	if (doc.items && typeof doc.items === "object") return Object.keys(doc.items).length;
	if (doc.entries && typeof doc.entries === "object") return Object.keys(doc.entries).length;
	if (doc.notes && typeof doc.notes === "object") return Object.keys(doc.notes).length;
	if (doc.proposals && typeof doc.proposals === "object") return Object.keys(doc.proposals).length;
	if (doc.tasks && typeof doc.tasks === "object") return Object.keys(doc.tasks).length;
	if (doc.events && typeof doc.events === "object") return Object.keys(doc.events).length;
	if (doc.threads && typeof doc.threads === "object") return Object.keys(doc.threads).length;
	if (doc.files && typeof doc.files === "object") return Object.keys(doc.files).length;
	return null;
}

routes.get("/api/content-tree", (c) => {
	const space = c.req.query("space") || c.req.param("space") || "demo";
	const moduleMeta = getModuleMeta();

	const allDocIds = syncServer.listDocs();
	const prefix = `${space}:`;
	const spaceDocIds = allDocIds.filter((id) => id.startsWith(prefix));

	// Group by module → collection → items
	const moduleMap = new Map<string, Map<string, Array<{
		docId: string;
		title: string | null;
		itemCount: number | null;
		createdAt: number | null;
		tags: string[];
		itemId: string | null;
	}>>>();

	for (const docId of spaceDocIds) {
		const parts = docId.split(":");
		// Format: {space}:{module}:{collection}[:{itemId}]
		if (parts.length < 3) continue;
		const [, modKey, collection, ...rest] = parts;
		const itemId = rest.length ? rest.join(":") : null;

		if (!moduleMap.has(modKey)) moduleMap.set(modKey, new Map());
		const colMap = moduleMap.get(modKey)!;
		if (!colMap.has(collection)) colMap.set(collection, []);

		const doc = syncServer.getDoc(docId);
		colMap.get(collection)!.push({
			docId,
			title: extractTitle(doc),
			itemCount: countItems(doc),
			createdAt: (doc as any)?.meta?.createdAt ?? null,
			tags: extractTags(doc),
			itemId,
		});
	}

	// Build response
	const modules: any[] = [];
	for (const [modKey, colMap] of moduleMap) {
		const meta = moduleMeta.get(modKey) || { name: modKey, icon: "📄" };
		const collections: any[] = [];
		for (const [colName, items] of colMap) {
			collections.push({
				collection: colName,
				items: items.map((it) => ({
					docId: it.docId,
					title: it.title || it.itemId || colName,
					itemCount: it.itemCount,
					createdAt: it.createdAt,
					tags: it.tags,
				})),
			});
		}
		modules.push({
			id: modKey,
			name: meta.name,
			icon: meta.icon,
			collections,
		});
	}

	// Sort modules alphabetically by name
	modules.sort((a, b) => a.name.localeCompare(b.name));

	return c.json({ space, modules });
});

// ── Tab routing ──

const DATA_TABS = [
	{ id: "tree", label: "Content Tree", icon: "🌳" },
	{ id: "cloud", label: "Data Cloud", icon: "☁️" },
	{ id: "analytics", label: "Analytics", icon: "📊" },
] as const;

const DATA_TAB_IDS = new Set(DATA_TABS.map((t) => t.id));

function renderDataPage(space: string, activeTab: string, isSubdomain: boolean) {
	const body = activeTab === "tree"
		? `<folk-content-tree space="${space}"></folk-content-tree>`
		: activeTab === "cloud"
		? `<folk-data-cloud space="${space}"></folk-data-cloud>`
		: `<folk-analytics-view space="${space}"></folk-analytics-view>`;
	const scripts = activeTab === "tree"
		? `<script type="module" src="/modules/rdata/folk-content-tree.js?v=2"></script>`
		: activeTab === "cloud"
		? `<script type="module" src="/modules/rdata/folk-data-cloud.js?v=2"></script>`
		: `<script type="module" src="/modules/rdata/folk-analytics-view.js"></script>`;

	return renderShell({
		title: `${space} — Data | rSpace`,
		moduleId: "rdata",
		spaceSlug: space,
		modules: getModuleInfoList(),
		theme: "dark",
		body,
		scripts,
		styles: `<link rel="stylesheet" href="/modules/rdata/data.css">`,
	});
}

// ── Page routes ──
routes.get("/", (c) => {
	const space = c.req.param("space") || "demo";
	return c.html(renderDataPage(space, "cloud", c.get("isSubdomain")));
});

routes.get("/:tabId", (c, next) => {
	const space = c.req.param("space") || "demo";
	const tabId = c.req.param("tabId");
	// Skip API and asset routes — let Hono fall through
	if (tabId.startsWith("api") || tabId.includes(".")) return next();
	if (!DATA_TAB_IDS.has(tabId as any)) {
		return c.redirect(c.get("isSubdomain") ? `/rdata` : `/${space}/rdata`, 302);
	}
	return c.html(renderDataPage(space, tabId, c.get("isSubdomain")));
});

// ── MI export ──

export function getDataSummaryForMI(_space: string, _limit = 5): { label: string; value: string }[] {
	return []; // rData proxies Umami analytics — no local data to summarize
}

export const dataModule: RSpaceModule = {
	id: "rdata",
	name: "rData",
	icon: "📊",
	description: "Privacy-first analytics for the r* ecosystem",
	scoping: { defaultScope: 'global', userConfigurable: false },
	routes,
	standaloneDomain: "rdata.online",
	landingPage: renderLanding,
	feeds: [
		{
			id: "analytics",
			name: "Analytics Stream",
			kind: "attention",
			description: "Page views, active visitors, and engagement metrics across rApps",
			filterable: true,
		},
		{
			id: "active-users",
			name: "Active Users",
			kind: "attention",
			description: "Real-time active visitor counts",
		},
	],
	outputPaths: [
		{ path: "tree", name: "Content Tree", icon: "🌳", description: "Hierarchical view of all CRDT documents" },
		{ path: "cloud", name: "Data Cloud", icon: "☁️", description: "3D force-directed graph of all data objects" },
		{ path: "analytics", name: "Analytics", icon: "📊", description: "Usage metrics and engagement data" },
	],
	acceptsFeeds: ["data", "economic"],
};
