import {
  useApplication,
  useExtend,
  useTick,
} from '@pixi/react'
import {
  Container,
  Graphics,
  Text,
  Ticker,
  type Graphics as PixiGraphics,
} from 'pixi.js'
import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  type PointerEvent as ReactPointerEvent,
} from 'react'
import { PixiSurface } from './pixi/PixiSurface'
import {
  buildTopologySceneModel,
  describeTarget,
  findHoverTarget,
  getFitViewport,
  worldToScreen,
  type HoverTarget,
  type SceneGpu,
  type SceneNode,
  type TargetDetails,
  type TopologySceneModel,
  type ViewportState,
} from '../lib/topologyScene'
import { matchesLinkedFocus, type LinkedFocus } from '../lib/linkedFocus'
import { type WorkbenchViewModel } from '../lib/workbenchPresenter'
import {
  TOPOLOGY_LOD_POLICY,
  getTopologyLodState,
  mix,
  screenStroke,
  screenWorld,
  type TopologyLodState,
} from '../lib/topologyLod'

type ClusterMapProps = {
  viewModel: WorkbenchViewModel
  debugEnabled: boolean
  snapshotMode: boolean
  linkedFocus: LinkedFocus | null
}

type DebugToggles = {
  bounds: boolean
  ids: boolean
  heat: boolean
  hitAreas: boolean
  stats: boolean
}

type ScenePointer = {
  x: number
  y: number
}

type DebugObjectMap = Record<
  string,
  {
    x: number
    y: number
    width: number
    height: number
  }
>

const MIN_SCALE = TOPOLOGY_LOD_POLICY.minScale
const MAX_SCALE = TOPOLOGY_LOD_POLICY.maxScale

const clamp = (value: number, min: number, max: number) =>
  Math.min(Math.max(value, min), max)

type ViewportConstraints = {
  minScale: number
  maxScale: number
  minX: number
  maxX: number
  minY: number
  maxY: number
}

const getViewportConstraints = (
  model: TopologySceneModel,
  width: number,
  height: number,
  scale: number,
): ViewportConstraints => {
  const fitViewport = getFitViewport(model, width, height)
  const minScale = fitViewport.scale
  const maxScale = clamp(Math.max(minScale * 180, minScale + 0.001), minScale, MAX_SCALE)
  const safeScale = clamp(scale, minScale, maxScale)
  const scaledWidth = model.width * safeScale
  const scaledHeight = model.height * safeScale
  const centeredX = (width - scaledWidth) / 2
  const centeredY = (height - scaledHeight) / 2

  if (scaledWidth <= width) {
    return {
      minScale,
      maxScale,
      minX: centeredX,
      maxX: centeredX,
      minY: scaledHeight <= height ? centeredY : height - scaledHeight,
      maxY: scaledHeight <= height ? centeredY : 0,
    }
  }

  if (scaledHeight <= height) {
    return {
      minScale,
      maxScale,
      minX: width - scaledWidth,
      maxX: 0,
      minY: centeredY,
      maxY: centeredY,
    }
  }

  return {
    minScale,
    maxScale,
    minX: width - scaledWidth,
    maxX: 0,
    minY: height - scaledHeight,
    maxY: 0,
  }
}

const clampViewportToScene = (
  nextViewport: ViewportState,
  model: TopologySceneModel,
  width: number,
  height: number,
): ViewportState => {
  if (width <= 0 || height <= 0) {
    return nextViewport
  }

  const constraints = getViewportConstraints(model, width, height, nextViewport.scale)
  const scale = clamp(nextViewport.scale, constraints.minScale, constraints.maxScale)
  const clamped = getViewportConstraints(model, width, height, scale)

  return {
    scale,
    x: clamp(nextViewport.x, clamped.minX, clamped.maxX),
    y: clamp(nextViewport.y, clamped.minY, clamped.maxY),
  }
}

const noopDraw = (graphics: PixiGraphics) => {
  graphics.clear()
}

const pulse = (timeMs: number, offset: number, depth: number) =>
  1 + Math.sin(timeMs / 1000 * 1.8 + offset) * depth

const drawCornerFocus = (
  graphics: PixiGraphics,
  bounds: { x: number; y: number; width: number; height: number },
  scale: number,
  color: number,
  alpha: number,
  lengthPx: number,
  insetPx: number,
  strokePx: number,
) => {
  const length = screenStroke(scale, lengthPx, 0.3, 16)
  const inset = screenStroke(scale, insetPx, 0.12, 8)
  const stroke = screenStroke(scale, strokePx, 0.08, 2.4)
  const left = bounds.x - inset
  const top = bounds.y - inset
  const right = bounds.x + bounds.width + inset
  const bottom = bounds.y + bounds.height + inset

  graphics
    .moveTo(left, top + length)
    .lineTo(left, top)
    .lineTo(left + length, top)
    .stroke({ color, alpha, width: stroke, cap: 'square', join: 'miter' })
  graphics
    .moveTo(right - length, top)
    .lineTo(right, top)
    .lineTo(right, top + length)
    .stroke({ color, alpha, width: stroke, cap: 'square', join: 'miter' })
  graphics
    .moveTo(left, bottom - length)
    .lineTo(left, bottom)
    .lineTo(left + length, bottom)
    .stroke({ color, alpha, width: stroke, cap: 'square', join: 'miter' })
  graphics
    .moveTo(right - length, bottom)
    .lineTo(right, bottom)
    .lineTo(right, bottom - length)
    .stroke({ color, alpha, width: stroke, cap: 'square', join: 'miter' })
}

function createDebugObjectMap(
  model: TopologySceneModel,
  viewport: ViewportState,
): DebugObjectMap {
  const pods = Object.fromEntries(
    model.pods.map((pod) => [pod.id, worldToScreen(pod.hitBounds, viewport)]),
  )
  const nodes = Object.fromEntries(
    model.nodes.map((node) => [node.id, worldToScreen(node.hitBounds, viewport)]),
  )
  const gpus = Object.fromEntries(
    model.nodes
      .flatMap((node) => node.gpus)
      .map((gpu) => [gpu.id, worldToScreen(gpu.hitBounds, viewport)]),
  )

  return {
    ...pods,
    ...nodes,
    ...gpus,
  }
}

const screenRadius = (
  scale: number,
  pixels: number,
  minWorld = 0.06,
  maxWorld = 12,
) => screenWorld(scale, pixels, minWorld, maxWorld)

const makeRect = (x: number, y: number, width: number, height: number) => ({
  x,
  y,
  width,
  height,
})

const insetRect = (
  rect: { x: number; y: number; width: number; height: number },
  insetX: number,
  insetY: number,
) =>
  makeRect(
    rect.x + insetX,
    rect.y + insetY,
    Math.max(rect.width - insetX * 2, 0.0001),
    Math.max(rect.height - insetY * 2, 0.0001),
  )

const getWorldViewportBounds = (
  viewport: ViewportState,
  width: number,
  height: number,
  paddingWorld: number,
) =>
  makeRect(
    -viewport.x / viewport.scale - paddingWorld,
    -viewport.y / viewport.scale - paddingWorld,
    width / viewport.scale + paddingWorld * 2,
    height / viewport.scale + paddingWorld * 2,
  )

const rectsIntersect = (
  left: { x: number; y: number; width: number; height: number },
  right: { x: number; y: number; width: number; height: number },
) =>
  left.x <= right.x + right.width &&
  left.x + left.width >= right.x &&
  left.y <= right.y + right.height &&
  left.y + left.height >= right.y

const lineBounds = (
  x1: number,
  y1: number,
  x2: number,
  y2: number,
  pad: number,
) =>
  makeRect(
    Math.min(x1, x2) - pad,
    Math.min(y1, y2) - pad,
    Math.abs(x2 - x1) + pad * 2,
    Math.abs(y2 - y1) + pad * 2,
  )

