/**
 * Work module — kanban workspace boards.
 *
 * Multi-tenant collaborative workspace with drag-and-drop kanban,
 * configurable statuses, and activity logging.
 */

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

const routes = new Hono();

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

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

async function seedDemoIfEmpty() {
	try {
		const count = await sql.unsafe("SELECT count(*)::int as cnt FROM rwork.spaces");
		if (parseInt(count[0].cnt) > 0) return;

		// Create workspace
		const space = await sql.unsafe(
			`INSERT INTO rwork.spaces (name, slug, description, icon, owner_did)
			 VALUES ('rSpace Development', 'rspace-dev', 'Building the cosmolocal r* ecosystem', '🚀', 'did:demo:seed')
			 RETURNING id`
		);
		const spaceId = space[0].id;

		// Seed tasks across all kanban columns
		const tasks = [
			{ title: "Add dark mode toggle to settings page", status: "TODO", priority: "MEDIUM", labels: ["feature"], sort: 0 },
			{ title: "Write API documentation for rPubs endpoints", status: "TODO", priority: "LOW", labels: ["docs"], sort: 1 },
			{ title: "Investigate slow PDF generation on large documents", status: "TODO", priority: "HIGH", labels: ["bug"], sort: 2 },
			{ title: "Implement file search and filtering in rFiles", status: "IN_PROGRESS", priority: "HIGH", labels: ["feature"], sort: 0 },
			{ title: "Set up SMTP relay for transactional notifications", status: "IN_PROGRESS", priority: "MEDIUM", labels: ["chore"], sort: 1 },
			{ title: "Add PDF export to rNotes notebooks", status: "REVIEW", priority: "MEDIUM", labels: ["feature"], sort: 0 },
			{ title: "Fix conviction score decay calculation in rVote", status: "REVIEW", priority: "HIGH", labels: ["bug"], sort: 1 },
			{ title: "Deploy EncryptID passkey authentication", status: "DONE", priority: "URGENT", labels: ["feature"], sort: 0 },
			{ title: "Set up Cloudflare tunnel for all r* domains", status: "DONE", priority: "HIGH", labels: ["chore"], sort: 1 },
			{ title: "Create cosmolocal provider directory with 6 printers", status: "DONE", priority: "MEDIUM", labels: ["feature"], sort: 2 },
			{ title: "Migrate email from Resend to self-hosted Mailcow", status: "DONE", priority: "MEDIUM", labels: ["chore"], sort: 3 },
		];

		for (const t of tasks) {
			await sql.unsafe(
				`INSERT INTO rwork.tasks (space_id, title, status, priority, labels, sort_order)
				 VALUES ($1, $2, $3, $4, $5, $6)`,
				[spaceId, t.title, t.status, t.priority, t.labels, t.sort]
			);
		}

		console.log("[Work] Demo data seeded: 1 workspace, 11 tasks");
	} catch (e) {
		console.error("[Work] Seed error:", e);
	}
}

initDB().then(seedDemoIfEmpty);

// ── API: Spaces ──

// GET /api/spaces — list workspaces
routes.get("/api/spaces", async (c) => {
	const rows = await sql.unsafe(
		`SELECT s.*, count(DISTINCT sm.id)::int as member_count, count(DISTINCT t.id)::int as task_count
		 FROM rwork.spaces s
		 LEFT JOIN rwork.space_members sm ON sm.space_id = s.id
		 LEFT JOIN rwork.tasks t ON t.space_id = s.id
		 GROUP BY s.id ORDER BY s.created_at DESC`
	);
	return c.json(rows);
});

// POST /api/spaces — create workspace
routes.post("/api/spaces", 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 body = await c.req.json();
	const { name, description, icon } = body;
	if (!name?.trim()) return c.json({ error: "Name required" }, 400);

	const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
	const rows = await sql.unsafe(
		`INSERT INTO rwork.spaces (name, slug, description, icon, created_by)
		 VALUES ($1, $2, $3, $4, $5) RETURNING *`,
		[name.trim(), slug, description || null, icon || null, claims.sub]
	);
	return c.json(rows[0], 201);
});

// GET /api/spaces/:slug — workspace detail
routes.get("/api/spaces/:slug", async (c) => {
	const slug = c.req.param("slug");
	const rows = await sql.unsafe("SELECT * FROM rwork.spaces WHERE slug = $1", [slug]);
	if (rows.length === 0) return c.json({ error: "Space not found" }, 404);
	return c.json(rows[0]);
});

// ── API: Tasks ──

// GET /api/spaces/:slug/tasks — list tasks in workspace
routes.get("/api/spaces/:slug/tasks", async (c) => {
	const slug = c.req.param("slug");
	const rows = await sql.unsafe(
		`SELECT t.*, u.username as assignee_name
		 FROM rwork.tasks t
		 JOIN rwork.spaces s ON s.id = t.space_id AND s.slug = $1
		 LEFT JOIN rwork.users u ON u.id = t.assignee_id
		 ORDER BY t.status, t.sort_order, t.created_at DESC`,
		[slug]
	);
	return c.json(rows);
});

