import { FolkShape } from "./folk-shape";
import { css, html } from "./tags";
import type { ShapeData, SpaceRef, NestPermissions } from "./community-sync";

const styles = css`
	:host {
		background: var(--rs-bg-surface, #1e293b);
		color: var(--rs-text-primary, #e2e8f0);
		border-radius: 8px;
		box-shadow: var(--rs-shadow-md, 0 2px 12px rgba(0, 0, 0, 0.08));
		min-width: 300px;
		min-height: 200px;
		overflow: hidden;
	}

	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 6px 12px;
		background: var(--rs-bg-surface-raised, #334155);
		color: var(--rs-text-primary, #e2e8f0);
		border-radius: 8px 8px 0 0;
		font-size: 12px;
		font-weight: 600;
		cursor: move;
		gap: 8px;
	}

	.header-left {
		display: flex;
		align-items: center;
		gap: 6px;
		min-width: 0;
	}

	.header-left .icon {
		flex-shrink: 0;
	}

	.source-name {
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.header-right {
		display: flex;
		align-items: center;
		gap: 4px;
		flex-shrink: 0;
	}

	.badge {
		font-size: 10px;
		padding: 2px 6px;
		border-radius: 9999px;
		font-weight: 500;
		white-space: nowrap;
	}

	.badge-read {
		background: rgba(59, 130, 246, 0.2);
		color: #93c5fd;
	}

	.badge-write {
		background: rgba(34, 197, 94, 0.2);
		color: #86efac;
	}

	.header-actions button {
		background: transparent;
		border: none;
		color: white;
		cursor: pointer;
		padding: 2px 6px;
		border-radius: 4px;
		font-size: 14px;
		line-height: 1;
	}

	.header-actions button:hover {
		background: rgba(255, 255, 255, 0.2);
	}

	.content {
		width: 100%;
		height: calc(100% - 32px);
		position: relative;
		overflow: auto;
	}

	.nested-canvas {
		position: relative;
		width: 100%;
		height: 100%;
		min-height: 150px;
	}

	.nested-shape {
		position: absolute;
		background: var(--rs-bg-surface-raised, #334155);
		border: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		border-radius: 6px;
		padding: 8px;
		font-size: 12px;
		overflow: hidden;
		box-shadow: var(--rs-shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.06));
	}

	.nested-shape .shape-type {
		font-size: 10px;
		color: var(--rs-text-secondary, #94a3b8);
		margin-bottom: 4px;
	}

	.nested-shape .shape-content {
		color: var(--rs-text-primary, #e2e8f0);
		word-break: break-word;
	}

	.status-bar {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 4px 12px;
		background: var(--rs-bg-surface-sunken, #0f172a);
		border-top: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		font-size: 10px;
		color: var(--rs-text-muted, #64748b);
	}

	.status-indicator {
		display: inline-block;
		width: 6px;
		height: 6px;
		border-radius: 50%;
		margin-right: 4px;
	}

	.status-connected { background: #22c55e; }
	.status-connecting { background: #eab308; }
	.status-disconnected { background: #ef4444; }

	.collapsed-view {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		height: calc(100% - 32px);
		padding: 16px;
		text-align: center;
		gap: 8px;
	}

	.collapsed-icon {
		font-size: 32px;
		opacity: 0.5;
	}

	.collapsed-label {
		font-size: 13px;
		color: var(--rs-text-primary, #e2e8f0);
		font-weight: 500;
	}

	.collapsed-meta {
		font-size: 11px;
		color: var(--rs-text-secondary, #94a3b8);
	}

	.enter-btn {
		margin-top: 8px;
		background: var(--rs-bg-surface-raised, #334155);
		color: var(--rs-text-primary, #e2e8f0);
		border: none;
		border-radius: 6px;
		padding: 6px 16px;
		cursor: pointer;
		font-size: 12px;
	}

	.enter-btn:hover {
		background: var(--rs-bg-surface-sunken, #1e293b);
	}
`;

declare global {
	interface HTMLElementTagNameMap {
		"folk-canvas": FolkCanvas;
	}
}

export class FolkCanvas extends FolkShape {
	static override tagName = "folk-canvas";

	static {
		const sheet = new CSSStyleSheet();
		const parentRules = Array.from(FolkShape.styles.cssRules)
			.map((r) => r.cssText)
			.join("\n");
		const childRules = Array.from(styles.cssRules)
			.map((r) => r.cssText)
			.join("\n");
		sheet.replaceSync(`${parentRules}\n${childRules}`);
		this.styles = sheet;
	}

