import simplifyGeom from '@turf/simplify';
import { lineString } from '@turf/helpers';
import type { Polygon, Position } from 'geojson';

/**
 * Zoom-aware simplification of a user-drawn ring.
 *
 * Tolerance shrinks as zoom increases so a stroke traced at low zoom comes out
 * clean, while a stroke traced zoomed-in keeps its detail.
 *
 * Tolerance is in degrees (Turf high-quality=false). Roughly:
 *   z=6  -> ~0.01  (~1km)
 *   z=9  -> ~0.001 (~100m)
 *   z=12 -> ~0.0001 (~10m)
 *   z=15+ -> ~0.00001 (~1m)
 */
function toleranceForZoom(zoom: number): number {
  return Math.max(0.00001, 0.01 / Math.pow(2, Math.max(0, zoom - 6)));
}

/**
 * Simplify a whole polygon. Used for shapes with no boundary-derived section.
 *
 * Returns the original polygon unchanged if it is too small to simplify (Turf
 * throws on rings with fewer than 4 vertices) or if simplification fails.
 */
export function simplifyPolygon(polygon: Polygon, zoom: number): Polygon {
  const outer = polygon.coordinates[0];
  if (!outer || outer.length < 4) return polygon;
  const feature = { type: 'Feature' as const, properties: {}, geometry: polygon };
  try {
    const simplified = simplifyGeom(feature, {
      tolerance: toleranceForZoom(zoom),
      highQuality: false,
    });
    return simplified.geometry as Polygon;
  } catch {
    return polygon;
  }
}

/**
 * Simplify ONLY the user-drawn portion of an auto-completed ring, leaving the
 * boundary-derived portion exactly as traced.
 *
 * This matters a great deal. Applying a single zoom-based tolerance to the
 * whole ring throws away the border trace: at the tool's default framing
 * (z≈6.7) the tolerance is ~685 m, which discards ~99.5% of the traced border
 * vertices and replaces a faithful outline with a handful of long chords. The
 * traced half is not an approximation of anything -- it IS the boundary data,
 * so there is nothing to gain by thinning it and a great deal to lose.
 *
 * @param ring        full closed ring: drawn portion, then boundary trace, then
 *                    the closing vertex
 * @param drawnCount  number of leading coordinates belonging to the user stroke
 */
export function simplifyDrawnPortion(
  ring: Position[],
  drawnCount: number,
  zoom: number,
): Position[] {
  if (drawnCount < 3 || drawnCount > ring.length) return ring;
  const drawn = ring.slice(0, drawnCount);
  const traced = ring.slice(drawnCount);

  let simplifiedDrawn = drawn;
  try {
    const simplified = simplifyGeom(lineString(drawn), {
      tolerance: toleranceForZoom(zoom),
      highQuality: false,
    });
    const coords = simplified.geometry.coordinates as Position[];
    // Never let simplification collapse the stroke below a usable arc, and keep
    // the true endpoints so the join onto the boundary trace stays exact.
    if (coords.length >= 2) {
      coords[0] = drawn[0];
      coords[coords.length - 1] = drawn[drawn.length - 1];
      simplifiedDrawn = coords;
    }
  } catch {
    // Fall through with the unsimplified stroke.
  }

  return [...simplifiedDrawn, ...traced];
}
