/**
 * Canonicalize a provider-neutral GeoJSON linear ring before it reaches a
 * Cesium geometry worker. GeoJSON closes a ring by repeating its first
 * coordinate, while GroundPolylineGeometry with `loop: true` expects an open
 * sequence. Some source snapshots contain both adjacent duplicates and more
 * than one closing coordinate; forwarding either shape can create a
 * zero-length segment and stop Cesium's render loop.
 *
 * Invalid coordinates invalidate the complete ring. Silently retaining a
 * partial polygon would display a boundary the provider never published.
 *
 * @param {Array<[number, number]>} ring
 * @returns {Array<[number, number]>}
 */
export function normalizeHGeoZoneRing(ring) {
  if (!Array.isArray(ring)) return [];
  /** @type {Array<[number, number]>} */
  const normalized = [];
  for (const coordinate of ring) {
    if (
      !Array.isArray(coordinate)
      || coordinate.length < 2
      || !Number.isFinite(coordinate[0])
      || !Number.isFinite(coordinate[1])
      || coordinate[0] < -180
      || coordinate[0] > 180
      || coordinate[1] < -90
      || coordinate[1] > 90
    ) return [];
    const next = /** @type {[number, number]} */ ([coordinate[0], coordinate[1]]);
    if (!sameCoordinate(normalized.at(-1), next)) normalized.push(next);
  }

  while (normalized.length > 1 && sameCoordinate(normalized[0], normalized.at(-1))) {
    normalized.pop();
  }
  const distinct = new Set(normalized.map(([longitude, latitude]) => `${longitude}:${latitude}`));
  return distinct.size >= 3 ? normalized : [];
}

/**
 * @param {[number, number] | undefined} left
 * @param {[number, number] | undefined} right
 */
function sameCoordinate(left, right) {
  return Boolean(left && right && left[0] === right[0] && left[1] === right[1]);
}
