@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.repository

import android.content.Context
import androidx.core.content.edit
import eu.gaudian.translator.R
import eu.gaudian.translator.model.communication.Asset
import eu.gaudian.translator.model.communication.FileDownloadManager
import eu.gaudian.translator.model.communication.FileInfo
import eu.gaudian.translator.model.communication.ManifestResponse
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.io.File

/**
 * Repository for managing downloaded dictionary files.
 */
class DictionaryFileRepository(private val context: Context) {

    private val fileDownloadManager = FileDownloadManager(context)
    private val _downloadedDictionaries = MutableStateFlow<List<FileInfo>>(emptyList())
    val downloadedDictionaries: Flow<List<FileInfo>> = _downloadedDictionaries.asStateFlow()

    private val _orphanedFiles = MutableStateFlow<List<FileInfo>>(emptyList())
    val orphanedFiles: Flow<List<FileInfo>> = _orphanedFiles.asStateFlow()

    private val _manifest = MutableStateFlow<ManifestResponse?>(null)
    val manifest: Flow<ManifestResponse?> = _manifest.asStateFlow()

    init {
        loadDownloadedDictionaries()
    }

    /**
     * Fetches the manifest and updates the state.
     */
    suspend fun fetchManifest() {
        try {
            val manifestResponse = fileDownloadManager.fetchManifest()
            if (manifestResponse != null) {
                _manifest.value = manifestResponse
                loadDownloadedDictionaries() // Refresh both downloaded and orphaned lists
            } else {
                throw Exception("Manifest response is null")
            }
        } catch (e: Exception) {
            Log.e("DictionaryFileRepository", "Error fetching manifest", e)
            throw e
        }
    }

    /**
     * Downloads a dictionary file.
     */
    suspend fun downloadDictionary(fileInfo: FileInfo, onProgress: (Float) -> Unit = {}): Boolean {
        try {
            val success = fileDownloadManager.downloadFile(fileInfo, onProgress)
            if (success) {
                loadDownloadedDictionaries()
            }
            return success
        } catch (e: Exception) {
            Log.e("DictionaryFileRepository", "Error downloading dictionary", e)
            throw e
        }
    }

    /**
     * Checks if a newer version is available for a dictionary.
     */
    fun isNewerVersionAvailable(fileInfo: FileInfo): Boolean {
        return fileDownloadManager.isNewerVersionAvailable(fileInfo)
    }

    /**
     * Gets the local version of a dictionary.
     */
    fun getLocalVersion(fileId: String): String {
        return fileDownloadManager.getLocalVersion(fileId)
    }

    /**
     * Deletes all assets of a downloaded dictionary.
     */
    @Suppress("SameReturnValue", "SameReturnValue")
    fun deleteDictionary(fileInfo: FileInfo): Boolean {
        try {
            var allDeleted = true
            val failedFiles = mutableListOf<String>()

            for (asset in fileInfo.assets) {
                val localFile = File(context.filesDir, asset.filename)
                if (localFile.exists()) {
                    val deleted = localFile.delete()
                    if (!deleted) {
                        allDeleted = false
                        failedFiles.add(asset.filename)
                        Log.e("DictionaryFileRepository", "Failed to delete asset: ${asset.filename}")
                    } else {
                        Log.d("DictionaryFileRepository", "Deleted asset: ${asset.filename}")
                    }
                } else {
                    Log.w("DictionaryFileRepository", "Asset file not found: ${asset.filename}")
                }
            }

            if (allDeleted) {
                // Remove version from SharedPreferences
                val sharedPreferences = context.getSharedPreferences("file_versions", Context.MODE_PRIVATE)
                sharedPreferences.edit { remove(fileInfo.id) }
                loadDownloadedDictionaries()
                Log.d("DictionaryFileRepository", "Deleted all assets for dictionary: ${fileInfo.name}")
            } else {
                throw Exception("Failed to delete some assets: ${failedFiles.joinToString(", ")}")
            }

            return true
        } catch (e: Exception) {
            Log.e("DictionaryFileRepository", "Error deleting dictionary", e)
            throw e
        }
    }