function drawModule(
  graphics: PixiGraphics,
  gpu: SceneGpu,
  scale: number,
  linked: boolean,
  lod: TopologyLodState,
  emphasis: number,
) {
  const outer = gpu.lodFrame
  const projectedOuterWidth = outer.width * scale
  const projectedOuterHeight = outer.height * scale
  const activeLoad = gpu.active ? mix(0.42, 1, gpu.utilization) : 0
  const shell = insetRect(outer, outer.width * 0.04, outer.height * 0.06)
  const carrier = insetRect(shell, shell.width * 0.05, shell.height * 0.08)
  const coldPlate = insetRect(carrier, carrier.width * 0.14, carrier.height * 0.18)
  const packageFrame = insetRect(coldPlate, coldPlate.width * 0.1, coldPlate.height * 0.13)
  const substrate = insetRect(packageFrame, packageFrame.width * 0.06, packageFrame.height * 0.1)
  const interposer = insetRect(substrate, substrate.width * 0.1, substrate.height * 0.14)
  const die = insetRect(interposer, interposer.width * 0.2, interposer.height * 0.2)
  const dieGrid = insetRect(die, die.width * 0.04, die.height * 0.05)
  const connectorStrip = makeRect(
    shell.x + shell.width * 0.24,
    shell.y + shell.height * 0.82,
    shell.width * 0.52,
    shell.height * 0.08,
  )
  const boardStroke = linked ? 0xffefc0 : 0xcfdbe2
  const overview = Math.max(lod.weights.overview - lod.weights.board * 0.18, 0)
  const board = Math.max(lod.weights.board - lod.weights.package * 0.42, 0)
  const packageAlpha = Math.max(lod.weights.package - lod.weights.silicon * 0.52, 0)
  const siliconAlpha = Math.max(lod.weights.silicon - lod.weights.micro * 0.4, 0)
  const microAlpha = lod.weights.micro
  const boardPresence = Math.max(
    lod.weights.board,
    lod.weights.package * 0.84,
    lod.weights.silicon * 0.66,
  )
  const coldPlatePresence = Math.max(board * 0.7, packageAlpha * 0.88, siliconAlpha * 0.9, microAlpha * 0.8)
  const shellAlpha = mix(gpu.active ? 0.84 : 0.42, gpu.active ? 0.96 : 0.56, boardPresence)
  const frameAlpha = emphasis * (linked ? 0.92 : 0.56)
  const boardStrokeWidth = screenStroke(scale, linked ? 1.25 : 0.9, 0.08, 0.95)
  const detailStroke = screenStroke(scale, 0.6, 0.03, 0.5)
  const boardCorner = screenRadius(scale, 8, 0.18, 2.6)
  const innerCorner = screenRadius(scale, 5, 0.16, 2)
  const dieCorner = screenRadius(scale, 4, 0.14, 1.5)
  const renderCarrier = projectedOuterWidth >= 10 && projectedOuterHeight >= 8
  const renderColdPlate = projectedOuterWidth >= 14 && projectedOuterHeight >= 10
  const renderOverviewGlyph = overview > 0.02 && projectedOuterWidth >= 10
  const renderConnectorStrip = (overview > 0.02 || board > 0.02) && projectedOuterWidth >= 15
  const renderBoardTier = board > 0.03 && projectedOuterWidth >= 18
  const renderPackageTier = packageAlpha > 0.04 && projectedOuterWidth >= 30
  const renderSiliconTier = siliconAlpha > 0.05 && die.width * scale >= 26
  const renderMicroTier = microAlpha > 0.06 && die.width * scale >= 72
  const glowFrame = makeRect(
    shell.x - outer.width * 0.035,
    shell.y - outer.height * 0.05,
    shell.width + outer.width * 0.07,
    shell.height + outer.height * 0.1,
  )

  if (activeLoad > 0.001) {
    graphics
      .roundRect(
        glowFrame.x,
        glowFrame.y,
        glowFrame.width,
        glowFrame.height,
        screenRadius(scale, 10, 0.22, 3),
      )
      .fill({
        color: 0x59e7d2,
        alpha:
          emphasis *
          mix(
            projectedOuterWidth < 18 ? 0.08 : 0.04,
            projectedOuterWidth < 18 ? 0.2 : 0.1,
            activeLoad,
          ),
      })
  }

  graphics
    .roundRect(shell.x, shell.y, shell.width, shell.height, boardCorner)
    .fill({ color: gpu.active ? 0x0d1f29 : 0x0b1821, alpha: shellAlpha * emphasis })
    .stroke({ color: boardStroke, alpha: frameAlpha, width: boardStrokeWidth })

  if (projectedOuterWidth < 8 || projectedOuterHeight < 6) {
    if (activeLoad > 0.001) {
      const signalWidth = Math.min(
        shell.width * 0.54,
        screenWorld(scale, 5.6, 0.14, shell.width * 0.54),
      )
      const signalHeight = Math.min(
        shell.height * 0.34,
        screenWorld(scale, 2.8, 0.1, shell.height * 0.34),
      )
      const signalX = shell.x + (shell.width - signalWidth) / 2
      const signalY = shell.y + (shell.height - signalHeight) / 2

      graphics
        .roundRect(
          signalX,
          signalY,
          signalWidth,
          signalHeight,
          screenRadius(scale, 2.2, 0.05, 0.34),
        )
        .fill({
          color: 0x76f1df,
          alpha: emphasis * mix(0.68, 1, activeLoad),
        })
    }

    return
  }

  if (projectedOuterWidth < 15 || projectedOuterHeight < 10) {
    const core = insetRect(shell, shell.width * 0.3, shell.height * 0.28)
    graphics
      .roundRect(
        core.x,
        core.y,
        core.width,
        core.height,
        screenRadius(scale, 1.8, 0.04, 0.4),
      )
      .fill({
        color: gpu.active ? 0x6ce9d7 : 0x193843,
        alpha: emphasis * (gpu.active ? mix(0.6, 0.95, activeLoad) : 0.36),
      })
    return
  }

  if (renderCarrier) {
    graphics
      .roundRect(carrier.x, carrier.y, carrier.width, carrier.height, innerCorner)
      .fill({
        color: gpu.active ? 0x112833 : 0x10202a,
        alpha: mix(0.56, 0.82, boardPresence) * emphasis,
      })
  }

  if (renderColdPlate) {
    graphics
      .roundRect(
        coldPlate.x,
        coldPlate.y,
        coldPlate.width,
        coldPlate.height,
        screenRadius(scale, 4.5, 0.12, 1.8),
      )
      .fill({
        color: 0x163643,
        alpha:
          mix(0.02, 0.34, coldPlatePresence) *
          emphasis *
          Math.max(1 - microAlpha * 0.24, 0.76),
      })
  }

  if (renderConnectorStrip) {
    const connectorAlpha = Math.max(overview * 0.8, board * 0.55) * emphasis * (gpu.active ? 0.84 : 0.36)
    const padCount = 6
    const padWidth = connectorStrip.width * 0.11
    const padGap = connectorStrip.width * 0.05
    const totalWidth = padCount * padWidth + (padCount - 1) * padGap
    const padStart = connectorStrip.x + (connectorStrip.width - totalWidth) / 2

    for (let index = 0; index < padCount; index += 1) {
      const padX = padStart + index * (padWidth + padGap)
      graphics
        .roundRect(
          padX,
          connectorStrip.y,
          padWidth,
          connectorStrip.height,
          screenRadius(scale, 2, 0.04, 0.6),
        )
        .fill({ color: 0xd6ba72, alpha: connectorAlpha })
    }
  }

  if (renderOverviewGlyph) {
    const moduleWindow = insetRect(carrier, carrier.width * 0.24, carrier.height * 0.26)
    const dieWindow = makeRect(
      moduleWindow.x + moduleWindow.width * 0.31,
      moduleWindow.y + moduleWindow.height * 0.26,
      moduleWindow.width * 0.38,
      moduleWindow.height * 0.48,
    )
    graphics
      .roundRect(
        moduleWindow.x,
        moduleWindow.y,
        moduleWindow.width,
        moduleWindow.height,
        screenRadius(scale, 2.8, 0.06, 0.9),
      )
      .fill({
        color: gpu.active ? 0x235560 : 0x1a3d48,
        alpha: overview * emphasis * mix(gpu.active ? 0.5 : 0.42, gpu.active ? 0.82 : 0.42, activeLoad),
      })

    for (const x of [
      moduleWindow.x + moduleWindow.width * 0.14,
      moduleWindow.x + moduleWindow.width * 0.76,
    ]) {
      graphics
        .roundRect(
          x,
          moduleWindow.y + moduleWindow.height * 0.28,
          moduleWindow.width * 0.08,
          moduleWindow.height * 0.44,
          screenRadius(scale, 1.3, 0.03, 0.35),
        )
        .fill({
          color: gpu.active ? 0xdaf08e : 0xcddd73,
          alpha: overview * emphasis * mix(gpu.active ? 0.8 : 0.62, 1, activeLoad * 0.7),
        })
    }

    graphics
      .roundRect(
        dieWindow.x,
        dieWindow.y,
        dieWindow.width,
        dieWindow.height,
        screenRadius(scale, 1.7, 0.03, 0.42),
      )
      .fill({
        color: gpu.active ? 0x0b1820 : 0x081219,
        alpha: overview * emphasis * mix(gpu.active ? 0.92 : 0.86, 1, activeLoad * 0.4),
      })
  }

  if (renderBoardTier) {
    graphics
      .roundRect(
        coldPlate.x,
        coldPlate.y,
        coldPlate.width,
        coldPlate.height,
        screenRadius(scale, 4.5, 0.1, 1.2),
      )
      .stroke({
        color: 0x88b9c6,
        alpha: board * emphasis * 0.34,
        width: detailStroke,
      })

    const mountRadius = screenWorld(scale, 2.6, 0.03, 0.26)
    const mountAlpha = board * emphasis * (gpu.active ? 0.32 : 0.14)
    for (const [x, y] of [
      [carrier.x + carrier.width * 0.16, carrier.y + carrier.height * 0.2],
      [carrier.x + carrier.width * 0.84, carrier.y + carrier.height * 0.2],
      [carrier.x + carrier.width * 0.16, carrier.y + carrier.height * 0.74],
      [carrier.x + carrier.width * 0.84, carrier.y + carrier.height * 0.74],
    ]) {
      graphics.circle(x, y, mountRadius).fill({ color: 0x8ab7b7, alpha: mountAlpha })
    }

    if (activeLoad > 0.001) {
      const liveZone = insetRect(coldPlate, coldPlate.width * 0.3, coldPlate.height * 0.28)
      graphics
        .roundRect(
          liveZone.x,
          liveZone.y,
          liveZone.width,
          liveZone.height,
          screenRadius(scale, 3, 0.06, 0.8),
        )
        .fill({
          color: 0x64e6d4,
          alpha: board * emphasis * mix(0.12, 0.28, activeLoad),
        })
    }
  }

  if (renderPackageTier) {
    graphics
      .roundRect(packageFrame.x, packageFrame.y, packageFrame.width, packageFrame.height, innerCorner)
      .stroke({ color: 0xb7c7cd, alpha: packageAlpha * emphasis * 0.8, width: detailStroke })

    graphics
      .roundRect(substrate.x, substrate.y, substrate.width, substrate.height, innerCorner)
      .fill({ color: 0x294546, alpha: packageAlpha * emphasis * 0.34 })

    graphics
      .roundRect(interposer.x, interposer.y, interposer.width, interposer.height, innerCorner)
      .fill({ color: 0x2a5960, alpha: packageAlpha * emphasis * 0.3 })
      .stroke({ color: 0x9deedb, alpha: packageAlpha * emphasis * 0.18, width: detailStroke })

    const hbmWidth = interposer.width * 0.18
    const hbmHeight = interposer.height * 0.16
    for (let index = 0; index < 4; index += 1) {
      const hbmX = interposer.x + interposer.width * 0.04 + index * (hbmWidth + interposer.width * 0.03)
      for (const y of [interposer.y + interposer.height * 0.09, interposer.y + interposer.height * 0.75]) {
        graphics
          .roundRect(
            hbmX,
            y,
            hbmWidth,
            hbmHeight,
            screenRadius(scale, 2, 0.04, 0.45),
          )
          .fill({ color: 0xcfd86f, alpha: packageAlpha * emphasis * 0.7 })
      }
    }

    graphics
      .roundRect(die.x, die.y, die.width, die.height, dieCorner)
      .fill({ color: 0x09161d, alpha: packageAlpha * emphasis * 0.76 })
      .stroke({ color: 0x8bdacd, alpha: packageAlpha * emphasis * 0.24, width: detailStroke })
  }

  if (renderSiliconTier) {
    graphics
      .roundRect(die.x, die.y, die.width, die.height, dieCorner)
      .fill({ color: 0x0c1c22, alpha: siliconAlpha * emphasis * 0.58 })

    const tileColumns = 7
    const tileRows = 5
    const tileWidth = dieGrid.width / tileColumns
    const tileHeight = dieGrid.height / tileRows
    for (let row = 0; row < tileRows; row += 1) {
      for (let column = 0; column < tileColumns; column += 1) {
        const tileX = dieGrid.x + column * tileWidth
        const tileY = dieGrid.y + row * tileHeight
        const tileFill =
          column === 0
            ? 0xa2d8ec
            : row === 0 || row === tileRows - 1
              ? 0x7fb7ca
              : 0xb8ece2
        graphics
          .roundRect(
            tileX + tileWidth * 0.08,
            tileY + tileHeight * 0.12,
            tileWidth * 0.8,
            tileHeight * 0.72,
            screenRadius(scale, 1.2, 0.03, 0.26),
          )
          .fill({ color: tileFill, alpha: siliconAlpha * emphasis * (column === 0 ? 0.22 : 0.14) })
      }
    }

    for (const block of [
      makeRect(die.x + die.width * 0.06, die.y + die.height * 0.18, die.width * 0.14, die.height * 0.64),
      makeRect(die.x + die.width * 0.78, die.y + die.height * 0.26, die.width * 0.1, die.height * 0.48),
    ]) {
      graphics
        .roundRect(
          block.x,
          block.y,
          block.width,
          block.height,
          screenRadius(scale, 1.2, 0.03, 0.3),
        )
        .fill({ color: 0xaee6ff, alpha: siliconAlpha * emphasis * 0.14 })
    }
  }

  if (renderMicroTier) {
    const cellColumns = 38
    const cellRows = 24
    const cellWidth = dieGrid.width / cellColumns
    const cellHeight = dieGrid.height / cellRows
    const cellAlpha = microAlpha * emphasis * 0.22
    for (let row = 0; row < cellRows; row += 1) {
      for (let column = 0; column < cellColumns; column += 1) {
        const x = dieGrid.x + column * cellWidth
        const y = dieGrid.y + row * cellHeight
        const edgeZone = column < 4 || column > cellColumns - 5 || row < 2 || row > cellRows - 3
        const seam = column % 6 === 0 || row % 5 === 0
        const primaryColor = edgeZone
          ? 0x79afbd
          : seam
            ? 0x91d2dc
            : (row + column) % 5 === 0
              ? 0xc7fff0
              : (row + column) % 3 === 0
                ? 0x94d9ef
                : 0xafe9dc
        graphics
          .roundRect(
            x + cellWidth * 0.12,
            y + cellHeight * 0.16,
            cellWidth * 0.72,
            cellHeight * 0.56,
            screenRadius(scale, 0.18, 0.002, 0.05),
          )
          .fill({ color: primaryColor, alpha: cellAlpha * (seam ? 0.58 : 1) })
      }
    }
  }
}

