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

import { Hono } from "hono";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { sql } from "../../shared/db/pool";
import { renderShell, renderExternalAppShell } from "../../server/shell";
import { getModuleInfoList } from "../../shared/module";
import { provisionInstance, destroyInstance } from "./lib/provisioner";
import type { RSpaceModule } from "../../shared/module";
import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server";
import { renderLanding } from "./landing";

const routes = new Hono();

const SCHEMA_SQL = readFileSync(resolve(import.meta.dir, "db/schema.sql"), "utf-8");

// ── DB initialization ──
async function initDB() {
	try {
		await sql.unsafe(SCHEMA_SQL);
		console.log("[Forum] DB schema initialized");
	} catch (e: any) {
		console.error("[Forum] DB init error:", e.message);
	}
}
initDB();

// ── Helpers ──
async function getOrCreateUser(did: string): Promise<any> {
	const [existing] = await sql.unsafe("SELECT * FROM rforum.users WHERE did = $1", [did]);
	if (existing) return existing;
	const [user] = await sql.unsafe(
		"INSERT INTO rforum.users (did) VALUES ($1) RETURNING *",
		[did],
	);
	return user;
}

// ── 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 verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const user = await getOrCreateUser(claims.sub);
	const rows = await sql.unsafe(
		"SELECT * FROM rforum.instances WHERE user_id = $1 AND status != 'destroyed' ORDER BY created_at DESC",
		[user.id],
	);
	return c.json({ instances: rows });
});

// ── 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 verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const user = await getOrCreateUser(claims.sub);
	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 [existing] = await sql.unsafe("SELECT id FROM rforum.instances WHERE domain = $1", [domain]);
	if (existing) return c.json({ error: "Domain already taken" }, 409);

	const [instance] = await sql.unsafe(
		`INSERT INTO rforum.instances (user_id, name, domain, region, size, admin_email, smtp_config)
		 VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb) RETURNING *`,
		[user.id, body.name, domain, body.region || "nbg1", body.size || "cx22", body.admin_email, JSON.stringify(body.smtp_config || {})],
	);

	// Start provisioning asynchronously
	provisionInstance(instance.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 verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const user = await getOrCreateUser(claims.sub);
	const [instance] = await sql.unsafe(
		"SELECT * FROM rforum.instances WHERE id = $1 AND user_id = $2",
		[c.req.param("id"), user.id],
	);
	if (!instance) return c.json({ error: "Instance not found" }, 404);

	const logs = await sql.unsafe(
		"SELECT * FROM rforum.provision_logs WHERE instance_id = $1 ORDER BY created_at ASC",
		[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 verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const user = await getOrCreateUser(claims.sub);
	const [instance] = await sql.unsafe(
		"SELECT * FROM rforum.instances WHERE id = $1 AND user_id = $2",
		[c.req.param("id"), user.id],
	);
	if (!instance) return c.json({ error: "Instance not found" }, 404);
	if (instance.status === "destroyed") return c.json({ error: "Already destroyed" }, 400);

	// Destroy asynchronously
	destroyInstance(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 logs = await sql.unsafe(
		"SELECT * FROM rforum.provision_logs WHERE instance_id = $1 ORDER BY created_at ASC",
		[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"></script>`,
		styles: `<link rel="stylesheet" href="/modules/rforum/forum.css">`,
	}));
});

export const forumModule: RSpaceModule = {
	id: "rforum",
	name: "rForum",
	icon: "💬",
	description: "Deploy and manage Discourse forums",
	scoping: { defaultScope: 'global', userConfigurable: true },
	routes,
	landingPage: renderLanding,
	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" },
	],
};
