/**
 * <map-indoor-view> — c3nav indoor map viewer using MapLibre GL raster tiles.
 * Level selector, participant markers, and Easter egg on Level 0 triple-click.
 */

import type { ParticipantState } from "./map-sync";

interface IndoorConfig {
	event: string;
	apiBase: string;
}

const MAPLIBRE_CSS = "https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.css";
const MAPLIBRE_JS = "https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.js";

class MapIndoorView extends HTMLElement {
	private shadow: ShadowRoot;
	private map: any = null;
	private config: IndoorConfig | null = null;
	private levels: { id: number; name: string }[] = [];
	private currentLevel = 0;
	private bounds: { west: number; south: number; east: number; north: number } | null = null;
	private participantMarkers: Map<string, any> = new Map();
	private loading = true;
	private error = "";
	private level0ClickCount = 0;
	private level0ClickTimer: ReturnType<typeof setTimeout> | null = null;

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

	set cfg(val: IndoorConfig) {
		this.config = val;
		if (this.isConnected) this.init();
	}

	connectedCallback() {
		this.renderShell();
		if (this.config) this.init();
	}

	disconnectedCallback() {
		if (this.map) { this.map.remove(); this.map = null; }
		this.participantMarkers.clear();
	}

	private renderShell() {
		this.shadow.innerHTML = `
			<style>
				:host { display: block; width: 100%; height: 100%; position: relative; }
				.indoor-map { width: 100%; height: 100%; }
				.level-selector {
					position: absolute; top: 50%; right: 10px; transform: translateY(-50%);
					display: flex; flex-direction: column; gap: 2px; z-index: 5;
				}
				.level-btn {
					width: 32px; height: 32px; border-radius: 6px;
					border: 1px solid var(--rs-border, #334155);
					background: var(--rs-bg-surface, #1a1a2e);
					color: var(--rs-text-secondary, #94a3b8);
					font-size: 12px; font-weight: 600; cursor: pointer;
					display: flex; align-items: center; justify-content: center;
					transition: all 0.15s;
				}
				.level-btn:hover { border-color: var(--rs-border-strong, #475569); color: var(--rs-text-primary, #e2e8f0); }
				.level-btn.active {
					background: #4f46e5; color: #fff; border-color: #6366f1;
				}
				.loading-overlay, .error-overlay {
					position: absolute; inset: 0; display: flex; flex-direction: column;
					align-items: center; justify-content: center; z-index: 6;
					background: var(--rs-bg-surface-sunken, #0d1117);
				}
				.loading-overlay { color: var(--rs-text-muted, #64748b); font-size: 13px; gap: 8px; }
				.error-overlay { color: #ef4444; font-size: 13px; gap: 12px; }
				.outdoor-btn {
					padding: 8px 16px; border-radius: 8px; border: 1px solid var(--rs-border, #334155);
					background: var(--rs-bg-surface, #1a1a2e); color: var(--rs-text-secondary, #94a3b8);
					cursor: pointer; font-size: 12px;
				}
				.outdoor-btn:hover { border-color: var(--rs-border-strong, #475569); }
				.spinner {
					width: 24px; height: 24px; border: 3px solid var(--rs-border, #334155);
					border-top-color: #6366f1; border-radius: 50%;
					animation: spin 0.8s linear infinite;
				}
				@keyframes spin { to { transform: rotate(360deg); } }
				.easter-egg {
					position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%);
					background: rgba(0,0,0,0.8); color: #22c55e; font-size: 11px;
					padding: 6px 12px; border-radius: 8px; z-index: 5;
					animation: eggFade 3s forwards;
				}
				@keyframes eggFade { 0%,80% { opacity: 1; } 100% { opacity: 0; } }
			</style>
			<div class="indoor-map" id="indoor-map"></div>
			<div class="level-selector" id="level-selector"></div>
			<div class="loading-overlay" id="loading-overlay">
				<div class="spinner"></div>
				<span>Loading indoor map...</span>
			</div>
		`;
	}

