/**
 * rTrips demo — client-side WebSocket controller.
 *
 * Connects to rSpace (no filter — needs all shape types) and
 * populates the 6-card trip dashboard: maps, notes/packing,
 * calendar, polls, funds, cart.
 */

import { DemoSync, type DemoShape } from "../../../lib/demo-sync-vanilla";

// ── Helpers ──

function shapesByType(shapes: Record<string, DemoShape>, type: string): DemoShape[] {
	return Object.values(shapes).filter((s) => s.type === type);
}

function shapeByType(shapes: Record<string, DemoShape>, type: string): DemoShape | undefined {
	return Object.values(shapes).find((s) => s.type === type);
}

function $(id: string): HTMLElement | null {
	return document.getElementById(id);
}

function escHtml(s: string): string {
	return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}

// ── Constants ──

const MEMBER_COLORS = ["#14b8a6", "#06b6d4", "#3b82f6", "#8b5cf6", "#f59e0b", "#f43f5e"];
const POLL_COLORS = ["#f59e0b", "#3b82f6", "#10b981", "#f43f5e"];
const CATEGORY_COLORS: Record<string, string> = {
	travel: "#14b8a6",
	hike: "#10b981",
	adventure: "#f59e0b",
	rest: "#64748b",
	culture: "#8b5cf6",
	FLIGHT: "#14b8a6",
	TRANSPORT: "#06b6d4",
	ACCOMMODATION: "#14b8a6",
	ACTIVITY: "#10b981",
	MEAL: "#f59e0b",
	FREE_TIME: "#64748b",
	OTHER: "#64748b",
};

// ── DemoSync (no filter — needs all shape types) ──

const sync = new DemoSync();

// ── Render functions ──

function render(shapes: Record<string, DemoShape>) {
	const itinerary = shapeByType(shapes, "folk-itinerary");
	const destinations = shapesByType(shapes, "folk-destination");
	const packingList = shapeByType(shapes, "folk-packing-list");
	const pollShapes = shapesByType(shapes, "demo-poll");
	const expenseShapes = shapesByType(shapes, "demo-expense");
	const cartShapes = shapesByType(shapes, "demo-cart-item");
	const budgetShape = shapeByType(shapes, "folk-budget");

	const hasShapes = Object.keys(shapes).length > 0;

	// Members from itinerary
	const travelers = (itinerary?.travelers ?? []) as string[];
	const members = travelers.map((name, i) => ({ name, color: MEMBER_COLORS[i % MEMBER_COLORS.length] }));

	// Trip header
	renderHeader(itinerary, destinations, budgetShape, members, hasShapes);

	// Show live badges
	for (const card of ["maps", "notes", "cal", "polls", "funds", "cart"]) {
		const el = $(`rd-${card}-live`);
		if (el) el.style.display = hasShapes ? "inline-flex" : "none";
	}

	// Cards
	renderMap(destinations);
	renderNotes(packingList);
	renderCalendar(itinerary);
	renderPolls(pollShapes);
	renderFunds(expenseShapes, members);
	renderCart(cartShapes);
}

// ── Header ──

function renderHeader(
	itinerary: DemoShape | undefined,
	destinations: DemoShape[],
	budgetShape: DemoShape | undefined,
	members: { name: string; color: string }[],
	hasShapes: boolean,
) {
	const title = $("rd-trip-title");
	const route = $("rd-trip-route");
	const meta = $("rd-trip-meta");
	const avatars = $("rd-avatars");

	if (title && itinerary?.tripTitle) {
		title.textContent = itinerary.tripTitle as string;
	}

	if (route && destinations.length > 0) {
		route.textContent = destinations.map((d) => d.destName as string).join(" → ");
	}

	if (meta && itinerary) {
		const start = itinerary.startDate as string;
		const end = itinerary.endDate as string;
		const budgetTotal = (budgetShape?.budgetTotal as number) || 4000;
		const dateRange = start && end
			? `${new Date(start).toLocaleDateString("en", { month: "short", day: "numeric" })}–${new Date(end).toLocaleDateString("en", { month: "short", day: "numeric" })}, 2026`
			: "Jul 6–20, 2026";
		const countries = destinations.length > 0
			? new Set(destinations.map((d) => d.country as string)).size
			: 3;
		meta.innerHTML = `
			<span>📅 ${dateRange}</span>
			<span>💶 ~€${budgetTotal.toLocaleString()} budget</span>
			<span>🏔️ ${countries} countr${countries !== 1 ? "ies" : "y"}</span>
			${hasShapes ? `<span style="display:flex;align-items:center;gap:0.25rem;color:#34d399;"><span style="width:6px;height:6px;border-radius:50%;background:#34d399;animation:rd-pulse 2s ease-in-out infinite;"></span> Live data</span>` : ""}
		`;
	}

	if (avatars && members.length > 0) {
		avatars.innerHTML = members.map((m) =>
			`<div class="rd-avatar" style="background:${m.color}" title="${escHtml(m.name)}">${m.name[0]}</div>`
		).join("") + `<span style="font-size:0.75rem;color:#94a3b8;margin-left:0.25rem;">${members.length} explorers</span>`;
	}
}

