/**
 * Forum module — Discourse cloud provisioner.
 * Deploy self-hosted Discourse forums on Hetzner VPS with Cloudflare DNS.
 */

import { Hono } from "hono";
import * as Automerge from "@automerge/automerge";
import { renderShell, renderExternalAppShell } from "../../server/shell";
import { getModuleInfoList } from "../../shared/module";
import { provisionInstance, destroyInstance } from "./lib/provisioner";
import type { RSpaceModule } from "../../shared/module";
import { verifyToken, extractToken } from "../../server/auth";
import { renderLanding } from "./landing";
import type { SyncServer } from '../../server/local-first/sync-server';
import {
	forumSchema,
	FORUM_DOC_ID,
	type ForumDoc,
	type ForumInstance,
} from './schemas';

let _syncServer: SyncServer | null = null;

const routes = new Hono();

// ── Helpers ──

function ensureDoc(): ForumDoc {
	let doc = _syncServer!.getDoc<ForumDoc>(FORUM_DOC_ID);
	if (!doc) {
		doc = Automerge.change(Automerge.init<ForumDoc>(), 'init forum doc', (d) => {
			const init = forumSchema.init();
			d.meta = init.meta;
			d.instances = {};
			d.provisionLogs = {};
		});
		_syncServer!.setDoc(FORUM_DOC_ID, doc);
	}
	return doc;
}

// ── API: List instances ──
routes.get("/api/instances", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	let claims;
	try { claims = await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const doc = ensureDoc();
	const instances = Object.values(doc.instances).filter(
		(inst) => inst.userId === claims.sub && inst.status !== 'destroyed',
	);
	// Sort by createdAt descending
	instances.sort((a, b) => b.createdAt - a.createdAt);
	return c.json({ instances });
});

// ── API: Create instance ──
routes.post("/api/instances", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	let claims;
	try { claims = await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const body = await c.req.json<{
		name: string;
		subdomain: string;
		region?: string;
		size?: string;
		admin_email: string;
		smtp_config?: Record<string, unknown>;
	}>();

	if (!body.name || !body.subdomain || !body.admin_email) {
		return c.json({ error: "name, subdomain, and admin_email are required" }, 400);
	}

	const domain = `${body.subdomain}.rforum.online`;

	// Check uniqueness
	const doc = ensureDoc();
	const existing = Object.values(doc.instances).find((inst) => inst.domain === domain);
	if (existing) return c.json({ error: "Domain already taken" }, 409);

	const id = crypto.randomUUID();
	const now = Date.now();

	const instance: ForumInstance = {
		id,
		userId: claims.sub,
		name: body.name,
		domain,
		status: 'pending',
		errorMessage: '',
		discourseVersion: 'stable',
		provider: 'hetzner',
		vpsId: '',
		vpsIp: '',
		region: body.region || 'nbg1',
		size: body.size || 'cx22',
		adminEmail: body.admin_email,
		smtpConfig: body.smtp_config || {},
		dnsRecordId: '',
		sslProvisioned: false,
		createdAt: now,
		updatedAt: now,
		provisionedAt: 0,
		destroyedAt: 0,
	};

	_syncServer!.changeDoc<ForumDoc>(FORUM_DOC_ID, 'create instance', (d) => {
		d.instances[id] = instance;
		d.provisionLogs[id] = [];
	});

	// Start provisioning asynchronously
	provisionInstance(_syncServer!, id).catch((e) => {
		console.error("[Forum] Provision failed:", e);
	});

	return c.json({ instance }, 201);
});

// ── API: Get instance detail ──
routes.get("/api/instances/:id", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	let claims;
	try { claims = await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const doc = ensureDoc();
	const instance = doc.instances[c.req.param("id")];
	if (!instance || instance.userId !== claims.sub) return c.json({ error: "Instance not found" }, 404);

	const logs = doc.provisionLogs[instance.id] || [];

	return c.json({ instance, logs });
});

// ── API: Destroy instance ──
routes.delete("/api/instances/:id", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);
	let claims;
	try { claims = await verifyToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const doc = ensureDoc();
	const instance = doc.instances[c.req.param("id")];
	if (!instance || instance.userId !== claims.sub) return c.json({ error: "Instance not found" }, 404);
	if (instance.status === "destroyed") return c.json({ error: "Already destroyed" }, 400);

	// Destroy asynchronously
	destroyInstance(_syncServer!, instance.id).catch((e) => {
		console.error("[Forum] Destroy failed:", e);
	});

	return c.json({ message: "Destroying instance...", instance: { ...instance, status: "destroying" } });
});

