/**
 * Pure helpers for turning a map tap into a selected place.
 *
 * A finger is much bigger than a POI dot (the `mmnav-poi` circle is ~3.5px),
 * so resolving a tap to a POI needs two things the raw single-pixel query
 * didn't give us:
 *   1. a tolerance box around the touch point (finger-sized), and
 *   2. name extraction that copes with OpenMapTiles' localised name keys —
 *      POI features carry `name`, but some only expose `name:en` / `name:latin`.
 *
 * Kept free of any React Native / native-map imports so the selection logic
 * is unit-testable without a device.
 */
import type { PixelPoint, PixelPointBounds } from "@maplibre/maplibre-react-native";
import type { SelectedPlace } from "../state/types";
import type { LatLng } from "../../modules/mapmap-react-native/src/MapmapNav.types";

/**
 * Layer ids to prefer when resolving a tap — the POI dots/labels the user
 * actually aims at. Our own appended layers plus the common OpenMapTiles /
 * hosted "MapMap Light" poi layer ids. Ids absent from the active style are
 * simply ignored by the native query, so listing extras is harmless.
 */
export const POI_TAP_LAYERS = [
  "mmnav-poi",
  "mmnav-poi-label",
  "poi",
  "poi-label",
  "poi_label",
] as const;

/** Half-width (px) of the tolerance box around a tap — roughly a fingertip. */
export const TAP_TOLERANCE_PX = 9;

/** OpenMapTiles name keys, in preference order (localised first is fine). */
const NAME_KEYS = ["name", "name:en", "name:latin"] as const;

/** First non-empty string name on a feature, or null. */
export function featureName(feature: GeoJSON.Feature): string | null {
  const props = feature.properties;
  if (!props) return null;
  for (const key of NAME_KEYS) {
    const value = (props as Record<string, unknown>)[key];
    if (typeof value === "string" && value.trim().length > 0) return value.trim();
  }
  return null;
}

/** A finger-sized query box centred on the tap point. */
export function tapToleranceBox(
  point: PixelPoint,
  tolerance = TAP_TOLERANCE_PX
): PixelPointBounds {
  const [x, y] = point;
  return [
    [x - tolerance, y - tolerance],
    [x + tolerance, y + tolerance],
  ];
}

/** A "Dropped pin" place at the tapped coordinate. */
export function droppedPin(position: LatLng): SelectedPlace {
  return {
    name: "Dropped pin",
    address: `${position.latitude.toFixed(5)}, ${position.longitude.toFixed(5)}`,
    position,
  };
}

/**
 * Pick the best place for a tap from the features under the finger. Uses the
 * first named feature (queries are handed to us POI-layers-first, so a real
 * POI wins over an incidental road/polygon); falls back to a dropped pin.
 */
export function pickPlaceFromFeatures(
  features: GeoJSON.Feature[],
  position: LatLng
): SelectedPlace {
  for (const feature of features) {
    const name = featureName(feature);
    if (!name) continue;
    const props = feature.properties as Record<string, unknown> | null;
    const address =
      typeof props?.address === "string"
        ? props.address
        : typeof props?.["addr:street"] === "string"
          ? (props["addr:street"] as string)
          : "";
    return { name, address, position };
  }
  return droppedPin(position);
}
