/**
 * Flows module — budget flows, river visualization, and treasury management.
 *
 * Proxies flow-service API calls and serves the FlowRiver visualization.
 */

import { Hono } from "hono";
import * as Automerge from "@automerge/automerge";
import { renderShell } from "../../server/shell";
import type { RSpaceModule } from "../../shared/module";
import { getModuleInfoList } from "../../shared/module";
import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server";
import { renderLanding } from "./landing";
import type { SyncServer } from '../../server/local-first/sync-server';
import { flowsSchema, flowsDocId, type FlowsDoc, type SpaceFlow } from './schemas';

let _syncServer: SyncServer | null = null;

const FLOW_SERVICE_URL = process.env.FLOW_SERVICE_URL || "http://payment-flow:3010";

function ensureDoc(space: string): FlowsDoc {
	const docId = flowsDocId(space);
	let doc = _syncServer!.getDoc<FlowsDoc>(docId);
	if (!doc) {
		doc = Automerge.change(Automerge.init<FlowsDoc>(), 'init', (d) => {
			const init = flowsSchema.init();
			d.meta = init.meta;
			d.meta.spaceSlug = space;
			d.spaceFlows = {};
		});
		_syncServer!.setDoc(docId, doc);
	}
	return doc;
}

const routes = new Hono();

// ─── Flow Service API proxy ─────────────────────────────
// These proxy to the payment-flow backend so the frontend
// can call them from the same origin.

routes.get("/api/flows", async (c) => {
	const owner = c.req.header("X-Owner-Address") || "";
	const space = c.req.query("space") || "";

	// If space filter provided, get flow IDs from Automerge doc
	if (space) {
		const doc = ensureDoc(space);
		const flowIds = Object.values(doc.spaceFlows).map((sf) => sf.flowId);
		if (flowIds.length === 0) return c.json([]);

		const flows = await Promise.all(
			flowIds.map(async (fid) => {
				try {
					const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${fid}`);
					if (res.ok) return await res.json();
				} catch {}
				return null;
			}),
		);
		return c.json(flows.filter(Boolean));
	}

	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows?owner=${encodeURIComponent(owner)}`);
	return c.json(await res.json(), res.status as any);
});

routes.get("/api/flows/:flowId", async (c) => {
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}`);
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows", async (c) => {
	// Auth-gated: require EncryptID token
	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 body = await c.req.text();
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows`, {
		method: "POST",
		headers: {
			"Content-Type": "application/json",
			"X-Owner-Address": claims.sub,
		},
		body,
	});
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/deposit", async (c) => {
	const body = await c.req.text();
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/deposit`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body,
	});
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/withdraw", async (c) => {
	const body = await c.req.text();
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/withdraw`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body,
	});
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/activate", async (c) => {
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/activate`, { method: "POST" });
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/pause", async (c) => {
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/pause`, { method: "POST" });
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/funnels", async (c) => {
	const body = await c.req.text();
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/funnels`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body,
	});
	return c.json(await res.json(), res.status as any);
});

routes.post("/api/flows/:flowId/outcomes", async (c) => {
	const body = await c.req.text();
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/outcomes`, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body,
	});
	return c.json(await res.json(), res.status as any);
});

