/**
 * folk-content-tree — Interactive content tree that indexes all
 * Automerge documents in a space, grouped by module and collection.
 *
 * Features: search, tag filter, module filter, sort, expand/collapse,
 * click-to-navigate, demo mode fallback.
 */

import { TourEngine } from '../../../shared/tour-engine';
import type { TourStep } from '../../../shared/tour-engine';
import { startPresenceHeartbeat } from '../../../shared/collab-presence';
import { rspaceNavUrl } from '../../../shared/url-helpers';

interface TreeItem {
	docId: string;
	title: string;
	itemCount: number | null;
	createdAt: number | null;
	tags: string[];
}

interface TreeCollection {
	collection: string;
	items: TreeItem[];
}

interface TreeModule {
	id: string;
	name: string;
	icon: string;
	collections: TreeCollection[];
}

interface TreeData {
	space: string;
	modules: TreeModule[];
}

type SortMode = "name" | "date" | "count";

const DEMO_DATA: TreeData = {
	space: "demo",
	modules: [
		{
			id: "notes", name: "rNotes", icon: "📝",
			collections: [
				{
					collection: "notebooks", items: [
						{ docId: "demo:notes:notebooks:nb1", title: "Product Roadmap", itemCount: 5, createdAt: 1710000000000, tags: ["dev", "planning"] },
						{ docId: "demo:notes:notebooks:nb2", title: "Personal Notes", itemCount: 7, createdAt: 1709500000000, tags: ["personal"] },
						{ docId: "demo:notes:notebooks:nb3", title: "Meeting Minutes", itemCount: 3, createdAt: 1709900000000, tags: ["meetings"] },
					],
				},
			],
		},
		{
			id: "vote", name: "rVote", icon: "🗳",
			collections: [
				{
					collection: "proposals", items: [
						{ docId: "demo:vote:proposals:p1", title: "Add dark mode", itemCount: null, createdAt: 1710100000000, tags: ["governance", "ux"] },
						{ docId: "demo:vote:proposals:p2", title: "Budget approval Q2", itemCount: null, createdAt: 1710200000000, tags: ["governance", "finance"] },
					],
				},
				{
					collection: "config", items: [
						{ docId: "demo:vote:config", title: "Space Config", itemCount: null, createdAt: 1709000000000, tags: [] },
					],
				},
			],
		},
		{
			id: "tasks", name: "rTasks", icon: "📋",
			collections: [
				{
					collection: "boards", items: [
						{ docId: "demo:tasks:boards:b1", title: "Development Board", itemCount: 8, createdAt: 1710050000000, tags: ["dev"] },
					],
				},
			],
		},
		{
			id: "cal", name: "rCal", icon: "📅",
			collections: [
				{
					collection: "calendars", items: [
						{ docId: "demo:cal:calendars:c1", title: "Team Calendar", itemCount: 12, createdAt: 1709800000000, tags: ["team"] },
						{ docId: "demo:cal:calendars:c2", title: "Personal", itemCount: 4, createdAt: 1709700000000, tags: ["personal"] },
					],
				},
			],
		},
		{
			id: "wallet", name: "rWallet", icon: "💰",
			collections: [
				{
					collection: "ledgers", items: [
						{ docId: "demo:wallet:ledgers:l1", title: "cUSDC Ledger", itemCount: 3, createdAt: 1710300000000, tags: ["tokens", "finance"] },
					],
				},
			],
		},
		{
			id: "flows", name: "rFlows", icon: "🌊",
			collections: [
				{
					collection: "streams", items: [
						{ docId: "demo:flows:streams:s1", title: "Contributor Payments", itemCount: null, createdAt: 1710150000000, tags: ["finance", "governance"] },
						{ docId: "demo:flows:streams:s2", title: "Community Fund", itemCount: null, createdAt: 1710250000000, tags: ["finance"] },
					],
				},
			],
		},
	],
};

/** Icons for well-known collection names */
const COLLECTION_ICONS: Record<string, string> = {
	notebooks: "📓", notes: "📄", proposals: "📜", config: "⚙️",
	boards: "📋", tasks: "✅", calendars: "📅", events: "🗓",
	ledgers: "💳", streams: "🌊", files: "📁", threads: "💬",
	contacts: "📇", companies: "🏢", trips: "✈️", photos: "📸",
	pages: "📃", books: "📚", items: "🏷", channels: "📺",
};