	private async loadMapLibre(): Promise<void> {
		if ((window as any).maplibregl) return;
		if (!document.querySelector(`link[href="${MAPLIBRE_CSS}"]`)) {
			const link = document.createElement("link");
			link.rel = "stylesheet";
			link.href = MAPLIBRE_CSS;
			document.head.appendChild(link);
		}
		return new Promise<void>((resolve, reject) => {
			const script = document.createElement("script");
			script.src = MAPLIBRE_JS;
			script.onload = () => resolve();
			script.onerror = reject;
			document.head.appendChild(script);
		});
	}

	private async init() {
		if (!this.config) return;
		this.loading = true;
		this.error = "";

		try {
			await this.loadMapLibre();
			await this.fetchMapData();
			this.createMap();
			this.renderLevelSelector();
			this.loading = false;
			this.shadow.getElementById("loading-overlay")?.remove();
		} catch (err: any) {
			this.loading = false;
			this.error = err?.message || "Failed to load indoor map";
			this.renderError();
		}
	}

	private async fetchMapData() {
		const { event, apiBase } = this.config!;

		// Fetch bounds
		const boundsRes = await fetch(`${apiBase}/api/c3nav/${event}?endpoint=map/bounds`, {
			signal: AbortSignal.timeout(8000),
		});
		if (boundsRes.ok) {
			const data = await boundsRes.json();
			if (data.bounds) {
				this.bounds = data.bounds;
			} else if (data.west !== undefined) {
				this.bounds = { west: data.west, south: data.south, east: data.east, north: data.north };
			}
		}

		// Fetch levels
		const levelsRes = await fetch(`${apiBase}/api/c3nav/${event}?endpoint=map/locations`, {
			signal: AbortSignal.timeout(8000),
		});
		if (levelsRes.ok) {
			const data = await levelsRes.json();
			const levelEntries = (Array.isArray(data) ? data : data.results || [])
				.filter((loc: any) => loc.locationtype === "level" || loc.type === "level")
				.map((loc: any) => ({
					id: typeof loc.on_top_of === "number" ? loc.on_top_of : (loc.id ?? loc.level ?? 0),
					name: loc.title || loc.name || `Level ${loc.id ?? 0}`,
				}))
				.sort((a: any, b: any) => b.id - a.id);

			this.levels = levelEntries.length > 0 ? levelEntries : [
				{ id: 4, name: "Level 4" },
				{ id: 3, name: "Level 3" },
				{ id: 2, name: "Level 2" },
				{ id: 1, name: "Level 1" },
				{ id: 0, name: "Ground" },
			];
		} else {
			// Fallback levels
			this.levels = [
				{ id: 4, name: "Level 4" },
				{ id: 3, name: "Level 3" },
				{ id: 2, name: "Level 2" },
				{ id: 1, name: "Level 1" },
				{ id: 0, name: "Ground" },
			];
		}
	}

	private createMap() {
		const container = this.shadow.getElementById("indoor-map");
		if (!container || !(window as any).maplibregl) return;

		const { event, apiBase } = this.config!;

		// Default center (CCH Hamburg for CCC events)
		const defaultCenter: [number, number] = [9.9905, 53.5545];
		const center = this.bounds
			? [(this.bounds.west + this.bounds.east) / 2, (this.bounds.south + this.bounds.north) / 2] as [number, number]
			: defaultCenter;

		const tileUrl = `${apiBase}/api/c3nav/tiles/${event}/${this.currentLevel}/{z}/{x}/{y}`;

		this.map = new (window as any).maplibregl.Map({
			container,
			style: {
				version: 8,
				sources: {
					"c3nav-tiles": {
						type: "raster",
						tiles: [tileUrl],
						tileSize: 256,
						maxzoom: 22,
					},
				},
				layers: [{
					id: "c3nav-layer",
					type: "raster",
					source: "c3nav-tiles",
				}],
			},
			center,
			zoom: 17,
			minZoom: 14,
			maxZoom: 22,
		});

		this.map.addControl(new (window as any).maplibregl.NavigationControl(), "top-right");
	}