function drawNodeShell(
  graphics: PixiGraphics,
  node: SceneNode,
  scale: number,
  linked: boolean,
  heatEnabled: boolean,
  lod: TopologyLodState,
  emphasis: number,
) {
  const shellAlpha = mix(0.04, 0.14, lod.weights.board) * emphasis
  const trayOutlineAlpha = mix(0.08, 0.22, lod.weights.board) * emphasis
  const nodeRadius = screenRadius(scale, 18, 0.8, 10)

  graphics
    .roundRect(node.x, node.y, node.width, node.height, nodeRadius)
    .fill({ color: 0x09131b, alpha: 0.86 })
    .stroke({
      color: linked ? 0xffdc8a : 0x6fd9cd,
      alpha: linked ? 0.82 : trayOutlineAlpha,
        width: screenStroke(scale, linked ? 1.2 : 0.7, 0.08, 0.85),
    })

  if (shellAlpha > 0.02) {
    graphics
      .roundRect(
        node.x + 2.5,
        node.y + 2.5,
        node.width - 5,
        node.height - 5,
        screenRadius(scale, 14, 0.6, 8),
      )
      .fill({ color: 0x0b1720, alpha: shellAlpha })
  }

  if (heatEnabled) {
    graphics
      .roundRect(node.x + 6, node.y + 6, node.width - 12, node.height - 12, 8)
      .fill({
        color: 0xe58a43,
        alpha: node.interNodeLoad * 0.08 * emphasis,
      })
  }
}

