package expo.modules.mapmapreactnative

import expo.modules.kotlin.exception.CodedException
import kotlinx.coroutines.CancellationException
import java.io.IOException

/**
 * Stable error codes — MUST match BridgeErrorCode in
 * modules/mapmap-react-native/src/MapmapNav.types.ts exactly.
 */
object E {
  const val NOT_INITIALISED = "E_NOT_INITIALISED"
  const val INIT_FAILED = "E_INIT_FAILED"
  const val NETWORK = "E_NETWORK"
  const val AUTH = "E_AUTH"
  const val TERRITORY_ALLOWANCE = "E_TERRITORY_ALLOWANCE"
  const val TERRITORY_VERIFY = "E_TERRITORY_VERIFY"
  const val TERRITORY_NOT_FOUND = "E_TERRITORY_NOT_FOUND"
  const val NO_TERRITORY = "E_NO_TERRITORY"
  const val NO_ROUTE = "E_NO_ROUTE"
  const val GUIDANCE_ACTIVE = "E_GUIDANCE_ACTIVE"
  const val NO_GUIDANCE = "E_NO_GUIDANCE"
  const val REPLAY_CORPUS = "E_REPLAY_CORPUS"
  const val INTERNAL = "E_INTERNAL"
}

/** Every rejected promise carries `code` + `message` (CONTRACTS.md error model). */
class BridgeException(
  code: String,
  message: String,
  cause: Throwable? = null,
) : CodedException(code, message, cause) {
  fun toEventMap(): Map<String, Any?> = mapOf(
    "code" to code,
    "message" to (message ?: ""),
  )
}

/** Wrap any unexpected throwable as E_INTERNAL, passing BridgeExceptions through. */
fun asBridgeException(t: Throwable): BridgeException = when (t) {
  is BridgeException -> t
  is IOException -> BridgeException(E.NETWORK, t.message ?: "network failure", t)
  else -> BridgeException(E.INTERNAL, t.message ?: t.javaClass.simpleName, t)
}

/** Coroutine cancellation must never be swallowed or remapped. */
fun rethrowIfCancellation(t: Throwable) {
  if (t is CancellationException) throw t
}
