/**
 * applet-circuit-canvas — Reusable SVG node graph renderer.
 *
 * Lightweight pan/zoom SVG canvas for rendering sub-node graphs
 * inside expanded folk-applet shapes. Extracted from folk-gov-circuit patterns.
 *
 * NOT a FolkShape — just an HTMLElement used inside folk-applet's shadow DOM.
 */

import type { AppletSubNode, AppletSubEdge } from "../shared/applet-types";

const NODE_WIDTH = 200;
const NODE_HEIGHT = 80;
const PORT_RADIUS = 5;

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

function bezierPath(x1: number, y1: number, x2: number, y2: number): string {
	const dx = Math.abs(x2 - x1) * 0.5;
	return `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`;
}

const STYLES = `
	:host {
		display: block;
		width: 100%;
		height: 100%;
		background: #0f172a;
		border-radius: 0 0 8px 8px;
		overflow: hidden;
	}

	svg {
		width: 100%;
		height: 100%;
	}

	.acc-node-body {
		width: 100%;
		height: 100%;
		box-sizing: border-box;
		background: #1e293b;
		border: 1.5px solid #334155;
		border-radius: 6px;
		padding: 8px;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 4px;
		font-family: inherit;
	}

	.acc-node-label {
		font-size: 11px;
		font-weight: 600;
		color: #e2e8f0;
		display: flex;
		align-items: center;
		gap: 4px;
	}

	.acc-node-meta {
		font-size: 10px;
		color: #94a3b8;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.acc-edge-path {
		fill: none;
		stroke-width: 1.5;
		stroke-opacity: 0.5;
		pointer-events: none;
	}

	.acc-edge-hit {
		fill: none;
		stroke: transparent;
		stroke-width: 10;
		cursor: pointer;
	}

	.acc-edge-hit:hover + .acc-edge-path {
		stroke-opacity: 1;
		stroke-width: 2.5;
	}

	.acc-port-dot {
		transition: r 0.1s;
	}

	.acc-port-hit {
		cursor: crosshair;
	}

	.acc-port-hit:hover ~ .acc-port-dot {
		r: 8;
	}

	.acc-grid-line {
		stroke: #1e293b;
		stroke-width: 0.5;
	}
`;

export class AppletCircuitCanvas extends HTMLElement {
	#shadow: ShadowRoot;
	#nodes: AppletSubNode[] = [];
	#edges: AppletSubEdge[] = [];
	#panX = 0;
	#panY = 0;
	#zoom = 1;
	#isPanning = false;
	#panStart = { x: 0, y: 0 };

	constructor() {
		super();
		this.#shadow = this.attachShadow({ mode: "open" });
	}

	get nodes() { return this.#nodes; }
	set nodes(v: AppletSubNode[]) {
		this.#nodes = v;
		this.#render();
	}

	get edges() { return this.#edges; }
	set edges(v: AppletSubEdge[]) {
		this.#edges = v;
		this.#render();
	}

	connectedCallback() {
		this.#render();
		this.#setupInteraction();
	}

	#render(): void {
		const gridDef = `
			<defs>
				<pattern id="acc-grid" width="30" height="30" patternUnits="userSpaceOnUse">
					<line x1="30" y1="0" x2="30" y2="30" class="acc-grid-line"/>
					<line x1="0" y1="30" x2="30" y2="30" class="acc-grid-line"/>
				</pattern>
			</defs>
			<rect width="8000" height="8000" x="-4000" y="-4000" fill="url(#acc-grid)"/>
		`;

		const edgesHtml = this.#edges.map(edge => {
			const fromNode = this.#nodes.find(n => n.id === edge.fromNode);
			const toNode = this.#nodes.find(n => n.id === edge.toNode);
			if (!fromNode || !toNode) return "";

			const x1 = fromNode.position.x + NODE_WIDTH;
			const y1 = fromNode.position.y + NODE_HEIGHT / 2;
			const x2 = toNode.position.x;
			const y2 = toNode.position.y + NODE_HEIGHT / 2;
			const d = bezierPath(x1, y1, x2, y2);

			return `
				<g data-edge-id="${esc(edge.id)}">
					<path class="acc-edge-hit" d="${d}"/>
					<path class="acc-edge-path" d="${d}" stroke="#6366f1" stroke-opacity="0.5"/>
				</g>
			`;
		}).join("");