    /**
     * Deletes all downloaded dictionary files and their assets.
     */
    @Suppress("SameReturnValue")
    fun deleteAllDictionaries(): Boolean {
        try {
            val filesDir = context.filesDir
            // Find all dictionary-related files (.db and .zstdict)
            val dictionaryFiles = filesDir.listFiles { file ->
                file.isFile && (
                    file.name.endsWith(".db") ||
                        file.name.endsWith(".zstdict") ||
                        file.name.startsWith("dictionary")
                    ) && !file.name.endsWith(".db-wal") &&
                    !file.name.endsWith(".db-shm")
            }

            if (dictionaryFiles.isNullOrEmpty()) {
                Log.d("DictionaryFileRepository", "No dictionary files found to delete")
                return true
            }

            var allDeleted = true
            val failedFiles = mutableListOf<String>()

            dictionaryFiles.forEach { file ->
                val deleted = file.delete()
                if (!deleted) {
                    allDeleted = false
                    failedFiles.add(file.name)
                    Log.e("DictionaryFileRepository", "Failed to delete file: ${file.name}")
                }
            }

            if (allDeleted) {
                // Clear all versions from SharedPreferences
                val sharedPreferences = context.getSharedPreferences("file_versions", Context.MODE_PRIVATE)
                sharedPreferences.edit { clear() }
                loadDownloadedDictionaries()
                Log.d("DictionaryFileRepository", "Deleted all dictionary files")
            } else {
                throw Exception("Failed to delete some files: ${failedFiles.joinToString(", ")}")
            }

            return true
        } catch (e: Exception) {
            Log.e("DictionaryFileRepository", "Error deleting all dictionaries", e)
            throw e
        }
    }

    /**
     * Deletes an orphaned file.
     */
    fun deleteOrphanedFile(fileInfo: FileInfo): Boolean {
        try {
            // For orphaned files, we only have one asset (the .db file)
            val asset = fileInfo.assets.firstOrNull()
                ?: throw Exception("No asset found for orphaned file: ${fileInfo.id}")

            val localFile = File(context.filesDir, asset.filename)
            if (!localFile.exists()) {
                throw Exception("Orphaned file not found: ${asset.filename}")
            }

            val deleted = localFile.delete()
            if (deleted) {
                loadDownloadedDictionaries()
                Log.d("DictionaryFileRepository", "Deleted orphaned file: ${asset.filename}")
            } else {
                throw Exception("Failed to delete orphaned file: ${asset.filename}")
            }

            return true
        } catch (e: Exception) {
            Log.e("DictionaryFileRepository", "Error deleting orphaned file", e)
            return false
        }
    }

    /**
     * Gets the total size of all assets for a downloaded dictionary.
     */
    fun getDictionarySize(fileInfo: FileInfo): Long {
        return fileInfo.assets.sumOf { asset ->
            val localFile = File(context.filesDir, asset.filename)
            if (localFile.exists()) localFile.length() else 0L
        }
    }

    /**
     * Loads the list of downloaded dictionaries and orphaned files based on local files.
     */
    private fun loadDownloadedDictionaries() {
        val filesDir = context.filesDir
        val allDictionaryFiles = filesDir.listFiles { file ->
            file.isFile && (
                file.name.endsWith(".db") ||
                file.name.endsWith(".zstdict") ||
                file.name.endsWith(".db.corrupt") ||
                        file.name.startsWith("dictionary")
            ) &&
                !file.name.endsWith(".db-wal") &&
                !file.name.endsWith(".db-shm")

        } ?: emptyArray()

        val manifestFiles = _manifest.value?.files ?: emptyList()

        // Find downloaded dictionaries (files where all assets exist locally)
        val downloadedFiles = manifestFiles.filter { fileInfo ->
            fileInfo.assets.all { asset ->
                File(context.filesDir, asset.filename).exists()
            }
        }

        // Collect all asset filenames from downloaded dictionaries
        val downloadedAssetFilenames = downloadedFiles.flatMap { it.assets }.map { it.filename }.toSet()

        // Find orphaned files (dictionary files that are not assets of any downloaded dictionary)
        val orphanedFiles = allDictionaryFiles
            .filter { file -> file.name !in downloadedAssetFilenames }
            .map { file ->
                // Create a FileInfo for orphaned files with minimal data
                FileInfo(
                    id = "orphaned_${file.name}",
                    name = context.getString(R.string.label_unknown_dictionary_d, file.name),
                    description = context.getString(R.string.text_orphaned_file_description),
                    version = context.getString(R.string.label_unknown),
                    assets = listOf(
                        Asset(
                            filename = file.name,
                            sizeBytes = file.length(),
                            checksumSha256 = context.getString(R.string.label_unknown),
                        )
                    )
                )
            }
            .toList()

        _downloadedDictionaries.value = downloadedFiles
        _orphanedFiles.value = orphanedFiles
    }
}