# Avatar Implementation Guide

## Обзор

Реализована полная система аватаров для Rosetta Messenger Android, совместимая с desktop версией:

- ✅ P2P доставка аватаров (PacketAvatar 0x0C)
- ✅ Multi-layer кэширование (Memory + SQLite + Encrypted Files)
- ✅ ChaCha20-Poly1305 шифрование для передачи
- ✅ Password-based шифрование для хранения
- ✅ Трекинг доставки аватаров
- ✅ UI компоненты (AvatarImage, AvatarPlaceholder)

## Архитектура

### 1. Crypto Layer (CryptoManager.kt)

```kotlin
// Шифрование для P2P передачи
val result = CryptoManager.chacha20Encrypt(base64Image)
// result.key, result.nonce, result.ciphertext

// Шифрование для локального хранения (пароль "rosetta-a")
val encrypted = CryptoManager.encryptWithPassword(data, "rosetta-a")
```

### 2. Database Layer

- **avatar_cache**: Хранит пути к зашифрованным файлам
- **avatar_delivery**: Трекинг доставки (кому отправлен аватар)

### 3. File Storage (AvatarFileManager.kt)

```kotlin
// Сохранение
val path = AvatarFileManager.saveAvatar(context, base64Image, publicKey)
// Путь: "a/md5hash"

// Чтение
val base64Image = AvatarFileManager.readAvatar(context, path)

// Конвертация изображения в PNG Base64
val base64Png = AvatarFileManager.imagePrepareForNetworkTransfer(context, imageBytes)
```

### 4. Repository Layer (AvatarRepository.kt)

```kotlin
val avatarRepository = AvatarRepository(
    context = context,
    avatarDao = database.avatarDao(),
    currentPublicKey = myPublicKey,
    currentPrivateKey = myPrivateKey,
    protocolManager = ProtocolManager
)

// Получить аватары пользователя
val avatars: StateFlow<List<AvatarInfo>> = avatarRepository.getAvatars(publicKey)

// Изменить свой аватар
avatarRepository.changeMyAvatar(base64Image)

// Отправить аватар контакту
avatarRepository.sendAvatarTo(contactPublicKey)

// Обработать входящий аватар
avatarRepository.handleIncomingAvatar(packetAvatar)
```

### 5. Network Layer (PacketAvatar)

```kotlin
class PacketAvatar : Packet() {
    var privateKey: String = ""      // Hash приватного ключа
    var fromPublicKey: String = ""   // Отправитель
    var toPublicKey: String = ""     // Получатель
    var chachaKey: String = ""       // RSA-encrypted ChaCha20 key+nonce
    var blob: String = ""            // ChaCha20-encrypted avatar data

    override fun getPacketId(): Int = 0x0C
}
```

### 6. UI Layer (AvatarImage.kt)

```kotlin
@Composable
fun AvatarImage(
    publicKey: String,
    avatarRepository: AvatarRepository?,
    size: Dp = 40.dp,
    isDarkTheme: Boolean,
    onClick: (() -> Unit)? = null,
    showOnlineIndicator: Boolean = false,
    isOnline: Boolean = false
)
```

## Интеграция

### Шаг 1: Инициализация в Application/Activity

```kotlin
class MainActivity : ComponentActivity() {
    private lateinit var avatarRepository: AvatarRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val database = RosettaDatabase.getDatabase(applicationContext)

        // После авторизации пользователя
        avatarRepository = AvatarRepository(
            context = applicationContext,
            avatarDao = database.avatarDao(),
            currentPublicKey = currentAccount.publicKey,
            currentPrivateKey = currentAccount.privateKey,
            protocolManager = ProtocolManager
        )

        // Передаем в ProtocolManager для обработки входящих пакетов
        ProtocolManager.setAvatarRepository(avatarRepository)
    }
}
```

### Шаг 2: Обновление ProtocolManager

Добавьте в ProtocolManager:

```kotlin
object ProtocolManager {
    private var avatarRepository: AvatarRepository? = null

    fun setAvatarRepository(repository: AvatarRepository) {
        avatarRepository = repository
    }

    // В setupPacketHandlers() обработчик уже добавлен:
    waitPacket(0x0C) { packet ->
        scope.launch(Dispatchers.IO) {
            avatarRepository?.handleIncomingAvatar(packet as PacketAvatar)
        }
    }
}
```

### Шаг 3: Использование в UI

#### Отображение аватара

```kotlin
@Composable
fun ChatListItem(
    publicKey: String,
    avatarRepository: AvatarRepository?,
    isDarkTheme: Boolean
) {
    Row {
        AvatarImage(
            publicKey = publicKey,
            avatarRepository = avatarRepository,
            size = 48.dp,
            isDarkTheme = isDarkTheme,
            showOnlineIndicator = true,
            isOnline = user.isOnline
        )
        // ... остальной контент
    }
}
```

#### Смена аватара

```kotlin
@Composable
fun ProfileScreen(
    avatarRepository: AvatarRepository,
    viewModel: ProfileViewModel
) {
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri: Uri? ->
        uri?.let {
            viewModel.uploadAvatar(it, avatarRepository)
        }
    }

    IconButton(onClick = { launcher.launch("image/*") }) {
        Icon(Icons.Default.Edit, "Change Avatar")
    }
}
```