function drawCampusPods(
  graphics: PixiGraphics,
  model: TopologySceneModel,
  scale: number,
  lod: TopologyLodState,
  visiblePods: typeof model.pods,
  podEmphasis: (podId: string) => number,
) {
  const rackFabricAlpha = mix(0.02, 0.08, lod.weights.overview)

  for (let row = 0; row < model.podRows; row += 1) {
    const rowPods = model.pods.slice(row * model.podColumns, row * model.podColumns + model.podColumns)
    if (rowPods.length < 2) {
      continue
    }

    graphics
      .moveTo(rowPods[0].centerX, rowPods[0].centerY)
      .lineTo(rowPods[rowPods.length - 1].centerX, rowPods[rowPods.length - 1].centerY)
      .stroke({
        color: 0xf1b067,
        alpha: rackFabricAlpha * Math.min(podEmphasis(rowPods[0].id), podEmphasis(rowPods[rowPods.length - 1].id)),
        width: screenStroke(scale, 2.4, 0.12, 2.2),
      })
  }

  for (let column = 0; column < model.podColumns; column += 1) {
    const columnPods = model.pods.filter((_, index) => index % model.podColumns === column)
    if (columnPods.length < 2) {
      continue
    }

    graphics
      .moveTo(columnPods[0].centerX, columnPods[0].centerY)
      .lineTo(columnPods[columnPods.length - 1].centerX, columnPods[columnPods.length - 1].centerY)
      .stroke({
        color: 0xf1b067,
        alpha:
          rackFabricAlpha *
          Math.min(podEmphasis(columnPods[0].id), podEmphasis(columnPods[columnPods.length - 1].id)),
        width: screenStroke(scale, 2.1, 0.12, 2),
      })
  }

  const rackInnerAlpha = mix(0.02, 0.08, lod.weights.board)

  for (const pod of visiblePods) {
    const emphasis = podEmphasis(pod.id)
    graphics
      .roundRect(pod.x, pod.y, pod.width, pod.height, screenRadius(scale, 22, 1.2, 18))
      .fill({
        color: 0x08131c,
        alpha: mix(pod.active ? 0.76 : 0.66, pod.active ? 0.88 : 0.8, lod.weights.board) * emphasis,
      })
      .stroke({
        color: pod.active ? 0xe6dbb1 : 0x5ecfca,
        alpha: (pod.active ? 0.34 : 0.14) * emphasis,
        width: screenStroke(scale, pod.active ? 1.3 : 0.8, 0.08, 1),
      })

    if (rackInnerAlpha > 0.02) {
      graphics
        .roundRect(
          pod.x + 8,
          pod.y + 8,
          pod.width - 16,
          pod.height - 16,
          screenRadius(scale, 18, 0.8, 14),
        )
        .stroke({
          color: 0x6fd9cd,
          alpha: rackInnerAlpha * emphasis,
          width: screenStroke(scale, 0.45, 0.04, 0.5),
        })
    }
  }
}