	private renderLevelSelector() {
		const selector = this.shadow.getElementById("level-selector");
		if (!selector) return;

		selector.innerHTML = this.levels.map(level =>
			`<button class="level-btn ${level.id === this.currentLevel ? "active" : ""}" data-level="${level.id}" title="${level.name}">${level.id}</button>`
		).join("");

		selector.querySelectorAll(".level-btn").forEach(btn => {
			btn.addEventListener("click", () => {
				const levelId = parseInt((btn as HTMLElement).dataset.level!, 10);
				this.switchLevel(levelId);

				// Easter egg: triple-click Level 0 reveals Level -1
				if (levelId === 0) {
					this.level0ClickCount++;
					if (this.level0ClickTimer) clearTimeout(this.level0ClickTimer);
					this.level0ClickTimer = setTimeout(() => { this.level0ClickCount = 0; }, 600);
					if (this.level0ClickCount >= 3) {
						this.level0ClickCount = 0;
						this.showEasterEgg();
					}
				}
			});
		});
	}

	private switchLevel(level: number) {
		if (!this.map || !this.config) return;
		this.currentLevel = level;

		const { event, apiBase } = this.config;
		const tileUrl = `${apiBase}/api/c3nav/tiles/${event}/${level}/{z}/{x}/{y}`;

		const source = this.map.getSource("c3nav-tiles");
		if (source) {
			source.setTiles([tileUrl]);
		}

		this.renderLevelSelector();
	}

	private showEasterEgg() {
		// Switch to Level -1 (underground)
		this.switchLevel(-1);

		const egg = document.createElement("div");
		egg.className = "easter-egg";
		egg.textContent = "🕳️ You found the secret underground level!";
		this.shadow.appendChild(egg);
		setTimeout(() => egg.remove(), 3500);
	}

	private renderError() {
		const overlay = this.shadow.getElementById("loading-overlay");
		if (overlay) {
			overlay.className = "error-overlay";
			overlay.innerHTML = `
				<span>⚠️ ${this.error}</span>
				<button class="outdoor-btn" id="switch-outdoor">Switch to Outdoor Map</button>
			`;
			overlay.querySelector("#switch-outdoor")?.addEventListener("click", () => {
				this.dispatchEvent(new CustomEvent("switch-outdoor", { bubbles: true, composed: true }));
			});
		}
	}

	/** Update indoor participant markers */
	updateParticipants(participants: Record<string, ParticipantState>) {
		if (!this.map || !(window as any).maplibregl) return;

		const currentIds = new Set<string>();

		for (const [id, p] of Object.entries(participants)) {
			if (!p.location?.indoor) continue;
			currentIds.add(id);

			// Only show participants on the current level
			if (p.location.indoor.level !== this.currentLevel) {
				if (this.participantMarkers.has(id)) {
					this.participantMarkers.get(id).remove();
					this.participantMarkers.delete(id);
				}
				continue;
			}

			const lngLat: [number, number] = [p.location.longitude, p.location.latitude];

			if (this.participantMarkers.has(id)) {
				this.participantMarkers.get(id).setLngLat(lngLat);
			} else {
				const el = document.createElement("div");
				el.style.cssText = `
					width: 32px; height: 32px; border-radius: 50%;
					border: 2px solid ${p.color}; background: #1a1a2e;
					display: flex; align-items: center; justify-content: center;
					font-size: 16px; cursor: pointer;
					box-shadow: 0 0 8px ${p.color}60;
				`;
				el.textContent = p.emoji;
				el.title = p.name;

				const marker = new (window as any).maplibregl.Marker({ element: el })
					.setLngLat(lngLat)
					.addTo(this.map);
				this.participantMarkers.set(id, marker);
			}
		}

		// Remove markers for participants no longer indoor
		for (const [id, marker] of this.participantMarkers) {
			if (!currentIds.has(id)) {
				marker.remove();
				this.participantMarkers.delete(id);
			}
		}
	}
}

customElements.define("map-indoor-view", MapIndoorView);
export { MapIndoorView };