// ── Map card ──

function renderMap(destinations: DemoShape[]) {
	if (destinations.length === 0) return;

	const pins = destinations.map((d, i) => ({
		name: d.destName as string,
		cx: 160 + i * 245,
		cy: 180 - i * 20,
		color: ["#14b8a6", "#06b6d4", "#8b5cf6"][i] || "#94a3b8",
		stroke: ["#0d9488", "#0891b2", "#7c3aed"][i] || "#64748b",
		dates: d.arrivalDate && d.departureDate
			? `${new Date(d.arrivalDate as string).toLocaleDateString("en", { month: "short", day: "numeric" })}–${new Date(d.departureDate as string).getUTCDate()}`
			: "",
	}));

	const pinsEl = $("rd-map-pins");
	if (pinsEl) {
		pinsEl.innerHTML = pins.map((p) => `
			<g>
				<circle cx="${p.cx}" cy="${p.cy}" r="8" fill="${p.color}" stroke="${p.stroke}" stroke-width="2"/>
				<text x="${p.cx}" y="${p.cy + 30}" text-anchor="middle" fill="#94a3b8" font-size="12" font-weight="600">${escHtml(p.name)}</text>
				<text x="${p.cx}" y="${p.cy + 44}" text-anchor="middle" fill="#64748b" font-size="10">${p.dates}</text>
			</g>
		`).join("");
	}

	if (pins.length >= 3) {
		const routeEl = $("rd-route-path");
		if (routeEl) {
			routeEl.setAttribute("d",
				`M${pins[0].cx} ${pins[0].cy} C${pins[0].cx + 90} ${pins[0].cy - 20}, ${pins[1].cx - 80} ${pins[1].cy + 50}, ${pins[1].cx} ${pins[1].cy} C${pins[1].cx + 80} ${pins[1].cy - 50}, ${pins[2].cx - 90} ${pins[2].cy + 20}, ${pins[2].cx} ${pins[2].cy}`
			);
		}
	}
}

// ── Notes card (packing checklist) ──

function renderNotes(packingList: DemoShape | undefined) {
	const container = $("rd-packing-list");
	if (!container || !packingList) return;

	const items = (packingList.items as Array<{ name: string; packed: boolean; category: string }>) || [];
	if (items.length === 0) return;

	container.innerHTML = `<ul style="list-style:none;margin:0;padding:0;">
		${items.map((item, idx) => `
			<li class="rd-trips-pack-item" data-pack-idx="${idx}">
				<div class="rd-trips-pack-check ${item.packed ? "rd-trips-pack-check--checked" : ""}">
					${item.packed ? `<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>` : ""}
				</div>
				<span style="${item.packed ? "color:#64748b;text-decoration:line-through;" : "color:#e2e8f0;"}">${escHtml(item.name)}</span>
			</li>
		`).join("")}
	</ul>`;
}

// ── Calendar card ──

function renderCalendar(itinerary: DemoShape | undefined) {
	const grid = $("rd-cal-grid");
	if (!grid) return;

	const items = (itinerary?.items ?? []) as Array<{ date: string; activity: string; category: string }>;

	// Build calendar events map
	const calEvents: Record<number, { label: string; color: string }[]> = {};
	for (const item of items) {
		if (!item.date) continue;
		const match = item.date.match(/(\d+)/);
		if (!match) continue;
		const day = parseInt(match[1], 10);
		if (!calEvents[day]) calEvents[day] = [];
		calEvents[day].push({
			label: item.activity,
			color: CATEGORY_COLORS[item.category] || "#64748b",
		});
	}

	const eventDays = Object.keys(calEvents).map(Number);
	const tripStart = eventDays.length > 0 ? Math.min(...eventDays) : 6;
	const tripEnd = eventDays.length > 0 ? Math.max(...eventDays) : 20;

	const daysLabel = $("rd-cal-days");
	if (daysLabel) daysLabel.textContent = `${tripEnd - tripStart + 1} days`;

	const dayNames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
	const offset = 2; // July 1 2026 = Wednesday (0-indexed Mon grid)
	const daysInJuly = 31;

	let html = `<div class="rd-cal-grid">`;

	// Day name headers
	for (const d of dayNames) {
		html += `<div class="rd-cal-day-name">${d}</div>`;
	}

	// Empty cells for offset
	for (let i = 0; i < offset; i++) {
		html += `<div class="rd-cal-cell" style="min-height:3.5rem;"></div>`;
	}

	// Day cells
	for (let day = 1; day <= daysInJuly; day++) {
		const isTrip = day >= tripStart && day <= tripEnd;
		const events = calEvents[day];
		html += `<div class="rd-cal-cell ${isTrip ? "rd-cal-cell--trip" : "rd-cal-cell--empty"}">
			<span class="rd-cal-cell-num ${isTrip ? "rd-cal-cell-num--trip" : "rd-cal-cell-num--off"}">${day}</span>`;
		if (events) {
			for (const ev of events) {
				html += `<span class="rd-cal-event" style="background:${ev.color};">${escHtml(ev.label)}</span>`;
			}
		}
		html += `</div>`;
	}

	html += `</div>`;
	grid.innerHTML = html;
}

