/**
 * CrowdSurf module — swipe-based community activity coordination.
 *
 * Inspired by gospelofchange/Crowdsurfing. Users propose activities with
 * commitment thresholds; others swipe to join and declare contributions.
 * When enough people commit, the activity triggers.
 *
 * The folk-crowdsurf-dashboard web component lives in components/.
 * This module provides:
 *  - A dashboard listing active/triggered prompts in the current space
 *  - API to query crowdsurf prompts from the Automerge store
 *  - Canvas shape integration for inline prompt cards
 */

import { Hono } from "hono";
import { renderShell } from "../../server/shell";
import type { RSpaceModule } from "../../shared/module";
import { renderLanding } from "./landing";
import { getModuleInfoList } from "../../shared/module";
import { getDocumentData, addShapes } from "../../server/community-store";
import { ELO_DEFAULT, computeElo } from "./schemas";

const routes = new Hono();

// GET /api/crowdsurf — list crowdsurf prompt shapes in the current space
routes.get("/api/crowdsurf", async (c) => {
	const space = c.req.param("space") || c.req.query("space") || "demo";
	const docData = getDocumentData(space);
	if (!docData?.shapes) {
		return c.json({ prompts: [], total: 0 });
	}

	const promptTypes = ["folk-crowdsurf-prompt"];
	const prompts: any[] = [];

	for (const [id, shape] of Object.entries(docData.shapes as Record<string, any>)) {
		if (shape.forgotten) continue;
		if (promptTypes.includes(shape.type)) {
			prompts.push({
				id,
				type: shape.type,
				text: shape.text || "Untitled",
				location: shape.location || "",
				threshold: shape.threshold || 3,
				swipeCount: Object.keys(shape.swipes || {}).length,
				triggered: shape.triggered || false,
				createdAt: shape.createdAt,
			});
		}
	}

	return c.json({ prompts, total: prompts.length });
});

// GET /api/crowdsurf/pair — get a random pair for pairwise comparison (sortition-weighted)
routes.get("/api/crowdsurf/pair", async (c) => {
	const space = c.req.param("space") || c.req.query("space") || "demo";
	const docData = getDocumentData(space);
	if (!docData?.shapes) return c.json({ error: "No prompts" }, 404);

	const prompts: any[] = [];
	for (const [id, shape] of Object.entries(docData.shapes as Record<string, any>)) {
		if (shape.forgotten || shape.type !== "folk-crowdsurf-prompt") continue;
		if (shape.expired || shape.triggered) continue;
		prompts.push({ id, ...shape, elo: shape.elo ?? ELO_DEFAULT, comparisons: shape.comparisons ?? 0, wins: shape.wins ?? 0 });
	}

	if (prompts.length < 2) return c.json({ error: "Need at least 2 active prompts" }, 404);

	// Sortition: weight by inverse comparisons (fewer comparisons = more likely to be picked)
	const maxComp = Math.max(...prompts.map((p: any) => p.comparisons), 1);
	const weights = prompts.map((p: any) => maxComp - p.comparisons + 1);
	const totalWeight = weights.reduce((a: number, b: number) => a + b, 0);

	const pickWeighted = (exclude?: string): any => {
		let r = Math.random() * (exclude ? totalWeight - (weights[prompts.findIndex((p: any) => p.id === exclude)] || 0) : totalWeight);
		for (let i = 0; i < prompts.length; i++) {
			if (prompts[i].id === exclude) continue;
			r -= weights[i];
			if (r <= 0) return prompts[i];
		}
		return prompts[prompts.length - 1];
	};

	const a = pickWeighted();
	const b = pickWeighted(a.id);
	return c.json({ a, b });
});

// POST /api/crowdsurf/compare — record a pairwise comparison result
routes.post("/api/crowdsurf/compare", async (c) => {
	const space = c.req.param("space") || c.req.query("space") || "demo";
	const body = await c.req.json();
	const { winnerId, loserId } = body;
	if (!winnerId || !loserId) return c.json({ error: "winnerId and loserId required" }, 400);

	const docData = getDocumentData(space);
	if (!docData?.shapes) return c.json({ error: "No data" }, 404);

	const winner = (docData.shapes as Record<string, any>)[winnerId];
	const loser = (docData.shapes as Record<string, any>)[loserId];
	if (!winner || !loser) return c.json({ error: "Prompt not found" }, 404);

	const winnerElo = winner.elo ?? ELO_DEFAULT;
	const loserElo = loser.elo ?? ELO_DEFAULT;
	const result = computeElo(winnerElo, loserElo);

	// Update shapes via addShapes (merge semantics)
	addShapes(space, [
		{ id: winnerId, elo: result.winner, comparisons: (winner.comparisons ?? 0) + 1, wins: (winner.wins ?? 0) + 1 },
		{ id: loserId, elo: result.loser, comparisons: (loser.comparisons ?? 0) + 1 },
	]);

	return c.json({
		ok: true,
		winner: { id: winnerId, elo: result.winner, delta: result.winner - winnerElo },
		loser: { id: loserId, elo: result.loser, delta: result.loser - loserElo },
	});
});