		const nodesHtml = this.#nodes.map(node => {
			const configSummary = Object.entries(node.config)
				.slice(0, 2)
				.map(([k, v]) => `${k}: ${v}`)
				.join(", ");

			return `
				<g data-node-id="${esc(node.id)}">
					<foreignObject x="${node.position.x}" y="${node.position.y}" width="${NODE_WIDTH}" height="${NODE_HEIGHT}">
						<div xmlns="http://www.w3.org/1999/xhtml" class="acc-node-body">
							<div class="acc-node-label">${esc(node.icon)} ${esc(node.label)}</div>
							${configSummary ? `<div class="acc-node-meta">${esc(configSummary)}</div>` : ""}
						</div>
					</foreignObject>
				</g>
			`;
		}).join("");

		this.#shadow.innerHTML = `
			<style>${STYLES}</style>
			<svg xmlns="http://www.w3.org/2000/svg">
				<g id="canvas-transform" transform="translate(${this.#panX},${this.#panY}) scale(${this.#zoom})">
					${gridDef}
					<g id="edge-layer">${edgesHtml}</g>
					<g id="node-layer">${nodesHtml}</g>
				</g>
			</svg>
		`;

		this.#fitView();
	}

	#fitView(): void {
		if (this.#nodes.length === 0) return;

		const svg = this.#shadow.querySelector("svg");
		if (!svg) return;
		const rect = svg.getBoundingClientRect();
		if (rect.width === 0 || rect.height === 0) return;

		let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
		for (const n of this.#nodes) {
			minX = Math.min(minX, n.position.x);
			minY = Math.min(minY, n.position.y);
			maxX = Math.max(maxX, n.position.x + NODE_WIDTH);
			maxY = Math.max(maxY, n.position.y + NODE_HEIGHT);
		}

		const pad = 30;
		const contentW = maxX - minX + pad * 2;
		const contentH = maxY - minY + pad * 2;
		const scaleX = rect.width / contentW;
		const scaleY = rect.height / contentH;
		this.#zoom = Math.min(scaleX, scaleY, 1.5);
		this.#panX = (rect.width - contentW * this.#zoom) / 2 - (minX - pad) * this.#zoom;
		this.#panY = (rect.height - contentH * this.#zoom) / 2 - (minY - pad) * this.#zoom;

		this.#updateTransform();
	}

	#updateTransform(): void {
		const g = this.#shadow.getElementById("canvas-transform");
		if (g) g.setAttribute("transform", `translate(${this.#panX},${this.#panY}) scale(${this.#zoom})`);
	}

	#setupInteraction(): void {
		const svg = this.#shadow.querySelector("svg");
		if (!svg) return;

		// Pan
		svg.addEventListener("pointerdown", (e) => {
			if (e.button !== 0 && e.button !== 1) return;
			this.#isPanning = true;
			this.#panStart = { x: e.clientX - this.#panX, y: e.clientY - this.#panY };
			svg.setPointerCapture(e.pointerId);
			e.preventDefault();
		});

		svg.addEventListener("pointermove", (e) => {
			if (!this.#isPanning) return;
			this.#panX = e.clientX - this.#panStart.x;
			this.#panY = e.clientY - this.#panStart.y;
			this.#updateTransform();
		});

		svg.addEventListener("pointerup", () => {
			this.#isPanning = false;
		});

		// Zoom
		svg.addEventListener("wheel", (e) => {
			e.preventDefault();
			const factor = e.deltaY > 0 ? 0.9 : 1.1;
			const oldZoom = this.#zoom;
			const newZoom = Math.max(0.2, Math.min(3, oldZoom * factor));
			const rect = svg.getBoundingClientRect();
			const mx = e.clientX - rect.left;
			const my = e.clientY - rect.top;
			this.#panX = mx - (mx - this.#panX) * (newZoom / oldZoom);
			this.#panY = my - (my - this.#panY) * (newZoom / oldZoom);
			this.#zoom = newZoom;
			this.#updateTransform();
		}, { passive: false });
	}
}

customElements.define("applet-circuit-canvas", AppletCircuitCanvas);
