/**
 * Trips module — collaborative trip planner.
 *
 * Plan trips with destinations, itinerary, bookings, expenses,
 * and packing lists. Collaborative with role-based access.
 */

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 OSRM_URL = process.env.OSRM_URL || "http://osrm-backend:5000";

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("[Trips] DB schema initialized");
	} catch (e) {
		console.error("[Trips] DB init error:", e);
	}
}

initDB();

// ── API: Trips ──

// GET /api/trips — list trips
routes.get("/api/trips", async (c) => {
	const rows = await sql.unsafe(
		`SELECT t.*,
			count(DISTINCT d.id)::int as destination_count,
			count(DISTINCT e.id)::int as expense_count,
			coalesce(sum(e.amount), 0)::numeric as total_spent
		 FROM rtrips.trips t
		 LEFT JOIN rtrips.destinations d ON d.trip_id = t.id
		 LEFT JOIN rtrips.expenses e ON e.trip_id = t.id
		 GROUP BY t.id ORDER BY t.created_at DESC`
	);
	return c.json(rows);
});

// POST /api/trips — create trip
routes.post("/api/trips", 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 { title, description, start_date, end_date, budget_total, budget_currency } = body;
	if (!title?.trim()) return c.json({ error: "Title required" }, 400);

	const slug = title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.trips (title, slug, description, start_date, end_date, budget_total, budget_currency, created_by)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,
		[title.trim(), slug, description || null, start_date || null, end_date || null,
		 budget_total || null, budget_currency || "USD", claims.sub]
	);
	return c.json(rows[0], 201);
});

// GET /api/trips/:id — trip detail with all sub-resources
routes.get("/api/trips/:id", async (c) => {
	const id = c.req.param("id");
	const trip = await sql.unsafe("SELECT * FROM rtrips.trips WHERE id = $1", [id]);
	if (trip.length === 0) return c.json({ error: "Trip not found" }, 404);

	const [destinations, itinerary, bookings, expenses, packing] = await Promise.all([
		sql.unsafe("SELECT * FROM rtrips.destinations WHERE trip_id = $1 ORDER BY sort_order", [id]),
		sql.unsafe("SELECT * FROM rtrips.itinerary_items WHERE trip_id = $1 ORDER BY date, sort_order", [id]),
		sql.unsafe("SELECT * FROM rtrips.bookings WHERE trip_id = $1 ORDER BY start_date", [id]),
		sql.unsafe("SELECT * FROM rtrips.expenses WHERE trip_id = $1 ORDER BY date DESC", [id]),
		sql.unsafe("SELECT * FROM rtrips.packing_items WHERE trip_id = $1 ORDER BY category, sort_order", [id]),
	]);

	return c.json({ ...trip[0], destinations, itinerary, bookings, expenses, packing });
});

// PUT /api/trips/:id — update trip
routes.put("/api/trips/:id", async (c) => {
	const id = c.req.param("id");
	const body = await c.req.json();
	const { title, description, start_date, end_date, budget_total, budget_currency, status } = 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 (start_date !== undefined) { fields.push(`start_date = $${idx}`); params.push(start_date); idx++; }
	if (end_date !== undefined) { fields.push(`end_date = $${idx}`); params.push(end_date); idx++; }
	if (budget_total !== undefined) { fields.push(`budget_total = $${idx}`); params.push(budget_total); idx++; }
	if (budget_currency !== undefined) { fields.push(`budget_currency = $${idx}`); params.push(budget_currency); idx++; }
	if (status !== undefined) { fields.push(`status = $${idx}`); params.push(status); idx++; }

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

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

// ── API: Destinations ──

routes.post("/api/trips/:id/destinations", 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 body = await c.req.json();
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.destinations (trip_id, name, country, lat, lng, arrival_date, departure_date, notes, sort_order)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`,
		[c.req.param("id"), body.name, body.country || null, body.lat || null, body.lng || null,
		 body.arrival_date || null, body.departure_date || null, body.notes || null, body.sort_order ?? 0]
	);
	return c.json(rows[0], 201);
});

// ── API: Itinerary ──

routes.post("/api/trips/:id/itinerary", 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 body = await c.req.json();
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.itinerary_items (trip_id, destination_id, title, category, date, start_time, end_time, notes, sort_order)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`,
		[c.req.param("id"), body.destination_id || null, body.title, body.category || "ACTIVITY",
		 body.date || null, body.start_time || null, body.end_time || null, body.notes || null, body.sort_order ?? 0]
	);
	return c.json(rows[0], 201);
});

