/**
 * rNotes demo — client-side WebSocket controller.
 *
 * Connects to rSpace via DemoSync, populates note cards,
 * packing list checkboxes, sidebar, and notebook header.
 */

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);
}

// ── Simple markdown renderer ──

function renderMarkdown(text: string): string {
	if (!text) return "";
	const lines = text.split("\n");
	const out: string[] = [];
	let inCodeBlock = false;
	let codeLang = "";
	let codeLines: string[] = [];
	let inList: "ul" | "ol" | null = null;

	function flushList() {
		if (inList) { out.push(inList === "ul" ? "</ul>" : "</ol>"); inList = null; }
	}

	function flushCode() {
		if (inCodeBlock) {
			const escaped = codeLines.join("\n").replace(/</g, "&lt;").replace(/>/g, "&gt;");
			out.push(`<div class="rd-md-codeblock">${codeLang ? `<div class="rd-md-codeblock-lang"><span>${codeLang}</span></div>` : ""}<pre>${escaped}</pre></div>`);
			inCodeBlock = false;
			codeLines = [];
			codeLang = "";
		}
	}

	for (const raw of lines) {
		const line = raw;

		// Code fence
		if (line.startsWith("```")) {
			if (inCodeBlock) { flushCode(); } else { flushList(); inCodeBlock = true; codeLang = line.slice(3).trim(); }
			continue;
		}
		if (inCodeBlock) { codeLines.push(line); continue; }

		// Blank line
		if (!line.trim()) { flushList(); continue; }

		// Headings
		if (line.startsWith("### ")) { flushList(); out.push(`<h3>${inlineFormat(line.slice(4))}</h3>`); continue; }
		if (line.startsWith("## ")) { flushList(); out.push(`<h3>${inlineFormat(line.slice(3))}</h3>`); continue; }
		if (line.startsWith("# ")) { flushList(); out.push(`<h3>${inlineFormat(line.slice(2))}</h3>`); continue; }
		if (line.startsWith("#### ")) { flushList(); out.push(`<h4>${inlineFormat(line.slice(5))}</h4>`); continue; }
		if (line.startsWith("##### ")) { flushList(); out.push(`<h5>${inlineFormat(line.slice(6))}</h5>`); continue; }

		// Blockquote
		if (line.startsWith("> ")) { flushList(); out.push(`<div class="rd-md-quote"><p>${inlineFormat(line.slice(2))}</p></div>`); continue; }

		// Unordered list
		const ulMatch = line.match(/^[-*]\s+(.+)/);
		if (ulMatch) {
			if (inList !== "ul") { flushList(); out.push("<ul>"); inList = "ul"; }
			out.push(`<li>${inlineFormat(ulMatch[1])}</li>`);
			continue;
		}

		// Ordered list
		const olMatch = line.match(/^(\d+)\.\s+(.+)/);
		if (olMatch) {
			if (inList !== "ol") { flushList(); out.push("<ol>"); inList = "ol"; }
			out.push(`<li><span class="rd-md-num">${olMatch[1]}.</span>${inlineFormat(olMatch[2])}</li>`);
			continue;
		}

		// Paragraph
		flushList();
		out.push(`<p>${inlineFormat(line)}</p>`);
	}

	flushCode();
	flushList();
	return out.join("\n");
}

