/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck

import type { WorldshaperStudioBootstrap } from "../worldshaperStudio/bootstrap";
import {
  loadWorldshaperStudioBootstrap,
  loadStandaloneWorldshaperBootstrap,
} from "../worldshaperStudio/bootstrap";
import { persistWorldshaperHeightViewerBounds } from "../worldshaperStudio/windowing";
import { createDebouncedCallback } from "../worldshaperStudio/debounce";

const VIEWER_STYLE_ID = "worldshaper-height-viewer-styles";

function ensureStyles(): void {
  let styleEl = document.getElementById(VIEWER_STYLE_ID) as HTMLStyleElement | null;
  if (!styleEl) {
    styleEl = document.createElement("style");
    styleEl.id = VIEWER_STYLE_ID;
    document.head.appendChild(styleEl);
  }
  styleEl.textContent = `
    :root { color-scheme: dark; }
    * { box-sizing: border-box; }
    html, body {
      margin: 0;
      width: 100%;
      height: 100%;
      background: #07111f;
      color: #d8e8ff;
      font-family: Segoe UI, Arial, sans-serif;
    }
    .viewer-shell {
      display: grid;
      grid-template-rows: 52px 1fr;
      width: 100vw;
      height: 100vh;
    }
    .viewer-bar {
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto auto;
      align-items: center;
      gap: 12px;
      padding: 8px 12px;
      border-bottom: 1px solid #274472;
      background: linear-gradient(180deg, #152645 0%, #0d1b33 100%);
    }
    .viewer-title {
      min-width: 0;
      display: grid;
      gap: 2px;
    }
    .viewer-title strong {
      font-size: 14px;
      color: #eef6ff;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .viewer-title span {
      font-size: 11px;
      color: #9fb8e5;
    }
    .viewer-controls {
      display: inline-flex;
      align-items: center;
      gap: 8px;
    }
    .viewer-btn {
      height: 34px;
      padding: 0 12px;
      border: 1px solid #3c5e95;
      border-radius: 8px;
      background: #1a345e;
      color: #d6e7ff;
      font-size: 12px;
      font-weight: 700;
      cursor: pointer;
    }
    .viewer-btn:hover {
      background: #214679;
    }
    .viewer-btn:disabled {
      opacity: 0.55;
      cursor: not-allowed;
      background: #132643;
    }
    .viewer-height-pill {
      min-width: 96px;
      height: 34px;
      padding: 0 12px;
      border: 1px solid #3c5e95;
      border-radius: 999px;
      background: #10284b;
      color: #d6e7ff;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
      font-weight: 700;
      white-space: nowrap;
    }
    .viewer-hint {
      font-size: 11px;
      color: #9fb8e5;
      white-space: nowrap;
    }
    .viewer-viewport {
      position: relative;
      min-width: 0;
      min-height: 0;
      overflow: auto;
      background:
        linear-gradient(180deg, rgba(20, 38, 69, 0.16), rgba(7, 17, 31, 0.04)),
        #07111f;
    }
    .viewer-viewport-layer {
      position: sticky;
      top: 0;
      left: 0;
      width: 100%;
      height: 0;
      overflow: visible;
      z-index: 1;
      pointer-events: none;
    }
    .viewer-viewport-layer canvas {
      display: block;
      pointer-events: none;
      image-rendering: pixelated;
      image-rendering: crisp-edges;
    }
    .viewer-viewport-spacer {
      position: relative;
      z-index: 0;
      pointer-events: none;
    }
  `;
}

function renderMessage(title: string, message: string): void {
  document.body.innerHTML = "";
  document.body.style.margin = "0";
  document.body.style.minHeight = "100vh";
  document.body.style.display = "grid";
  document.body.style.placeItems = "center";
  document.body.style.background = "#07111f";
  document.body.style.color = "#d8e8ff";
  document.body.style.fontFamily = "Segoe UI, Arial, sans-serif";

  const panel = document.createElement("div");
  panel.style.maxWidth = "460px";
  panel.style.padding = "24px";
  panel.style.border = "1px solid #2e426c";
  panel.style.borderRadius = "10px";
  panel.style.background = "#0e1a33";
  panel.style.boxShadow = "0 12px 36px rgba(3, 8, 18, 0.45)";

  const heading = document.createElement("h1");
  heading.textContent = title;
  heading.style.margin = "0 0 8px";
  heading.style.fontSize = "18px";

  const text = document.createElement("p");
  text.textContent = message;
  text.style.margin = "0";
  text.style.fontSize = "14px";
  text.style.lineHeight = "1.5";

  panel.appendChild(heading);
  panel.appendChild(text);
  document.body.appendChild(panel);
}