// ── API: Get provision logs ──
routes.get("/api/instances/:id/logs", async (c) => {
	const doc = ensureDoc();
	const logs = doc.provisionLogs[c.req.param("id")] || [];
	return c.json({ logs });
});

// ── API: Health ──
routes.get("/api/health", (c) => {
	return c.json({ status: "ok", service: "rforum" });
});

// ── Page route ──
routes.get("/", (c) => {
	const spaceSlug = c.req.param("space") || "demo";
	const view = c.req.query("view");

	if (view === "app") {
		return c.html(renderExternalAppShell({
			title: `${spaceSlug} — Discourse | rSpace`,
			moduleId: "rforum",
			spaceSlug,
			modules: getModuleInfoList(),
			appUrl: "https://commons.rforum.online",
			appName: "Discourse",
			theme: "dark",
		}));
	}

	return c.html(renderShell({
		title: `${spaceSlug} — Forum | rSpace`,
		moduleId: "rforum",
		spaceSlug,
		modules: getModuleInfoList(),
		theme: "dark",
		body: `<div class="rapp-nav" style="padding:0 1rem;margin-top:8px"><span class="rapp-nav__title"></span><a href="?view=app" class="rapp-nav__btn--app-toggle">Open Full App</a></div>
<folk-forum-dashboard space="${spaceSlug}"></folk-forum-dashboard>`,
		scripts: `<script type="module" src="/modules/rforum/folk-forum-dashboard.js?v=2"></script>`,
		styles: `<link rel="stylesheet" href="/modules/rforum/forum.css">`,
	}));
});

// ── Seed template data ──

function seedTemplateForum(_space: string) {
	if (!_syncServer) return;
	const doc = ensureDoc();
	if (Object.keys(doc.instances).length > 0) return;

	const now = Date.now();
	const instanceId = crypto.randomUUID();

	_syncServer.changeDoc<ForumDoc>(FORUM_DOC_ID, 'seed template forum instance', (d) => {
		d.instances[instanceId] = {
			id: instanceId, userId: 'did:demo:seed', name: 'Commons Forum',
			domain: 'commons.rforum.online', status: 'active', errorMessage: '',
			discourseVersion: '3.2.0', provider: 'hetzner', vpsId: 'demo-vps',
			vpsIp: '0.0.0.0', region: 'eu-central', size: 'cx21',
			adminEmail: 'admin@commons.rforum.online', smtpConfig: {},
			dnsRecordId: '', sslProvisioned: true,
			createdAt: now, updatedAt: now, provisionedAt: now, destroyedAt: 0,
		};
	});

	console.log(`[Forum] Template seeded: 1 demo instance`);
}

// ── MI export ──

export function getForumInstancesForMI(_space: string, limit = 5): { id: string; name: string; domain: string; status: string; createdAt: number }[] {
	if (!_syncServer) return [];
	const doc = _syncServer.getDoc<ForumDoc>(FORUM_DOC_ID);
	if (!doc) return [];
	return Object.values(doc.instances)
		.filter((i) => i.status !== "destroyed")
		.sort((a, b) => b.createdAt - a.createdAt)
		.slice(0, limit)
		.map((i) => ({ id: i.id, name: i.name, domain: i.domain || "", status: i.status, createdAt: i.createdAt }));
}

export const forumModule: RSpaceModule = {
	id: "rforum",
	name: "rForum",
	icon: "💬",
	description: "Deploy and manage Discourse forums",
	scoping: { defaultScope: 'global', userConfigurable: true },
	docSchemas: [{ pattern: 'global:forum:instances', description: 'Forum provisioning metadata', init: forumSchema.init }],
	routes,
	landingPage: renderLanding,
	seedTemplate: seedTemplateForum,
	standaloneDomain: "rforum.online",
	externalApp: { url: "https://commons.rforum.online", name: "Discourse" },
	feeds: [
		{
			id: "threads",
			name: "Threads",
			kind: "data",
			description: "Discussion threads and topics from provisioned Discourse instances",
			filterable: true,
		},
		{
			id: "activity",
			name: "Activity",
			kind: "attention",
			description: "Forum engagement — new posts, replies, and participation metrics",
		},
	],
	acceptsFeeds: ["data", "governance"],
	outputPaths: [
		{ path: "threads", name: "Threads", icon: "💬", description: "Forum discussion threads" },
		{ path: "categories", name: "Categories", icon: "📂", description: "Forum categories and topics" },
	],
	async onInit(ctx) {
		_syncServer = ctx.syncServer;
		console.log("[Forum] Module initialized (Automerge-only, no PG)");
	},
};
