package expo.modules.devlauncher.launcher.manifest

import android.net.Uri
import expo.modules.devlauncher.helpers.await
import expo.modules.devlauncher.helpers.fetch
import expo.modules.manifests.core.Manifest
import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.OkHttpClient
import org.json.JSONObject
import java.io.Reader

class DevLauncherManifestParser(
  private val httpClient: OkHttpClient,
  private val url: Uri,
  private val installationID: String?
) {
  suspend fun isManifestUrl(): Boolean {
    val response = fetch(url, "HEAD", getHeaders()).await(httpClient)
    val contentType = response.header("Content-Type")
    // published projects may respond unsuccessfully to HEAD requests sent with no headers
    return !response.isSuccessful ||
      response.header("Exponent-Server", null) != null ||
      (contentType != null && !contentType.startsWith("text/html") && !contentType.contains("/javascript"))
  }

  private suspend fun downloadManifest(): Reader {
    val response = fetch(url, "GET", getHeaders()).await(httpClient)
    if (!response.isSuccessful) {
      throw Exception("Failed to open app.\n\nIf you are trying to load the app from a development server, check your network connectivity and make sure you can access the server from your device.\n\nIf you are trying to open a published project, install a compatible version of expo-updates and follow all setup and integration steps.")
    }
    @Suppress("DEPRECATION_ERROR")
    return response.body()!!.charStream()
  }

  suspend fun parseManifest(): Manifest {
    downloadManifest().use {
      return Manifest.fromManifestJson(JSONObject(it.readText()))
    }
  }

  private fun getHeaders(): Headers {
    val headersMap = mutableMapOf(
      "expo-platform" to "android",
      "accept" to "application/expo+json,application/json"
    )
    if (installationID != null) {
      headersMap["expo-dev-client-id"] = installationID
    }
    return headersMap.toHeaders()
  }
}
