package expo.modules.mapmapreactnative

import java.io.ByteArrayOutputStream
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL

/**
 * Minimal blocking HTTP client for the territory channel — HttpURLConnection on
 * purpose (no extra dependency). Every request carries
 * `Authorization: Bearer <apiKey>` per docs/territories.
 *
 * Status → contract error mapping:
 *   401/403 → E_AUTH, 402 → E_TERRITORY_ALLOWANCE (download allowance, distinct
 *   from the x402 payment challenge), 404 → E_TERRITORY_NOT_FOUND,
 *   other non-2xx and transport failures → E_NETWORK.
 */
class GatewayClient(
  /** Gateway base, no trailing slash, e.g. https://api.mapmap.ai */
  val baseUrl: String,
  private val apiKey: String,
) {

  /**
   * GET `path` (absolute URL, "/rooted" path, or channel-relative path) and
   * return the body. [onChunk] receives the byte count of each chunk read —
   * used for territoryProgress accounting.
   */
  fun getBytes(path: String, onChunk: ((Int) -> Unit)? = null): ByteArray {
    val url = resolve(path)
    val connection = try {
      URL(url).openConnection() as HttpURLConnection
    } catch (t: Throwable) {
      throw BridgeException(E.NETWORK, "cannot open connection to $url: ${t.message}", t)
    }
    try {
      connection.requestMethod = "GET"
      connection.connectTimeout = 20_000
      connection.readTimeout = 120_000
      connection.setRequestProperty("Authorization", "Bearer $apiKey")
      connection.setRequestProperty("Accept", "*/*")

      val status = try {
        connection.responseCode
      } catch (io: IOException) {
        throw BridgeException(E.NETWORK, "request to $url failed: ${io.message}", io)
      }
      if (status !in 200..299) {
        throw statusError(status, url)
      }

      val out = ByteArrayOutputStream(maxOf(connection.contentLength, 64 * 1024))
      val buffer = ByteArray(64 * 1024)
      connection.inputStream.use { input ->
        while (true) {
          val read = try {
            input.read(buffer)
          } catch (io: IOException) {
            throw BridgeException(E.NETWORK, "download from $url interrupted: ${io.message}", io)
          }
          if (read < 0) break
          out.write(buffer, 0, read)
          onChunk?.invoke(read)
        }
      }
      return out.toByteArray()
    } finally {
      connection.disconnect()
    }
  }

  /**
   * The SDK's LayerFetcher hands us a path whose exact shape is undocumented;
   * accept absolute URLs, gateway-rooted paths, and channel-relative paths
   * ("uk/1.2.0/layers/<addr>/<file>" → "$base/territories/…").
   */
  fun resolve(path: String): String = when {
    path.startsWith("http://") || path.startsWith("https://") -> path
    path.startsWith("/") -> baseUrl + path
    else -> "$baseUrl/territories/$path"
  }

  private fun statusError(status: Int, url: String): BridgeException = when (status) {
    401, 403 -> BridgeException(E.AUTH, "API key rejected (HTTP $status) for $url")
    402 -> BridgeException(
      E.TERRITORY_ALLOWANCE,
      "download allowance exhausted (HTTP 402) — territory layer bytes are metered; upgrade the key or pick a smaller territory",
    )
    404 -> BridgeException(E.TERRITORY_NOT_FOUND, "not found (HTTP 404): $url")
    else -> BridgeException(E.NETWORK, "gateway returned HTTP $status for $url")
  }
}
