package de.krisenvorrat.app.data.repository

import de.krisenvorrat.app.domain.model.VersionInfo
import de.krisenvorrat.app.domain.repository.UpdateRepository
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.prepareGet
import io.ktor.client.statement.bodyAsChannel
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentLength
import io.ktor.utils.io.readAvailable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.net.ConnectException
import java.net.SocketTimeoutException
import javax.inject.Inject

internal class UpdateRepositoryImpl @Inject constructor(
    private val httpClient: HttpClient
) : UpdateRepository {

    override suspend fun checkForUpdate(serverUrl: String): Result<VersionInfo> =
        withContext(Dispatchers.IO) {
            try {
                val url = "${serverUrl.trimEnd('/')}/api/version"
                val response = httpClient.get(url)
                when (response.status) {
                    HttpStatusCode.OK -> Result.success(response.body<VersionInfo>())
                    else -> Result.failure(
                        Exception("Server returned ${response.status.value}: ${response.status.description}")
                    )
                }
            } catch (e: SocketTimeoutException) {
                Result.failure(e)
            } catch (e: ConnectException) {
                Result.failure(e)
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    override suspend fun downloadApk(
        apkUrl: String,
        targetFile: File,
        onProgress: (Float) -> Unit
    ): Result<File> = withContext(Dispatchers.IO) {
        try {
            targetFile.parentFile?.mkdirs()

            httpClient.prepareGet(apkUrl).execute { response ->
                if (response.status != HttpStatusCode.OK) {
                    return@execute Result.failure(
                        Exception("Download failed: ${response.status.value}")
                    )
                }

                val totalBytes = response.contentLength() ?: -1L
                val channel = response.bodyAsChannel()
                val buffer = ByteArray(DOWNLOAD_BUFFER_SIZE)
                var bytesRead = 0L

                targetFile.outputStream().buffered().use { output ->
                    while (!channel.isClosedForRead) {
                        val read = channel.readAvailable(buffer)
                        if (read <= 0) break
                        output.write(buffer, 0, read)
                        bytesRead += read
                        if (totalBytes > 0) {
                            onProgress(bytesRead.toFloat() / totalBytes.toFloat())
                        }
                    }
                }

                onProgress(1f)
                Result.success(targetFile)
            }
        } catch (e: Exception) {
            targetFile.delete()
            Result.failure(e)
        }
    }

    private companion object {
        const val DOWNLOAD_BUFFER_SIZE = 8192
    }
}
