package com.rosetta.messenger.repository

import android.content.Context
import com.rosetta.messenger.database.AvatarCacheEntity
import com.rosetta.messenger.database.AvatarDao
import com.rosetta.messenger.utils.AvatarFileManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import java.io.Closeable

/**
 * Репозиторий для работы с аватарами
 * Совместимо с desktop версией (AvatarProvider)
 * 
 * Desktop логика:
 * - Аватары передаются как attachment в сообщениях (AttachmentType.AVATAR)
 * - Локальное хранение: SQLite (avatar_cache) + зашифрованные файлы
 * - Memory cache для декодированных изображений
 * 
 * НЕТ отдельного P2P пакета для аватаров!
 */
class AvatarRepository(
    private val context: Context,
    private val avatarDao: AvatarDao,
    private val currentPublicKey: String
) : Closeable {
    companion object {
        private const val TAG = "AvatarRepository"
        private const val MAX_AVATAR_HISTORY = 5 // Хранить последние N аватаров
        private const val MAX_CACHE_SIZE = 100
    }

    // Repository scope для coroutines
    private val supervisorJob = kotlinx.coroutines.SupervisorJob()
    private val repositoryScope = kotlinx.coroutines.CoroutineScope(
        supervisorJob + Dispatchers.IO
    )

    // In-memory LRU cache: publicKey -> (Flow, Job)
    // При вытеснении отменяем Job подписки на БД
    private data class CacheEntry(val flow: MutableStateFlow<List<AvatarInfo>>, val job: Job?)
    private val memoryCache = object : LinkedHashMap<String, CacheEntry>(MAX_CACHE_SIZE, 0.75f, true) {
        override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, CacheEntry>?): Boolean {
            if (size > MAX_CACHE_SIZE) {
                eldest?.value?.job?.cancel()
                return true
            }
            return false
        }
    }

    private fun normalizeOwnerKey(publicKey: String): String {
        val trimmed = publicKey.trim()
        if (trimmed.isBlank()) return trimmed
        return when {
            trimmed.startsWith("#group:") -> {
                val groupId = trimmed.removePrefix("#group:").trim()
                if (groupId.isBlank()) trimmed else "#group:$groupId"
            }
            trimmed.startsWith("group:", ignoreCase = true) -> {
                val groupId = trimmed.substringAfter(':').trim()
                if (groupId.isBlank()) trimmed else "#group:$groupId"
            }
            else -> trimmed
        }
    }

    private fun lookupKeys(publicKey: String): List<String> {
        val normalized = normalizeOwnerKey(publicKey)
        if (normalized.isBlank()) return emptyList()
        val keys = linkedSetOf(normalized)
        if (normalized.startsWith("#group:")) {
            keys.add(normalized.removePrefix("#group:"))
        }
        val trimmed = publicKey.trim()
        if (trimmed.isNotBlank()) {
            keys.add(trimmed)
        }
        return keys.toList()
    }
    
    /**
     * Получить аватары пользователя
     * @param publicKey Публичный ключ пользователя
     * @param allDecode true = вся история, false = только последний (для списков)
     */
    fun getAvatars(publicKey: String, allDecode: Boolean = false): StateFlow<List<AvatarInfo>> {
        val normalizedKey = normalizeOwnerKey(publicKey)
        val keys = lookupKeys(publicKey)
        if (normalizedKey.isBlank() || keys.isEmpty()) {
            return MutableStateFlow(emptyList<AvatarInfo>()).asStateFlow()
        }

        // Проверяем LRU cache (accessOrder=true обновляет позицию при get)
        memoryCache[normalizedKey]?.let { return it.flow.asStateFlow() }

        // Создаем новый flow для этого пользователя
        val flow = MutableStateFlow<List<AvatarInfo>>(emptyList())

        // Подписываемся на изменения в БД
        val job = avatarDao.getAvatarsByKeys(keys)
            .onEach { entities ->
                val avatars = if (allDecode) {
                    // Параллельная загрузка всей истории
                    coroutineScope {
                        entities.map { entity -> async { loadAndDecryptAvatar(entity) } }
                            .awaitAll()
                            .filterNotNull()
                    }
                } else {
                    // Загружаем только последний
                    entities.firstOrNull()?.let { entity ->
                        loadAndDecryptAvatar(entity)
                    }?.let { listOf(it) } ?: emptyList()
                }
                flow.value = avatars
            }
            .launchIn(repositoryScope)

        memoryCache[normalizedKey] = CacheEntry(flow, job)
        return flow.asStateFlow()
    }
    
    /**
     * Получить последний аватар пользователя (suspend версия)
     */
    suspend fun getLatestAvatar(publicKey: String): AvatarInfo? {
        val keys = lookupKeys(publicKey)
        if (keys.isEmpty()) return null
        val entity = avatarDao.getLatestAvatarByKeys(keys) ?: return null
        return loadAndDecryptAvatar(entity)
    }
    
    /**
     * Сохранить полученный аватар от другого пользователя
     * Вызывается при получении attachment с типом AVATAR в сообщении
     * 
     * @param fromPublicKey Публичный ключ отправителя
     * @param base64Image Base64-encoded изображение
     */
    suspend fun saveAvatar(fromPublicKey: String, base64Image: String) {
        withContext(Dispatchers.IO) {
            try {
                val ownerKey = normalizeOwnerKey(fromPublicKey)
                if (ownerKey.isBlank()) return@withContext
                // Сохраняем файл
                val filePath = AvatarFileManager.saveAvatar(context, base64Image, ownerKey)
                
                // Сохраняем в БД
                val entity = AvatarCacheEntity(
                    publicKey = ownerKey,
                    avatar = filePath,
                    timestamp = System.currentTimeMillis()
                )
                avatarDao.insertAvatar(entity)
                
                // Очищаем старые аватары (оставляем только последние N)
                avatarDao.deleteOldAvatars(ownerKey, MAX_AVATAR_HISTORY)
                
                // 🔄 Обновляем memory cache если он существует
                val cached = memoryCache[ownerKey]
                if (cached != null) {
                    val avatarInfo = loadAndDecryptAvatar(entity)
                    if (avatarInfo != null) {
                        cached.flow.value = listOf(avatarInfo)
                    }
                }
                
            } catch (e: Exception) {
            }
        }
    }
    
    /**
     * Изменить свой аватар (сохранить локально)
     * Отправка происходит через сообщение с attachment типа AVATAR
     * 
     * @param base64Image Base64-encoded изображение
     */
    suspend fun changeMyAvatar(base64Image: String) {
        withContext(Dispatchers.IO) {
            try {
                
                // Сохраняем файл
                val filePath = AvatarFileManager.saveAvatar(context, base64Image, currentPublicKey)
                
                // Сохраняем в БД
                val entity = AvatarCacheEntity(
                    publicKey = currentPublicKey,
                    avatar = filePath,
                    timestamp = System.currentTimeMillis()
                )
                avatarDao.insertAvatar(entity)
                
                // Очищаем старые аватары
                avatarDao.deleteOldAvatars(currentPublicKey, MAX_AVATAR_HISTORY)
                
            } catch (e: Exception) {
                throw e
            }
        }
    }
    
    /**
     * Удалить свой аватар
     */
    suspend fun deleteMyAvatar() {
        withContext(Dispatchers.IO) {
            try {
                
                // Получаем все аватары пользователя
                val avatars = avatarDao.getAvatarsByPublicKey(currentPublicKey)
                
                // Удаляем файлы
                for (avatar in avatars) {
                    try {
                        AvatarFileManager.deleteAvatar(context, avatar.avatar)
                    } catch (e: Exception) {
                    }
                }
                
                // Удаляем из БД
                avatarDao.deleteAllAvatars(currentPublicKey)
                
                // Важно: не удаляем cache entry и не отменяем Job здесь.
                // Иначе уже подписанные composable продолжают слушать "мертвый" flow
                // со старым значением до следующей рекомпозиции.
                // Сразу пушим пустой список в существующий flow, чтобы UI обновился мгновенно.
                memoryCache[currentPublicKey]?.flow?.value = emptyList()
                
            } catch (e: Exception) {
                throw e
            }
        }
    }
    
    /**
     * Загрузить и расшифровать аватар из файла
     */
    private suspend fun loadAndDecryptAvatar(entity: AvatarCacheEntity): AvatarInfo? {
        return withContext(Dispatchers.IO) {
            try {
                val base64Image = AvatarFileManager.readAvatar(context, entity.avatar)
                if (base64Image != null) {
                    AvatarInfo(
                        base64Data = base64Image,
                        timestamp = entity.timestamp
                    )
                } else {
                    null
                }
            } catch (e: Exception) {
                null
            }
        }
    }
    
    /**
     * Очистить memory cache (для освобождения памяти)
     */
    fun clearMemoryCache() {
        memoryCache.values.forEach { it.job?.cancel() }
        memoryCache.clear()
    }

    override fun close() {
        clearMemoryCache()
        supervisorJob.cancel()
    }
    
    /**
     * Предзагрузить системные аватары (для ботов/системных аккаунтов)
     * Аналогично desktop версии, которая использует hardcoded аватары
     */
    suspend fun preloadSystemAvatars(systemAccounts: Map<String, String>) {
        withContext(Dispatchers.IO) {
            systemAccounts.forEach { (publicKey, base64Avatar) ->
                // Сохраняем только в memory cache, не в БД (job=null — нет подписки)
                val flow = MutableStateFlow(listOf(
                    AvatarInfo(
                        base64Data = base64Avatar,
                        timestamp = 0
                    )
                ))
                memoryCache[publicKey] = CacheEntry(flow, job = null)
            }
        }
    }
}

/**
 * Информация об аватаре
 */
data class AvatarInfo(
    val base64Data: String,  // Base64-encoded изображение
    val timestamp: Long      // Unix timestamp
)