// POST /api/spaces/:slug/tasks — create task
routes.post("/api/spaces/:slug/tasks", 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 slug = c.req.param("slug");
	const body = await c.req.json();
	const { title, description, status, priority, labels } = body;
	if (!title?.trim()) return c.json({ error: "Title required" }, 400);

	const space = await sql.unsafe("SELECT id, statuses FROM rwork.spaces WHERE slug = $1", [slug]);
	if (space.length === 0) return c.json({ error: "Space not found" }, 404);

	const taskStatus = status || space[0].statuses?.[0] || "TODO";
	const rows = await sql.unsafe(
		`INSERT INTO rwork.tasks (space_id, title, description, status, priority, labels, created_by)
		 VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *`,
		[space[0].id, title.trim(), description || null, taskStatus, priority || "MEDIUM", labels || [], claims.sub]
	);
	return c.json(rows[0], 201);
});

// PATCH /api/tasks/:id — update task (status change, assignment, etc.)
routes.patch("/api/tasks/:id", async (c) => {
	// Optional auth — track who updated
	const token = extractToken(c.req.raw.headers);
	let updatedBy: string | null = null;
	if (token) {
		try { const claims = await verifyEncryptIDToken(token); updatedBy = claims.sub; } catch {}
	}

	const id = c.req.param("id");
	const body = await c.req.json();
	const { title, description, status, priority, labels, sort_order, assignee_id } = body;

	const fields: string[] = [];
	const params: any[] = [];
	let idx = 1;

	if (title !== undefined) { fields.push(`title = $${idx}`); params.push(title); idx++; }
	if (description !== undefined) { fields.push(`description = $${idx}`); params.push(description); idx++; }
	if (status !== undefined) { fields.push(`status = $${idx}`); params.push(status); idx++; }
	if (priority !== undefined) { fields.push(`priority = $${idx}`); params.push(priority); idx++; }
	if (labels !== undefined) { fields.push(`labels = $${idx}`); params.push(labels); idx++; }
	if (sort_order !== undefined) { fields.push(`sort_order = $${idx}`); params.push(sort_order); idx++; }
	if (assignee_id !== undefined) { fields.push(`assignee_id = $${idx}`); params.push(assignee_id || null); idx++; }

	if (fields.length === 0) return c.json({ error: "No fields to update" }, 400);
	fields.push("updated_at = NOW()");
	params.push(id);

	const rows = await sql.unsafe(
		`UPDATE rwork.tasks SET ${fields.join(", ")} WHERE id = $${idx} RETURNING *`,
		params
	);
	if (rows.length === 0) return c.json({ error: "Task not found" }, 404);
	return c.json(rows[0]);
});

// DELETE /api/tasks/:id
routes.delete("/api/tasks/:id", async (c) => {
	const result = await sql.unsafe("DELETE FROM rwork.tasks WHERE id = $1 RETURNING id", [c.req.param("id")]);
	if (result.length === 0) return c.json({ error: "Task not found" }, 404);
	return c.json({ ok: true });
});

// ── API: Activity ──

// GET /api/spaces/:slug/activity — recent activity
routes.get("/api/spaces/:slug/activity", async (c) => {
	const slug = c.req.param("slug");
	const rows = await sql.unsafe(
		`SELECT a.*, u.username
		 FROM rwork.activity_log a
		 JOIN rwork.spaces s ON s.id = a.space_id AND s.slug = $1
		 LEFT JOIN rwork.users u ON u.id = a.user_id
		 ORDER BY a.created_at DESC LIMIT 50`,
		[slug]
	);
	return c.json(rows);
});

// ── Page route ──
routes.get("/", (c) => {
	const space = c.req.param("space") || "demo";
	return c.html(renderShell({
		title: `${space} — Work | rSpace`,
		moduleId: "rwork",
		spaceSlug: space,
		modules: getModuleInfoList(),
		theme: "dark",
		body: `<folk-work-board space="${space}"></folk-work-board>`,
		scripts: `<script type="module" src="/modules/rwork/folk-work-board.js?v=3"></script>`,
		styles: `<link rel="stylesheet" href="/modules/rwork/work.css">`,
	}));
});

export const workModule: RSpaceModule = {
	id: "rwork",
	name: "rWork",
	icon: "📋",
	description: "Kanban workspace boards for collaborative task management",
	scoping: { defaultScope: 'space', userConfigurable: false },
	routes,
	standaloneDomain: "rwork.online",
	landingPage: renderLanding,
	feeds: [
		{
			id: "task-activity",
			name: "Task Activity",
			kind: "data",
			description: "Task creation, status changes, and assignment updates",
			filterable: true,
		},
		{
			id: "board-summary",
			name: "Board Summary",
			kind: "data",
			description: "Kanban board state — counts by status column",
		},
	],
	acceptsFeeds: ["governance", "data"],
	outputPaths: [
		{ path: "projects", name: "Projects", icon: "📋", description: "Kanban project boards" },
		{ path: "tasks", name: "Tasks", icon: "✅", description: "Task cards across all boards" },
	],
};
