/**
 * PresenceManager - Shows other users' cursors and selections in real-time
 */

export interface PresenceData {
	peerId: string;
	cursor?: { x: number; y: number };
	selection?: string; // Selected shape ID
	username?: string;
	color?: string;
}

interface UserPresence extends PresenceData {
	lastUpdate: number;
	element: HTMLElement;
}

// Generate consistent color from peer ID
export function peerIdToColor(peerId: string): string {
	const colors = [
		"#ef4444", // red
		"#f97316", // orange
		"#eab308", // yellow
		"#22c55e", // green
		"#14b8a6", // teal
		"#3b82f6", // blue
		"#8b5cf6", // violet
		"#ec4899", // pink
	];
	let hash = 0;
	for (let i = 0; i < peerId.length; i++) {
		hash = (hash << 5) - hash + peerId.charCodeAt(i);
		hash = hash & hash;
	}
	return colors[Math.abs(hash) % colors.length];
}

export class PresenceManager extends EventTarget {
	#container: HTMLElement;
	#users = new Map<string, UserPresence>();
	#fadeTimeout = 5000; // 5 seconds
	#fadeInterval: number | null = null;
	#localPeerId: string;
	#localUsername: string;
	#panX = 0;
	#panY = 0;
	#scale = 1;
	#soloModeHandler: ((e: Event) => void) | null = null;

	constructor(container: HTMLElement, peerId: string, username?: string) {
		super();
		this.#container = container;
		this.#localPeerId = peerId;
		this.#localUsername = username || `User ${peerId.slice(0, 4)}`;

		// Create cursor container
		this.#ensureCursorContainer();

		// Start fade check interval
		this.#fadeInterval = window.setInterval(() => this.#checkFades(), 1000);

		// Listen for solo-mode-change events from collab overlay
		this.#soloModeHandler = (e: Event) => {
			const solo = (e as CustomEvent).detail?.solo ?? false;
			this.setVisible(!solo);
		};
		document.addEventListener('solo-mode-change', this.#soloModeHandler);

		// Apply initial solo mode state
		if (localStorage.getItem('rspace_solo_mode') === '1') {
			this.setVisible(false);
		}
	}

	get localPeerId() {
		return this.#localPeerId;
	}

	get localUsername() {
		return this.#localUsername;
	}

	set localUsername(name: string) {
		this.#localUsername = name;
	}

	/**
	 * Update camera transform so remote cursors render at correct screen positions
	 */
	setCamera(panX: number, panY: number, scale: number) {
		this.#panX = panX;
		this.#panY = panY;
		this.#scale = scale;
		this.#refreshCursors();
	}

