/**
 * folk-holon — Canvas shape for H3 geospatial holons.
 *
 * Ported from canvas-website HolonShapeUtil.tsx (React/tldraw) to
 * FolkShape web component with Automerge-backed local-first data.
 *
 * Two states:
 *   - Disconnected: ID entry form (H3 cell ID or numeric)
 *   - Connected: header with name/coords, lens selector, lens data grid
 */

import * as h3 from 'h3-js';
import { FolkShape } from './folk-shape';
import { css, html } from './tags';
import {
	isValidHolonId,
	isH3CellId,
	getResolutionName,
	getResolutionDescription,
	STANDARD_LENSES,
	LENS_ICONS,
	saveHolon,
	loadHolon,
	putLensData,
	getLensData,
	getAvailableLenses,
	subscribeLenses,
	type HolonData,
} from './holon-service';

const styles = css`
	:host {
		background: var(--rs-bg-surface, #fff);
		color: var(--rs-text-primary, #1e293b);
		border-radius: 8px;
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
		min-width: 360px;
		min-height: 280px;
	}

	.header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 8px 12px;
		background: #22c55e;
		color: white;
		border-radius: 8px 8px 0 0;
		font-size: 12px;
		font-weight: 600;
		cursor: move;
	}

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

	.header-title span:last-child {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.header-actions {
		display: flex;
		gap: 4px;
		flex-shrink: 0;
	}

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

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

	.content {
		padding: 16px;
		overflow: auto;
		height: calc(100% - 36px);
	}

	/* -- Disconnected state -- */

	.connect-form {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		height: 100%;
		gap: 16px;
		text-align: center;
	}

	.connect-icon {
		font-size: 48px;
	}

	.connect-label {
		font-size: 14px;
		font-weight: 600;
		color: var(--rs-text-primary, #e2e8f0);
	}

	.connect-hint {
		font-size: 12px;
		color: var(--rs-text-muted, #64748b);
		max-width: 320px;
	}

	.connect-row {
		display: flex;
		gap: 8px;
		width: 100%;
		max-width: 400px;
	}

	.connect-input {
		flex: 1;
		padding: 8px 12px;
		border: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		border-radius: 6px;
		font-size: 13px;
		font-family: monospace;
		outline: none;
	}

	.connect-input:focus {
		border-color: #22c55e;
		box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2);
	}

	.connect-btn {
		padding: 8px 16px;
		background: #22c55e;
		color: white;
		border: none;
		border-radius: 6px;
		cursor: pointer;
		font-size: 13px;
		font-weight: 600;
		white-space: nowrap;
	}

	.connect-btn:hover {
		background: #16a34a;
	}

	.connect-btn:disabled {
		background: #9ca3af;
		cursor: not-allowed;
	}

	.error {
		color: #dc2626;
		font-size: 12px;
		margin-top: 4px;
	}

	/* -- Connected state -- */

	.info-grid {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 8px;
		margin-bottom: 12px;
		font-size: 12px;
	}

	.info-label {
		color: var(--rs-text-muted, #64748b);
		font-size: 11px;
	}

	.info-value {
		color: var(--rs-text-primary, #e2e8f0);
		font-family: monospace;
		font-size: 12px;
		word-break: break-all;
	}

	.lens-bar {
		display: flex;
		flex-wrap: wrap;
		gap: 4px;
		margin-bottom: 12px;
		padding-bottom: 8px;
		border-bottom: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
	}

	.lens-btn {
		padding: 4px 10px;
		border: none;
		border-radius: 12px;
		font-size: 11px;
		cursor: pointer;
		background: var(--rs-bg-surface-raised, #334155);
		color: var(--rs-text-primary, #e2e8f0);
		white-space: nowrap;
	}

	.lens-btn:hover {
		background: var(--rs-bg-hover, rgba(255, 255, 255, 0.05));
	}

	.lens-btn.active {
		background: #22c55e;
		color: white;
	}

	.lens-data {
		background: var(--rs-bg-surface-sunken, #0f172a);
		border-radius: 6px;
		padding: 10px;
		font-size: 12px;
		max-height: 200px;
		overflow: auto;
	}

	.lens-data pre {
		margin: 0;
		white-space: pre-wrap;
		word-break: break-word;
		font-family: monospace;
		font-size: 11px;
		color: var(--rs-text-secondary, #94a3b8);
	}

	.lens-empty {
		color: var(--rs-text-muted, #64748b);
		font-style: italic;
		font-size: 12px;
		padding: 8px;
	}

	.lens-add-row {
		display: flex;
		gap: 6px;
		margin-top: 8px;
	}

	.lens-add-row input {
		flex: 1;
		padding: 6px 10px;
		border: 1px solid var(--rs-border, rgba(255, 255, 255, 0.1));
		border-radius: 4px;
		font-size: 12px;
		outline: none;
	}

	.lens-add-row input:focus {
		border-color: #22c55e;
	}

	.lens-add-row button {
		padding: 6px 12px;
		background: #22c55e;
		color: white;
		border: none;
		border-radius: 4px;
		font-size: 12px;
		cursor: pointer;
	}

	.lens-add-row button:hover {
		background: #16a34a;
	}

	.loading {
		display: flex;
		align-items: center;
		justify-content: center;
		padding: 20px;
		color: #6b7280;
		font-size: 13px;
	}

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

	.status-dot.connected { background: #22c55e; }
	.status-dot.disconnected { background: #ef4444; }
`;