	#sourceSlug: string = "";
	#parentSlug: string = "";    // slug of the space this shape lives in (for nest-from context)
	#permissions: NestPermissions = {
		read: true, write: false, addShapes: false, deleteShapes: false, reshare: false
	};
	#collapsed = false;
	#label: string | null = null;
	#sourceDID: string | null = null;

	// WebSocket connection to nested space
	#ws: WebSocket | null = null;
	#connectionStatus: "disconnected" | "connecting" | "connected" = "disconnected";
	#nestedShapes: Map<string, ShapeData> = new Map();
	#reconnectAttempts = 0;
	#maxReconnectAttempts = 5;

	// DOM refs
	#nestedCanvasEl: HTMLElement | null = null;
	#statusIndicator: HTMLElement | null = null;
	#statusText: HTMLElement | null = null;
	#shapeCountEl: HTMLElement | null = null;

	get sourceSlug() { return this.#sourceSlug; }
	set sourceSlug(value: string) {
		if (this.#sourceSlug === value) return;
		this.#sourceSlug = value;
		this.requestUpdate("sourceSlug");
		this.dispatchEvent(new CustomEvent("content-change", { detail: { sourceSlug: value } }));
		// Reconnect to new source
		this.#disconnect();
		if (value) this.#connectToSource();
	}

	get permissions(): NestPermissions { return this.#permissions; }
	set permissions(value: NestPermissions) {
		this.#permissions = value;
		this.requestUpdate("permissions");
	}

	get collapsed() { return this.#collapsed; }
	set collapsed(value: boolean) {
		this.#collapsed = value;
		this.requestUpdate("collapsed");
		this.#renderView();
	}

	get label() { return this.#label; }
	set label(value: string | null) {
		this.#label = value;
		this.requestUpdate("label");
	}

	get sourceDID() { return this.#sourceDID; }
	set sourceDID(value: string | null) { this.#sourceDID = value; }

	get parentSlug() { return this.#parentSlug; }
	set parentSlug(value: string) { this.#parentSlug = value; }

	override createRenderRoot() {
		const root = super.createRenderRoot();

		// Read initial attributes
		this.#sourceSlug = this.getAttribute("source-slug") || "";
		this.#label = this.getAttribute("label") || null;
		this.#collapsed = this.getAttribute("collapsed") === "true";

		const wrapper = document.createElement("div");
		wrapper.style.cssText = "width: 100%; height: 100%; display: flex; flex-direction: column;";
		wrapper.innerHTML = html`
			<div class="header" data-drag>
				<div class="header-left">
					<span class="icon">🖼</span>
					<span class="source-name"></span>
				</div>
				<div class="header-right">
					<span class="permission-badge badge"></span>
					<div class="header-actions">
						<button class="collapse-btn" title="Toggle collapse">▼</button>
						<button class="enter-space-btn" title="Open space">↗</button>
						<button class="close-btn" title="Remove">×</button>
					</div>
				</div>
			</div>
			<div class="content">
				<div class="nested-canvas"></div>
			</div>
			<div class="status-bar">
				<span><span class="status-indicator status-disconnected"></span><span class="status-text">Disconnected</span></span>
				<span class="shape-count">0 shapes</span>
			</div>
		`;

		// Replace the slot container
		const slot = root.querySelector("slot");
		const containerDiv = slot?.parentElement as HTMLElement;
		if (containerDiv) containerDiv.replaceWith(wrapper);

		// Cache DOM refs
		this.#nestedCanvasEl = wrapper.querySelector(".nested-canvas");
		this.#statusIndicator = wrapper.querySelector(".status-indicator");
		this.#statusText = wrapper.querySelector(".status-text");
		this.#shapeCountEl = wrapper.querySelector(".shape-count");

		const sourceNameEl = wrapper.querySelector(".source-name") as HTMLElement;
		const permBadge = wrapper.querySelector(".permission-badge") as HTMLElement;
		const collapseBtn = wrapper.querySelector(".collapse-btn") as HTMLButtonElement;
		const enterBtn = wrapper.querySelector(".enter-space-btn") as HTMLButtonElement;
		const closeBtn = wrapper.querySelector(".close-btn") as HTMLButtonElement;

		// Set header text
		sourceNameEl.textContent = this.#label || this.#sourceSlug || "Nested Space";

		// Permission badge
		if (this.#permissions.write) {
			permBadge.textContent = "read + write";
			permBadge.className = "badge badge-write";
		} else {
			permBadge.textContent = "read-only";
			permBadge.className = "badge badge-read";
		}

		// Collapse toggle
		collapseBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			this.collapsed = !this.#collapsed;
			collapseBtn.textContent = this.#collapsed ? "▶" : "▼";
			this.dispatchEvent(new CustomEvent("content-change", { detail: { collapsed: this.#collapsed } }));
		});

		// Enter space (navigate)
		enterBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			if (this.#sourceSlug) {
				window.open(`/${this.#sourceSlug}/rspace`, "_blank");
			}
		});

		// Close (remove nesting)
		closeBtn.addEventListener("click", (e) => {
			e.stopPropagation();
			this.dispatchEvent(new CustomEvent("close"));
		});

		// Prevent drag on interactive content
		const content = wrapper.querySelector(".content") as HTMLElement;
		content.addEventListener("pointerdown", (e) => e.stopPropagation());

		// Connect to the nested space
		if (this.#sourceSlug && !this.#collapsed) {
			this.#connectToSource();
		}

		return root;
	}

	#connectToSource(): void {
		if (!this.#sourceSlug || this.#connectionStatus === "connected" || this.#connectionStatus === "connecting") return;

		this.#setStatus("connecting");

		const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
		const nestParam = this.#parentSlug ? `&nest-from=${encodeURIComponent(this.#parentSlug)}` : "";
		let tokenParam = "";
		try {
			const sess = JSON.parse(localStorage.getItem('encryptid_session') || '');
			if (sess?.accessToken) tokenParam = `&token=${encodeURIComponent(sess.accessToken)}`;
		} catch {}
		const wsUrl = `${protocol}//${window.location.host}/ws/${this.#sourceSlug}?mode=json${nestParam}${tokenParam}`;

		this.#ws = new WebSocket(wsUrl);

		this.#ws.onopen = () => {
			this.#connectionStatus = "connected";
			this.#reconnectAttempts = 0;
			this.#setStatus("connected");
		};

		this.#ws.onmessage = (event) => {
			try {
				const msg = JSON.parse(event.data);
				if (msg.type === "snapshot" && msg.shapes) {
					this.#nestedShapes.clear();
					for (const [id, shape] of Object.entries(msg.shapes)) {
						const s = shape as ShapeData;
						if (!s.forgotten) {
							this.#nestedShapes.set(id, s);
						}
					}
					this.#renderNestedShapes();
				}
			} catch (e) {
				console.error("[FolkCanvas] Failed to handle message:", e);
			}
		};

		this.#ws.onclose = () => {
			this.#connectionStatus = "disconnected";
			this.#setStatus("disconnected");
			this.#attemptReconnect();
		};

		this.#ws.onerror = () => {
			this.#setStatus("disconnected");
		};
	}

	#attemptReconnect(): void {
		if (this.#reconnectAttempts >= this.#maxReconnectAttempts) return;
		this.#reconnectAttempts++;
		const delay = Math.min(1000 * Math.pow(2, this.#reconnectAttempts - 1), 16000);
		setTimeout(() => {
			if (this.#connectionStatus === "disconnected") {
				this.#connectToSource();
			}
		}, delay);
	}

	#disconnect(): void {
		if (this.#ws) {
			this.#ws.onclose = null; // prevent reconnect
			this.#ws.close();
			this.#ws = null;
		}
		this.#connectionStatus = "disconnected";
		this.#nestedShapes.clear();
		this.#setStatus("disconnected");
	}

	#setStatus(status: "disconnected" | "connecting" | "connected"): void {
		this.#connectionStatus = status;
		if (this.#statusIndicator) {
			this.#statusIndicator.className = `status-indicator status-${status}`;
		}
		if (this.#statusText) {
			this.#statusText.textContent = status.charAt(0).toUpperCase() + status.slice(1);
		}
	}

	#renderView(): void {
		if (!this.shadowRoot) return;
		const content = this.shadowRoot.querySelector(".content") as HTMLElement;
		const statusBar = this.shadowRoot.querySelector(".status-bar") as HTMLElement;
		if (!content || !statusBar) return;

		if (this.#collapsed) {
			content.innerHTML = `
				<div class="collapsed-view">
					<div class="collapsed-icon">🖼</div>
					<div class="collapsed-label">${this.#label || this.#sourceSlug}</div>
					<div class="collapsed-meta">${this.#nestedShapes.size} shapes</div>
					<button class="enter-btn">Open space →</button>
				</div>
			`;
			statusBar.style.display = "none";
			const enterBtn = content.querySelector(".enter-btn");
			enterBtn?.addEventListener("click", () => {
				if (this.#sourceSlug) window.open(`/${this.#sourceSlug}/rspace`, "_blank");
			});
			// Disconnect when collapsed
			this.#disconnect();
		} else {
			content.innerHTML = `<div class="nested-canvas"></div>`;
			this.#nestedCanvasEl = content.querySelector(".nested-canvas");
			statusBar.style.display = "flex";
			// Reconnect when expanded
			if (this.#sourceSlug) this.#connectToSource();
			this.#renderNestedShapes();
		}
	}

	#renderNestedShapes(): void {
		if (!this.#nestedCanvasEl) return;

		this.#nestedCanvasEl.innerHTML = "";

		if (this.#nestedShapes.size === 0) {
			this.#nestedCanvasEl.innerHTML = `
				<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--rs-text-secondary,#94a3b8);font-size:12px;">
					${this.#connectionStatus === "connected" ? "Empty space" : "Connecting..."}
				</div>
			`;
		}

		// Calculate bounding box of all shapes to fit within our viewport
		let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
		for (const shape of this.#nestedShapes.values()) {
			minX = Math.min(minX, shape.x);
			minY = Math.min(minY, shape.y);
			maxX = Math.max(maxX, shape.x + (shape.width || 300));
			maxY = Math.max(maxY, shape.y + (shape.height || 200));
		}

		const contentWidth = maxX - minX || 1;
		const contentHeight = maxY - minY || 1;
		const canvasWidth = this.#nestedCanvasEl.clientWidth || 600;
		const canvasHeight = this.#nestedCanvasEl.clientHeight || 400;
		const scale = Math.min(canvasWidth / contentWidth, canvasHeight / contentHeight, 1) * 0.9;
		const offsetX = (canvasWidth - contentWidth * scale) / 2;
		const offsetY = (canvasHeight - contentHeight * scale) / 2;

		for (const shape of this.#nestedShapes.values()) {
			const el = document.createElement("div");
			el.className = "nested-shape";
			el.style.left = `${offsetX + (shape.x - minX) * scale}px`;
			el.style.top = `${offsetY + (shape.y - minY) * scale}px`;
			el.style.width = `${(shape.width || 300) * scale}px`;
			el.style.height = `${(shape.height || 200) * scale}px`;

			const typeLabel = shape.type.replace("folk-", "").replace(/-/g, " ");
			const content = shape.content
				? shape.content.slice(0, 100) + (shape.content.length > 100 ? "..." : "")
				: "";

			el.innerHTML = `
				<div class="shape-type">${typeLabel}</div>
				<div class="shape-content">${content}</div>
			`;

			this.#nestedCanvasEl.appendChild(el);
		}

		// Update shape count
		if (this.#shapeCountEl) {
			this.#shapeCountEl.textContent = `${this.#nestedShapes.size} shape${this.#nestedShapes.size !== 1 ? "s" : ""}`;
		}
	}

	disconnectedCallback(): void {
		this.#disconnect();
	}

	static override fromData(data: Record<string, any>): FolkCanvas {
		const shape = FolkShape.fromData(data) as FolkCanvas;
		if (data.sourceSlug !== undefined) shape.sourceSlug = data.sourceSlug;
		if (data.sourceDID !== undefined) shape.sourceDID = data.sourceDID;
		if (data.permissions !== undefined) shape.permissions = data.permissions;
		if (typeof data.collapsed === "boolean") shape.collapsed = data.collapsed;
		if (data.label !== undefined) shape.label = data.label;
		return shape;
	}

	override applyData(data: Record<string, any>): void {
		super.applyData(data);
		if (data.sourceSlug !== undefined && data.sourceSlug !== this.sourceSlug) this.sourceSlug = data.sourceSlug;
		if (data.sourceDID !== undefined && data.sourceDID !== this.sourceDID) this.sourceDID = data.sourceDID;
		if (data.permissions !== undefined && JSON.stringify(data.permissions) !== JSON.stringify(this.permissions)) this.permissions = data.permissions;
		if (typeof data.collapsed === "boolean" && data.collapsed !== this.collapsed) this.collapsed = data.collapsed;
		if (data.label !== undefined && data.label !== this.label) this.label = data.label;
	}

	override toJSON() {
		return {
			...super.toJSON(),
			type: "folk-canvas",
			sourceSlug: this.#sourceSlug,
			sourceDID: this.#sourceDID,
			permissions: this.#permissions,
			collapsed: this.#collapsed,
			label: this.#label,
		};
	}
}