const CONTENT_TREE_TOUR_STEPS: TourStep[] = [
	{ target: '.ct-search', title: 'Search Content', message: 'Find items across all modules by name or tag.' },
	{ target: '.ct-tags', title: 'Filter', message: 'Narrow results by module or tag.' },
	{ target: '.ct-tree', title: 'Content Tree', message: 'Browse all space data hierarchically — expand modules and collections.' },
];

class FolkContentTree extends HTMLElement {
	private shadow: ShadowRoot;
	private space = "demo";
	private data: TreeData | null = null;
	private _tour!: TourEngine;
	private search = "";
	private activeTags = new Set<string>();
	private activeModules = new Set<string>();
	private sortMode: SortMode = "name";
	private expanded = new Set<string>();
	private allTags: string[] = [];
	private loading = true;
	private _stopPresence: (() => void) | null = null;

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: "open" });
		this._tour = new TourEngine(
			this.shadow,
			CONTENT_TREE_TOUR_STEPS,
			'rdata_tour_done',
			() => this.shadow.querySelector('.ct') as HTMLElement,
		);
	}

	connectedCallback() {
		this.space = this.getAttribute("space") || "demo";
		this.loadData();
		this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rdata', context: 'Data' }));
	}

	disconnectedCallback() {
		this._stopPresence?.();
	}

	private async loadData() {
		this.loading = true;
		this.render();

		if (this.space === "demo") {
			this.data = DEMO_DATA;
			this.initFromData();
			return;
		}

		try {
			const base = window.location.pathname.replace(/\/(tree|analytics)?\/?$/, "");
			const resp = await fetch(`${base}/api/content-tree?space=${encodeURIComponent(this.space)}`);
			if (resp.ok) {
				this.data = await resp.json();
			} else {
				this.data = { space: this.space, modules: [] };
			}
		} catch {
			this.data = { space: this.space, modules: [] };
		}
		this.initFromData();
	}

	private initFromData() {
		this.loading = false;
		// Collect all unique tags
		const tagSet = new Set<string>();
		for (const mod of this.data!.modules) {
			for (const col of mod.collections) {
				for (const item of col.items) {
					for (const tag of item.tags) tagSet.add(tag);
				}
			}
		}
		this.allTags = Array.from(tagSet).sort();
		// Expand all modules by default
		for (const mod of this.data!.modules) {
			this.expanded.add(`mod:${mod.id}`);
		}
		this.render();
		if (!localStorage.getItem('rdata_tour_done')) {
			setTimeout(() => this._tour.start(), 800);
		}
	}

	startTour() { this._tour.start(); }

	private matchesSearch(text: string): boolean {
		if (!this.search) return true;
		return text.toLowerCase().includes(this.search.toLowerCase());
	}

	private matchesTags(tags: string[]): boolean {
		if (this.activeTags.size === 0) return true;
		return tags.some((t) => this.activeTags.has(t));
	}

	private matchesModuleFilter(modId: string): boolean {
		if (this.activeModules.size === 0) return true;
		return this.activeModules.has(modId);
	}

	/** Check if an item passes all filters */
	private itemVisible(item: TreeItem, modId: string): boolean {
		if (!this.matchesModuleFilter(modId)) return false;
		if (!this.matchesTags(item.tags)) return false;
		if (!this.matchesSearch(item.title) && !item.tags.some((t) => this.matchesSearch(t))) return false;
		return true;
	}

	/** Get sorted modules */
	private getSortedModules(): TreeModule[] {
		if (!this.data) return [];
		const mods = [...this.data.modules];
		switch (this.sortMode) {
			case "name":
				mods.sort((a, b) => a.name.localeCompare(b.name));
				break;
			case "date": {
				const newest = (m: TreeModule) => {
					let max = 0;
					for (const c of m.collections) for (const i of c.items) if (i.createdAt && i.createdAt > max) max = i.createdAt;
					return max;
				};
				mods.sort((a, b) => newest(b) - newest(a));
				break;
			}
			case "count": {
				const total = (m: TreeModule) => m.collections.reduce((s, c) => s + c.items.length, 0);
				mods.sort((a, b) => total(b) - total(a));
				break;
			}
		}
		return mods;
	}

	/** Sort items within a collection */
	private getSortedItems(items: TreeItem[]): TreeItem[] {
		const sorted = [...items];
		switch (this.sortMode) {
			case "name":
				sorted.sort((a, b) => a.title.localeCompare(b.title));
				break;
			case "date":
				sorted.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
				break;
			case "count":
				sorted.sort((a, b) => (b.itemCount || 0) - (a.itemCount || 0));
				break;
		}
		return sorted;
	}

	private toggle(key: string) {
		if (this.expanded.has(key)) this.expanded.delete(key);
		else this.expanded.add(key);
		this.render();
	}

	private navigate(modId: string) {
		const modPath = modId.startsWith("r") ? modId : `r${modId}`;
		window.open(rspaceNavUrl(this.space, modPath), "_blank");
	}

	private render() {
		const modules = this.getSortedModules();

		// Count total visible items
		let totalItems = 0;
		let totalModules = 0;
		for (const mod of modules) {
			let modHasVisible = false;
			for (const col of mod.collections) {
				for (const item of col.items) {
					if (this.itemVisible(item, mod.id)) {
						totalItems++;
						modHasVisible = true;
					}
				}
			}
			if (modHasVisible) totalModules++;
		}

		this.shadow.innerHTML = `
			<style>${this.styles()}</style>
			<div class="ct">
				${this.loading ? `<div class="ct-loading">Loading content tree...</div>` : `
				<div class="ct-toolbar">
					<div class="ct-search-row">
						<input type="text" class="ct-search" placeholder="Search content..." value="${this.escAttr(this.search)}" />
						<select class="ct-sort">
							<option value="name"${this.sortMode === "name" ? " selected" : ""}>Sort: Name</option>
							<option value="date"${this.sortMode === "date" ? " selected" : ""}>Sort: Newest</option>
							<option value="count"${this.sortMode === "count" ? " selected" : ""}>Sort: Most items</option>
						</select>
					</div>
					${this.allTags.length > 0 ? `
					<div class="ct-tags">
						${this.allTags.map((tag) => `
							<button class="ct-tag${this.activeTags.has(tag) ? " ct-tag--active" : ""}" data-tag="${this.escAttr(tag)}">${this.esc(tag)}</button>
						`).join("")}
						${this.activeTags.size > 0 ? `<button class="ct-tag ct-tag--clear">Clear filters</button>` : ""}
					</div>` : ""}
					<div class="ct-summary">${totalModules} module${totalModules !== 1 ? "s" : ""}, ${totalItems} item${totalItems !== 1 ? "s" : ""} <button style="margin-left:8px;padding:2px 8px;border-radius:6px;border:1px solid var(--rs-border);background:var(--rs-bg-surface);color:var(--rs-text-secondary);cursor:pointer;font-size:0.72rem;" id="btn-tour">Tour</button></div>
				</div>
				<div class="ct-tree">
					${totalItems === 0 ? `<div class="ct-empty">No content found${this.search || this.activeTags.size ? " matching your filters" : " in this space"}.</div>` : ""}
					${modules.map((mod) => this.renderModule(mod)).join("")}
				</div>
				`}
			</div>
		`;

		this._tour.renderOverlay();
		this.attachEvents();
	}

	private renderModule(mod: TreeModule): string {
		// Check if any item in this module is visible
		let visibleCount = 0;
		for (const col of mod.collections) {
			for (const item of col.items) {
				if (this.itemVisible(item, mod.id)) visibleCount++;
			}
		}
		if (visibleCount === 0) return "";

		const key = `mod:${mod.id}`;
		const isExp = this.expanded.has(key);
		const totalItems = mod.collections.reduce((s, c) => s + c.items.length, 0);

		return `
			<div class="ct-node ct-node--module">
				<div class="ct-node__row" data-toggle="${key}">
					<span class="ct-node__toggle">${isExp ? "▾" : "▸"}</span>
					<span class="ct-node__icon">${mod.icon}</span>
					<span class="ct-node__label">${this.esc(mod.name)}</span>
					<span class="ct-node__badge">${totalItems}</span>
				</div>
				${isExp ? `<div class="ct-node__children">
					${mod.collections.map((col) => this.renderCollection(col, mod.id)).join("")}
				</div>` : ""}
			</div>
		`;
	}

	private renderCollection(col: TreeCollection, modId: string): string {
		const visibleItems = this.getSortedItems(col.items).filter((it) => this.itemVisible(it, modId));
		if (visibleItems.length === 0) return "";

		const key = `col:${modId}:${col.collection}`;
		const isExp = this.expanded.has(key);
		const icon = COLLECTION_ICONS[col.collection] || "📂";

		// If only one item and collection name matches, flatten
		if (visibleItems.length === 1 && !isExp) {
			// Show collection as expandable still
		}

		return `
			<div class="ct-node ct-node--collection">
				<div class="ct-node__row" data-toggle="${key}">
					<span class="ct-node__toggle">${isExp ? "▾" : "▸"}</span>
					<span class="ct-node__icon">${icon}</span>
					<span class="ct-node__label">${this.esc(col.collection)}</span>
					<span class="ct-node__badge">${visibleItems.length}</span>
				</div>
				${isExp ? `<div class="ct-node__children">
					${visibleItems.map((item) => this.renderItem(item, modId)).join("")}
				</div>` : ""}
			</div>
		`;
	}

	private renderItem(item: TreeItem, modId: string): string {
		const dateStr = item.createdAt ? new Date(item.createdAt).toLocaleDateString() : "";
		return `
			<div class="ct-node ct-node--item">
				<div class="ct-node__row ct-node__row--leaf" data-nav="${modId}">
					<span class="ct-node__toggle"></span>
					<span class="ct-node__icon">📄</span>
					<span class="ct-node__label">${this.esc(item.title)}</span>
					${item.itemCount !== null ? `<span class="ct-node__badge">${item.itemCount}</span>` : ""}
					${item.tags.length > 0 ? `<span class="ct-node__tags">${item.tags.map((t) =>
						`<span class="ct-node__tag">${this.esc(t)}</span>`
					).join("")}</span>` : ""}
					${dateStr ? `<span class="ct-node__date">${dateStr}</span>` : ""}
				</div>
			</div>
		`;
	}

	private attachEvents() {
		// Search
		const searchInput = this.shadow.querySelector<HTMLInputElement>(".ct-search");
		if (searchInput) {
			searchInput.addEventListener("input", () => {
				this.search = searchInput.value;
				this.render();
				// Re-focus and restore cursor
				const newInput = this.shadow.querySelector<HTMLInputElement>(".ct-search");
				if (newInput) {
					newInput.focus();
					newInput.setSelectionRange(newInput.value.length, newInput.value.length);
				}
			});
		}

		// Sort
		const sortSelect = this.shadow.querySelector<HTMLSelectElement>(".ct-sort");
		if (sortSelect) {
			sortSelect.addEventListener("change", () => {
				this.sortMode = sortSelect.value as SortMode;
				this.render();
			});
		}

		// Tag chips
		for (const btn of this.shadow.querySelectorAll<HTMLButtonElement>(".ct-tag[data-tag]")) {
			btn.addEventListener("click", () => {
				const tag = btn.dataset.tag!;
				if (this.activeTags.has(tag)) this.activeTags.delete(tag);
				else this.activeTags.add(tag);
				this.render();
			});
		}
		const clearBtn = this.shadow.querySelector<HTMLButtonElement>(".ct-tag--clear");
		if (clearBtn) {
			clearBtn.addEventListener("click", () => {
				this.activeTags.clear();
				this.render();
			});
		}

		// Toggle expand/collapse
		for (const row of this.shadow.querySelectorAll<HTMLElement>("[data-toggle]")) {
			row.addEventListener("click", (e) => {
				// Don't toggle if clicking a nav link
				if ((e.target as HTMLElement).closest("[data-nav]") && !((e.target as HTMLElement).closest("[data-toggle]") as HTMLElement)?.dataset?.toggle) return;
				this.toggle(row.dataset.toggle!);
			});
		}

		// Tour button
		this.shadow.getElementById("btn-tour")?.addEventListener("click", () => this.startTour());

		// Navigate on leaf click
		for (const row of this.shadow.querySelectorAll<HTMLElement>(".ct-node__row--leaf[data-nav]")) {
			row.addEventListener("click", () => {
				this.navigate(row.dataset.nav!);
			});
		}
	}

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

	private escAttr(s: string): string {
		return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
	}

	private styles(): string {
		return `
			:host { display: block; min-height: 60vh; font-family: system-ui, sans-serif; color: var(--rs-text-primary); }

			.ct { max-width: 900px; margin: 0 auto; }

			.ct-loading {
				text-align: center; padding: 3rem 1rem;
				color: var(--rs-text-muted); font-size: 0.9rem;
			}

			/* Toolbar */
			.ct-toolbar {
				position: sticky; top: 0; z-index: 10;
				background: var(--rs-bg-primary, #0f172a);
				padding: 0.75rem 0; margin-bottom: 0.5rem;
				border-bottom: 1px solid var(--rs-border);
			}
			.ct-search-row {
				display: flex; gap: 0.5rem; margin-bottom: 0.5rem;
			}
			.ct-search {
				flex: 1; padding: 0.5rem 0.75rem;
				background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
				border-radius: 8px; color: var(--rs-text-primary);
				font-size: 0.85rem; outline: none;
			}
			.ct-search:focus { border-color: #22d3ee; }
			.ct-search::placeholder { color: var(--rs-text-muted); }
			.ct-sort {
				padding: 0.5rem 0.75rem;
				background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
				border-radius: 8px; color: var(--rs-text-primary);
				font-size: 0.8rem; cursor: pointer; outline: none;
			}

			/* Tags */
			.ct-tags {
				display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.5rem;
				overflow-x: auto; scrollbar-width: none;
			}
			.ct-tags::-webkit-scrollbar { display: none; }
			.ct-tag {
				padding: 0.2rem 0.6rem; border-radius: 12px;
				font-size: 0.75rem; cursor: pointer;
				background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
				color: var(--rs-text-secondary); transition: all 0.15s;
			}
			.ct-tag:hover { border-color: #22d3ee; color: var(--rs-text-primary); }
			.ct-tag--active {
				background: rgba(34, 211, 238, 0.15); border-color: #22d3ee;
				color: #22d3ee;
			}
			.ct-tag--clear {
				background: transparent; border-color: var(--rs-border);
				color: var(--rs-text-muted); font-style: italic;
			}

			.ct-summary {
				font-size: 0.75rem; color: var(--rs-text-muted);
			}

			/* Tree */
			.ct-tree { padding-bottom: 2rem; }

			.ct-empty {
				text-align: center; padding: 3rem 1rem;
				color: var(--rs-text-muted); font-size: 0.9rem;
			}

			/* Node */
			.ct-node { margin: 0; }
			.ct-node__row {
				display: flex; align-items: center; gap: 0.4rem;
				padding: 0.4rem 0.5rem; border-radius: 6px;
				cursor: pointer; user-select: none; transition: background 0.1s;
			}
			.ct-node__row:hover { background: var(--rs-bg-surface); }
			.ct-node__toggle {
				width: 1rem; text-align: center; font-size: 0.7rem;
				color: var(--rs-text-muted); flex-shrink: 0;
			}
			.ct-node__icon { font-size: 1rem; flex-shrink: 0; }
			.ct-node__label {
				flex: 1; font-size: 0.85rem; min-width: 0;
				overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
			}
			.ct-node__badge {
				padding: 0.1rem 0.45rem; border-radius: 10px;
				font-size: 0.7rem; background: var(--rs-bg-surface);
				border: 1px solid var(--rs-border); color: var(--rs-text-muted);
				flex-shrink: 0;
			}
			.ct-node__tags { display: flex; gap: 0.25rem; flex-shrink: 0; }
			.ct-node__tag {
				padding: 0.1rem 0.4rem; border-radius: 8px;
				font-size: 0.65rem; background: rgba(34, 211, 238, 0.1);
				color: #22d3ee;
			}
			.ct-node__date {
				font-size: 0.7rem; color: var(--rs-text-muted); flex-shrink: 0;
			}

			/* Nesting */
			.ct-node--module { }
			.ct-node--module > .ct-node__row .ct-node__label { font-weight: 600; font-size: 0.9rem; }
			.ct-node--collection { padding-left: 1.25rem; }
			.ct-node--collection > .ct-node__row .ct-node__label { color: var(--rs-text-secondary); }
			.ct-node--item { padding-left: 2.5rem; }
			.ct-node__row--leaf { cursor: pointer; }
			.ct-node__row--leaf:hover { background: rgba(34, 211, 238, 0.08); }

			.ct-node__children { }

			/* Responsive */
			@media (max-width: 600px) {
				.ct-search-row { flex-direction: column; }
				.ct-node--collection { padding-left: 0.75rem; }
				.ct-node--item { padding-left: 1.5rem; }
				.ct-node__date { display: none; }
				.ct-node__tags { display: none; }
			}
		`;
	}
}

customElements.define("folk-content-tree", FolkContentTree);