// ── API: Bookings ──

routes.post("/api/trips/:id/bookings", 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 body = await c.req.json();
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.bookings (trip_id, type, provider, confirmation_number, cost, currency, start_date, end_date, notes)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`,
		[c.req.param("id"), body.type || "OTHER", body.provider || null, body.confirmation_number || null,
		 body.cost || null, body.currency || "USD", body.start_date || null, body.end_date || null, body.notes || null]
	);
	return c.json(rows[0], 201);
});

// ── API: Expenses ──

routes.post("/api/trips/:id/expenses", 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 body = await c.req.json();
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.expenses (trip_id, description, amount, currency, category, date, split_type)
		 VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *`,
		[c.req.param("id"), body.description, body.amount, body.currency || "USD",
		 body.category || "OTHER", body.date || null, body.split_type || "EQUAL"]
	);
	return c.json(rows[0], 201);
});

// ── API: Packing ──

routes.get("/api/trips/:id/packing", async (c) => {
	const rows = await sql.unsafe(
		"SELECT * FROM rtrips.packing_items WHERE trip_id = $1 ORDER BY category, sort_order",
		[c.req.param("id")]
	);
	return c.json(rows);
});

routes.post("/api/trips/:id/packing", 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 body = await c.req.json();
	const rows = await sql.unsafe(
		`INSERT INTO rtrips.packing_items (trip_id, name, category, quantity, sort_order)
		 VALUES ($1, $2, $3, $4, $5) RETURNING *`,
		[c.req.param("id"), body.name, body.category || "GENERAL", body.quantity || 1, body.sort_order ?? 0]
	);
	return c.json(rows[0], 201);
});

routes.patch("/api/packing/:id", async (c) => {
	const body = await c.req.json();
	const rows = await sql.unsafe(
		"UPDATE rtrips.packing_items SET packed = $1 WHERE id = $2 RETURNING *",
		[body.packed ?? false, c.req.param("id")]
	);
	if (rows.length === 0) return c.json({ error: "Not found" }, 404);
	return c.json(rows[0]);
});

// ── OSRM proxy for route planner ──
routes.post("/api/route", async (c) => {
	const body = await c.req.json<{ start: { lng: number; lat: number }; end: { lng: number; lat: number } }>();
	const { start, end } = body;
	if (!start || !end) return c.json({ error: "start and end are required" }, 400);

	const url = `${OSRM_URL}/route/v1/driving/${start.lng},${start.lat};${end.lng},${end.lat}?geometries=geojson&overview=full`;
	try {
		const res = await fetch(url);
		const data = await res.json();
		return c.json(data, res.status as any);
	} catch (e) {
		return c.json({ error: "OSRM backend unavailable", detail: String(e) }, 502);
	}
});

// ── Route planner page ──
routes.get("/routes", (c) => {
	const space = c.req.param("space") || "demo";
	return c.html(renderShell({
		title: `${space} — Route Planner | rTrips`,
		moduleId: "rtrips",
		spaceSlug: space,
		modules: getModuleInfoList(),
		theme: "dark",
		styles: `<link rel="stylesheet" href="/modules/rtrips/route-planner.css">`,
		body: `<folk-route-planner></folk-route-planner>`,
		scripts: `<script type="module" src="/modules/rtrips/folk-route-planner.js"></script>`,
	}));
});

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

export const tripsModule: RSpaceModule = {
	id: "rtrips",
	name: "rTrips",
	icon: "✈️",
	description: "Collaborative trip planner with itinerary, bookings, and expense splitting",
	scoping: { defaultScope: 'global', userConfigurable: true },
	routes,
	landingPage: renderLanding,
	standaloneDomain: "rtrips.online",
	feeds: [
		{
			id: "trip-expenses",
			name: "Trip Expenses",
			kind: "economic",
			description: "Expense tracking with split amounts per traveler",
			filterable: true,
		},
		{
			id: "itinerary",
			name: "Itinerary",
			kind: "data",
			description: "Destinations, activities, and bookings timeline",
			emits: ["folk-itinerary", "folk-destination", "folk-booking"],
		},
	],
	acceptsFeeds: ["economic", "data"],
	outputPaths: [
		{ path: "itineraries", name: "Itineraries", icon: "🗓️", description: "Trip itineraries with bookings and activities" },
	],
};
