package expo.modules.mapmapreactnative

import ai.mapmap.nav.NavigationState
import ai.mapmap.routing.Maneuver
import ai.mapmap.routing.Route

/**
 * SDK payloads → contract-shaped, JSON-serialisable maps
 * (schemas: modules/mapmap-react-native/src/MapmapNav.types.ts).
 */
object ContractMapping {

  /**
   * Valhalla maneuver type codes → contract ManeuverType strings.
   * Codes per the Valhalla turn-by-turn API (the engine under
   * routing.ValhallaMobileRouter). Unknown codes degrade to "straight".
   */
  fun maneuverType(code: Int): String = when (code) {
    1, 2, 3 -> "depart" // start / startRight / startLeft
    4, 5, 6 -> "arrive" // destination / destinationRight / destinationLeft
    7, 8 -> "straight" // becomes / continue
    9 -> "slight-right"
    10 -> "turn-right"
    11 -> "sharp-right"
    12, 13 -> "uturn" // uturnRight / uturnLeft
    14 -> "sharp-left"
    15 -> "turn-left"
    16 -> "slight-left"
    17, 18, 19 -> "on-ramp" // rampStraight / rampRight / rampLeft
    20, 21 -> "off-ramp" // exitRight / exitLeft
    22 -> "straight" // stayStraight
    23 -> "fork-right" // stayRight
    24 -> "fork-left" // stayLeft
    25, 37, 38 -> "merge" // merge / mergeRight / mergeLeft
    26, 27 -> "roundabout" // roundaboutEnter / roundaboutExit
    else -> "straight"
  }

  fun latLng(latitude: Double, longitude: Double): Map<String, Any?> =
    mapOf("latitude" to latitude, "longitude" to longitude)

  private fun maneuver(m: Maneuver): Map<String, Any?> = buildMap {
    put("type", maneuverType(m.type))
    put("instruction", m.instruction)
    put("distanceMeters", m.lengthMeters)
    put("position", latLng(m.position.latitude, m.position.longitude))
    m.roundaboutExitCount?.let { put("roundaboutExit", it) }
  }

  /** Contract Route (index is the position in the RouteResult array). */
  fun route(index: Int, r: Route): Map<String, Any?> = mapOf(
    "index" to index,
    "geometry" to r.geometry.map { latLng(it.latitude, it.longitude) },
    "distanceMeters" to r.distanceMeters,
    "durationSeconds" to r.durationSeconds,
    "maneuvers" to r.maneuvers.map { maneuver(it) },
  )

  fun routeResult(routes: List<Route>): Map<String, Any?> =
    mapOf("routes" to routes.mapIndexed { i, r -> route(i, r) })

  /** Contract GuidanceEvent from an SDK NavigationState. */
  fun guidanceEvent(st: NavigationState): Map<String, Any?> = buildMap {
    put(
      "state",
      when {
        st.hasArrived -> "arrived"
        st.isRerouting -> "rerouting"
        else -> "navigating"
      },
    )
    put(
      "location",
      mapOf(
        "latitude" to st.latitude,
        "longitude" to st.longitude,
        "bearingDegrees" to st.bearingDegrees,
        "speedMps" to st.speedMps,
      ),
    )
    val primary = st.bannerPrimary
    if (primary != null) {
      put(
        "banner",
        buildMap {
          put("primary", primary)
          st.bannerSecondary?.let { put("secondary", it) }
          put("maneuverType", maneuverType(st.maneuverType))
          st.roundaboutExitCount?.let { put("roundaboutExit", it) }
          st.thenBannerPrimary?.let { put("thenPrimary", it) }
          st.thenManeuverType?.let { put("thenManeuverType", maneuverType(it)) }
        },
      )
    }
    put("distanceToManeuverMeters", st.distanceToManeuverMeters)
    put("remainingDistanceMeters", st.remainingDistanceMeters)
    put("remainingDurationSeconds", st.remainingDurationSeconds)
    put(
      "etaEpochMs",
      (System.currentTimeMillis() + (st.remainingDurationSeconds * 1000.0).toLong()).toDouble(),
    )
    put("isOffRoute", st.isOffRoute)
  }
}