function renderLoading(message: string): void {
  renderMessage("Loading Worldshaper Height Viewer", message);
}

function renderError(message: string): void {
  renderMessage("Worldshaper Height Viewer unavailable", message);
}

function cloneValue<T>(value: T): T {
  if (typeof structuredClone === "function") {
    return structuredClone(value);
  }
  return value == null ? value : JSON.parse(JSON.stringify(value));
}

function buildViewerMarkup(): string {
  return `
    <div class="viewer-shell">
      <div class="viewer-bar">
        <div class="viewer-title">
          <strong id="viewerTitle">Worldshaper Height Viewer</strong>
          <span id="viewerMeta">Previewing current world snapshot.</span>
        </div>
        <div class="viewer-controls">
          <button class="viewer-btn" id="heightDownBtn" type="button">Height -</button>
          <div class="viewer-height-pill" id="heightLabel">Height 0</div>
          <button class="viewer-btn" id="heightUpBtn" type="button">Height +</button>
        </div>
        <div class="viewer-hint">Use Up / Down arrows to change height.</div>
      </div>
      <div class="viewer-viewport" id="viewerViewport">
        <div class="viewer-viewport-layer">
          <canvas id="viewerCanvas"></canvas>
        </div>
        <div class="viewer-viewport-spacer" id="viewerViewportSpacer" aria-hidden="true"></div>
      </div>
    </div>
  `;
}