#### ViewModel для загрузки

```kotlin
class ProfileViewModel : ViewModel() {
    fun uploadAvatar(uri: Uri, avatarRepository: AvatarRepository) {
        viewModelScope.launch {
            try {
                // Читаем файл
                val inputStream = context.contentResolver.openInputStream(uri)
                val imageBytes = inputStream?.readBytes()
                inputStream?.close()

                // Конвертируем в PNG Base64
                val base64Png = AvatarFileManager.imagePrepareForNetworkTransfer(
                    context,
                    imageBytes!!
                )

                // Сохраняем
                avatarRepository.changeMyAvatar(base64Png)

                // Показываем успех
                _uiState.value = _uiState.value.copy(
                    showSuccess = true,
                    successMessage = "Avatar updated"
                )
            } catch (e: Exception) {
                _uiState.value = _uiState.value.copy(
                    showError = true,
                    errorMessage = e.message
                )
            }
        }
    }
}
```

### Шаг 4: Автоматическая доставка аватаров

#### В ChatDetailScreen

```kotlin
LaunchedEffect(opponentPublicKey) {
    // Проверяем нужно ли отправить аватар
    val isDelivered = avatarRepository.isAvatarDelivered(opponentPublicKey)
    val hasAvatar = avatarRepository.getLatestAvatar(currentPublicKey) != null

    if (!isDelivered && hasAvatar) {
        // Показываем prompt или отправляем автоматически
        avatarRepository.sendAvatarTo(opponentPublicKey)
    }
}
```

## Тестирование

### 1. Локальное тестирование

```kotlin
// В тестовом классе
@Test
fun testAvatarEncryptionRoundTrip() {
    val originalData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="

    // Encrypt
    val result = CryptoManager.chacha20Encrypt(originalData)

    // Decrypt
    val decrypted = CryptoManager.chacha20Decrypt(
        result.ciphertext,
        result.nonce,
        result.key
    )

    assertEquals(originalData, decrypted)
}

@Test
fun testPasswordEncryption() {
    val data = "test image data"
    val password = "rosetta-a"

    val encrypted = CryptoManager.encryptWithPassword(data, password)
    val decrypted = CryptoManager.decryptWithPassword(encrypted, password)

    assertEquals(data, decrypted)
}
```

### 2. P2P тестирование

1. Установите приложение на 2 устройства/эмулятора
2. Авторизуйтесь с разными аккаунтами
3. Установите аватар на первом устройстве
4. Откройте чат с первым пользователем на втором устройстве
5. Проверьте что аватар отобразился

### 3. Проверка в Logcat

```bash
adb logcat -s Protocol:D AvatarRepository:D
```

Ожидаемые логи:

```
Protocol: 🖼️ Received avatar from 02a1b2c3...
AvatarRepository: Received avatar from 02a1b2c3...
AvatarRepository: Saved avatar for 02a1b2c3...
```

## Производительность

### Memory Management

- Memory cache ограничен (хранит только активные диалоги)
- Используйте `avatarRepository.clearMemoryCache()` при выходе из чата
- Файлы хранятся зашифрованными (экономия памяти)

### Network

- Аватары отправляются только 1 раз каждому контакту
- Используется трекинг доставки (avatar_delivery таблица)
- Chunking не реализован (лимит ~5MB на аватар)

### Database

- WAL mode включен для производительности
- Индексы на (public_key, timestamp) для быстрого поиска
- Автоматическое удаление старых аватаров (хранится MAX_AVATAR_HISTORY = 5)

## Troubleshooting

### Аватары не отображаются

1. Проверьте что AvatarRepository инициализирован
2. Проверьте логи декрипции файлов
3. Проверьте наличие файлов в `context.filesDir/avatars/`

### Аватары не отправляются

1. Проверьте что PacketAvatar (0x0C) зарегистрирован в Protocol.kt
2. Проверьте что ProtocolManager имеет ссылку на AvatarRepository
3. Проверьте статус соединения (AUTHENTICATED)

### Ошибки шифрования

1. Проверьте версию Google Tink (должна быть 1.10.0+)
2. Проверьте что пароль "rosetta-a" используется везде
3. Проверьте совместимость с desktop (тестируйте кросс-платформенно)

## Будущие улучшения

### Приоритет Высокий

- [ ] Интеграция AvatarRepository в MainActivity
- [ ] Image Picker в AvatarPicker composable
- [ ] Avatar upload UI в ProfileScreen
- [ ] Automatic delivery prompt (как в desktop)

### Приоритет Средний

- [ ] Avatar compression перед upload
- [ ] Multiple avatar sizes (thumbnail, full)
- [ ] Avatar history viewer в профиле
- [ ] Group avatars support

### Приоритет Низкий

- [ ] Chunking для больших файлов (>5MB)
- [ ] Coil disk cache integration
- [ ] Avatar delete functionality
- [ ] System avatars preloading

## Совместимость

### Desktop версия

✅ ChaCha20-Poly1305 шифрование  
✅ PBKDF2+AES для хранения  
✅ MD5 path generation  
✅ PacketAvatar структура  
✅ Delivery tracking

### React Native версия

⚠️ Требует тестирования (скорее всего совместимо)

### Telegram версия

❌ Не совместимо (другой протокол)