function TopologyScene({
  model,
  viewport,
  surfaceSize,
  hoveredTarget,
  pinnedTarget,
  linkedFocus,
  linkedGpuIds,
  linkedNodeIds,
  linkedPodIds,
  debugEnabled,
  snapshotMode,
  debugToggles,
  onFpsChange,
}: {
  model: TopologySceneModel
  viewport: ViewportState
  surfaceSize: { width: number; height: number }
  hoveredTarget: HoverTarget | null
  pinnedTarget: HoverTarget | null
  linkedFocus: LinkedFocus | null
  linkedGpuIds: Set<string>
  linkedNodeIds: Set<string>
  linkedPodIds: Set<string>
  debugEnabled: boolean
  snapshotMode: boolean
  debugToggles: DebugToggles
  onFpsChange: (value: number) => void
}) {
  useExtend({ Container, Graphics, Text })
  const { app } = useApplication()
  const dynamicRef = useRef<PixiGraphics | null>(null)
  const hoverRef = useRef<HoverTarget | null>(hoveredTarget)
  const pinnedRef = useRef<HoverTarget | null>(pinnedTarget)
  const statsRef = useRef({ elapsed: 0, frames: 0 })
  const allGpus = useMemo(
    () => model.nodes.flatMap((node) => node.gpus),
    [model.nodes],
  )
  const gpuById = useMemo(() => new Map(allGpus.map((gpu) => [gpu.id, gpu])), [allGpus])
  const nodeById = useMemo(() => new Map(model.nodes.map((node) => [node.id, node])), [model.nodes])
  const podById = useMemo(() => new Map(model.pods.map((pod) => [pod.id, pod])), [model.pods])
  const lodState = useMemo(() => getTopologyLodState(viewport.scale), [viewport.scale])
  const worldViewportBounds = useMemo(
    () =>
      getWorldViewportBounds(
        viewport,
        surfaceSize.width,
        surfaceSize.height,
        screenWorld(viewport.scale, 180, 12, 240),
      ),
    [surfaceSize.height, surfaceSize.width, viewport],
  )
  const visiblePods = useMemo(
    () => model.pods.filter((pod) => rectsIntersect(pod.hitBounds, worldViewportBounds)),
    [model.pods, worldViewportBounds],
  )
  const visibleNodes = useMemo(
    () => model.nodes.filter((node) => rectsIntersect(node.hitBounds, worldViewportBounds)),
    [model.nodes, worldViewportBounds],
  )
  const visibleGpus = useMemo(
    () => visibleNodes.flatMap((node) => node.gpus),
    [visibleNodes],
  )
  const visibleLinks = useMemo(
    () => ({
      row: model.rowLinks.filter((link) =>
        rectsIntersect(lineBounds(link.x1, link.y1, link.x2, link.y2, link.hitWidth), worldViewportBounds),
      ),
      column: model.columnLinks.filter((link) =>
        rectsIntersect(lineBounds(link.x1, link.y1, link.x2, link.y2, link.hitWidth), worldViewportBounds),
      ),
      bus: model.busLinks.filter((link) =>
        rectsIntersect(lineBounds(link.x1, link.y1, link.x2, link.y2, link.hitWidth), worldViewportBounds),
      ),
    }),
    [model.busLinks, model.columnLinks, model.rowLinks, worldViewportBounds],
  )
  const visibleLinkCount = useMemo(
    () => visibleLinks.row.length + visibleLinks.column.length + visibleLinks.bus.length,
    [visibleLinks.bus.length, visibleLinks.column.length, visibleLinks.row.length],
  )

  useEffect(() => {
    hoverRef.current = hoveredTarget
  }, [hoveredTarget])

  useEffect(() => {
    pinnedRef.current = pinnedTarget
  }, [pinnedTarget])

  useEffect(() => {
    if (debugEnabled || snapshotMode) {
      window.__PIXI_TOPOLOGY_APP__ = app
      return () => {
        delete window.__PIXI_TOPOLOGY_APP__
      }
    }

    return undefined
  }, [app, debugEnabled, snapshotMode])

  const getEmphasis = useCallback(
    (kind: 'pod' | 'node' | 'gpu', id: string) => {
      const focusTarget = pinnedRef.current ?? hoverRef.current
      const base = 1
      if (!focusTarget || lodState.deepIsolation <= 0.001) {
        return base
      }

      const fadeTo = mix(1, 0.08, lodState.deepIsolation)
      if (kind === 'gpu') {
        if (focusTarget.kind === 'gpu') {
          const gpu = gpuById.get(id)
          const focusedGpu = gpuById.get(focusTarget.id)
          if (!gpu || !focusedGpu) {
            return fadeTo
          }
          if (gpu.id === focusedGpu.id) {
            return 1
          }
          if (gpu.nodeId === focusedGpu.nodeId) {
            return mix(1, 0.34, lodState.deepIsolation)
          }
          if (gpu.domainIndex === focusedGpu.domainIndex) {
            return mix(1, 0.16, lodState.deepIsolation)
          }
          return fadeTo
        }

        if (focusTarget.kind === 'node') {
          const gpu = gpuById.get(id)
          const focusedNode = nodeById.get(focusTarget.id)
          if (!gpu || !focusedNode) {
            return fadeTo
          }
          if (gpu.nodeId === focusedNode.id) {
            return mix(1, 0.9, lodState.deepIsolation * 0.2)
          }
          if (gpu.domainIndex === focusedNode.domainIndex) {
            return mix(1, 0.18, lodState.deepIsolation)
          }
          return fadeTo
        }

        const gpu = gpuById.get(id)
        const focusedPod = podById.get(focusTarget.id)
        if (!gpu || !focusedPod) {
          return fadeTo
        }
        return gpu.domainIndex === focusedPod.index ? mix(1, 0.72, lodState.deepIsolation * 0.3) : fadeTo
      }

      if (kind === 'node') {
        const node = nodeById.get(id)
        if (!node) {
          return fadeTo
        }
        if (focusTarget.kind === 'gpu') {
          const gpu = gpuById.get(focusTarget.id)
          if (!gpu) {
            return fadeTo
          }
          if (node.id === gpu.nodeId) {
            return mix(1, 0.5, lodState.deepIsolation)
          }
          if (node.domainIndex === gpu.domainIndex) {
            return mix(1, 0.18, lodState.deepIsolation)
          }
          return fadeTo
        }

        if (focusTarget.kind === 'node') {
          const focusedNode = nodeById.get(focusTarget.id)
          if (!focusedNode) {
            return fadeTo
          }
          if (node.id === focusedNode.id) {
            return 1
          }
          if (node.domainIndex === focusedNode.domainIndex) {
            return mix(1, 0.2, lodState.deepIsolation)
          }
          return fadeTo
        }

        const focusedPod = podById.get(focusTarget.id)
        if (!focusedPod) {
          return fadeTo
        }
        return node.domainIndex === focusedPod.index ? mix(1, 0.3, lodState.deepIsolation) : fadeTo
      }

      const pod = podById.get(id)
      if (!pod) {
        return fadeTo
      }
      if (focusTarget.kind === 'gpu') {
        const gpu = gpuById.get(focusTarget.id)
        return gpu && gpu.domainIndex === pod.index ? mix(1, 0.25, lodState.deepIsolation) : fadeTo
      }
      if (focusTarget.kind === 'node') {
        const node = nodeById.get(focusTarget.id)
        return node && node.domainIndex === pod.index ? mix(1, 0.32, lodState.deepIsolation) : fadeTo
      }
      return focusTarget.id === id ? 1 : fadeTo
    },
    [gpuById, lodState.deepIsolation, nodeById, podById],
  )

  const drawStatic = useCallback(
    (graphics: PixiGraphics) => {
      graphics.clear()

      drawCampusPods(graphics, model, viewport.scale, lodState, visiblePods, (podId) =>
        getEmphasis('pod', podId),
      )

      const localStructurePresence = Math.max(
        lodState.weights.board,
        lodState.weights.package * 0.9,
        lodState.weights.silicon * 0.7,
        lodState.weights.micro * 0.45,
      )
      const connectorAlpha = 0.18 * localStructurePresence
      const linkPresence = mix(lodState.weights.overview * 0.35, 1, lodState.weights.board)
      const hubRadius = screenWorld(viewport.scale, 6, 0.1, 2.4)

      const drawStaticLink = (link: (typeof model.rowLinks)[number]) => {
        const isRackScope = link.scope === 'rack'
        if (!isRackScope && localStructurePresence < 0.08) {
          return
        }

        const rackFrom =
          isRackScope
            ? model.pods.find((pod) => pod.centerX === link.x1 && pod.centerY === link.y1)
            : null
        const rackTo =
          isRackScope
            ? model.pods.find((pod) => pod.centerX === link.x2 && pod.centerY === link.y2)
            : null
        const emphasis =
          isRackScope
            ? Math.min(
                rackFrom ? getEmphasis('pod', rackFrom.id) : 1,
                rackTo ? getEmphasis('pod', rackTo.id) : 1,
              )
            : 1
        graphics
          .moveTo(link.x1, link.y1)
          .lineTo(link.x2, link.y2)
          .stroke({
            color: link.color,
            alpha:
              (isRackScope
                ? 0.08 + link.load * 0.24
                : (0.04 + link.load * 0.12) * localStructurePresence) *
              linkPresence *
              emphasis,
            width: screenStroke(
              viewport.scale,
              isRackScope ? 1.6 + link.load * 2 : 0.75 + link.load * 0.9,
              0.05,
              2.2,
            ),
          })
      }

      visibleLinks.row.forEach(drawStaticLink)
      visibleLinks.column.forEach(drawStaticLink)
      visibleLinks.bus.forEach((link) => {
        if (localStructurePresence < 0.12) {
          return
        }

        graphics
          .moveTo(link.x1, link.y1)
          .lineTo(link.x2, link.y2)
          .stroke({
            color: link.color,
            alpha: (0.05 + link.load * 0.16) * linkPresence * localStructurePresence,
            width: screenStroke(viewport.scale, 0.55 + link.load * 0.55, 0.05, 1.1),
          })
      })

      for (const node of visibleNodes) {
        const nodeEmphasis = getEmphasis('node', node.id)
        if (localStructurePresence >= 0.08) {
          drawNodeShell(
            graphics,
            node,
            viewport.scale,
            linkedNodeIds.has(node.id),
            debugToggles.heat,
            lodState,
            nodeEmphasis,
          )

          graphics.circle(node.hubX, node.hubY, hubRadius).fill({
            color: linkedNodeIds.has(node.id) ? 0xffcf7a : 0x89f8ea,
            alpha:
              ((linkedNodeIds.has(node.id) ? 0.68 : 0.08 + node.interNodeLoad * 0.22) *
                nodeEmphasis *
                localStructurePresence),
          })
        }

        for (const gpu of node.gpus) {
          const gpuEmphasis = getEmphasis('gpu', gpu.id)
          const gpuCenterX = gpu.x + gpu.width / 2
          const connectorStartY =
            gpu.y + gpu.height / 2 <= node.hubY ? gpu.y + gpu.height : gpu.y
          const connectorEndY =
            gpu.y + gpu.height / 2 <= node.hubY ? node.hubY - 4 : node.hubY + 4

          if (localStructurePresence >= 0.08) {
            graphics
              .moveTo(gpuCenterX, connectorStartY)
              .lineTo(gpuCenterX, connectorEndY)
              .stroke({
                color: linkedGpuIds.has(gpu.id) ? 0xffd28a : 0x88efe0,
                alpha:
                  (linkedGpuIds.has(gpu.id)
                    ? 0.72
                    : connectorAlpha * (gpu.active ? 0.38 + gpu.linkLoad * 0.34 : 0.12)) * gpuEmphasis,
                width: screenStroke(
                  viewport.scale,
                  linkedGpuIds.has(gpu.id) ? 1 : gpu.active ? 0.55 + gpu.linkLoad * 0.4 : 0.28,
                  0.03,
                  0.8,
                ),
              })
          }

          drawModule(graphics, gpu, viewport.scale, linkedGpuIds.has(gpu.id), lodState, gpuEmphasis)
        }
      }

      if (debugToggles.bounds) {
        for (const pod of visiblePods) {
          graphics.roundRect(
            pod.hitBounds.x,
            pod.hitBounds.y,
            pod.hitBounds.width,
            pod.hitBounds.height,
            screenRadius(viewport.scale, 24, 1.2, 16),
          ).stroke({
            color: 0xfde6ab,
            alpha: 0.18,
            width: screenStroke(viewport.scale, 1, 0.06, 1),
          })
        }

        for (const node of visibleNodes) {
          graphics.roundRect(
            node.hitBounds.x,
            node.hitBounds.y,
            node.hitBounds.width,
            node.hitBounds.height,
            screenRadius(viewport.scale, 12, 0.6, 8),
          ).stroke({
            color: 0xfdf4cc,
            alpha: 0.34,
            width: screenStroke(viewport.scale, 1, 0.06, 1),
          })

          for (const gpu of node.gpus) {
            graphics.roundRect(
              gpu.hitBounds.x,
              gpu.hitBounds.y,
              gpu.hitBounds.width,
              gpu.hitBounds.height,
              screenRadius(viewport.scale, 6, 0.4, 4),
            ).stroke({
              color: 0x7adfff,
              alpha: 0.24,
              width: screenStroke(viewport.scale, 1, 0.06, 1),
            })
          }
        }
      }

      if (debugToggles.hitAreas) {
        for (const link of [...visibleLinks.row, ...visibleLinks.column, ...visibleLinks.bus]) {
          graphics
            .moveTo(link.x1, link.y1)
            .lineTo(link.x2, link.y2)
            .stroke({
              color: link.kind === 'column' ? 0x60aaf7 : 0xffd08a,
              alpha: 0.15,
              width: screenStroke(viewport.scale, link.hitWidth, 0.5, 16),
            })
        }
      }
    },
    [
      debugToggles.bounds,
      debugToggles.heat,
      debugToggles.hitAreas,
      getEmphasis,
      linkedGpuIds,
      linkedNodeIds,
      lodState,
      model,
      viewport.scale,
      visibleLinks.bus,
      visibleLinks.column,
      visibleLinks.row,
      visibleNodes,
      visiblePods,
    ],
  )

  const redrawDynamic = useCallback(
    (timeMs: number) => {
      const graphics = dynamicRef.current
      if (!graphics) {
        return
      }

      graphics.clear()
      const pulseTime = snapshotMode ? 0.42 : timeMs / 1000
      const visibleTarget = pinnedRef.current ?? hoverRef.current
      const linkGlowAlpha = mix(0.08, 0.18, lodState.weights.board)
      const animateLinkGlow =
        lodState.weights.board > 0.14 &&
        visibleLinkCount < 900 &&
        viewport.scale >= 0.28

      const drawGlowLink = (link: (typeof model.rowLinks)[number], index: number, color: number) => {
        const glow = pulse(timeMs, index * 0.19, snapshotMode ? 0 : 0.12)
        graphics
          .moveTo(link.x1, link.y1)
          .lineTo(link.x2, link.y2)
          .stroke({
            color,
            alpha: linkGlowAlpha * (0.12 + link.load * 0.5) * glow,
            width: screenStroke(viewport.scale, 1.2 + link.load * 2.2, 0.08, 3.2),
          })
      }

      if (animateLinkGlow) {
        visibleLinks.row.forEach((link, index) => {
          drawGlowLink(link, index, link.color)
        })
        visibleLinks.column.forEach((link, index) => {
          drawGlowLink(link, index + visibleLinks.row.length, link.color)
        })
        visibleLinks.bus.forEach((link, index) => {
          drawGlowLink(
            link,
            index + visibleLinks.row.length + visibleLinks.column.length,
            0x9efef2,
          )
        })
      }

      if (linkedFocus) {
        const wave = 0.58 + Math.sin(pulseTime * 2.4) * 0.18

        for (const pod of visiblePods) {
          if (!linkedPodIds.has(pod.id)) {
            continue
          }

          drawCornerFocus(graphics, pod.focusFrame, viewport.scale, 0xffd78e, wave, 18, 4, 2)
        }

        for (const node of visibleNodes) {
          if (!linkedNodeIds.has(node.id)) {
            continue
          }

          drawCornerFocus(graphics, node.focusFrame, viewport.scale, 0xffd78e, wave, 9, 2, 1.1)
        }

        for (const gpu of visibleGpus) {
          if (!linkedGpuIds.has(gpu.id)) {
            continue
          }

          drawCornerFocus(graphics, gpu.focusFrame, viewport.scale, 0xffefc3, wave + 0.12, 6, 1, 1)
        }
      }

      if (!visibleTarget) {
        return
      }

      if (visibleTarget.kind === 'pod') {
        const pod = podById.get(visibleTarget.id)
        if (!pod) {
          return
        }

        drawCornerFocus(graphics, pod.focusFrame, viewport.scale, 0xf9f5bc, 0.86, 22, 6, 2.3)
        return
      }

      if (visibleTarget.kind === 'node') {
        const node = nodeById.get(visibleTarget.id)
        if (!node) {
          return
        }

        drawCornerFocus(graphics, node.focusFrame, viewport.scale, 0xf9f5bc, 0.9, 10, 2, 1.7)
        return
      }

      if (visibleTarget.kind === 'gpu') {
        const gpu = gpuById.get(visibleTarget.id)
        if (!gpu) {
          return
        }

        drawCornerFocus(graphics, gpu.focusFrame, viewport.scale, 0xffffff, 0.96, 7, 1.5, 1.3)
        return
      }

      const link = [...model.rowLinks, ...model.columnLinks, ...model.busLinks].find(
        (item) => item.id === visibleTarget.id,
      )
      if (!link) {
        return
      }

      graphics
        .moveTo(link.x1, link.y1)
        .lineTo(link.x2, link.y2)
        .stroke({
          color: 0xfef4c8,
          alpha: 0.92,
          width: screenStroke(viewport.scale, 2.6 + link.load * 2.8, 0.14, 4.2),
        })
    },
    [
      gpuById,
      linkedFocus,
      linkedGpuIds,
      linkedNodeIds,
      linkedPodIds,
      lodState.weights.board,
      model,
      nodeById,
      podById,
      snapshotMode,
      visibleGpus,
      visibleLinkCount,
      viewport.scale,
      visibleLinks.bus,
      visibleLinks.column,
      visibleLinks.row,
      visibleNodes,
      visiblePods,
    ],
  )

  useEffect(() => {
    redrawDynamic(0)
  }, [redrawDynamic, hoveredTarget, pinnedTarget, linkedFocus])

  useTick(
    useCallback(
      (ticker: Ticker) => {
        if (snapshotMode) {
          return
        }

        const shouldAnimate =
          linkedFocus != null ||
          (lodState.weights.board > 0.14 &&
            visibleLinkCount < 900 &&
            viewport.scale >= 0.28)

        if (!shouldAnimate) {
          return
        }

        redrawDynamic(performance.now())

        statsRef.current.elapsed += ticker.deltaMS
        statsRef.current.frames += 1
        if (statsRef.current.elapsed >= 500) {
          const fps = (statsRef.current.frames * 1000) / statsRef.current.elapsed
          onFpsChange(fps)
          statsRef.current.elapsed = 0
          statsRef.current.frames = 0
        }
      },
      [
        linkedFocus,
        lodState.weights.board,
        onFpsChange,
        redrawDynamic,
        snapshotMode,
        viewport.scale,
        visibleLinkCount,
      ],
    ),
  )

  const debugLabels = debugEnabled && debugToggles.ids

  return (
    <pixiContainer x={viewport.x} y={viewport.y} scale={viewport.scale}>
      <pixiGraphics draw={drawStatic} />
      <pixiGraphics ref={dynamicRef} draw={noopDraw} />

      {debugLabels
        ? visiblePods.map((pod) => (
            <pixiText
              key={`pod-label-${pod.id}`}
              x={pod.x + 30}
              y={pod.y + 24}
              text={pod.active ? 'ACTIVE RACK' : `R${pod.index + 1}`}
              style={{
                fill: 0xdff7f0,
                fontSize: screenWorld(viewport.scale, 18, 3.5, 24) * lodState.textScale,
                fontFamily: 'IBM Plex Mono',
                letterSpacing: screenWorld(viewport.scale, 2, 0.2, 2),
              }}
            />
          ))
        : null}

      {debugLabels
        ? visibleNodes.map((node) => (
            <pixiText
              key={`node-label-${node.id}`}
              x={node.x + 10}
              y={node.y + 8}
              text={`N${node.index + 1}`}
              style={{
                fill: 0xdff7f0,
                fontSize: screenWorld(viewport.scale, 8, 2, 10) * lodState.textScale,
                fontFamily: 'IBM Plex Mono',
              }}
            />
          ))
        : null}
    </pixiContainer>
  )
}