function inlineFormat(text: string): string {
	return text
		.replace(/`([^`]+)`/g, "<code>$1</code>")
		.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
		.replace(/\*([^*]+)\*/g, "<em>$1</em>");
}

// ── Note card rendering ──

const TAG_COLORS: Record<string, string> = {
	planning: "rgba(245,158,11,0.15)",
	travel: "rgba(20,184,166,0.15)",
	food: "rgba(251,146,60,0.15)",
	gear: "rgba(168,85,247,0.15)",
	safety: "rgba(239,68,68,0.15)",
	accommodation: "rgba(59,130,246,0.15)",
};

function renderNoteCard(note: DemoShape, expanded: boolean): string {
	const title = (note.title as string) || "Untitled";
	const content = (note.content as string) || "";
	const tags = (note.tags as string[]) || [];
	const lastEdited = note.lastEdited as string;
	const synced = note.synced !== false;

	const preview = content.split("\n").slice(0, 3).join(" ").slice(0, 120);
	const previewText = preview.replace(/[#*>`\-]/g, "").trim();

	return `
<div class="rd-card rd-note-card ${expanded ? "rd-note-card--expanded" : ""}" data-note-id="${note.id}" style="cursor:pointer;">
  <div style="padding:1rem 1.25rem;">
    <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:0.5rem;">
      <h3 style="font-size:0.9375rem;font-weight:600;color:white;margin:0;">${escHtml(title)}</h3>
      ${synced ? `<span class="rd-synced-badge"><span style="width:6px;height:6px;border-radius:50%;background:#2dd4bf;"></span>synced</span>` : ""}
    </div>
    ${expanded
			? `<div class="rd-md" style="margin-top:0.75rem;">${renderMarkdown(content)}</div>`
			: `<p style="font-size:0.8125rem;color:#94a3b8;margin:0 0 0.75rem;line-height:1.5;">${escHtml(previewText)}${content.length > 120 ? "..." : ""}</p>`
		}
    <div style="display:flex;align-items:center;justify-content:space-between;margin-top:0.75rem;">
      <div style="display:flex;flex-wrap:wrap;gap:0.375rem;">
        ${tags.map((t) => `<span class="rd-note-tag" style="background:${TAG_COLORS[t] || "rgba(51,65,85,0.5)"}">${escHtml(t)}</span>`).join("")}
      </div>
      ${lastEdited ? `<span style="font-size:0.6875rem;color:#64748b;">${formatRelative(lastEdited)}</span>` : ""}
    </div>
  </div>
</div>`;
}

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

function formatRelative(iso: string): string {
	try {
		const d = new Date(iso);
		const now = Date.now();
		const diff = now - d.getTime();
		if (diff < 60_000) return "just now";
		if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`;
		if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`;
		return d.toLocaleDateString("en", { month: "short", day: "numeric" });
	} catch { return ""; }
}