routes.get("/api/flows/:flowId/transactions", async (c) => {
	const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/transactions`);
	return c.json(await res.json(), res.status as any);
});

// ─── Transak fiat on-ramp ────────────────────────────────

routes.get("/api/transak/config", (c) => {
	return c.json({
		apiKey: process.env.TRANSAK_API_KEY || "STAGING_KEY",
		environment: process.env.TRANSAK_ENV || "STAGING",
	});
});

routes.post("/api/transak/webhook", async (c) => {
	let body: any;
	try { body = await c.req.json(); } catch { return c.json({ error: "Invalid JSON" }, 400); }

	// HMAC verification — if TRANSAK_WEBHOOK_SECRET is set, validate signature
	const webhookSecret = process.env.TRANSAK_WEBHOOK_SECRET;
	if (webhookSecret) {
		const signature = c.req.header("x-transak-signature") || "";
		const { createHmac } = await import("crypto");
		// Re-serialize for HMAC (Transak signs the raw JSON body)
		const expected = createHmac("sha256", webhookSecret).update(JSON.stringify(body)).digest("hex");
		if (signature !== expected) {
			console.error("[Transak] Invalid webhook signature");
			return c.json({ error: "Invalid signature" }, 401);
		}
	}

	const { webhookData } = body;

	// Ack non-completion events (Transak sends multiple status updates)
	if (!webhookData || webhookData.status !== "COMPLETED") {
		return c.json({ ok: true });
	}

	const { partnerOrderId, cryptoAmount, cryptocurrency, network } = webhookData;
	if (!partnerOrderId || cryptocurrency !== "USDC" || !network?.toLowerCase().includes("base")) {
		return c.json({ error: "Invalid webhook data" }, 400);
	}

	// partnerOrderId format: "flowId:funnelId" or "flowId" (uses env default)
	const [flowId, funnelId] = partnerOrderId.split(":");
	if (!flowId) return c.json({ error: "Missing flowId in partnerOrderId" }, 400);
	const resolvedFunnelId = funnelId || process.env.FUNNEL_ID || "";
	if (!resolvedFunnelId) return c.json({ error: "Missing funnelId" }, 400);

	// Convert crypto amount to USDC units (6 decimals)
	const amountUnits = Math.round(parseFloat(cryptoAmount) * 1e6).toString();

	const depositUrl = `${FLOW_SERVICE_URL}/api/flows/${flowId}/deposit`;
	const res = await fetch(depositUrl, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify({
			amount: amountUnits,
			source: "card",
			funnelId: resolvedFunnelId,
		}),
	});

	if (!res.ok) {
		console.error(`[Transak] Deposit failed: ${await res.text()}`);
		return c.json({ error: "Deposit failed" }, 500);
	}

	console.log(`[Transak] Deposit OK: flow=${flowId} amount=${amountUnits} USDC`);
	return c.json({ ok: true });
});

// ─── Space-flow association endpoints ────────────────────

routes.post("/api/space-flows", 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 { space, flowId } = await c.req.json();
	if (!space || !flowId) return c.json({ error: "space and flowId required" }, 400);

	const docId = flowsDocId(space);
	ensureDoc(space);
	_syncServer!.changeDoc<FlowsDoc>(docId, 'add space flow', (d) => {
		const key = `${space}:${flowId}`;
		if (!d.spaceFlows[key]) {
			d.spaceFlows[key] = { id: key, spaceSlug: space, flowId, addedBy: claims.sub, createdAt: Date.now() };
		}
	});
	return c.json({ ok: true });
});

routes.delete("/api/space-flows/:flowId", async (c) => {
	const token = extractToken(c.req.raw.headers);
	if (!token) return c.json({ error: "Authentication required" }, 401);

	try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }

	const flowId = c.req.param("flowId");
	const space = c.req.query("space") || "";
	if (!space) return c.json({ error: "space query param required" }, 400);

	const docId = flowsDocId(space);
	const doc = _syncServer!.getDoc<FlowsDoc>(docId);
	if (doc) {
		const key = `${space}:${flowId}`;
		if (doc.spaceFlows[key]) {
			_syncServer!.changeDoc<FlowsDoc>(docId, 'remove space flow', (d) => {
				delete d.spaceFlows[key];
			});
		}
	}
	return c.json({ ok: true });
});

// ─── Page routes ────────────────────────────────────────

const flowsScripts = `
	<script src="https://cdn.jsdelivr.net/npm/lz-string@1.5.0/libs/lz-string.min.js"></script>
	<script type="module" src="/modules/rflows/folk-flows-app.js"></script>
	<script type="module" src="/modules/rflows/folk-flow-river.js"></script>`;

const flowsStyles = `<link rel="stylesheet" href="/modules/rflows/flows.css">`;

// Landing page (also serves demo via centralized /demo → space="demo" rewrite)
routes.get("/", (c) => {
	const spaceSlug = c.req.param("space") || "demo";
	return c.html(renderShell({
		title: `${spaceSlug} — Flows | rSpace`,
		moduleId: "rflows",
		spaceSlug,
		modules: getModuleInfoList(),
		theme: "dark",
		body: `<folk-flows-app space="${spaceSlug}"${spaceSlug === "demo" ? ' mode="demo"' : ''}></folk-flows-app>`,
		scripts: flowsScripts,
		styles: flowsStyles,
	}));
});

// Flow detail — specific flow from API
routes.get("/flow/:flowId", (c) => {
	const spaceSlug = c.req.param("space") || "demo";
	const flowId = c.req.param("flowId");
	return c.html(renderShell({
		title: `Flow — rFlows | rSpace`,
		moduleId: "rflows",
		spaceSlug,
		modules: getModuleInfoList(),
		theme: "dark",
		styles: flowsStyles,
		body: `<folk-flows-app space="${spaceSlug}" flow-id="${flowId}"></folk-flows-app>`,
		scripts: flowsScripts,
	}));
});

// ── Seed template data ──

function seedTemplateFlows(space: string) {
	if (!_syncServer) return;
	const doc = ensureDoc(space);
	if (Object.keys(doc.spaceFlows).length > 0) return;

	const docId = flowsDocId(space);
	const now = Date.now();
	const flowId = crypto.randomUUID();

	// Create a SpaceFlow entry pointing to "demo" — the frontend
	// already renders demoNodes from presets.ts in demo mode.
	_syncServer.changeDoc<FlowsDoc>(docId, 'seed template flow', (d) => {
		d.spaceFlows[flowId] = {
			id: flowId, spaceSlug: space, flowId: 'demo',
			addedBy: 'did:demo:seed', createdAt: now,
		};
	});

	console.log(`[Flows] Template seeded for "${space}": 1 demo flow association`);
}

export const flowsModule: RSpaceModule = {
	id: "rflows",
	name: "rFlows",
	icon: "🌊",
	description: "Budget flows, river visualization, and treasury management",
	scoping: { defaultScope: 'space', userConfigurable: false },
	docSchemas: [{ pattern: '{space}:flows:data', description: 'Space flow associations', init: flowsSchema.init }],
	routes,
	landingPage: renderLanding,
	seedTemplate: seedTemplateFlows,
	async onInit(ctx) {
		_syncServer = ctx.syncServer;
	},
	standaloneDomain: "rflows.online",
	feeds: [
		{
			id: "treasury-flows",
			name: "Treasury Flows",
			kind: "economic",
			description: "Budget flow states, deposits, withdrawals, and funnel allocations",
			filterable: true,
		},
		{
			id: "transactions",
			name: "Transaction Stream",
			kind: "economic",
			description: "Real-time deposit and withdrawal events",
		},
	],
	acceptsFeeds: ["governance", "data"],
	outputPaths: [
		{ path: "budgets", name: "Budgets", icon: "💰", description: "Budget allocations and funnels" },
		{ path: "flows", name: "Flows", icon: "🌊", description: "Revenue and resource flow visualizations" },
	],
	subPageInfos: [
		{
			path: "flow",
			title: "Flow Viewer",
			icon: "🌊",
			tagline: "rFlows Tool",
			description: "Visualize a single budget flow — deposits, withdrawals, funnel allocations, and real-time balance. Drill into transactions and manage outcomes.",
			features: [
				{ icon: "📈", title: "River Visualization", text: "See funds flow through funnels and outcomes as an animated river diagram." },
				{ icon: "💸", title: "Deposits & Withdrawals", text: "Track every transaction with full history and on-chain verification." },
				{ icon: "🎯", title: "Outcome Tracking", text: "Define funding outcomes and monitor how capital reaches its destination." },
			],
		},
	],
};