// GET / — crowdsurf dashboard page
routes.get("/", (c) => {
	const spaceSlug = c.req.param("space") || "demo";
	return c.html(renderShell({
		title: `${spaceSlug} — CrowdSurf | rSpace`,
		moduleId: "crowdsurf",
		spaceSlug,
		modules: getModuleInfoList(),
		theme: "dark",
		body: `<folk-crowdsurf-dashboard space="${spaceSlug}"></folk-crowdsurf-dashboard>`,
		scripts: `<script type="module" src="/modules/crowdsurf/folk-crowdsurf-dashboard.js?v=2"></script>`,
		styles: `<link rel="stylesheet" href="/modules/crowdsurf/crowdsurf.css">`,
	}));
});

// ── Seed template data ──

function seedTemplateCrowdSurf(space: string) {
	const docData = getDocumentData(space);
	const promptTypes = ["folk-crowdsurf-prompt"];
	if (docData?.shapes) {
		const existing = Object.values(docData.shapes as Record<string, any>)
			.filter((s: any) => !s.forgotten && promptTypes.includes(s.type));
		if (existing.length > 0) return;
	}

	const now = Date.now();
	const shapes: Record<string, unknown>[] = [
		{
			id: `tmpl-crowdsurf-1-${now}`, type: 'folk-crowdsurf-prompt',
			x: 50, y: 2200, width: 420, height: 300, rotation: 0,
			text: 'Community Garden Planting Day',
			location: 'Community Center Garden',
			threshold: 5,
			duration: 4,
			activityDuration: '3 hours',
			swipes: {},
			triggered: false,
			expired: false,
			createdAt: now,
		},
		{
			id: `tmpl-crowdsurf-2-${now}`, type: 'folk-crowdsurf-prompt',
			x: 520, y: 2200, width: 420, height: 300, rotation: 0,
			text: 'Open Mic & Jam Session',
			location: 'Local Park Bandstand',
			threshold: 8,
			duration: 6,
			activityDuration: '2 hours',
			swipes: {},
			triggered: false,
			expired: false,
			createdAt: now,
		},
		{
			id: `tmpl-crowdsurf-3-${now}`, type: 'folk-crowdsurf-prompt',
			x: 990, y: 2200, width: 420, height: 300, rotation: 0,
			text: 'Repair Cafe — Bring Your Broken Stuff',
			location: 'Maker Space',
			threshold: 3,
			duration: 8,
			activityDuration: '4 hours',
			swipes: {},
			triggered: false,
			expired: false,
			createdAt: now,
		},
	];

	addShapes(space, shapes);
	console.log(`[CrowdSurf] Template seeded for "${space}": 3 prompt shapes`);
}

// ── MI export ──

export function getActivePromptsForMI(space: string, limit = 5): { id: string; text: string; swipeCount: number; threshold: number; triggered: boolean; createdAt: number }[] {
	const docData = getDocumentData(space);
	if (!docData?.shapes) return [];
	const results: { id: string; text: string; swipeCount: number; threshold: number; triggered: boolean; createdAt: number }[] = [];
	for (const [id, shape] of Object.entries(docData.shapes as Record<string, any>)) {
		if (shape.forgotten || shape.type !== "folk-crowdsurf-prompt") continue;
		results.push({
			id,
			text: shape.text || "Untitled",
			swipeCount: Object.keys(shape.swipes || {}).length,
			threshold: shape.threshold || 3,
			triggered: shape.triggered || false,
			createdAt: shape.createdAt,
		});
	}
	return results
		.sort((a, b) => b.createdAt - a.createdAt)
		.slice(0, limit);
}

export const crowdsurfModule: RSpaceModule = {
	id: "crowdsurf",
	name: "CrowdSurf",
	icon: "🏄",
	description: "Swipe-based community activity coordination",
	scoping: { defaultScope: 'space', userConfigurable: false },
	routes,
	hidden: true, // CrowdSurf is now a sub-tab of rChoices
	landingPage: renderLanding,
	seedTemplate: seedTemplateCrowdSurf,
	feeds: [
		{
			id: "activity-triggers",
			name: "Activity Triggers",
			kind: "governance",
			description: "Activity proposals and triggered events",
			emits: ["folk-crowdsurf-prompt"],
		},
	],
	acceptsFeeds: ["data", "governance"],
	outputPaths: [
		{ path: "prompts", name: "Prompts", icon: "🏄", description: "Active activity proposals" },
		{ path: "triggered", name: "Triggered", icon: "🚀", description: "Activities that reached their threshold" },
		{ path: "rank", name: "Rank", icon: "🎲", description: "Pairwise Elo ranking of activities" },
	],
};