export function ClusterMap({
  viewModel,
  debugEnabled,
  snapshotMode,
  linkedFocus,
}: ClusterMapProps) {
  const model = useMemo(() => buildTopologySceneModel(viewModel), [viewModel])
  const [viewport, setViewport] = useState<ViewportState>({ x: 0, y: 0, scale: 1 })
  const [surfaceSize, setSurfaceSize] = useState({ width: 0, height: 0 })
  const [sceneReady, setSceneReady] = useState(false)
  const [hoveredTarget, setHoveredTarget] = useState<HoverTarget | null>(null)
  const [pinnedTarget, setPinnedTarget] = useState<HoverTarget | null>(null)
  const [isDragging, setIsDragging] = useState(false)
  const [fps, setFps] = useState(0)
  const [debugToggles, setDebugToggles] = useState<DebugToggles>({
    bounds: false,
    ids: false,
    heat: false,
    hitAreas: false,
    stats: true,
  })
  const surfaceRef = useRef<HTMLDivElement | null>(null)
  const interactionLayerRef = useRef<HTMLDivElement | null>(null)
  const interactionRef = useRef({
    dragging: false,
    moved: false,
    distance: 0,
    lastPointer: null as ScenePointer | null,
    pointers: new Map<number, ScenePointer>(),
    pinchDistance: 0,
    pinchMidpoint: null as ScenePointer | null,
  })

  const linkedGpuIds = useMemo(() => {
    return new Set(
      model.nodes
        .flatMap((node) => node.gpus)
        .filter((gpu) => matchesLinkedFocus(gpu, linkedFocus))
        .map((gpu) => gpu.id),
    )
  }, [linkedFocus, model.nodes])

  const linkedNodeIds = useMemo(() => {
    return new Set(
      model.nodes
        .filter((node) => node.gpus.some((gpu) => matchesLinkedFocus(gpu, linkedFocus)))
        .map((node) => node.id),
    )
  }, [linkedFocus, model.nodes])

  const linkedPodIds = useMemo(() => {
    if (!linkedFocus) {
      return new Set<string>()
    }

    return new Set(
      model.nodes
        .filter((node) => node.gpus.some((gpu) => matchesLinkedFocus(gpu, linkedFocus)))
        .map((node) => `pod-${node.domainIndex}`),
    )
  }, [linkedFocus, model.nodes])

  useEffect(() => {
    if (surfaceSize.width === 0 || surfaceSize.height === 0) {
      return
    }

    let settleFrame = 0
    const frame = requestAnimationFrame(() => {
      setViewport(getFitViewport(model, surfaceSize.width, surfaceSize.height))
      settleFrame = requestAnimationFrame(() => {
        setSceneReady(true)
      })
    })

    return () => {
      cancelAnimationFrame(frame)
      cancelAnimationFrame(settleFrame)
    }
  }, [model, surfaceSize.height, surfaceSize.width])

  const focusedDetails = useMemo<TargetDetails | null>(() => {
    return describeTarget(model, viewModel, pinnedTarget ?? hoveredTarget)
  }, [hoveredTarget, model, pinnedTarget, viewModel])

  const debugObjects = useMemo(
    () => createDebugObjectMap(model, viewport),
    [model, viewport],
  )
  const detailLevel = useMemo(() => getTopologyLodState(viewport.scale).primaryBand, [viewport.scale])
  const viewportConstraints = useMemo(() => {
    if (surfaceSize.width === 0 || surfaceSize.height === 0) {
      return null
    }

    return getViewportConstraints(model, surfaceSize.width, surfaceSize.height, viewport.scale)
  }, [model, surfaceSize.height, surfaceSize.width, viewport.scale])

  useEffect(() => {
    if (!(debugEnabled || snapshotMode)) {
      delete window.__TOPOLOGY_DEBUG__
      return
    }

    window.__TOPOLOGY_DEBUG__ = {
      ready: sceneReady,
      viewport,
      surfaceSize,
      objectCounts: model.objectCounts,
      objects: debugObjects,
      hoveredTarget,
      pinnedTarget,
      detailLevel,
      setViewport: (nextViewport: ViewportState) => {
        setViewport(clampViewportToScene(nextViewport, model, surfaceSize.width, surfaceSize.height))
      },
    }

    return () => {
      delete window.__TOPOLOGY_DEBUG__
    }
  }, [
    debugEnabled,
    debugObjects,
    hoveredTarget,
    model.objectCounts,
    pinnedTarget,
    sceneReady,
    snapshotMode,
    surfaceSize,
    detailLevel,
    model,
    viewport,
  ])

  const scenePointerFromClient = useCallback((clientX: number, clientY: number) => {
    const bounds = interactionLayerRef.current?.getBoundingClientRect()
    if (!bounds) {
      return null
    }

    return {
      x: clientX - bounds.left,
      y: clientY - bounds.top,
    }
  }, [])

  const scenePointerFromEvent = useCallback(
    (event: Pick<ReactPointerEvent<HTMLDivElement>, 'clientX' | 'clientY'>) =>
      scenePointerFromClient(event.clientX, event.clientY),
    [scenePointerFromClient],
  )

  const toWorldPoint = useCallback(
    (pointer: ScenePointer) => ({
      x: (pointer.x - viewport.x) / viewport.scale,
      y: (pointer.y - viewport.y) / viewport.scale,
    }),
    [viewport],
  )

  const setViewportClamped = useCallback(
    (updater: ViewportState | ((current: ViewportState) => ViewportState)) => {
      setViewport((current) => {
        const nextViewport =
          typeof updater === 'function'
            ? (updater as (current: ViewportState) => ViewportState)(current)
            : updater

        return clampViewportToScene(nextViewport, model, surfaceSize.width, surfaceSize.height)
      })
    },
    [model, surfaceSize.height, surfaceSize.width],
  )

  const applyZoomAtPointer = useCallback((screenPoint: ScenePointer, zoomFactor: number) => {
    setViewportClamped((current) => {
      const nextScale = clamp(
        current.scale * zoomFactor,
        viewportConstraints?.minScale ?? MIN_SCALE,
        viewportConstraints?.maxScale ?? MAX_SCALE,
      )
      const worldX = (screenPoint.x - current.x) / current.scale
      const worldY = (screenPoint.y - current.y) / current.scale

      return {
        scale: nextScale,
        x: screenPoint.x - worldX * nextScale,
        y: screenPoint.y - worldY * nextScale,
      }
    })
  }, [setViewportClamped, viewportConstraints?.maxScale, viewportConstraints?.minScale])

  const updateHoverFromPointer = useCallback(
    (pointer: ScenePointer | null) => {
      if (!pointer) {
        setHoveredTarget((current) => (current === null ? current : null))
        return
      }

      const worldPoint = toWorldPoint(pointer)
      const next = findHoverTarget(model, worldPoint.x, worldPoint.y)

      setHoveredTarget((current) => {
        if (current?.kind === next?.kind && current?.id === next?.id) {
          return current
        }

        return next
      })
    },
    [model, toWorldPoint],
  )

  const resetViewport = useCallback(() => {
    if (surfaceSize.width === 0 || surfaceSize.height === 0) {
      return
    }

    setViewport(getFitViewport(model, surfaceSize.width, surfaceSize.height))
  }, [model, surfaceSize.height, surfaceSize.width])

  const handleSurfaceSizeChange = useCallback((width: number, height: number) => {
    setSurfaceSize((current) => {
      if (current.width === width && current.height === height) {
        return current
      }

      return { width, height }
    })
    setSceneReady(false)
  }, [])

  useEffect(() => {
    const element = interactionLayerRef.current
    if (!element) {
      return
    }

    const handleWheel = (event: WheelEvent) => {
      if (event.target instanceof Element && event.target.closest('.scene-inspector, .scene-debug-panel')) {
        return
      }

      const pointer = scenePointerFromClient(event.clientX, event.clientY)
      if (!pointer) {
        return
      }

      event.preventDefault()
      event.stopPropagation()

      const delta = event.ctrlKey ? event.deltaY * 1.8 : event.deltaY
      const zoomFactor = Math.exp(-delta * 0.0015)
      applyZoomAtPointer(pointer, zoomFactor)
    }

    element.addEventListener('wheel', handleWheel, { passive: false })
    return () => {
      element.removeEventListener('wheel', handleWheel)
    }
  }, [applyZoomAtPointer, scenePointerFromClient])

  const togglePinnedTarget = useCallback(
    (pointer: ScenePointer) => {
      const worldPoint = toWorldPoint(pointer)
      const target = findHoverTarget(model, worldPoint.x, worldPoint.y)

      if (!target || target.kind === 'link') {
        setPinnedTarget(null)
        return
      }

      setPinnedTarget((current) => {
        if (current?.kind === target.kind && current.id === target.id) {
          return null
        }

        return target
      })
    },
    [model, toWorldPoint],
  )

  const handlePointerDown = useCallback(
    (event: ReactPointerEvent<HTMLDivElement>) => {
      if (event.target !== event.currentTarget) {
        return
      }

      const pointer = scenePointerFromEvent(event)
      if (!pointer) {
        return
      }

      const interaction = interactionRef.current
      interaction.pointers.set(event.pointerId, pointer)
      interaction.lastPointer = pointer
      interaction.moved = false
      interaction.distance = 0

      if (interaction.pointers.size === 1) {
        interaction.dragging = true
        setIsDragging(true)
      } else if (interaction.pointers.size === 2) {
        const [first, second] = Array.from(interaction.pointers.values())
        const deltaX = second.x - first.x
        const deltaY = second.y - first.y
        interaction.dragging = false
        interaction.pinchDistance = Math.hypot(deltaX, deltaY)
        interaction.pinchMidpoint = {
          x: (first.x + second.x) / 2,
          y: (first.y + second.y) / 2,
        }
        setIsDragging(false)
      }

      event.currentTarget.setPointerCapture(event.pointerId)
    },
    [scenePointerFromEvent],
  )

  const handlePointerMove = useCallback(
    (event: ReactPointerEvent<HTMLDivElement>) => {
      const pointer = scenePointerFromEvent(event)
      if (!pointer) {
        return
      }

      const interaction = interactionRef.current
      if (interaction.pointers.has(event.pointerId)) {
        interaction.pointers.set(event.pointerId, pointer)
      }

      if (interaction.pointers.size === 2) {
        const [first, second] = Array.from(interaction.pointers.values())
        const deltaX = second.x - first.x
        const deltaY = second.y - first.y
        const distance = Math.max(Math.hypot(deltaX, deltaY), 1)
        const midpoint = {
          x: (first.x + second.x) / 2,
          y: (first.y + second.y) / 2,
        }

        if (interaction.pinchDistance > 0 && interaction.pinchMidpoint) {
          const zoomFactor = distance / interaction.pinchDistance
          setViewportClamped((current) => {
            const nextScale = clamp(
              current.scale * zoomFactor,
              viewportConstraints?.minScale ?? MIN_SCALE,
              viewportConstraints?.maxScale ?? MAX_SCALE,
            )
            const worldX = (midpoint.x - current.x) / current.scale
            const worldY = (midpoint.y - current.y) / current.scale

            return {
              scale: nextScale,
              x:
                midpoint.x -
                worldX * nextScale +
                (midpoint.x - interaction.pinchMidpoint!.x),
              y:
                midpoint.y -
                worldY * nextScale +
                (midpoint.y - interaction.pinchMidpoint!.y),
            }
          })
        }

        interaction.pinchDistance = distance
        interaction.pinchMidpoint = midpoint
        interaction.moved = true
        return
      }

      if (interaction.dragging && interaction.lastPointer) {
        const deltaMoveX = pointer.x - interaction.lastPointer.x
        const deltaMoveY = pointer.y - interaction.lastPointer.y
        interaction.lastPointer = pointer
        interaction.distance += Math.abs(deltaMoveX) + Math.abs(deltaMoveY)

        if (interaction.distance > 2) {
          interaction.moved = true
        }

        setViewportClamped((current) => ({
          ...current,
          x: current.x + deltaMoveX,
          y: current.y + deltaMoveY,
        }))
        return
      }

      if (event.target !== event.currentTarget) {
        return
      }

      updateHoverFromPointer(pointer)
    },
    [
      scenePointerFromEvent,
      setViewportClamped,
      updateHoverFromPointer,
      viewportConstraints?.maxScale,
      viewportConstraints?.minScale,
    ],
  )

  const releasePointer = useCallback((pointerId: number) => {
    const interaction = interactionRef.current
    interaction.pointers.delete(pointerId)

    if (interaction.pointers.size < 2) {
      interaction.pinchDistance = 0
      interaction.pinchMidpoint = null
    }

    if (interaction.pointers.size === 0) {
      interaction.dragging = false
      interaction.lastPointer = null
      setIsDragging(false)
      return
    }

    const remainingPointer = Array.from(interaction.pointers.values())[0]
    interaction.lastPointer = remainingPointer
    interaction.dragging = true
  }, [])

  const handlePointerUp = useCallback(
    (event: ReactPointerEvent<HTMLDivElement>) => {
      const pointer = scenePointerFromEvent(event)
      const interaction = interactionRef.current
      const wasClick = !interaction.moved && interaction.distance < 8 && interaction.pointers.size <= 1

      if (event.currentTarget.hasPointerCapture(event.pointerId)) {
        event.currentTarget.releasePointerCapture(event.pointerId)
      }

      releasePointer(event.pointerId)

      if (pointer) {
        updateHoverFromPointer(pointer)
      }

      if (!pointer || !wasClick || event.target !== event.currentTarget) {
        return
      }

      togglePinnedTarget(pointer)
    },
    [releasePointer, scenePointerFromEvent, togglePinnedTarget, updateHoverFromPointer],
  )

  const handlePointerLeave = useCallback(() => {
    interactionRef.current.dragging = false
    interactionRef.current.lastPointer = null
    interactionRef.current.pointers.clear()
    interactionRef.current.pinchDistance = 0
    interactionRef.current.pinchMidpoint = null
    setIsDragging(false)
    setHoveredTarget(null)
  }, [])

  const toggleDebugFlag = (key: keyof DebugToggles) => {
    setDebugToggles((current) => ({
      ...current,
      [key]: !current[key],
    }))
  }

  const linkedSummary = linkedFocus ? linkedFocus.label : null

  return (
    <div className="topology-scene-shell">
      <div className="scene-toolbar">
        <div className="scene-toolbar-actions">
          <button
            type="button"
            className="scene-button"
            onClick={resetViewport}
            data-testid="camera-reset"
          >
            reset camera
          </button>
        </div>
      </div>

      <div
        ref={surfaceRef}
        className="pixi-surface-wrap topology-surface-wrap"
      >
        <PixiSurface
          className="pixi-surface"
          canvasClassName="pixi-canvas"
          testId="topology-scene"
          onSizeChange={handleSurfaceSizeChange}
        >
          {() => (
            <TopologyScene
              model={model}
              viewport={viewport}
              surfaceSize={surfaceSize}
              hoveredTarget={hoveredTarget}
              pinnedTarget={pinnedTarget}
              linkedFocus={linkedFocus}
              linkedGpuIds={linkedGpuIds}
              linkedNodeIds={linkedNodeIds}
              linkedPodIds={linkedPodIds}
              debugEnabled={debugEnabled}
              snapshotMode={snapshotMode}
              debugToggles={debugToggles}
              onFpsChange={setFps}
            />
          )}
        </PixiSurface>

        <div
          ref={interactionLayerRef}
          className={`topology-interaction-layer${isDragging ? ' is-dragging' : ''}`}
          data-testid="topology-interaction-layer"
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerCancel={handlePointerLeave}
          onPointerLeave={handlePointerLeave}
          onDoubleClick={(event) => {
            if (event.target !== event.currentTarget) {
              return
            }

            resetViewport()
          }}
        >
          <div className="scene-inspector" data-testid="topology-inspector">
            <p className="mini-label">
              {pinnedTarget ? 'Pinned target' : hoveredTarget ? 'Hover target' : 'Topology inspector'}
            </p>
            {focusedDetails ? (
              <>
                <h3>{focusedDetails.heading}</h3>
                <p className="inspector-subheading">{focusedDetails.subheading}</p>
                {linkedSummary ? (
                  <p className="inspector-link-note">Transformer highlight: {linkedSummary}</p>
                ) : null}
                <dl className="inspector-grid">
                  {focusedDetails.metrics.map((metric) => (
                    <div key={`${focusedDetails.id}-${metric.label}`}>
                      <dt>{metric.label}</dt>
                      <dd>{metric.value}</dd>
                    </div>
                  ))}
                </dl>
              </>
            ) : (
              <>
                <h3>Inspect the cluster</h3>
                <p className="inspector-subheading">
                  Hover a rack or GPU to inspect placement, memory headroom, and link load.
                  Pan and zoom to move between fabric and package detail.
                </p>
                {linkedSummary ? (
                  <p className="inspector-link-note">Transformer highlight: {linkedSummary}</p>
                ) : null}
              </>
            )}
          </div>

          {(debugEnabled || snapshotMode) && (
            <div className="scene-debug-panel" data-testid="topology-debug">
              <p className="mini-label">Debug overlay</p>
              <div className="debug-toggle-grid">
                <label>
                  <input
                    type="checkbox"
                    checked={debugToggles.bounds}
                    onChange={() => toggleDebugFlag('bounds')}
                  />
                  Bounds
                </label>
                <label>
                  <input
                    type="checkbox"
                    checked={debugToggles.ids}
                    onChange={() => toggleDebugFlag('ids')}
                  />
                  Node / GPU ids
                </label>
                <label>
                  <input
                    type="checkbox"
                    checked={debugToggles.heat}
                    onChange={() => toggleDebugFlag('heat')}
                  />
                  Load heat
                </label>
                <label>
                  <input
                    type="checkbox"
                    checked={debugToggles.hitAreas}
                    onChange={() => toggleDebugFlag('hitAreas')}
                  />
                  Link hit areas
                </label>
                <label>
                  <input
                    type="checkbox"
                    checked={debugToggles.stats}
                    onChange={() => toggleDebugFlag('stats')}
                  />
                  FPS / counts
                </label>
              </div>

              {debugToggles.stats ? (
                <div className="debug-stats">
                  <span>FPS {snapshotMode ? 'snapshot' : fps.toFixed(0)}</span>
                  <span>Racks {model.objectCounts.pods}</span>
                  <span>Nodes {model.objectCounts.nodes}</span>
                  <span>GPUs {model.objectCounts.gpus}</span>
                  <span>Detail {detailLevel}</span>
                  <span>Zoom {viewport.scale.toFixed(2)}x</span>
                </div>
              ) : null}
            </div>
          )}
        </div>
      </div>
    </div>
  )
}