// ── Polls card ──

function renderPolls(pollShapes: DemoShape[]) {
	const body = $("rd-polls-body");
	if (!body) return;

	if (pollShapes.length === 0) return;

	let html = "";
	for (const shape of pollShapes) {
		const question = shape.question as string;
		const options = (shape.options ?? []) as Array<{ label: string; votes: number }>;
		const totalVotes = options.reduce((s, o) => s + o.votes, 0);

		html += `<div style="margin-bottom:1.25rem;">
			<h4 style="font-size:0.8125rem;font-weight:500;color:#e2e8f0;margin:0 0 0.5rem;">${escHtml(question)}</h4>`;

		for (let i = 0; i < options.length; i++) {
			const opt = options[i];
			const pct = totalVotes > 0 ? Math.round((opt.votes / totalVotes) * 100) : 0;
			const color = POLL_COLORS[i % POLL_COLORS.length];
			html += `<div style="margin-bottom:0.5rem;">
				<div style="display:flex;align-items:center;justify-content:space-between;font-size:0.75rem;margin-bottom:0.25rem;">
					<span style="color:#cbd5e1;">${escHtml(opt.label)}</span>
					<span style="color:#94a3b8;">${opt.votes} vote${opt.votes !== 1 ? "s" : ""} (${pct}%)</span>
				</div>
				<div class="rd-trips-poll-bar-bg">
					<div class="rd-trips-poll-bar" style="width:${pct}%;background:${color};"></div>
				</div>
			</div>`;
		}

		html += `<p style="font-size:0.6875rem;color:#64748b;margin:0.25rem 0 0;">${totalVotes} votes cast</p></div>`;
	}

	body.innerHTML = html;
}

// ── Funds card ──

function renderFunds(expenseShapes: DemoShape[], members: { name: string; color: string }[]) {
	const totalEl = $("rd-funds-total");
	const skeleton = $("rd-funds-skeleton");
	const expensesEl = $("rd-funds-expenses");
	const balancesEl = $("rd-funds-balances");

	if (!totalEl || !expensesEl || !balancesEl) return;
	if (expenseShapes.length === 0) return;

	const expenses = expenseShapes.map((s) => ({
		desc: s.description as string,
		who: s.paidBy as string,
		amount: s.amount as number,
		split: members.length || 4,
	}));

	const totalSpent = expenses.reduce((s, e) => s + e.amount, 0);
	totalEl.textContent = `€${totalSpent.toLocaleString()}`;

	if (skeleton) skeleton.style.display = "none";
	expensesEl.style.display = "block";
	balancesEl.style.display = "block";

	// Recent expenses
	let expHtml = `<h4 class="rd-trips-sub-heading" style="margin-top:0.75rem;">Recent</h4>`;
	for (const e of expenses.slice(0, 4)) {
		expHtml += `<div style="display:flex;align-items:center;justify-content:space-between;font-size:0.8125rem;margin-bottom:0.5rem;">
			<div style="min-width:0;">
				<p style="color:#e2e8f0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escHtml(e.desc)}</p>
				<p style="font-size:0.6875rem;color:#64748b;margin:0;">${escHtml(e.who)} · split ${e.split} ways</p>
			</div>
			<span style="color:#cbd5e1;font-weight:500;margin-left:0.5rem;flex-shrink:0;">€${e.amount}</span>
		</div>`;
	}
	expensesEl.innerHTML = expHtml;

	// Balances
	const balances: Record<string, number> = {};
	for (const m of members) balances[m.name] = 0;
	for (const e of expenses) {
		const share = e.amount / e.split;
		balances[e.who] = (balances[e.who] || 0) + e.amount;
		for (const m of members.slice(0, e.split)) {
			balances[m.name] = (balances[m.name] || 0) - share;
		}
	}

	let balHtml = `<h4 class="rd-trips-sub-heading" style="margin-top:0.75rem;">Balances</h4>
		<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.375rem;">`;
	for (const m of members) {
		const bal = balances[m.name] || 0;
		balHtml += `<div style="display:flex;align-items:center;justify-content:space-between;font-size:0.75rem;">
			<span style="color:#cbd5e1;">${escHtml(m.name)}</span>
			<span style="color:${bal >= 0 ? "#34d399" : "#fb7185"};">${bal >= 0 ? "+" : ""}€${Math.round(bal)}</span>
		</div>`;
	}
	balHtml += `</div>`;
	balancesEl.innerHTML = balHtml;
}

