package com.example.filedrop

import android.content.ContentResolver
import android.net.Uri
import android.provider.OpenableColumns
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.IOException
import java.util.concurrent.TimeUnit

/** Basic file metadata resolved from a content Uri. */
data class FileMeta(val name: String, val size: Long)

/** Streams a content Uri to the PC receiver as a multipart upload, one file per request. */
class Uploader(private val contentResolver: ContentResolver) {

    private val client = OkHttpClient.Builder()
        .connectTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(0, TimeUnit.SECONDS) // large files stream indefinitely
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    fun queryMeta(uri: Uri): FileMeta {
        var name = "file"
        var size = -1L
        contentResolver.query(uri, null, null, null, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val nameIdx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                val sizeIdx = cursor.getColumnIndex(OpenableColumns.SIZE)
                if (nameIdx >= 0 && !cursor.isNull(nameIdx)) name = cursor.getString(nameIdx)
                if (sizeIdx >= 0 && !cursor.isNull(sizeIdx)) size = cursor.getLong(sizeIdx)
            }
        }
        return FileMeta(name, size)
    }

    /**
     * Uploads [uri] to `http://<address>/upload`. [onProgress] receives a 0f..1f fraction
     * (or -1f when the size is unknown). Returns the server's response text on success.
     */
    suspend fun upload(
        address: String,
        token: String,
        uri: Uri,
        meta: FileMeta,
        onProgress: (Float) -> Unit,
    ): Result<String> = withContext(Dispatchers.IO) {
        runCatching {
            val url = "http://${address.trim().trimEnd('/')}/upload"
            val mediaType = contentResolver.getType(uri)?.toMediaTypeOrNull()

            val fileBody = object : RequestBody() {
                override fun contentType() = mediaType
                override fun contentLength() = meta.size
                override fun writeTo(sink: BufferedSink) {
                    val input = contentResolver.openInputStream(uri)
                        ?: throw IOException("Cannot open $uri")
                    input.use {
                        val buffer = ByteArray(64 * 1024)
                        var uploaded = 0L
                        while (true) {
                            val read = it.read(buffer)
                            if (read == -1) break
                            sink.write(buffer, 0, read)
                            uploaded += read
                            onProgress(if (meta.size > 0) uploaded.toFloat() / meta.size else -1f)
                        }
                    }
                }
            }

            val multipart = MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", meta.name, fileBody)
                .build()

            val requestBuilder = Request.Builder().url(url).post(multipart)
            if (token.isNotBlank()) requestBuilder.header("X-Auth", token.trim())

            client.newCall(requestBuilder.build()).execute().use { response ->
                val body = response.body?.string().orEmpty()
                if (!response.isSuccessful) {
                    throw IOException("HTTP ${response.code}: ${body.ifBlank { response.message }}")
                }
                onProgress(1f)
                body.ifBlank { "saved" }
            }
        }
    }
}