declare global {
	interface HTMLElementTagNameMap {
		'folk-holon': FolkHolon;
	}
}

export class FolkHolon extends FolkShape {
	static override tagName = 'folk-holon';

	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;
	}

	// ── State ──
	#holonId = '';
	#name = 'New Holon';
	#description = '';
	#latitude = 0;
	#longitude = 0;
	#resolution = -1;
	#connected = false;
	#selectedLens = 'users';
	#lensData: Record<string, Record<string, any>> = {};
	#loading = false;
	#error = '';
	#space = '';
	#unsubLenses: (() => void) | null = null;

	// ── DOM refs ──
	#contentEl: HTMLElement | null = null;

	// ── Accessors for Automerge sync ──

	get holonId() { return this.#holonId; }
	set holonId(v: string) { this.#holonId = v; this.#render(); }

	get holonName() { return this.#name; }
	set holonName(v: string) { this.#name = v; this.#render(); }

	get selectedLens() { return this.#selectedLens; }
	set selectedLens(v: string) { this.#selectedLens = v; this.#render(); }

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

		// Parse attributes
		this.#holonId = this.getAttribute('holon-id') ?? '';
		this.#name = this.getAttribute('name') ?? 'New Holon';
		this.#description = this.getAttribute('description') ?? '';
		this.#selectedLens = this.getAttribute('selected-lens') ?? 'users';
		this.#space = this.getAttribute('space') ?? '';

		const latAttr = this.getAttribute('latitude');
		const lngAttr = this.getAttribute('longitude');
		const resAttr = this.getAttribute('resolution');
		if (latAttr) this.#latitude = parseFloat(latAttr) || 0;
		if (lngAttr) this.#longitude = parseFloat(lngAttr) || 0;
		if (resAttr) this.#resolution = parseInt(resAttr, 10);

		// Build content wrapper
		const wrapper = document.createElement('div');
		wrapper.className = 'content-wrapper';
		wrapper.style.cssText = 'width:100%;height:100%;display:flex;flex-direction:column;';
		wrapper.innerHTML = html`
			<div class="header">
				<span class="header-title">
					<span>🌐</span>
					<span class="holon-title">Holon</span>
				</span>
				<div class="header-actions">
					<button class="refresh-btn" title="Refresh">↻</button>
					<button class="close-btn" title="Close">×</button>
				</div>
			</div>
			<div class="content"></div>
		`;

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

		this.#contentEl = wrapper.querySelector('.content');

		// Wire header buttons
		const refreshBtn = wrapper.querySelector('.refresh-btn') as HTMLButtonElement;
		const closeBtn = wrapper.querySelector('.close-btn') as HTMLButtonElement;

		refreshBtn.addEventListener('click', (e) => {
			e.stopPropagation();
			this.#handleRefresh();
		});
		closeBtn.addEventListener('click', (e) => {
			e.stopPropagation();
			this.dispatchEvent(new CustomEvent('close'));
		});

		// Auto-connect if holonId was set via attribute
		if (this.#holonId) {
			this.#connected = true;
			this.#resolveHolonInfo(this.#holonId);
			this.#loadData();
		}

		this.#render();
		return root;
	}

	disconnectedCallback() {
		this.#unsubLenses?.();
		this.#unsubLenses = null;
	}

	// ── Connect flow ──

	async #handleConnect() {
		const id = this.#holonId.trim();
		if (!id) {
			this.#error = 'Please enter a Holon ID';
			this.#render();
			return;
		}
		if (!isValidHolonId(id)) {
			this.#error = 'Invalid ID. Enter an H3 cell (e.g. 872a1070bffffff) or numeric ID.';
			this.#render();
			return;
		}

		this.#holonId = id;
		this.#error = '';
		this.#connected = true;
		this.#resolveHolonInfo(id);
		await this.#loadData();

		// Persist metadata
		await saveHolon({
			id,
			name: this.#name,
			description: this.#description,
			latitude: this.#latitude,
			longitude: this.#longitude,
			resolution: this.#resolution,
			timestamp: Date.now(),
		});

		this.#render();
	}

	#resolveHolonInfo(id: string) {
		if (isH3CellId(id)) {
			try {
				const [lat, lng] = h3.cellToLatLng(id);
				this.#latitude = lat;
				this.#longitude = lng;
				this.#resolution = h3.getResolution(id);
				this.#name = `${getResolutionName(this.#resolution)} Holon`;
			} catch { /* keep defaults */ }
		} else {
			this.#resolution = -1;
			this.#name = `Holon ${id.slice(-8)}`;
		}

		// Update header title
		const titleEl = this.renderRoot.querySelector('.holon-title');
		if (titleEl) titleEl.textContent = this.#name;
	}

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

		try {
			// Load existing holon metadata
			const existing = await loadHolon(this.#holonId);
			if (existing?.name) {
				this.#name = existing.name;
				this.#description = existing.description;
				const titleEl = this.renderRoot.querySelector('.holon-title');
				if (titleEl) titleEl.textContent = this.#name;
			}

			// Load lens data
			for (const lens of STANDARD_LENSES) {
				const data = await getLensData(this.#holonId, lens);
				if (data && Object.keys(data).length > 0) {
					this.#lensData[lens] = data;
				}
			}

			// Subscribe for live updates
			this.#unsubLenses?.();
			this.#unsubLenses = subscribeLenses(this.#holonId, (lenses) => {
				this.#lensData = { ...lenses };
				this.#render();
			});
		} catch (e) {
			console.error('[folk-holon] Load error:', e);
			this.#error = 'Failed to load data';
		} finally {
			this.#loading = false;
			this.#render();
		}
	}

	async #handleRefresh() {
		if (!this.#connected || !this.#holonId) return;
		await this.#loadData();
	}

	async #handleAddData() {
		if (!this.#holonId || !this.#connected) return;
		const input = this.#contentEl?.querySelector('.add-data-input') as HTMLInputElement;
		if (!input?.value.trim()) return;

		const entryId = `entry-${Date.now()}`;
		await putLensData(this.#holonId, this.#selectedLens, {
			[entryId]: { content: input.value.trim(), timestamp: Date.now() },
		});

		input.value = '';
		// Data will update via subscription
	}

	// ── Rendering ──

	#render() {
		if (!this.#contentEl) return;

		if (!this.#connected) {
			this.#renderDisconnected();
		} else if (this.#loading) {
			this.#contentEl.innerHTML = '<div class="loading">Loading holon data...</div>';
		} else {
			this.#renderConnected();
		}
	}

	#renderDisconnected() {
		this.#contentEl!.innerHTML = html`
			<div class="connect-form">
				<div class="connect-icon">🌐</div>
				<div class="connect-label">Connect to a Holon</div>
				<div class="connect-hint">
					Enter an H3 cell ID (e.g. <code>872a1070bffffff</code>) or numeric Holon ID
				</div>
				<div class="connect-row">
					<input class="connect-input" type="text" placeholder="Holon ID..." />
					<button class="connect-btn">Connect</button>
				</div>
				${this.#error ? `<div class="error">${this.#error}</div>` : ''}
			</div>
		`;

		const input = this.#contentEl!.querySelector('.connect-input') as HTMLInputElement;
		const btn = this.#contentEl!.querySelector('.connect-btn') as HTMLButtonElement;

		// Set value from state
		if (this.#holonId) input.value = this.#holonId;

		input.addEventListener('input', (e) => {
			this.#holonId = (e.target as HTMLInputElement).value;
		});
		input.addEventListener('pointerdown', (e) => e.stopPropagation());
		input.addEventListener('keydown', (e) => {
			e.stopPropagation();
			if (e.key === 'Enter') this.#handleConnect();
		});
		btn.addEventListener('click', (e) => {
			e.stopPropagation();
			this.#handleConnect();
		});
	}

	#renderConnected() {
		const resName = getResolutionName(this.#resolution);
		const resDesc = getResolutionDescription(this.#resolution);
		const isGeo = this.#resolution >= 0;

		// Available lenses (ones with data + all standard for selection)
		const withData = new Set(Object.keys(this.#lensData));

		// Build info section
		const infoHtml = isGeo
			? html`
				<div class="info-grid">
					<div>
						<div class="info-label">Coordinates</div>
						<div class="info-value">${this.#latitude.toFixed(6)}, ${this.#longitude.toFixed(6)}</div>
					</div>
					<div>
						<div class="info-label">Resolution</div>
						<div class="info-value">${resName} (Level ${this.#resolution})</div>
					</div>
					<div>
						<div class="info-label">Holon ID</div>
						<div class="info-value">${this.#holonId}</div>
					</div>
					<div>
						<div class="info-label">Area Info</div>
						<div class="info-value">${resDesc}</div>
					</div>
				</div>`
			: html`
				<div class="info-grid">
					<div>
						<div class="info-label">Type</div>
						<div class="info-value" style="color:#22c55e;font-weight:600">${resName}</div>
					</div>
					<div>
						<div class="info-label">Holon ID</div>
						<div class="info-value">${this.#holonId}</div>
					</div>
				</div>`;

		// Build lens buttons
		const lensButtons = STANDARD_LENSES.map((lens) => {
			const icon = LENS_ICONS[lens] ?? '📁';
			const active = lens === this.#selectedLens ? ' active' : '';
			const badge = withData.has(lens) ? ' *' : '';
			return `<button class="lens-btn${active}" data-lens="${lens}">${icon} ${lens}${badge}</button>`;
		}).join('');

		// Current lens data
		const currentData = this.#lensData[this.#selectedLens];
		let dataHtml: string;
		if (currentData && Object.keys(currentData).length > 0) {
			dataHtml = `<div class="lens-data"><pre>${escapeHtml(JSON.stringify(currentData, null, 2))}</pre></div>`;
		} else {
			dataHtml = `<div class="lens-empty">No data in "${this.#selectedLens}" lens</div>`;
		}

		this.#contentEl!.innerHTML = html`
			${infoHtml}
			<div class="lens-bar">${lensButtons}</div>
			${dataHtml}
			<div class="lens-add-row">
				<input class="add-data-input" type="text" placeholder="Add data to ${this.#selectedLens}..." />
				<button class="add-data-btn">Add</button>
			</div>
		`;

		// Wire lens buttons
		this.#contentEl!.querySelectorAll('.lens-btn').forEach((btn) => {
			btn.addEventListener('click', (e) => {
				e.stopPropagation();
				this.#selectedLens = (btn as HTMLElement).dataset.lens!;
				this.#render();
			});
		});

		// Wire add data
		const addInput = this.#contentEl!.querySelector('.add-data-input') as HTMLInputElement;
		const addBtn = this.#contentEl!.querySelector('.add-data-btn') as HTMLButtonElement;
		addInput?.addEventListener('pointerdown', (e) => e.stopPropagation());
		addInput?.addEventListener('keydown', (e) => {
			e.stopPropagation();
			if (e.key === 'Enter') this.#handleAddData();
		});
		addBtn?.addEventListener('click', (e) => {
			e.stopPropagation();
			this.#handleAddData();
		});
	}

	// ── Serialization ──

	override toJSON() {
		return {
			...super.toJSON(),
			type: 'folk-holon',
			holonId: this.#holonId,
			name: this.#name,
			description: this.#description,
			latitude: this.#latitude,
			longitude: this.#longitude,
			resolution: this.#resolution,
			connected: this.#connected,
			selectedLens: this.#selectedLens,
		};
	}

	static override fromData(data: Record<string, any>): FolkHolon {
		const shape = FolkShape.fromData(data) as FolkHolon;
		if (data.holonId) shape.setAttribute('holon-id', data.holonId);
		if (data.name) shape.setAttribute('name', data.name);
		if (data.description) shape.setAttribute('description', data.description);
		if (data.latitude != null) shape.setAttribute('latitude', String(data.latitude));
		if (data.longitude != null) shape.setAttribute('longitude', String(data.longitude));
		if (data.resolution != null) shape.setAttribute('resolution', String(data.resolution));
		if (data.selectedLens) shape.setAttribute('selected-lens', data.selectedLens);
		return shape;
	}

	override applyData(data: Record<string, any>): void {
		super.applyData(data);
		if (data.holonId && data.holonId !== this.#holonId) {
			this.#holonId = data.holonId;
			this.#connected = !!data.connected;
			if (this.#connected) {
				this.#resolveHolonInfo(this.#holonId);
				this.#loadData();
			}
		}
		if (data.name) this.#name = data.name;
		if (data.selectedLens && data.selectedLens !== this.#selectedLens) {
			this.#selectedLens = data.selectedLens;
		}
		this.#render();
	}
}

function escapeHtml(str: string): string {
	return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
