/**
 * MiCanvasBridge — Singleton that connects canvas state to the MI assistant.
 *
 * Listens to selection, viewport, and connection changes on the canvas
 * and exposes a structured context snapshot for the MI system prompt.
 */

export interface ShapeInfo {
	id: string;
	type: string;
	x: number;
	y: number;
	width: number;
	height: number;
	content?: string;
	title?: string;
}

export interface Connection {
	arrowId: string;
	sourceId: string;
	targetId: string;
}

export interface ShapeGroup {
	shapeIds: string[];
}

export interface CanvasContext {
	selectedShapes: ShapeInfo[];
	allShapes: ShapeInfo[];
	connections: Connection[];
	viewport: { x: number; y: number; scale: number };
	shapeGroups: ShapeGroup[];
	shapeCountByType: Record<string, number>;
}

export class MiCanvasBridge {
	static instance: MiCanvasBridge | null = null;

	selectedShapeIds: string[] = [];
	viewport = { x: 0, y: 0, scale: 1 };

	private canvasContent: HTMLElement | null = null;

	constructor(canvasContent?: HTMLElement) {
		if (MiCanvasBridge.instance) return MiCanvasBridge.instance;
		this.canvasContent = canvasContent || document.getElementById("canvas-content");
		MiCanvasBridge.instance = this;
	}

	setSelection(ids: string[]) {
		this.selectedShapeIds = ids;
	}

	setViewport(x: number, y: number, scale: number) {
		this.viewport = { x, y, scale };
	}

	/** Build full context snapshot for the MI system prompt. */
	getCanvasContext(): CanvasContext {
		const allShapes = this.#collectShapes();
		const connections = this.#collectConnections();
		const selectedShapes = allShapes.filter((s) => this.selectedShapeIds.includes(s.id));
		const shapeGroups = this.#buildShapeGroups(allShapes, connections);
		const shapeCountByType: Record<string, number> = {};
		for (const s of allShapes) {
			shapeCountByType[s.type] = (shapeCountByType[s.type] || 0) + 1;
		}

		return {
			selectedShapes,
			allShapes,
			connections,
			viewport: { ...this.viewport },
			shapeGroups,
			shapeCountByType,
		};
	}

	#collectShapes(): ShapeInfo[] {
		if (!this.canvasContent) return [];
		return [...this.canvasContent.children]
			.filter(
				(el) =>
					el.tagName?.includes("-") &&
					el.id &&
					!el.tagName.toLowerCase().includes("arrow"),
			)
			.map((el: any) => ({
				id: el.id,
				type: el.tagName.toLowerCase(),
				x: el.x ?? 0,
				y: el.y ?? 0,
				width: el.width ?? 0,
				height: el.height ?? 0,
				...(el.content ? { content: String(el.content).slice(0, 120) } : {}),
				...(el.title ? { title: String(el.title).slice(0, 80) } : {}),
			}));
	}

	#collectConnections(): Connection[] {
		if (!this.canvasContent) return [];
		return [...this.canvasContent.querySelectorAll("folk-arrow")]
			.filter((el: any) => el.id && el.sourceId && el.targetId)
			.map((el: any) => ({
				arrowId: el.id,
				sourceId: el.sourceId,
				targetId: el.targetId,
			}));
	}

	/** BFS on the arrow graph to find clusters of connected shapes. */
	#buildShapeGroups(shapes: ShapeInfo[], connections: Connection[]): ShapeGroup[] {
		const adj = new Map<string, Set<string>>();
		for (const c of connections) {
			if (!adj.has(c.sourceId)) adj.set(c.sourceId, new Set());
			if (!adj.has(c.targetId)) adj.set(c.targetId, new Set());
			adj.get(c.sourceId)!.add(c.targetId);
			adj.get(c.targetId)!.add(c.sourceId);
		}

		const visited = new Set<string>();
		const groups: ShapeGroup[] = [];

		for (const shape of shapes) {
			if (visited.has(shape.id) || !adj.has(shape.id)) continue;
			const group: string[] = [];
			const queue = [shape.id];
			while (queue.length) {
				const id = queue.shift()!;
				if (visited.has(id)) continue;
				visited.add(id);
				group.push(id);
				for (const neighbor of adj.get(id) || []) {
					if (!visited.has(neighbor)) queue.push(neighbor);
				}
			}
			if (group.length > 1) groups.push({ shapeIds: group });
		}

		return groups;
	}
}