function startViewer(bootstrap: WorldshaperStudioBootstrap): void {
  document.body.removeAttribute("style");
  document.body.innerHTML = buildViewerMarkup();
  document.title = "Worldshaper Height Viewer - " + (bootstrap.mapName || bootstrap.mapId || "Untitled");

  const titleEl = document.getElementById("viewerTitle");
  const metaEl = document.getElementById("viewerMeta");
  const heightLabelEl = document.getElementById("heightLabel");
  const heightDownBtn = document.getElementById("heightDownBtn") as HTMLButtonElement | null;
  const heightUpBtn = document.getElementById("heightUpBtn") as HTMLButtonElement | null;
  const viewportEl = document.getElementById("viewerViewport") as HTMLDivElement | null;
  const viewportSpacerEl = document.getElementById("viewerViewportSpacer") as HTMLDivElement | null;
  const canvasEl = document.getElementById("viewerCanvas") as HTMLCanvasElement | null;
  const ctx = canvasEl?.getContext("2d") || null;

  if (!viewportEl || !viewportSpacerEl || !canvasEl || !ctx) {
    renderError("The height viewer could not initialize its canvas.");
    return;
  }

  const mapWidth = Math.max(1, Number(bootstrap.width) || 1);
  const mapHeight = Math.max(1, Number(bootstrap.height) || 1);
  const tileSize = Math.max(8, Number(bootstrap.tileSize) || 32);
  const worldPixelWidth = mapWidth * tileSize;
  const worldPixelHeight = mapHeight * tileSize;
  const backgroundColor = /^#[0-9a-fA-F]{6}$/.test(String(bootstrap.backgroundColor || "").trim())
    ? String(bootstrap.backgroundColor).trim().toUpperCase()
    : "#060A14";
  const layers = Array.isArray(bootstrap.roomLayers)
    ? cloneValue(bootstrap.roomLayers).map((layer) => ({
      layer: Number(layer.layer) || 0,
      name: typeof layer.name === "string" ? layer.name.trim() : "",
      rows: Array.isArray(layer.rows) ? layer.rows.map((row) => String(row || "")) : [],
    })).sort((a, b) => a.layer - b.layer)
    : [];
  const heightLayers = Array.isArray(bootstrap.heightLayers)
    ? cloneValue(bootstrap.heightLayers).map((entry, index) => {
      const rows = Array.isArray(entry?.rows) ? entry.rows.map((row) => String(row || "").replace(/\./g, " ")) : [];
      const width = rows.reduce((max, row) => Math.max(max, row.length), 0);
      const height = rows.length;
      const x = Math.max(0, Number(entry?.x) || 0);
      const y = Math.max(0, Number(entry?.y) || 0);
      return {
        id: String(entry?.id || ("height_" + String(index + 1))).trim() || ("height_" + String(index + 1)),
        name: typeof entry?.name === "string" ? entry.name.trim() : "",
        z: Math.max(1, Math.floor(Number(entry?.z) || 1)),
        x,
        y,
        rows,
        width,
        height,
        pixelX: x * tileSize,
        pixelY: y * tileSize,
        pixelWidth: width * tileSize,
        pixelHeight: height * tileSize,
      };
    })
    : [];
  const heightLayersByZ = new Map<number, Array<Record<string, unknown>>>();
  heightLayers.forEach((entry) => {
    const z = Math.max(1, Number(entry.z) || 1);
    if (!heightLayersByZ.has(z)) {
      heightLayersByZ.set(z, []);
    }
    heightLayersByZ.get(z)?.push(entry);
  });
  const maxHeight = heightLayers.reduce((max, entry) => Math.max(max, Math.max(1, Number(entry?.z) || 1)), 0);

  const tileCatalogBySymbol: Record<string, {
    symbol: string;
    color: string;
    dataUrl: string | null;
  }> = {};

  Object.entries(bootstrap.tileColors || {}).forEach(([symbol, color]) => {
    tileCatalogBySymbol[symbol] = {
      symbol,
      color: String(color || "#7AA7FF"),
      dataUrl: null,
    };
  });
  Object.values(bootstrap.tileCatalogById || {}).forEach((entry) => {
    const symbol = String(entry?.symbol || "").charAt(0);
    if (!symbol) {
      return;
    }
    tileCatalogBySymbol[symbol] = {
      symbol,
      color: String(entry?.color || tileCatalogBySymbol[symbol]?.color || "#7AA7FF"),
      dataUrl: entry?.dataUrl || null,
    };
  });

  const backgroundTileId = String(bootstrap.backgroundTileId || "").trim();
  const backgroundSymbol = backgroundTileId
    ? String(bootstrap.tileCatalogById?.[backgroundTileId]?.symbol || ".").charAt(0) || "."
    : "";
  const imageCache: Record<string, HTMLImageElement> = {};
  const patchSurfaceCache = new Map<string, HTMLCanvasElement>();
  const baseSurfaceCanvas = document.createElement("canvas");
  const baseSurfaceCtx = baseSurfaceCanvas.getContext("2d");
  const baseSurfaceState = {
    dirty: true,
    width: 0,
    height: 0,
    viewportLeft: -1,
    viewportTop: -1,
    tileSize: 0,
  };
  const state = {
    currentHeight: 0,
    pendingDrawFrame: 0,
  };
  const heightBlurStep = Math.max(0, Math.min(1, Number(bootstrap.heightBlurStep ?? bootstrap.heightDetailStep) || 0.1));
  function clampViewerHeight(value: unknown): number {
    return Math.max(0, Math.min(maxHeight, Number(value) || 0));
  }

  function getHeightBlurStrength(height: number): number {
    const normalizedHeight = Math.max(0, Number(height) || 0);
    return Math.min(8, normalizedHeight * heightBlurStep * (tileSize / 4));
  }

  function syncViewportDimensions(): void {
    const nextCanvasWidth = Math.max(1, Math.ceil(Number(viewportEl.clientWidth) || 0));
    const nextCanvasHeight = Math.max(1, Math.ceil(Number(viewportEl.clientHeight) || 0));
    if (canvasEl.width !== nextCanvasWidth || canvasEl.height !== nextCanvasHeight) {
      canvasEl.width = nextCanvasWidth;
      canvasEl.height = nextCanvasHeight;
    }
    canvasEl.style.width = nextCanvasWidth + "px";
    canvasEl.style.height = nextCanvasHeight + "px";
    viewportSpacerEl.style.width = Math.max(nextCanvasWidth, worldPixelWidth) + "px";
    viewportSpacerEl.style.height = Math.max(nextCanvasHeight, worldPixelHeight) + "px";
  }

  function getViewportRenderRect() {
    const viewportWidth = Math.max(1, Math.ceil(Number(viewportEl.clientWidth) || 0));
    const viewportHeight = Math.max(1, Math.ceil(Number(viewportEl.clientHeight) || 0));
    const left = Math.max(0, Math.min(worldPixelWidth, Math.floor(Number(viewportEl.scrollLeft) || 0)));
    const top = Math.max(0, Math.min(worldPixelHeight, Math.floor(Number(viewportEl.scrollTop) || 0)));
    const right = Math.max(left + 1, Math.min(worldPixelWidth, Math.ceil((Number(viewportEl.scrollLeft) || 0) + viewportWidth)));
    const bottom = Math.max(top + 1, Math.min(worldPixelHeight, Math.ceil((Number(viewportEl.scrollTop) || 0) + viewportHeight)));
    return {
      left,
      top,
      right,
      bottom,
      width: right - left,
      height: bottom - top,
    };
  }

  function rectIntersects(rect, x, y, width, height): boolean {
    return x + width > rect.left && x < rect.right && y + height > rect.top && y < rect.bottom;
  }

  function updateHeightLabel(): void {
    if (heightLabelEl) {
      heightLabelEl.textContent = "Height " + state.currentHeight;
    }
    if (heightDownBtn) {
      heightDownBtn.disabled = state.currentHeight <= 0;
    }
    if (heightUpBtn) {
      heightUpBtn.disabled = state.currentHeight >= maxHeight;
    }
  }

  function invalidateBaseSurface(): void {
    baseSurfaceState.dirty = true;
    baseSurfaceState.viewportLeft = -1;
    baseSurfaceState.viewportTop = -1;
    baseSurfaceState.tileSize = 0;
  }

  function drawSymbolAtPixel(targetCtx: CanvasRenderingContext2D, symbol: string, drawX: number, drawY: number): void {
    const tileEntry = tileCatalogBySymbol[symbol] || tileCatalogBySymbol["."] || { color: "#7AA7FF", dataUrl: null };
    const img = getTileImage(symbol);
    if (img && img.complete && img.naturalWidth > 0) {
      targetCtx.drawImage(img, drawX, drawY, tileSize, tileSize);
      return;
    }
    targetCtx.fillStyle = tileEntry.color || "#7AA7FF";
    targetCtx.fillRect(drawX, drawY, tileSize, tileSize);
  }

  function getTileImage(symbol: string): HTMLImageElement | null {
    const tileEntry = tileCatalogBySymbol[symbol];
    if (!tileEntry?.dataUrl) {
      return null;
    }
    if (imageCache[symbol]) {
      return imageCache[symbol];
    }
    const img = new Image();
    img.src = tileEntry.dataUrl;
    img.onload = () => {
      patchSurfaceCache.clear();
      invalidateBaseSurface();
      draw();
    };
    imageCache[symbol] = img;
    return img;
  }

  function drawVisibleBaseTiles(targetCtx: CanvasRenderingContext2D, viewportRect): void {
    const startTileX = Math.max(0, Math.floor(viewportRect.left / tileSize));
    const endTileX = Math.min(mapWidth - 1, Math.ceil(viewportRect.right / tileSize));
    const startTileY = Math.max(0, Math.floor(viewportRect.top / tileSize));
    const endTileY = Math.min(mapHeight - 1, Math.ceil(viewportRect.bottom / tileSize));

    targetCtx.save();
    targetCtx.setTransform(1, 0, 0, 1, -viewportRect.left, -viewportRect.top);
    targetCtx.imageSmoothingEnabled = false;
    layers.forEach((layer) => {
      const isBackgroundLayer = (Number(layer.layer) || 0) === 0;
      const fillChar = isBackgroundLayer ? "." : " ";
      const rows = Array.isArray(layer.rows) ? layer.rows : [];
      for (let tileY = startTileY; tileY <= endTileY; tileY += 1) {
        const row = String(rows[tileY] || "");
        for (let tileX = startTileX; tileX <= endTileX; tileX += 1) {
          let ch = row.charAt(tileX) || fillChar;
          if (isBackgroundLayer && ch === " ") {
            continue;
          }
          if (isBackgroundLayer && ch === "." && backgroundSymbol) {
            ch = backgroundSymbol;
          }
          if (!isBackgroundLayer && ch === " ") {
            continue;
          }
          if (ch === ".") {
            continue;
          }
          drawSymbolAtPixel(targetCtx, ch, tileX * tileSize, tileY * tileSize);
        }
      }
    });
    targetCtx.restore();
  }

  function baseSurfaceNeedsRefresh(viewportRect, canvasWidth: number, canvasHeight: number): boolean {
    if (baseSurfaceState.dirty || baseSurfaceState.width !== canvasWidth || baseSurfaceState.height !== canvasHeight) {
      return true;
    }
    if (baseSurfaceState.viewportLeft !== viewportRect.left || baseSurfaceState.viewportTop !== viewportRect.top) {
      return true;
    }
    if (baseSurfaceState.tileSize !== tileSize) {
      return true;
    }
    return false;
  }

  function refreshBaseSurface(viewportRect, canvasWidth: number, canvasHeight: number): void {
    if (!baseSurfaceCtx) {
      return;
    }
    if (baseSurfaceCanvas.width !== canvasWidth || baseSurfaceCanvas.height !== canvasHeight) {
      baseSurfaceCanvas.width = canvasWidth;
      baseSurfaceCanvas.height = canvasHeight;
    }
    baseSurfaceCtx.setTransform(1, 0, 0, 1, 0, 0);
    baseSurfaceCtx.clearRect(0, 0, canvasWidth, canvasHeight);
    drawVisibleBaseTiles(baseSurfaceCtx, viewportRect);
    baseSurfaceState.width = canvasWidth;
    baseSurfaceState.height = canvasHeight;
    baseSurfaceState.viewportLeft = viewportRect.left;
    baseSurfaceState.viewportTop = viewportRect.top;
    baseSurfaceState.tileSize = tileSize;
    baseSurfaceState.dirty = false;
  }

  function getOrBuildPatchSurface(entry): HTMLCanvasElement | null {
    if (!entry || entry.pixelWidth <= 0 || entry.pixelHeight <= 0) {
      return null;
    }
    const entryId = String(entry.id || "").trim();
    if (!entryId) {
      return null;
    }
    const cached = patchSurfaceCache.get(entryId);
    if (cached) {
      return cached;
    }
    const surface = document.createElement("canvas");
    surface.width = Math.max(1, Number(entry.pixelWidth) || 1);
    surface.height = Math.max(1, Number(entry.pixelHeight) || 1);
    const surfaceCtx = surface.getContext("2d");
    if (!surfaceCtx) {
      return null;
    }
    surfaceCtx.imageSmoothingEnabled = false;
    const rows = Array.isArray(entry.rows) ? entry.rows : [];
    rows.forEach((rawRow, localY) => {
      const row = String(rawRow || "");
      for (let localX = 0; localX < row.length; localX += 1) {
        const symbol = String(row.charAt(localX) || " ").charAt(0) || " ";
        if (symbol === " " || symbol === ".") {
          continue;
        }
        drawSymbolAtPixel(surfaceCtx, symbol, localX * tileSize, localY * tileSize);
      }
    });
    patchSurfaceCache.set(entryId, surface);
    return surface;
  }

  function drawVisibleHeightPatches(viewportRect): void {
    const visibleEntries = heightLayersByZ.get(state.currentHeight) || [];
    if (visibleEntries.length <= 0) {
      return;
    }
    ctx.save();
    ctx.imageSmoothingEnabled = false;
    visibleEntries.forEach((entry) => {
      const patchPixelX = Math.max(0, Number(entry.pixelX) || 0);
      const patchPixelY = Math.max(0, Number(entry.pixelY) || 0);
      const patchPixelWidth = Math.max(0, Number(entry.pixelWidth) || 0);
      const patchPixelHeight = Math.max(0, Number(entry.pixelHeight) || 0);
      if (patchPixelWidth <= 0 || patchPixelHeight <= 0) {
        return;
      }
      if (!rectIntersects(viewportRect, patchPixelX, patchPixelY, patchPixelWidth, patchPixelHeight)) {
        return;
      }
      const surface = getOrBuildPatchSurface(entry);
      if (!surface) {
        return;
      }
      const cropLeft = Math.max(viewportRect.left, patchPixelX);
      const cropTop = Math.max(viewportRect.top, patchPixelY);
      const cropRight = Math.min(viewportRect.right, patchPixelX + patchPixelWidth);
      const cropBottom = Math.min(viewportRect.bottom, patchPixelY + patchPixelHeight);
      const sourceX = cropLeft - patchPixelX;
      const sourceY = cropTop - patchPixelY;
      const drawWidth = cropRight - cropLeft;
      const drawHeight = cropBottom - cropTop;
      if (drawWidth <= 0 || drawHeight <= 0) {
        return;
      }
      ctx.drawImage(
        surface,
        sourceX,
        sourceY,
        drawWidth,
        drawHeight,
        cropLeft - viewportRect.left,
        cropTop - viewportRect.top,
        drawWidth,
        drawHeight,
      );
    });
    ctx.restore();
  }

  function performDraw(): void {
    syncViewportDimensions();
    const canvasWidth = Math.max(1, canvasEl.width || Math.ceil(Number(viewportEl.clientWidth) || 0));
    const canvasHeight = Math.max(1, canvasEl.height || Math.ceil(Number(viewportEl.clientHeight) || 0));
    const viewportRect = getViewportRenderRect();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, canvasWidth, canvasHeight);
    if (baseSurfaceNeedsRefresh(viewportRect, canvasWidth, canvasHeight)) {
      refreshBaseSurface(viewportRect, canvasWidth, canvasHeight);
    }
    if (baseSurfaceCanvas.width > 0 && baseSurfaceCanvas.height > 0) {
      ctx.save();
      if (state.currentHeight > 0) {
        const blurStrength = getHeightBlurStrength(state.currentHeight);
        ctx.globalAlpha = Math.max(0.95, 1 - (state.currentHeight * 0.01));
        ctx.filter = blurStrength > 0 ? `blur(${blurStrength}px)` : "none";
        ctx.imageSmoothingEnabled = blurStrength > 0;
        ctx.drawImage(baseSurfaceCanvas, 0, 0);
      } else {
        ctx.globalAlpha = 1;
        ctx.filter = "none";
        ctx.imageSmoothingEnabled = false;
        ctx.drawImage(baseSurfaceCanvas, 0, 0);
      }
      ctx.restore();
      if (state.currentHeight > 0) {
        ctx.save();
        ctx.fillStyle = "rgba(7, 12, 20, 0.06)";
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);
        ctx.restore();
      }
    }
    drawVisibleHeightPatches(viewportRect);
    updateHeightLabel();
  }

  function drawNow(): void {
    if (state.pendingDrawFrame) {
      window.cancelAnimationFrame(state.pendingDrawFrame);
      state.pendingDrawFrame = 0;
    }
    performDraw();
  }

  function draw(): void {
    if (state.pendingDrawFrame) {
      return;
    }
    state.pendingDrawFrame = window.requestAnimationFrame(() => {
      state.pendingDrawFrame = 0;
      performDraw();
    });
  }

  function setHeight(nextHeight: number): void {
    const normalizedHeight = clampViewerHeight(nextHeight);
    if (normalizedHeight === state.currentHeight) {
      updateHeightLabel();
      return;
    }
    state.currentHeight = normalizedHeight;
    draw();
  }

  function changeHeight(delta: number): void {
    setHeight(state.currentHeight + (Number(delta) || 0));
  }

  function handleWindowKeydown(event: KeyboardEvent): void {
    if (event.defaultPrevented) {
      return;
    }
    if (event.key === "ArrowUp") {
      event.preventDefault();
      changeHeight(1);
      return;
    }
    if (event.key === "ArrowDown") {
      event.preventDefault();
      changeHeight(-1);
    }
  }

  titleEl.textContent = bootstrap.mapName || bootstrap.mapId || "Worldshaper Height Viewer";
  metaEl.textContent = bootstrap.mapId + " | " + mapWidth + "x" + mapHeight + " | tile " + tileSize + "px | " + heightLayers.length + " height patch" + (heightLayers.length === 1 ? "" : "es");
  const persistBounds = () => {
    persistWorldshaperHeightViewerBounds(window);
  };
  const persistBoundsDeferred = createDebouncedCallback(() => {
    persistBounds();
  }, 160);
  heightDownBtn?.addEventListener("click", () => changeHeight(-1));
  heightUpBtn?.addEventListener("click", () => changeHeight(1));
  window.addEventListener("keydown", handleWindowKeydown);
  window.addEventListener("resize", () => {
    invalidateBaseSurface();
    draw();
    persistBoundsDeferred();
  });
  viewportEl.addEventListener("scroll", () => {
    draw();
  }, { passive: true });

  drawNow();
  window.addEventListener("beforeunload", persistBounds);
}

async function initHeightViewer(): Promise<void> {
  ensureStyles();
  renderLoading("Preparing world snapshot...");
  const params = new URLSearchParams(window.location.search);
  const token = params.get("token")?.trim() || "";
  const requestedWorldId = params.get("worldId")?.trim() || params.get("mapId")?.trim() || "";

  let bootstrap = loadWorldshaperStudioBootstrap(token);
  if (!bootstrap) {
    try {
      bootstrap = await loadStandaloneWorldshaperBootstrap(requestedWorldId, window.location.origin);
    } catch (error) {
      renderError(String(error || "Failed to load the height viewer."));
      return;
    }
  }

  if (!bootstrap) {
    renderError("No world data was available for the height viewer.");
    return;
  }

  startViewer(bootstrap);
}

void initHeightViewer();