// ── Cart card ──

function renderCart(cartShapes: DemoShape[]) {
	const skeleton = $("rd-cart-skeleton");
	const content = $("rd-cart-content");
	if (!content) return;

	if (cartShapes.length === 0) return;

	if (skeleton) skeleton.style.display = "none";
	content.style.display = "block";

	const items = cartShapes.map((s) => ({
		name: s.name as string,
		target: s.price as number,
		funded: s.funded as number,
		status: ((s.funded as number) >= (s.price as number)) ? "Purchased" : "Funding",
	}));

	const totalFunded = items.reduce((s, i) => s + i.funded, 0);
	const totalTarget = items.reduce((s, i) => s + i.target, 0);
	const purchased = items.filter((i) => i.status === "Purchased").length;
	const overallPct = totalTarget > 0 ? Math.round((totalFunded / totalTarget) * 100) : 0;

	let html = `
		<div style="display:flex;align-items:center;justify-content:space-between;font-size:0.8125rem;margin-bottom:0.25rem;">
			<span style="color:#cbd5e1;">€${totalFunded} / €${totalTarget} funded</span>
			<span style="font-size:0.75rem;color:#94a3b8;">${purchased}/${items.length} purchased</span>
		</div>
		<div style="height:0.5rem;background:rgba(51,65,85,0.7);border-radius:9999px;overflow:hidden;margin-bottom:1rem;">
			<div style="height:100%;width:${overallPct}%;background:#14b8a6;border-radius:9999px;transition:width 0.3s;"></div>
		</div>
		<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;">`;

	for (const item of items) {
		const pct = item.target > 0 ? Math.round((item.funded / item.target) * 100) : 0;
		html += `<div class="rd-trips-cart-item">
			<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.375rem;">
				<span style="font-size:0.8125rem;color:#e2e8f0;">${escHtml(item.name)}</span>
				${item.status === "Purchased"
					? `<span style="font-size:0.6875rem;padding:0.125rem 0.5rem;background:rgba(16,185,129,0.2);color:#34d399;border-radius:9999px;">✓ Bought</span>`
					: `<span style="font-size:0.6875rem;color:#94a3b8;">€${item.funded}/€${item.target}</span>`
				}
			</div>
			<div class="rd-trips-cart-bar-bg">
				<div class="rd-trips-cart-bar" style="width:${pct}%;background:${pct === 100 ? "#10b981" : "#14b8a6"};"></div>
			</div>
		</div>`;
	}

	html += `</div>`;
	content.innerHTML = html;
}

// ── Event listeners ──

sync.addEventListener("snapshot", ((e: CustomEvent) => {
	render(e.detail.shapes);
}) as EventListener);

sync.addEventListener("connected", () => {
	const dot = $("rd-hero-dot");
	const label = $("rd-hero-label");
	if (dot) dot.style.background = "#10b981";
	if (label) label.textContent = "Live — Connected to rSpace";
	const resetBtn = $("rd-reset-btn") as HTMLButtonElement | null;
	if (resetBtn) resetBtn.disabled = false;
});

sync.addEventListener("disconnected", () => {
	const dot = $("rd-hero-dot");
	const label = $("rd-hero-label");
	if (dot) dot.style.background = "#64748b";
	if (label) label.textContent = "Reconnecting...";
	const resetBtn = $("rd-reset-btn") as HTMLButtonElement | null;
	if (resetBtn) resetBtn.disabled = true;
});

// ── Event delegation ──

document.addEventListener("click", (e) => {
	const target = e.target as HTMLElement;

	// Packing checkbox toggle
	const packItem = target.closest<HTMLElement>("[data-pack-idx]");
	if (packItem) {
		const idx = parseInt(packItem.dataset.packIdx!, 10);
		const packingList = shapeByType(sync.shapes, "folk-packing-list");
		if (packingList) {
			const items = [...(packingList.items as Array<{ name: string; packed: boolean; category: string }>)];
			items[idx] = { ...items[idx], packed: !items[idx].packed };
			sync.updateShape(packingList.id, { items });
		}
		return;
	}

	// Reset button
	if (target.closest("#rd-reset-btn")) {
		sync.resetDemo().catch((err) => console.error("[Trips] Reset failed:", err));
	}
});

// ── Start ──

sync.connect();
