package app.closer.data.remote

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageMetadata
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

@Singleton
class FirebaseStorageDataSource @Inject constructor(
    @ApplicationContext private val context: Context
) {

    // Cap retries so an offline upload surfaces a failure in seconds instead of the ~2-minute default.
    private val storage = FirebaseStorage.getInstance().apply {
        maxUploadRetryTimeMillis = 30_000
        maxOperationRetryTimeMillis = 30_000
    }

    private fun isOnline(): Boolean {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return true
        val caps = cm.activeNetwork?.let { cm.getNetworkCapabilities(it) } ?: return false
        return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    }

    suspend fun uploadProfilePhoto(uid: String, uri: Uri): String =
        suspendCancellableCoroutine { cont ->
            val ref = storage.reference.child("users/$uid/profile.jpg")
            val mimeType = context.contentResolver.getType(uri)
                ?.takeIf { it.startsWith("image/") }
                ?: "image/jpeg"
            val metadata = StorageMetadata.Builder()
                .setContentType(mimeType)
                .build()
            ref.putFile(uri, metadata)
                .continueWithTask { ref.downloadUrl }
                .addOnSuccessListener { cont.resume(it.toString()) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    /**
     * Uploads already-encrypted chat-media bytes under the author's own storage path (mirrors the
     * profile-photo ownership model) and returns the tokenized download URL. The bytes are
     * ciphertext, so Storage never holds anything readable.
     */
    suspend fun uploadEncryptedMedia(uid: String, encryptedBytes: ByteArray): String =
        suspendCancellableCoroutine { cont ->
            // Fail fast when clearly offline so the chat shows a retry instead of a stuck spinner.
            if (!isOnline()) {
                cont.resumeWithException(IOException("No internet connection"))
                return@suspendCancellableCoroutine
            }
            val ref = storage.reference.child("users/$uid/chat_media/${java.util.UUID.randomUUID()}")
            val metadata = StorageMetadata.Builder()
                .setContentType("application/octet-stream")
                .build()
            ref.putBytes(encryptedBytes, metadata)
                .continueWithTask { ref.downloadUrl }
                .addOnSuccessListener { cont.resume(it.toString()) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    /**
     * Downloads the raw (still-encrypted) bytes for a media message over HTTP using the tokenized
     * download URL, so the partner can read the author's object (the URL token authorizes it,
     * bypassing the owner-scoped Storage rule — same model as profile photos).
     */
    suspend fun downloadBytes(downloadUrl: String): ByteArray =
        kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
            val connection = (java.net.URL(downloadUrl).openConnection() as java.net.HttpURLConnection).apply {
                connectTimeout = 20_000
                readTimeout = 20_000
            }
            try {
                connection.inputStream.use { it.readBytes() }
            } finally {
                connection.disconnect()
            }
        }
}