// ── Packing list rendering ──

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

	// Group by category
	const groups: Record<string, typeof items> = {};
	for (const item of items) {
		const cat = item.category || "General";
		if (!groups[cat]) groups[cat] = [];
		groups[cat].push(item);
	}

	const checked = items.filter((i) => i.packed).length;
	const pct = Math.round((checked / items.length) * 100);

	let html = `
<div class="rd-card" style="overflow:hidden;">
  <div style="padding:0.75rem 1rem;border-bottom:1px solid rgba(51,65,85,0.5);display:flex;align-items:center;justify-content:space-between;">
    <span style="font-size:0.8125rem;font-weight:600;color:white;">Packing Checklist</span>
    <span style="font-size:0.75rem;color:#94a3b8;">${checked}/${items.length} packed (${pct}%)</span>
  </div>
  <div style="padding:0.75rem 1rem 0.25rem;">
    <div style="height:0.375rem;background:rgba(51,65,85,0.5);border-radius:9999px;overflow:hidden;margin-bottom:0.75rem;">
      <div style="height:100%;width:${pct}%;background:linear-gradient(90deg,#f59e0b,#fb923c);border-radius:9999px;transition:width 0.3s;"></div>
    </div>
  </div>`;

	for (const [cat, catItems] of Object.entries(groups)) {
		html += `<div style="padding:0 0.75rem 0.75rem;">
      <h4 style="font-size:0.6875rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.05em;margin:0.5rem 0 0.25rem;">${escHtml(cat)}</h4>`;
		for (let i = 0; i < catItems.length; i++) {
			const item = catItems[i];
			const globalIdx = items.indexOf(item);
			html += `
      <div class="rd-pack-item" data-pack-idx="${globalIdx}">
        <div class="rd-pack-check ${item.packed ? "rd-pack-check--checked" : ""}">
          ${item.packed ? `<svg width="12" height="12" 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="font-size:0.8125rem;${item.packed ? "color:#64748b;text-decoration:line-through;" : "color:#e2e8f0;"}">${escHtml(item.name)}</span>
      </div>`;
		}
		html += `</div>`;
	}

	html += `</div>`;
	return html;
}

// ── Avatars ──

const AVATAR_COLORS = ["#14b8a6", "#06b6d4", "#8b5cf6", "#f59e0b", "#f43f5e"];

function renderAvatars(members: string[]): string {
	if (!members.length) return "";
	return members.map((name, i) =>
		`<div class="rd-avatar" style="background:${AVATAR_COLORS[i % AVATAR_COLORS.length]}" title="${escHtml(name)}">${name[0]}</div>`
	).join("") + `<span style="font-size:0.75rem;color:#94a3b8;margin-left:0.25rem;">${members.length} collaborators</span>`;
}

// ── Main ──

const expandedNotes = new Set<string>();

const sync = new DemoSync({ filter: ["folk-notebook", "folk-note", "folk-packing-list"] });

function render(shapes: Record<string, DemoShape>) {
	const notebook = shapeByType(shapes, "folk-notebook");
	const notes = shapesByType(shapes, "folk-note").sort((a, b) => {
		const aTime = a.lastEdited ? new Date(a.lastEdited as string).getTime() : 0;
		const bTime = b.lastEdited ? new Date(b.lastEdited as string).getTime() : 0;
		return bTime - aTime;
	});
	const packingList = shapeByType(shapes, "folk-packing-list");

	// Hide loading skeleton
	const loading = $("rd-loading");
	if (loading) loading.style.display = "none";

	// Notebook header
	if (notebook) {
		const nbTitle = $("rd-nb-title");
		const nbCount = $("rd-nb-count");
		const nbDesc = $("rd-nb-desc");
		const sbTitle = $("rd-sb-nb-title");
		const sbCount = $("rd-sb-note-count");
		const sbNum = $("rd-sb-notes-num");

		if (nbTitle) nbTitle.textContent = (notebook.name as string) || "Trip Notebook";
		if (nbCount) nbCount.textContent = `${notes.length} notes`;
		if (nbDesc) nbDesc.textContent = (notebook.description as string) || "";
		if (sbTitle) sbTitle.textContent = (notebook.name as string) || "Trip Notebook";
		if (sbCount) sbCount.textContent = `${notes.length} note${notes.length !== 1 ? "s" : ""}`;
		if (sbNum) sbNum.textContent = String(notes.length);
	}

	// Notes count
	const notesCount = $("rd-notes-count");
	if (notesCount) notesCount.textContent = `${notes.length} note${notes.length !== 1 ? "s" : ""}`;

	// Notes container
	const container = $("rd-notes-container");
	const empty = $("rd-notes-empty");
	if (container) {
		if (notes.length === 0) {
			container.innerHTML = "";
			if (empty) empty.style.display = "block";
		} else {
			if (empty) empty.style.display = "none";
			container.innerHTML = notes.map((n) => renderNoteCard(n, expandedNotes.has(n.id))).join("");
		}
	}

	// Packing list
	const packSection = $("rd-packing-section");
	const packContainer = $("rd-packing-container");
	if (packingList && packSection && packContainer) {
		packSection.style.display = "block";
		packContainer.innerHTML = renderPackingList(packingList);
	}

	// Avatars — extract from notebook members or note authors
	const members = (notebook?.members as string[]) || [];
	const avatarsEl = $("rd-avatars");
	if (avatarsEl && members.length > 0) {
		avatarsEl.innerHTML = renderAvatars(members);
	}
}

// ── 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;

	// Note card expand/collapse
	const noteCard = target.closest<HTMLElement>("[data-note-id]");
	if (noteCard) {
		const id = noteCard.dataset.noteId!;
		if (expandedNotes.has(id)) expandedNotes.delete(id);
		else expandedNotes.add(id);
		render(sync.shapes);
		return;
	}

	// 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("[Notes] Reset failed:", err));
	}
});

// ── Start ──

sync.connect();
