/**
 * Shared rules for streaming regional PMTiles archives over HTTP.
 *
 * There is no separate "online" tileset: streaming reads the same
 * `www/static/tiles/<region>.pmtiles` archives the download flow stores in
 * IndexedDB, just over byte-range requests instead. That's why the same
 * `layers(sourceName)` style applies to both, and why these thresholds live
 * here rather than in either map component — the app map (m-map.ts) and the
 * public share viewer (share-view.ts) must agree on when streaming happens.
 */
import type { Bounds } from './bounds.ts'
import type { TileManifestEntry } from './tile-manifest.ts'

/**
 * Archives stream from whatever origin served the page. This requires the
 * host to answer range requests with 206 — against a host that returns the
 * whole file, pmtiles throws rather than falling back.
 */
export const TILE_HOST = globalThis.location.origin

/**
 * Streamed regions cost bandwidth on every pan, so they wait for the zoom
 * where the world archive (built to z7) is overzooming and street detail
 * actually adds something.
 */
export const MIN_ONLINE_ZOOM = 8

/**
 * Each regional source carries ~34 style layers. Downloaded regions are
 * self-limiting — the user picked them — but every region is eligible to
 * stream, so a border between extracts could otherwise stack several at once.
 */
export const MAX_ONLINE_SOURCES = 2

/** Fraction of the viewport to pre-attach around the edges. */
export const VIEWPORT_BUFFER = 0.5

export function streamedTileUrl(filename: string): string {
  return `pmtiles://${TILE_HOST}/static/tiles/${filename}`
}

/**
 * Whether the device is asking us not to spend bandwidth. Streaming a region
 * costs megabytes per screen at street zoom, so data-saver mode and 2g
 * connections suppress it regardless of any setting. The Network Information
 * API is absent on Safari and Firefox, which then just stream normally.
 */
export function dataSaverActive(): boolean {
  const conn = navigator.connection
  if (!conn) return false
  if (conn.saveData) return true
  return conn.effectiveType === '2g' || conn.effectiveType === 'slow-2g'
}

/** Pointless without a connection, and rude on a metered one. */
export function canStreamTiles(): boolean {
  return navigator.onLine && !dataSaverActive()
}

/** Squared distance from a region's bbox centroid to a point; ordering only. */
export function centroidDistanceSq(
  bounds: Bounds,
  lng: number,
  lat: number,
): number {
  const dx = (bounds[0] + bounds[2]) / 2 - lng
  const dy = (bounds[1] + bounds[3]) / 2 - lat
  return dx * dx + dy * dy
}

/**
 * Which of `candidates` to actually stream: nearest bbox centroid first,
 * capped. Region extracts have rectangular bounds that overlap at borders, so
 * without a cap a tri-point could attach several sources at once.
 */
export function pickStreamableRegions(
  candidates: TileManifestEntry[],
  lng: number,
  lat: number,
  limit: number = MAX_ONLINE_SOURCES,
): TileManifestEntry[] {
  return candidates
    .filter((t) => t.filename && t.bounds)
    .sort((a, b) =>
      centroidDistanceSq(a.bounds!, lng, lat) -
      centroidDistanceSq(b.bounds!, lng, lat)
    )
    .slice(0, limit)
}
