package eu.gaudian.translator.model.communication

import android.content.Context
import androidx.core.content.edit
import eu.gaudian.translator.R
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
import java.io.File
import java.io.FileOutputStream
import java.security.MessageDigest

/**
 * Manages downloading files from the server, verifying checksums, and checking versions.
 */
class FileDownloadManager(private val context: Context) {

    private val baseUrl = "http://23.88.48.47/"

    private val retrofit = Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .client(OkHttpClient.Builder().build())
        .build()

    private val manifestApiService = retrofit.create<ManifestApiService>()

    @Suppress("HardCodedStringLiteral")
    private val sharedPreferences = context.getSharedPreferences("file_versions", Context.MODE_PRIVATE)

    /**
     * Fetches the manifest from the server.
     */
    suspend fun fetchManifest(): ManifestResponse? = withContext(Dispatchers.IO) {
        try {
            val response = manifestApiService.getManifest().execute()
            if (response.isSuccessful) {
                response.body()
            } else {
                @Suppress("HardCodedStringLiteral") val errorMessage = response.errorBody()?.string() ?: "HTTP ${response.code()}"

                @Suppress("HardCodedStringLiteral")
                Log.e("FileDownloadManager", "Failed to fetch manifest: $errorMessage")
                throw Exception(context.getString(R.string.failed_to_fetch_manifest, errorMessage))
            }
        } catch (e: Exception) {
            @Suppress("HardCodedStringLiteral")
            Log.e("FileDownloadManager", "Error fetching manifest", e)
            throw e
        }
    }

    /**
     * Downloads all assets for a file and verifies their checksums.
     */
    suspend fun downloadFile(fileInfo: FileInfo, onProgress: (Float) -> Unit = {}): Boolean = withContext(Dispatchers.IO) {
        val totalAssets = fileInfo.assets.size

        for ((completedAssets, asset) in fileInfo.assets.withIndex()) {
            val success = downloadAsset(asset) { assetProgress ->
                // Calculate overall progress
                val assetContribution = assetProgress / totalAssets
                val previousAssetsProgress = completedAssets.toFloat() / totalAssets
                onProgress(previousAssetsProgress + assetContribution)
            }
            if (!success) {
                return@withContext false
            }
        }

        // Save version after all assets are downloaded successfully
        sharedPreferences.edit { putString(fileInfo.id, fileInfo.version) }
        @Suppress("HardCodedStringLiteral")
        Log.d("FileDownloadManager", "Download successful for all assets of ${fileInfo.name}")
        true
    }

    /**
     * Downloads a specific asset and verifies its checksum.
     */
    private suspend fun downloadAsset(asset: Asset, onProgress: (Float) -> Unit = {}): Boolean = withContext(Dispatchers.IO) {
        val fileUrl = "${baseUrl}${asset.filename}"
        val localFile = File(context.filesDir, asset.filename)

        try {
            val client = OkHttpClient()
            val request = Request.Builder().url(fileUrl).build()
            val response = client.newCall(request).execute()

            if (!response.isSuccessful) {
                val errorMessage = context.getString(
                    R.string.text_download_failed_http,
                    response.code,
                    response.message
                )
                Log.e("FileDownloadManager", errorMessage)
                throw Exception(errorMessage)
            }

            val body = response.body

            val contentLength = body.contentLength()
            if (contentLength <= 0) {
                throw Exception("Invalid file size: $contentLength")
            }

            FileOutputStream(localFile).use { output ->
                body.byteStream().use { input ->
                    val buffer = ByteArray(8192)
                    var bytesRead: Int
                    var totalBytesRead: Long = 0
                    @Suppress("HardCodedStringLiteral") val digest = MessageDigest.getInstance("SHA-256")

                    while (input.read(buffer).also { bytesRead = it } != -1) {
                        output.write(buffer, 0, bytesRead)
                        digest.update(buffer, 0, bytesRead)
                        totalBytesRead += bytesRead
                        onProgress((totalBytesRead.toFloat() / contentLength))
                    }

                    output.flush()

                    // Compute checksum
                    val computedChecksum = digest.digest().joinToString("") {
                        @Suppress("HardCodedStringLiteral")
                        "%02X".format(it)
                    }

                    if (computedChecksum.equals(asset.checksumSha256, ignoreCase = true)) {
                        @Suppress("HardCodedStringLiteral")
                        Log.d("FileDownloadManager", "Download successful for ${asset.filename}")
                        true
                    } else {
                        Log.e("FileDownloadManager",
                            context.getString(
                                R.string.text_checksum_mismatch_for_expected_got,
                                asset.filename,
                                asset.checksumSha256,
                                computedChecksum
                            ))
                        localFile.delete() // Delete corrupted file
                        throw Exception("Checksum verification failed for ${asset.filename}")
                    }
                }
            }
        } catch (e: Exception) {
            @Suppress("HardCodedStringLiteral")
            Log.e("FileDownloadManager", "Error downloading asset", e)
            // Clean up partial download
            if (localFile.exists()) {
                localFile.delete()
            }
            throw e
        }
    }

    /**
     * Checks if a newer version is available for a file.
     */
    fun isNewerVersionAvailable(fileInfo: FileInfo): Boolean {
        val localVersion = sharedPreferences.getString(fileInfo.id, "0.0.0") ?: "0.0.0"
        return compareVersions(fileInfo.version, localVersion) > 0
    }

    /**
     * Compares two version strings (assuming semantic versioning).
     */
    private fun compareVersions(version1: String, version2: String): Int {
        val parts1 = version1.split(".").map { it.toIntOrNull() ?: 0 }
        val parts2 = version2.split(".").map { it.toIntOrNull() ?: 0 }

        for (i in 0 until maxOf(parts1.size, parts2.size)) {
            val part1 = parts1.getOrElse(i) { 0 }
            val part2 = parts2.getOrElse(i) { 0 }
            if (part1 != part2) {
                return part1.compareTo(part2)
            }
        }
        return 0
    }

    /**
     * Gets the local version of a file.
     */
    fun getLocalVersion(fileId: String): String {
        return sharedPreferences.getString(fileId, "0.0.0") ?: "0.0.0"
    }
}