	/**
	 * Update presence for a remote user
	 */
	updatePresence(data: PresenceData) {
		if (data.peerId === this.#localPeerId) return; // Ignore our own presence

		let user = this.#users.get(data.peerId);
		if (!user) {
			// Create new cursor element
			user = {
				...data,
				lastUpdate: Date.now(),
				element: this.#createCursorElement(data),
			};
			this.#users.set(data.peerId, user);
		} else {
			// Update existing user
			Object.assign(user, data);
			user.lastUpdate = Date.now();
		}

		// Update cursor position (world → screen conversion)
		if (data.cursor) {
			const screenX = data.cursor.x * this.#scale + this.#panX;
			const screenY = data.cursor.y * this.#scale + this.#panY;
			user.element.style.left = `${screenX}px`;
			user.element.style.top = `${screenY}px`;
			user.element.style.opacity = "1";
		}

		// Update username label
		if (data.username) {
			const label = user.element.querySelector(".cursor-label");
			if (label) label.textContent = data.username;
		}

		// Update selection highlight
		this.#updateSelectionHighlight(data.peerId, data.selection);
	}

	/**
	 * Get presence data for broadcasting
	 */
	getLocalPresence(cursor: { x: number; y: number }, selection?: string): PresenceData {
		return {
			peerId: this.#localPeerId,
			cursor,
			selection,
			username: this.#localUsername,
			color: peerIdToColor(this.#localPeerId),
		};
	}

	/**
	 * Show or hide all remote cursors (for single/multiplayer toggle)
	 */
	setVisible(visible: boolean) {
		const container = this.#container.querySelector(".presence-cursors") as HTMLElement;
		if (container) container.style.display = visible ? "" : "none";
	}

	/**
	 * Remove a user's presence
	 */
	removeUser(peerId: string) {
		const user = this.#users.get(peerId);
		if (user) {
			user.element.remove();
			this.#users.delete(peerId);
			this.#clearSelectionHighlight(peerId);
		}
	}

	/**
	 * Get all current users
	 */
	getUsers(): Map<string, UserPresence> {
		return new Map(this.#users);
	}

	/**
	 * Clean up
	 */
	destroy() {
		if (this.#fadeInterval !== null) {
			clearInterval(this.#fadeInterval);
		}
		for (const [peerId] of this.#users) {
			this.removeUser(peerId);
		}
		if (this.#soloModeHandler) {
			document.removeEventListener('solo-mode-change', this.#soloModeHandler);
			this.#soloModeHandler = null;
		}
	}

	#refreshCursors() {
		for (const [, user] of this.#users) {
			if (user.cursor) {
				const screenX = user.cursor.x * this.#scale + this.#panX;
				const screenY = user.cursor.y * this.#scale + this.#panY;
				user.element.style.left = `${screenX}px`;
				user.element.style.top = `${screenY}px`;
			}
		}
	}

	#ensureCursorContainer() {
		let cursorsEl = this.#container.querySelector(".presence-cursors") as HTMLElement;
		if (!cursorsEl) {
			cursorsEl = document.createElement("div");
			cursorsEl.className = "presence-cursors";
			cursorsEl.style.cssText = `
				position: absolute;
				top: 0;
				left: 0;
				width: 100%;
				height: 100%;
				pointer-events: none;
				z-index: 9999;
				overflow: visible;
			`;
			this.#container.appendChild(cursorsEl);
		}
		return cursorsEl;
	}

	#createCursorElement(data: PresenceData): HTMLElement {
		const color = data.color || peerIdToColor(data.peerId);
		const username = data.username || `User ${data.peerId.slice(0, 4)}`;

		const el = document.createElement("div");
		el.className = "presence-cursor";
		el.dataset.peerId = data.peerId;
		el.style.cssText = `
			position: absolute;
			pointer-events: none;
			transition: left 0.1s, top 0.1s, opacity 0.3s;
			z-index: 10000;
		`;

		el.innerHTML = `
			<svg width="24" height="24" viewBox="0 0 24 24" fill="none" style="filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));">
				<path d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.86a.5.5 0 0 0-.85.35z" fill="${color}"/>
				<path d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.86a.5.5 0 0 0-.85.35z" stroke="white" stroke-width="1.5"/>
			</svg>
			<span class="cursor-label" style="
				position: absolute;
				left: 20px;
				top: 16px;
				background: ${color};
				color: white;
				padding: 2px 8px;
				border-radius: 4px;
				font-size: 12px;
				font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
				font-weight: 500;
				white-space: nowrap;
				box-shadow: 0 1px 4px rgba(0,0,0,0.2);
			">${username}</span>
		`;

		const container = this.#ensureCursorContainer();
		container.appendChild(el);

		return el;
	}

	#updateSelectionHighlight(peerId: string, shapeId?: string) {
		// Remove previous selection
		this.#clearSelectionHighlight(peerId);

		if (!shapeId) return;

		// Find and highlight the selected shape
		const shape = document.getElementById(shapeId);
		if (shape) {
			const user = this.#users.get(peerId);
			const color = user?.color || peerIdToColor(peerId);

			shape.style.outline = `2px solid ${color}`;
			shape.style.outlineOffset = "2px";
			shape.dataset.selectedBy = peerId;
		}
	}

	#clearSelectionHighlight(peerId: string) {
		// Find any shape selected by this peer and remove highlight
		const selected = document.querySelector(`[data-selected-by="${peerId}"]`) as HTMLElement;
		if (selected) {
			selected.style.outline = "";
			selected.style.outlineOffset = "";
			delete selected.dataset.selectedBy;
		}
	}

	#checkFades() {
		const now = Date.now();
		for (const [peerId, user] of this.#users) {
			const elapsed = now - user.lastUpdate;
			if (elapsed > this.#fadeTimeout) {
				// Fade out after timeout
				user.element.style.opacity = "0.3";
			}
			if (elapsed > this.#fadeTimeout * 3) {
				// Remove completely after 3x timeout
				this.removeUser(peerId);
			}
		}
	}
}

// Generate peer ID
export function generatePeerId(): string {
	return crypto.randomUUID().slice(0, 8);
}
