from textual.app import App, ComposeResult
from textual.containers import Vertical, Horizontal, Center, VerticalScroll
from textual.widgets import Button, Input, Static, ListView, ListItem, Label, RadioSet, RadioButton, Checkbox
from textual.screen import Screen, ModalScreen
from textual import events
from config import APP_DIR
from database import DB
from global_settings import load_global_config, save_global_config
import re
import pathlib
import asyncio
import json
import websockets
from crypto_utils import Identity, b64e, b64d

LAUNCHER_I18N = {
    "en": {
        "account_label": "Account: {alias}",
        "encrypted": "🔒 Encrypted",
        "btn_login": "Login",
        "btn_edit_server": "Edit Server",
        "btn_remove_passphrase": "🔓 Remove Passphrase",
        "btn_add_passphrase": "🔒 Add Passphrase",
        "btn_delete_local": "Delete (Local Only)",
        "btn_delete_server": "Delete (Server & Local)",
        "btn_cancel": "Cancel",
        "passphrase_title": "🔒 {alias}",
        "db_encrypted": "This database is encrypted",
        "ph_enter_passphrase": "Enter passphrase",
        "btn_unlock": "Unlock",
        "add_passphrase_title": "🔒 Add Passphrase",
        "passphrase_warning": "⚠️ If you forget it, data cannot be recovered!",
        "ph_new_passphrase": "New passphrase",
        "ph_confirm_passphrase": "Confirm passphrase",
        "btn_encrypt_db": "Encrypt Database",
        "remove_passphrase_title": "🔓 Remove Passphrase",
        "unencrypted_warning": "⚠️ Database will be stored unencrypted!",
        "ph_current_passphrase": "Current passphrase",
        "btn_remove_encryption": "Remove Encryption",
        "ip_leak_title": "YOUR IP LEAK, ARE YOU SURE?",
        "ip_leak_body1": "You are connecting directly without a Proxy/Tor.",
        "ip_leak_body2": "Your real IP address will be visible to the server.",
        "ip_leak_body3": "To fix this, go to Settings and enable SOCKS5 Proxy (Tor).",
        "dont_show_again": "Don't show this again",
        "btn_continue_anyway": "Continue Anyway",
        "btn_cancel_settings": "Cancel & Go to Settings",
        "welcome_title": "Welcome to Antch",
        "select_account": "Select Account:",
        "btn_new_account": "New Account",
        "btn_network_settings": "Settings",
        "create_new_account": "Create New Account",
        "ph_alias": "Alias (username)",
        "ph_server_url": "Server URL (ws://...)",
        "ph_passphrase_optional": "Passphrase (optional - encrypts database)",
        "btn_create_login": "Create & Login",
        "btn_back": "Back",
        "login_edit_server": "Login / Edit Server",
        "ph_alias_short": "Alias",
        "ph_server_url_short": "Server URL",
        "ph_passphrase_encrypted": "Passphrase (if encrypted)",
        "btn_connect": "Connect",
        "network_mode": "Network Mode",
        "mode_direct": "Direct Connection",
        "mode_socks5": "SOCKS5 Proxy",
        "proxy_settings": "Proxy Settings",
        "ph_proxy_host": "Proxy Host (default: 127.0.0.1)",
        "ph_proxy_port": "Proxy Port (default: 9050)",
        "file_seeding": "File Seeding",
        "enable_seeding": "Enable Seeding (share my ciphertext files / cloud blobs)",
        "ph_seed_upload": "Seed Upload Limit (Mbit/s) — 0 = unlimited",
        "ph_cloud_upload": "Cloud Upload Limit (Mbit/s) — 0 = unlimited",
        "ph_seed_dir": "Seed Directory (shared across accounts)",
        "ph_max_seed_disk": "Max Seed Disk (MB) — 0 = unlimited",
        "ph_downloads_dir": "Downloads Directory",
        "btn_save_settings": "Save Settings",
        "ui_language": "UI Language",
        "lang_en": "English",
        "lang_tr": "Turkish",
        "lang_de": "German",
        "lang_es": "Spanish",
        "lang_fr": "French",
        "err_enter_passphrase": "Please enter a passphrase",
        "err_passphrase_mismatch": "Passphrases do not match",
        "err_enter_current_passphrase": "Please enter your current passphrase",
        "db_encrypted_success": "✅ Database for {alias} is now encrypted!",
        "encryption_removed": "✅ Encryption removed from {alias}!",
        "invalid_passphrase": "Invalid passphrase: {e}",
        "err_alias_server_required": "Alias and Server are required",
        "err_invalid_alias": "Invalid alias. Use only letters, numbers, underscore.",
        "err_passphrase_short": "Passphrase must be at least 4 characters",
        "err_account_exists": "Account already exists!",
        "account_deleted_locally": "Account {alias} deleted locally.",
        "account_deleted_server": "Account deleted from server.",
        "err_delete": "Error deleting: {e}",
        "err_server_delete": "Server delete error: {e}",
        "settings_saved": "Settings Saved!",
        "server_required": "Server URL required",
        "delete_verify_passphrase": "Enter passphrase to verify identity",
        "delete_verify_title": "🗑️ Delete Account: {alias}",
    },
    "tr": {
        "account_label": "Hesap: {alias}",
        "encrypted": "🔒 Şifreli",
        "btn_login": "Giriş",
        "btn_edit_server": "Sunucuyu Düzenle",
        "btn_remove_passphrase": "🔓 Şifreyi Kaldır",
        "btn_add_passphrase": "🔒 Şifre Ekle",
        "btn_delete_local": "Sil (Sadece Yerel)",
        "btn_delete_server": "Sil (Sunucu & Yerel)",
        "btn_cancel": "İptal",
        "passphrase_title": "🔒 {alias}",
        "db_encrypted": "Bu veritabanı şifrelidir",
        "ph_enter_passphrase": "Şifreyi girin",
        "btn_unlock": "Aç",
        "add_passphrase_title": "🔒 Şifre Ekle",
        "passphrase_warning": "⚠️ Unutursanız veriler kurtarılamaz!",
        "ph_new_passphrase": "Yeni şifre",
        "ph_confirm_passphrase": "Şifreyi onayla",
        "btn_encrypt_db": "Veritabanını Şifrele",
        "remove_passphrase_title": "🔓 Şifreyi Kaldır",
        "unencrypted_warning": "⚠️ Veritabanı şifresiz saklanacak!",
        "ph_current_passphrase": "Mevcut şifre",
        "btn_remove_encryption": "Şifrelemeyi Kaldır",
        "ip_leak_title": "IP ADRESİNİZ SIZACAK, EMİN MİSİNİZ?",
        "ip_leak_body1": "Proxy/Tor olmadan doğrudan bağlanıyorsunuz.",
        "ip_leak_body2": "Gerçek IP adresiniz sunucuya görünür olacak.",
        "ip_leak_body3": "Bunu düzeltmek için Ayarlara gidin ve SOCKS5 Proxy (Tor) etkinleştirin.",
        "dont_show_again": "Bir daha gösterme",
        "btn_continue_anyway": "Yine de Devam Et",
        "btn_cancel_settings": "İptal & Ayarlara Git",
        "welcome_title": "Antch'e Hoş Geldiniz",
        "select_account": "Hesap Seçin:",
        "btn_new_account": "Yeni Hesap",
        "btn_network_settings": "Ayarlar",
        "create_new_account": "Yeni Hesap Oluştur",
        "ph_alias": "Takma Ad (kullanıcı adı)",
        "ph_server_url": "Sunucu URL'si (ws://...)",
        "ph_passphrase_optional": "Şifre (isteğe bağlı - veritabanını şifreler)",
        "btn_create_login": "Oluştur & Giriş Yap",
        "btn_back": "Geri",
        "login_edit_server": "Giriş / Sunucu Düzenle",
        "ph_alias_short": "Takma Ad",
        "ph_server_url_short": "Sunucu URL'si",
        "ph_passphrase_encrypted": "Şifre (şifreli ise)",
        "btn_connect": "Bağlan",
        "network_mode": "Ağ Modu",
        "mode_direct": "Doğrudan Bağlantı",
        "mode_socks5": "SOCKS5 Proxy",
        "proxy_settings": "Proxy Ayarları",
        "ph_proxy_host": "Proxy Sunucusu (varsayılan: 127.0.0.1)",
        "ph_proxy_port": "Proxy Portu (varsayılan: 9050)",
        "file_seeding": "Dosya Paylaşımı",
        "enable_seeding": "Paylaşımı Etkinleştir (şifreli dosyalarımı / cloud bloblarını paylaş)",
        "ph_seed_upload": "Paylaşım Yükleme Limiti (Mbit/s) — 0 = sınırsız",
        "ph_cloud_upload": "Cloud Yükleme Limiti (Mbit/s) — 0 = sınırsız",
        "ph_seed_dir": "Paylaşım Dizini (hesaplar arası paylaşılır)",
        "ph_max_seed_disk": "Maks Paylaşım Disk (MB) — 0 = sınırsız",
        "ph_downloads_dir": "İndirilenler Dizini",
        "btn_save_settings": "Ayarları Kaydet",
        "ui_language": "Arayüz Dili",
        "lang_en": "İngilizce",
        "lang_tr": "Türkçe",
        "lang_de": "Almanca",
        "lang_es": "İspanyolca",
        "lang_fr": "Fransızca",
        "err_enter_passphrase": "Lütfen bir şifre girin",
        "err_passphrase_mismatch": "Şifreler eşleşmiyor",
        "err_enter_current_passphrase": "Lütfen mevcut şifrenizi girin",
        "db_encrypted_success": "✅ {alias} için veritabanı şifrelendi!",
        "encryption_removed": "✅ {alias} için şifreleme kaldırıldı!",
        "invalid_passphrase": "Geçersiz şifre: {e}",
        "err_alias_server_required": "Takma ad ve Sunucu gereklidir",
        "err_invalid_alias": "Geçersiz takma ad. Sadece harf, rakam, alt çizgi kullanın.",
        "err_passphrase_short": "Şifre en az 4 karakter olmalıdır",
        "err_account_exists": "Hesap zaten mevcut!",
        "account_deleted_locally": "{alias} hesabı yerel olarak silindi.",
        "account_deleted_server": "Hesap sunucudan silindi.",
        "err_delete": "Silme hatası: {e}",
        "err_server_delete": "Sunucu silme hatası: {e}",
        "settings_saved": "Ayarlar Kaydedildi!",
        "server_required": "Sunucu URL'si gerekli",
        "delete_verify_passphrase": "Kimliğinizi doğrulamak için şifreyi girin",
        "delete_verify_title": "🗑️ Hesabı Sil: {alias}",
    },
    "de": {
        "account_label": "Konto: {alias}",
        "encrypted": "🔒 Verschlüsselt",
        "btn_login": "Anmelden",
        "btn_edit_server": "Server bearbeiten",
        "btn_remove_passphrase": "🔓 Passwort entfernen",
        "btn_add_passphrase": "🔒 Passwort hinzufügen",
        "btn_delete_local": "Löschen (nur lokal)",
        "btn_delete_server": "Löschen (Server & lokal)",
        "btn_cancel": "Abbrechen",
        "passphrase_title": "🔒 {alias}",
        "db_encrypted": "Diese Datenbank ist verschlüsselt",
        "ph_enter_passphrase": "Passwort eingeben",
        "btn_unlock": "Entsperren",
        "add_passphrase_title": "🔒 Passwort hinzufügen",
        "passphrase_warning": "⚠️ Bei Vergessen können Daten nicht wiederhergestellt werden!",
        "ph_new_passphrase": "Neues Passwort",
        "ph_confirm_passphrase": "Passwort bestätigen",
        "btn_encrypt_db": "Datenbank verschlüsseln",
        "remove_passphrase_title": "🔓 Passwort entfernen",
        "unencrypted_warning": "⚠️ Datenbank wird unverschlüsselt gespeichert!",
        "ph_current_passphrase": "Aktuelles Passwort",
        "btn_remove_encryption": "Verschlüsselung entfernen",
        "ip_leak_title": "IP-LEAK, SIND SIE SICHER?",
        "ip_leak_body1": "Sie verbinden sich direkt ohne Proxy/Tor.",
        "ip_leak_body2": "Ihre echte IP-Adresse wird für den Server sichtbar sein.",
        "ip_leak_body3": "Um dies zu beheben, gehen Sie zu Einstellungen und aktivieren Sie SOCKS5 Proxy (Tor).",
        "dont_show_again": "Nicht mehr anzeigen",
        "btn_continue_anyway": "Trotzdem fortfahren",
        "btn_cancel_settings": "Abbrechen & Einstellungen",
        "welcome_title": "Willkommen bei Antch",
        "select_account": "Konto auswählen:",
        "btn_new_account": "Neues Konto",
        "btn_network_settings": "Einstellungen",
        "create_new_account": "Neues Konto erstellen",
        "ph_alias": "Alias (Benutzername)",
        "ph_server_url": "Server-URL (ws://...)",
        "ph_passphrase_optional": "Passwort (optional - verschlüsselt Datenbank)",
        "btn_create_login": "Erstellen & Anmelden",
        "btn_back": "Zurück",
        "login_edit_server": "Anmelden / Server bearbeiten",
        "ph_alias_short": "Alias",
        "ph_server_url_short": "Server-URL",
        "ph_passphrase_encrypted": "Passwort (falls verschlüsselt)",
        "btn_connect": "Verbinden",
        "network_mode": "Netzwerkmodus",
        "mode_direct": "Direkte Verbindung",
        "mode_socks5": "SOCKS5 Proxy",
        "proxy_settings": "Proxy-Einstellungen",
        "ph_proxy_host": "Proxy-Host (Standard: 127.0.0.1)",
        "ph_proxy_port": "Proxy-Port (Standard: 9050)",
        "file_seeding": "Datei-Seeding",
        "enable_seeding": "Seeding aktivieren (meine verschlüsselten Dateien teilen)",
        "ph_seed_upload": "Seed-Upload-Limit (Mbit/s) — 0 = unbegrenzt",
        "ph_cloud_upload": "Cloud-Upload-Limit (Mbit/s) — 0 = unbegrenzt",
        "ph_seed_dir": "Seed-Verzeichnis (kontoübergreifend)",
        "ph_max_seed_disk": "Max Seed-Disk (MB) — 0 = unbegrenzt",
        "ph_downloads_dir": "Download-Verzeichnis",
        "btn_save_settings": "Einstellungen speichern",
        "ui_language": "UI-Sprache",
        "lang_en": "Englisch",
        "lang_tr": "Türkisch",
        "lang_de": "Deutsch",
        "lang_es": "Spanisch",
        "lang_fr": "Französisch",
        "err_enter_passphrase": "Bitte geben Sie ein Passwort ein",
        "err_passphrase_mismatch": "Passwörter stimmen nicht überein",
        "err_enter_current_passphrase": "Bitte geben Sie Ihr aktuelles Passwort ein",
        "db_encrypted_success": "✅ Datenbank für {alias} ist jetzt verschlüsselt!",
        "encryption_removed": "✅ Verschlüsselung für {alias} entfernt!",
        "invalid_passphrase": "Ungültiges Passwort: {e}",
        "err_alias_server_required": "Alias und Server sind erforderlich",
        "err_invalid_alias": "Ungültiger Alias. Nur Buchstaben, Zahlen, Unterstrich.",
        "err_passphrase_short": "Passwort muss mindestens 4 Zeichen haben",
        "err_account_exists": "Konto existiert bereits!",
        "account_deleted_locally": "Konto {alias} lokal gelöscht.",
        "account_deleted_server": "Konto vom Server gelöscht.",
        "err_delete": "Löschfehler: {e}",
        "err_server_delete": "Server-Löschfehler: {e}",
        "settings_saved": "Einstellungen gespeichert!",
        "server_required": "Server-URL erforderlich",
        "delete_verify_passphrase": "Passwort eingeben, um Identität zu bestätigen",
        "delete_verify_title": "🗑️ Konto löschen: {alias}",
    },
    "es": {
        "account_label": "Cuenta: {alias}",
        "encrypted": "🔒 Cifrado",
        "btn_login": "Iniciar sesión",
        "btn_edit_server": "Editar servidor",
        "btn_remove_passphrase": "🔓 Quitar contraseña",
        "btn_add_passphrase": "🔒 Agregar contraseña",
        "btn_delete_local": "Eliminar (solo local)",
        "btn_delete_server": "Eliminar (Servidor y local)",
        "btn_cancel": "Cancelar",
        "passphrase_title": "🔒 {alias}",
        "db_encrypted": "Esta base de datos está cifrada",
        "ph_enter_passphrase": "Ingrese contraseña",
        "btn_unlock": "Desbloquear",
        "add_passphrase_title": "🔒 Agregar Contraseña",
        "passphrase_warning": "⚠️ ¡Si lo olvida, los datos no se pueden recuperar!",
        "ph_new_passphrase": "Nueva contraseña",
        "ph_confirm_passphrase": "Confirmar contraseña",
        "btn_encrypt_db": "Cifrar Base de Datos",
        "remove_passphrase_title": "🔓 Quitar Contraseña",
        "unencrypted_warning": "⚠️ ¡La base de datos se almacenará sin cifrar!",
        "ph_current_passphrase": "Contraseña actual",
        "btn_remove_encryption": "Quitar Cifrado",
        "ip_leak_title": "¿FUGA DE IP, ESTÁ SEGURO?",
        "ip_leak_body1": "Te estás conectando directamente sin Proxy/Tor.",
        "ip_leak_body2": "Tu dirección IP real será visible para el servidor.",
        "ip_leak_body3": "Para solucionarlo, ve a Ajustes y activa SOCKS5 Proxy (Tor).",
        "dont_show_again": "No mostrar de nuevo",
        "btn_continue_anyway": "Continuar de todos modos",
        "btn_cancel_settings": "Cancelar e ir a Configuración",
        "welcome_title": "Bienvenido a Antch",
        "select_account": "Seleccionar cuenta:",
        "btn_new_account": "Nueva cuenta",
        "btn_network_settings": "Ajustes",
        "create_new_account": "Crear nueva cuenta",
        "ph_alias": "Alias (nombre de usuario)",
        "ph_server_url": "URL del servidor (ws://...)",
        "ph_passphrase_optional": "Contraseña (opcional - cifra la base de datos)",
        "btn_create_login": "Crear e Iniciar",
        "btn_back": "Atrás",
        "login_edit_server": "Iniciar / Editar Servidor",
        "ph_alias_short": "Alias",
        "ph_server_url_short": "URL del servidor",
        "ph_passphrase_encrypted": "Contraseña (si está cifrado)",
        "btn_connect": "Conectar",
        "network_mode": "Modo de Red",
        "mode_direct": "Conexión Directa",
        "mode_socks5": "Proxy SOCKS5",
        "proxy_settings": "Configuración de Proxy",
        "ph_proxy_host": "Host del Proxy (por defecto: 127.0.0.1)",
        "ph_proxy_port": "Puerto del Proxy (por defecto: 9050)",
        "file_seeding": "Compartir Archivos",
        "enable_seeding": "Habilitar compartir (compartir mis archivos cifrados)",
        "ph_seed_upload": "Límite de subida seed (Mbit/s) — 0 = ilimitado",
        "ph_cloud_upload": "Límite de subida cloud (Mbit/s) — 0 = ilimitado",
        "ph_seed_dir": "Directorio de seed (compartido entre cuentas)",
        "ph_max_seed_disk": "Máx disco seed (MB) — 0 = ilimitado",
        "ph_downloads_dir": "Directorio de descargas",
        "btn_save_settings": "Guardar configuración",
        "ui_language": "Idioma de la interfaz",
        "lang_en": "Inglés",
        "lang_tr": "Turco",
        "lang_de": "Alemán",
        "lang_es": "Español",
        "lang_fr": "Francés",
        "err_enter_passphrase": "Por favor ingrese una contraseña",
        "err_passphrase_mismatch": "Las contraseñas no coinciden",
        "err_enter_current_passphrase": "Por favor ingrese su contraseña actual",
        "db_encrypted_success": "✅ ¡Base de datos para {alias} ahora está cifrada!",
        "encryption_removed": "✅ ¡Cifrado eliminado de {alias}!",
        "invalid_passphrase": "Contraseña inválida: {e}",
        "err_alias_server_required": "Alias y Servidor son requeridos",
        "err_invalid_alias": "Alias inválido. Use solo letras, números, guión bajo.",
        "err_passphrase_short": "La contraseña debe tener al menos 4 caracteres",
        "err_account_exists": "¡La cuenta ya existe!",
        "account_deleted_locally": "Cuenta {alias} eliminada localmente.",
        "account_deleted_server": "Cuenta eliminada del servidor.",
        "err_delete": "Error al eliminar: {e}",
        "err_server_delete": "Error al eliminar del servidor: {e}",
        "settings_saved": "¡Configuración guardada!",
        "server_required": "URL del servidor requerida",
        "delete_verify_passphrase": "Ingrese contraseña para verificar identidad",
        "delete_verify_title": "🗑️ Eliminar cuenta: {alias}",
    },
    "fr": {
        "account_label": "Compte: {alias}",
        "encrypted": "🔒 Chiffré",
        "btn_login": "Connexion",
        "btn_edit_server": "Modifier serveur",
        "btn_remove_passphrase": "🔓 Supprimer mot de passe",
        "btn_add_passphrase": "🔒 Ajouter mot de passe",
        "btn_delete_local": "Supprimer (local seulement)",
        "btn_delete_server": "Supprimer (Serveur et local)",
        "btn_cancel": "Annuler",
        "passphrase_title": "🔒 {alias}",
        "db_encrypted": "Cette base de données est chiffrée",
        "ph_enter_passphrase": "Entrez le mot de passe",
        "btn_unlock": "Déverrouiller",
        "add_passphrase_title": "🔒 Ajouter Mot de Passe",
        "passphrase_warning": "⚠️ Si vous l'oubliez, les données ne peuvent pas être récupérées!",
        "ph_new_passphrase": "Nouveau mot de passe",
        "ph_confirm_passphrase": "Confirmer le mot de passe",
        "btn_encrypt_db": "Chiffrer la Base de Données",
        "remove_passphrase_title": "🔓 Supprimer Mot de Passe",
        "unencrypted_warning": "⚠️ La base de données sera stockée non chiffrée!",
        "ph_current_passphrase": "Mot de passe actuel",
        "btn_remove_encryption": "Supprimer le Chiffrement",
        "ip_leak_title": "FUITE IP, ÊTES-VOUS SÛR?",
        "ip_leak_body1": "Vous vous connectez directement sans Proxy/Tor.",
        "ip_leak_body2": "Votre vraie adresse IP sera visible pour le serveur.",
        "ip_leak_body3": "Pour corriger, allez dans Paramètres et activez SOCKS5 Proxy (Tor).",
        "dont_show_again": "Ne plus afficher",
        "btn_continue_anyway": "Continuer quand même",
        "btn_cancel_settings": "Annuler et Paramètres",
        "welcome_title": "Bienvenue sur Antch",
        "select_account": "Sélectionner compte:",
        "btn_new_account": "Nouveau compte",
        "btn_network_settings": "Paramètres",
        "create_new_account": "Créer un nouveau compte",
        "ph_alias": "Alias (nom d'utilisateur)",
        "ph_server_url": "URL du serveur (ws://...)",
        "ph_passphrase_optional": "Mot de passe (optionnel - chiffre la base)",
        "btn_create_login": "Créer et Connexion",
        "btn_back": "Retour",
        "login_edit_server": "Connexion / Modifier Serveur",
        "ph_alias_short": "Alias",
        "ph_server_url_short": "URL du serveur",
        "ph_passphrase_encrypted": "Mot de passe (si chiffré)",
        "btn_connect": "Connecter",
        "network_mode": "Mode Réseau",
        "mode_direct": "Connexion Directe",
        "mode_socks5": "Proxy SOCKS5",
        "proxy_settings": "Paramètres Proxy",
        "ph_proxy_host": "Hôte Proxy (défaut: 127.0.0.1)",
        "ph_proxy_port": "Port Proxy (défaut: 9050)",
        "file_seeding": "Partage de Fichiers",
        "enable_seeding": "Activer le partage (partager mes fichiers chiffrés)",
        "ph_seed_upload": "Limite upload seed (Mbit/s) — 0 = illimité",
        "ph_cloud_upload": "Limite upload cloud (Mbit/s) — 0 = illimité",
        "ph_seed_dir": "Répertoire seed (partagé entre comptes)",
        "ph_max_seed_disk": "Max disque seed (Mo) — 0 = illimité",
        "ph_downloads_dir": "Répertoire de téléchargements",
        "btn_save_settings": "Enregistrer",
        "ui_language": "Langue de l’interface",
        "lang_en": "Anglais",
        "lang_tr": "Turc",
        "lang_de": "Allemand",
        "lang_es": "Espagnol",
        "lang_fr": "Français",
        "err_enter_passphrase": "Veuillez entrer un mot de passe",
        "err_passphrase_mismatch": "Les mots de passe ne correspondent pas",
        "err_enter_current_passphrase": "Veuillez entrer votre mot de passe actuel",
        "db_encrypted_success": "✅ Base de données pour {alias} maintenant chiffrée!",
        "encryption_removed": "✅ Chiffrement supprimé de {alias}!",
        "invalid_passphrase": "Mot de passe invalide: {e}",
        "err_alias_server_required": "Alias et Serveur sont requis",
        "err_invalid_alias": "Alias invalide. Utilisez uniquement lettres, chiffres, tiret bas.",
        "err_passphrase_short": "Le mot de passe doit avoir au moins 4 caractères",
        "err_account_exists": "Le compte existe déjà!",
        "account_deleted_locally": "Compte {alias} supprimé localement.",
        "account_deleted_server": "Compte supprimé du serveur.",
        "err_delete": "Erreur de suppression: {e}",
        "err_server_delete": "Erreur de suppression serveur: {e}",
        "settings_saved": "Paramètres enregistrés!",
        "server_required": "URL du serveur requise",
        "delete_verify_passphrase": "Entrez le mot de passe pour vérifier l'identité",
        "delete_verify_title": "🗑️ Supprimer compte: {alias}",
    },
}

def _launcher_t(lang: str, key: str, **kwargs) -> str:
    lang = lang if lang in LAUNCHER_I18N else "en"
    text = LAUNCHER_I18N[lang].get(key) or LAUNCHER_I18N["en"].get(key) or key
    if kwargs:
        try:
            text = text.format(**kwargs)
        except Exception:
            pass
    return text

class AccountContextMenu(ModalScreen[str]):
    CSS = """
    AccountContextMenu { align: center middle; background: $surface 50%; }
    #ctx_menu { width: 45; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    .ctx-title { text-align: center; text-style: bold; margin-bottom: 1; border-bottom: solid $secondary; }
    .ctx-subtitle { text-align: center; color: $text-muted; margin-bottom: 1; }
    Button { margin-bottom: 1; width: 100%; }
    """

    def __init__(self, alias: str, has_passphrase: bool = False, lang: str = "en"):
        super().__init__()
        self.alias = alias
        self.has_passphrase = has_passphrase
        self.lang = lang

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def compose(self) -> ComposeResult:
        with Vertical(id="ctx_menu"):
            yield Label(self._t("account_label", alias=self.alias), classes="ctx-title")
            if self.has_passphrase:
                yield Label(self._t("encrypted"), classes="ctx-subtitle")
            yield Button(self._t("btn_login"), id="ctx_login", variant="success")
            yield Button(self._t("btn_edit_server"), id="ctx_edit", variant="primary")
            if self.has_passphrase:
                yield Button(self._t("btn_remove_passphrase"), id="ctx_remove_pass", variant="warning")
            else:
                yield Button(self._t("btn_add_passphrase"), id="ctx_add_pass", variant="primary")
            yield Button(self._t("btn_delete_local"), id="ctx_del_local", variant="warning")
            yield Button(self._t("btn_delete_server"), id="ctx_del_server", variant="error")
            yield Button(self._t("btn_cancel"), id="ctx_cancel")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss(event.button.id)

class PassphraseInputScreen(ModalScreen[str | None]):
    CSS = """
    PassphraseInputScreen { align: center middle; background: $surface 50%; }
    #pass_dialog { width: 50; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    .pass-title { text-align: center; text-style: bold; margin-bottom: 1; }
    .pass-subtitle { text-align: center; color: $text-muted; margin-bottom: 1; }
    Input { margin-bottom: 1; }
    Button { margin-bottom: 1; width: 100%; }
    """

    def __init__(self, alias: str, message: str = "", lang: str = "en"):
        super().__init__()
        self.alias = alias
        self.message = message
        self.lang = lang

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def compose(self) -> ComposeResult:
        with Vertical(id="pass_dialog"):
            yield Label(self._t("passphrase_title", alias=self.alias), classes="pass-title")
            if self.message:
                yield Label(self.message, classes="pass-subtitle")
            else:
                yield Label(self._t("db_encrypted"), classes="pass-subtitle")
            yield Input(placeholder=self._t("ph_enter_passphrase"), id="inp_pass", password=True)
            yield Button(self._t("btn_unlock"), id="btn_unlock", variant="success")
            yield Button(self._t("btn_cancel"), id="btn_cancel")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn_unlock":
            passphrase = self.query_one("#inp_pass", Input).value.strip()
            self.dismiss(passphrase if passphrase else None)
        else:
            self.dismiss(None)

    def on_input_submitted(self, event: Input.Submitted) -> None:
        passphrase = self.query_one("#inp_pass", Input).value.strip()
        self.dismiss(passphrase if passphrase else None)

class AddPassphraseScreen(ModalScreen[str | None]):
    CSS = """
    AddPassphraseScreen { align: center middle; background: $surface 50%; }
    #add_pass_dialog { width: 50; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    .pass-title { text-align: center; text-style: bold; margin-bottom: 1; }
    .pass-subtitle { text-align: center; color: $text-muted; margin-bottom: 1; }
    .pass-warn { text-align: center; color: yellow; margin-bottom: 1; }
    Input { margin-bottom: 1; }
    Button { margin-bottom: 1; width: 100%; }
    """

    def __init__(self, alias: str, lang: str = "en"):
        super().__init__()
        self.alias = alias
        self.lang = lang

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def compose(self) -> ComposeResult:
        with Vertical(id="add_pass_dialog"):
            yield Label(self._t("add_passphrase_title"), classes="pass-title")
            yield Label(self._t("account_label", alias=self.alias), classes="pass-subtitle")
            yield Label(self._t("passphrase_warning"), classes="pass-warn")
            yield Input(placeholder=self._t("ph_new_passphrase"), id="inp_new_pass", password=True)
            yield Input(placeholder=self._t("ph_confirm_passphrase"), id="inp_confirm_pass", password=True)
            yield Button(self._t("btn_encrypt_db"), id="btn_encrypt", variant="warning")
            yield Button(self._t("btn_cancel"), id="btn_cancel")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn_encrypt":
            new_pass = self.query_one("#inp_new_pass", Input).value.strip()
            confirm_pass = self.query_one("#inp_confirm_pass", Input).value.strip()
            if not new_pass:
                self.notify(self._t("err_enter_passphrase"), severity="error")
                return
            if new_pass != confirm_pass:
                self.notify(self._t("err_passphrase_mismatch"), severity="error")
                return
            self.dismiss(new_pass)
        else:
            self.dismiss(None)

class RemovePassphraseScreen(ModalScreen[str | None]):
    CSS = """
    RemovePassphraseScreen { align: center middle; background: $surface 50%; }
    #rem_pass_dialog { width: 50; height: auto; background: $panel; border: solid $secondary; padding: 1 2; }
    .pass-title { text-align: center; text-style: bold; margin-bottom: 1; }
    .pass-subtitle { text-align: center; color: $text-muted; margin-bottom: 1; }
    .pass-warn { text-align: center; color: yellow; margin-bottom: 1; }
    Input { margin-bottom: 1; }
    Button { margin-bottom: 1; width: 100%; }
    """

    def __init__(self, alias: str, lang: str = "en"):
        super().__init__()
        self.alias = alias
        self.lang = lang

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def compose(self) -> ComposeResult:
        with Vertical(id="rem_pass_dialog"):
            yield Label(self._t("remove_passphrase_title"), classes="pass-title")
            yield Label(self._t("account_label", alias=self.alias), classes="pass-subtitle")
            yield Label(self._t("unencrypted_warning"), classes="pass-warn")
            yield Input(placeholder=self._t("ph_current_passphrase"), id="inp_current_pass", password=True)
            yield Button(self._t("btn_remove_encryption"), id="btn_decrypt", variant="error")
            yield Button(self._t("btn_cancel"), id="btn_cancel")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn_decrypt":
            current_pass = self.query_one("#inp_current_pass", Input).value.strip()
            if not current_pass:
                self.notify(self._t("err_enter_current_passphrase"), severity="error")
                return
            self.dismiss(current_pass)
        else:
            self.dismiss(None)

class SecurityWarningScreen(ModalScreen[bool]):
    CSS = """
    SecurityWarningScreen { align: center middle; background: $surface 50%; }
    #warn_dialog { width: 60; height: auto; background: $panel; border: solid red; padding: 1 2; }
    .warn-title { color: red; text-style: bold; text-align: center; margin-bottom: 1; }
    .warn-text { margin-bottom: 1; }
    #chk_dont_show { margin-bottom: 1; }
    #btn_continue { margin-bottom: 1; }
    """

    def __init__(self, lang: str = "en"):
        super().__init__()
        self.lang = lang

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def compose(self) -> ComposeResult:
        with Vertical(id="warn_dialog"):
            yield Label(self._t("ip_leak_title"), classes="warn-title")
            yield Label(self._t("ip_leak_body1"), classes="warn-text")
            yield Label(self._t("ip_leak_body2"), classes="warn-text")
            yield Label(self._t("ip_leak_body3"), classes="warn-text")

            yield Checkbox(self._t("dont_show_again"), id="chk_dont_show")
            yield Button(self._t("btn_continue_anyway"), id="btn_continue", variant="error")
            yield Button(self._t("btn_cancel_settings"), id="btn_cancel", variant="primary")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn_continue":
            dont_show = self.query_one("#chk_dont_show", Checkbox).value
            if dont_show:
                app = self.app
                app.global_config.setdefault("network", {})["suppress_proxy_warning"] = True
                save_global_config(app.global_config)
            self.dismiss(True)
        else:
            self.dismiss(False)

class AccountItem(ListItem):
    def __init__(self, alias: str, db_path: str, encrypted: bool = False):
        super().__init__()
        self.alias = alias
        self.db_path = db_path
        self.encrypted = encrypted

    def compose(self) -> ComposeResult:
        if self.encrypted:
            yield Label(f"🔒 {self.alias}")
        else:
            yield Label(self.alias)

    def on_mouse_down(self, event: events.MouseDown) -> None:
        if event.button == 3:
             self.app.show_account_context_menu(self.alias, self.db_path)
             event.stop()

class Launcher(App):
    CSS = """
    Screen { align: center middle; }
    #container { width: 70; height: 95%; border: solid $secondary; padding: 1 2; background: $panel; }
    .title { text-align: center; text-style: bold; margin-bottom: 1; }
    #account_list { height: 10; border: solid $secondary; margin-bottom: 1; }
    Input { margin-bottom: 1; }
    Button { width: 100%; margin-bottom: 1; }
    .hidden { display: none; }
    RadioSet { margin-bottom: 1; border: solid $secondary; padding: 1; }
    Checkbox { margin-bottom: 1; }
    .section-title { text-style: bold; margin-top: 1; }
    #view_settings { height: 1fr; }
    """

    def __init__(self):
        super().__init__()
        self.selected_config = None
        self.global_config = load_global_config()
        self.previous_view = "view_select"
        self.lang = self.global_config.get("ui", {}).get("language", "en")

    def _t(self, key: str, **kwargs) -> str:
        return _launcher_t(self.lang, key, **kwargs)

    def _lang_from_settings_ui(self) -> str:
        try:
            rs = self.query_one("#rs_lang", RadioSet)
            pressed = rs.pressed_button
            if pressed and pressed.id:

                code = pressed.id.replace("lang_", "")
                return code if code in LAUNCHER_I18N else "en"
        except Exception:
            pass
        return self.lang if self.lang in LAUNCHER_I18N else "en"

    def _apply_language(self, lang: str) -> None:
        self.lang = lang if lang in LAUNCHER_I18N else "en"

        try:
            self.query_one("#welcome_title", Static).update(self._t("welcome_title"))
        except Exception:
            pass

        for widget_id, key in [
            ("#lbl_select_account", "select_account"),
        ]:
            try:
                self.query_one(widget_id, Label).update(self._t(key))
            except Exception:
                pass
        for button_id, key in [
            ("#btn_new", "btn_new_account"),
            ("#btn_settings", "btn_network_settings"),
        ]:
            try:
                self.query_one(button_id, Button).label = self._t(key)
            except Exception:
                pass

        try:
            self.query_one("#lbl_create_new", Label).update(self._t("create_new_account"))
        except Exception:
            pass
        for input_id, key in [
            ("#inp_alias", "ph_alias"),
            ("#inp_server", "ph_server_url"),
            ("#inp_passphrase", "ph_passphrase_optional"),
            ("#inp_passphrase_confirm", "ph_confirm_passphrase"),
        ]:
            try:
                self.query_one(input_id, Input).placeholder = self._t(key)
            except Exception:
                pass
        for button_id, key in [
            ("#btn_create", "btn_create_login"),
            ("#btn_settings_new", "btn_network_settings"),
            ("#btn_back", "btn_back"),
        ]:
            try:
                self.query_one(button_id, Button).label = self._t(key)
            except Exception:
                pass

        try:
            self.query_one("#lbl_login_edit", Label).update(self._t("login_edit_server"))
        except Exception:
            pass
        for input_id, key in [
            ("#inp_alias_readonly", "ph_alias_short"),
            ("#inp_server_existing", "ph_server_url_short"),
            ("#inp_login_passphrase", "ph_passphrase_encrypted"),
        ]:
            try:
                self.query_one(input_id, Input).placeholder = self._t(key)
            except Exception:
                pass
        for button_id, key in [
            ("#btn_connect", "btn_connect"),
            ("#btn_settings_server", "btn_network_settings"),
            ("#btn_back_server", "btn_back"),
        ]:
            try:
                self.query_one(button_id, Button).label = self._t(key)
            except Exception:
                pass

        try:
            self.query_one("#lbl_network_mode", Label).update(self._t("network_mode"))
            self.query_one("#mode_direct", RadioButton).label = self._t("mode_direct")
            self.query_one("#mode_proxy", RadioButton).label = self._t("mode_socks5")
            self.query_one("#lbl_proxy_settings", Label).update(self._t("proxy_settings"))
            self.query_one("#inp_proxy_host", Input).placeholder = self._t("ph_proxy_host")
            self.query_one("#inp_proxy_port", Input).placeholder = self._t("ph_proxy_port")
            self.query_one("#lbl_file_seeding", Label).update(self._t("file_seeding"))
            self.query_one("#chk_seeding_enabled", Checkbox).label = self._t("enable_seeding")
            self.query_one("#inp_seed_upload_mbit", Input).placeholder = self._t("ph_seed_upload")
            self.query_one("#inp_cloud_upload_mbit", Input).placeholder = self._t("ph_cloud_upload")
            self.query_one("#inp_seed_dir", Input).placeholder = self._t("ph_seed_dir")
            self.query_one("#inp_max_seed_mb", Input).placeholder = self._t("ph_max_seed_disk")
            self.query_one("#inp_downloads_dir", Input).placeholder = self._t("ph_downloads_dir")
            self.query_one("#lbl_ui_language", Label).update(self._t("ui_language"))
            self.query_one("#lang_en", RadioButton).label = self._t("lang_en")
            self.query_one("#lang_tr", RadioButton).label = self._t("lang_tr")
            self.query_one("#lang_de", RadioButton).label = self._t("lang_de")
            self.query_one("#lang_es", RadioButton).label = self._t("lang_es")
            self.query_one("#lang_fr", RadioButton).label = self._t("lang_fr")
            self.query_one("#btn_save_settings", Button).label = self._t("btn_save_settings")
            self.query_one("#btn_back_settings", Button).label = self._t("btn_back")
        except Exception:
            pass

    def compose(self) -> ComposeResult:
        with Vertical(id="container"):
            yield Static(self._t("welcome_title"), classes="title", id="welcome_title")

            with Vertical(id="view_select"):
                yield Label(self._t("select_account"), id="lbl_select_account")
                yield ListView(id="account_list")
                yield Button(self._t("btn_new_account"), id="btn_new", variant="primary")
                yield Button(self._t("btn_network_settings"), id="btn_settings", variant="warning")

            with Vertical(id="view_new", classes="hidden"):
                yield Label(self._t("create_new_account"), id="lbl_create_new")
                yield Input(placeholder=self._t("ph_alias"), id="inp_alias")
                yield Input(placeholder=self._t("ph_server_url"), id="inp_server", value="wss://antch.lama2923.com")
                yield Input(placeholder=self._t("ph_passphrase_optional"), id="inp_passphrase", password=True)
                yield Input(placeholder=self._t("ph_confirm_passphrase"), id="inp_passphrase_confirm", password=True)
                yield Button(self._t("btn_create_login"), id="btn_create", variant="primary")
                yield Button(self._t("btn_network_settings"), id="btn_settings_new", variant="warning")
                yield Button(self._t("btn_back"), id="btn_back")

            with Vertical(id="view_server", classes="hidden"):
                yield Label(self._t("login_edit_server"), id="lbl_login_edit")
                yield Input(placeholder=self._t("ph_alias_short"), id="inp_alias_readonly", disabled=True)
                yield Input(placeholder=self._t("ph_server_url_short"), id="inp_server_existing", value="wss://antch.lama2923.com")
                yield Input(placeholder=self._t("ph_passphrase_encrypted"), id="inp_login_passphrase", password=True)
                yield Button(self._t("btn_connect"), id="btn_connect", variant="primary")
                yield Button(self._t("btn_network_settings"), id="btn_settings_server", variant="warning")
                yield Button(self._t("btn_back"), id="btn_back_server")

            with VerticalScroll(id="view_settings", classes="hidden"):
                yield Label(self._t("network_mode"), classes="section-title", id="lbl_network_mode")
                with RadioSet(id="rs_mode"):
                    yield RadioButton(self._t("mode_direct"), id="mode_direct")
                    yield RadioButton(self._t("mode_socks5"), id="mode_proxy")

                yield Label(self._t("proxy_settings"), classes="section-title", id="lbl_proxy_settings")
                yield Input(placeholder=self._t("ph_proxy_host"), id="inp_proxy_host", value="127.0.0.1")
                yield Input(placeholder=self._t("ph_proxy_port"), id="inp_proxy_port", value="9050")

                yield Label(self._t("file_seeding"), classes="section-title", id="lbl_file_seeding")
                yield Checkbox(self._t("enable_seeding"), id="chk_seeding_enabled")
                yield Input(
                    placeholder=self._t("ph_seed_upload"),
                    id="inp_seed_upload_mbit",
                    value="0",
                )
                yield Input(
                    placeholder=self._t("ph_cloud_upload"),
                    id="inp_cloud_upload_mbit",
                    value="0",
                )
                yield Input(
                    placeholder=self._t("ph_seed_dir"),
                    id="inp_seed_dir",
                    value=str(APP_DIR / "files"),
                )
                yield Input(
                    placeholder=self._t("ph_max_seed_disk"),
                    id="inp_max_seed_mb",
                    value="0",
                )
                yield Input(
                    placeholder=self._t("ph_downloads_dir"),
                    id="inp_downloads_dir",
                    value=str(APP_DIR / "downloads"),
                )

                yield Label(self._t("ui_language"), classes="section-title", id="lbl_ui_language")
                with RadioSet(id="rs_lang"):
                    yield RadioButton(self._t("lang_en"), id="lang_en")
                    yield RadioButton(self._t("lang_tr"), id="lang_tr")
                    yield RadioButton(self._t("lang_de"), id="lang_de")
                    yield RadioButton(self._t("lang_es"), id="lang_es")
                    yield RadioButton(self._t("lang_fr"), id="lang_fr")

                yield Button(self._t("btn_save_settings"), id="btn_save_settings", variant="success")
                yield Button(self._t("btn_back"), id="btn_back_settings")

    def show_account_context_menu(self, alias, db_path):

        try:
            has_passphrase = DB.has_passphrase_file(pathlib.Path(db_path))
        except Exception:
            has_passphrase = False

        def _handler(res):
            if res == "ctx_login":
                self.select_account(alias, db_path, edit_mode=False)
            elif res == "ctx_edit":
                self.select_account(alias, db_path, edit_mode=True)
            elif res == "ctx_del_local":
                self.delete_account_local(alias, db_path)
            elif res == "ctx_del_server":
                self.delete_account_server(alias, db_path)
            elif res == "ctx_add_pass":
                self.add_passphrase_to_account(alias, db_path)
            elif res == "ctx_remove_pass":
                self.remove_passphrase_from_account(alias, db_path)

        self.push_screen(AccountContextMenu(alias, has_passphrase, lang=self.lang), _handler)

    def add_passphrase_to_account(self, alias, db_path):
        def _handler(passphrase):
            if passphrase:
                try:
                    db = DB(pathlib.Path(db_path))
                    try:
                        db.set_passphrase(passphrase)
                    finally:
                        db.close()
                    self.notify(self._t("db_encrypted_success", alias=alias), severity="information")
                    self.refresh_accounts()
                except Exception as e:
                    self.notify(f"Error: {e}", severity="error")

        self.push_screen(AddPassphraseScreen(alias, lang=self.lang), _handler)

    def remove_passphrase_from_account(self, alias, db_path):
        def _handler(current_passphrase):
            if current_passphrase:
                try:
                    db = DB(pathlib.Path(db_path), passphrase=current_passphrase)
                    try:
                        db.remove_passphrase()
                    finally:
                        db.close()
                    self.notify(self._t("encryption_removed", alias=alias), severity="information")
                    self.refresh_accounts()
                except ValueError as e:
                    self.notify(self._t("invalid_passphrase", e=str(e)), severity="error")
                except Exception as e:
                    self.notify(f"Error: {e}", severity="error")

        self.push_screen(RemovePassphraseScreen(alias, lang=self.lang), _handler)

    def delete_account_local(self, alias, db_path):
        try:
            p = pathlib.Path(db_path)
            if p.exists():
                p.unlink()
            self.refresh_accounts()
            self.notify(self._t("account_deleted_locally", alias=alias))
        except Exception as e:
            self.notify(self._t("err_delete", e=str(e)), severity="error")

    def delete_account_server(self, alias, db_path):

        if DB.has_passphrase_file(pathlib.Path(db_path)):

            self.push_screen(
                PassphraseInputScreen(
                    alias,
                    self._t("delete_verify_passphrase"),
                    lang=self.lang
                ),
                lambda result: self._on_delete_passphrase_entered(alias, db_path, result)
            )
        else:

            self.run_worker(self._do_server_delete(alias, db_path, None))

    def _on_delete_passphrase_entered(self, alias, db_path, passphrase):
        if passphrase is None:
            return
        if not passphrase.strip():
            self.notify(self._t("err_enter_passphrase"), severity="error")
            return

        try:
            test_db = DB(pathlib.Path(db_path), passphrase=passphrase.strip())
            test_db.close()
        except Exception as e:
            self.notify(self._t("invalid_passphrase", e=str(e)), severity="error")
            return

        self.run_worker(self._do_server_delete(alias, db_path, passphrase.strip()))

    async def _do_server_delete(self, alias, db_path, passphrase: str | None):
        db = None
        try:

            if passphrase:
                db = DB(pathlib.Path(db_path), passphrase=passphrase)
            else:
                db = DB(pathlib.Path(db_path))
            server_url = db.get_setting("server") or "wss://antch.lama2923.com"
            ident = Identity.load_or_create(db, alias)

            net_conf = self.global_config.get("network", {})
            mode = net_conf.get("mode", "direct")
            connect_args = {}

            if mode == "proxy" or mode == "tor":
                import socks
                from urllib.parse import urlparse
                proxy_host = net_conf.get("proxy_host", "127.0.0.1")
                proxy_port = int(net_conf.get("proxy_port", 9050))

                loop = asyncio.get_running_loop()
                def create_proxy_socket():
                    s = socks.socksocket()
                    s.set_proxy(socks.SOCKS5, proxy_host, proxy_port, rdns=True)
                    u = urlparse(server_url)
                    host = u.hostname
                    port = u.port or (443 if u.scheme == "wss" else 80)
                    s.settimeout(10)
                    s.connect((host, port))
                    return s

                s = await loop.run_in_executor(None, create_proxy_socket)
                connect_args["sock"] = s

            async with websockets.connect(server_url, open_timeout=20, **connect_args) as ws:

                await ws.send(json.dumps({
                    "type": "register",
                    "alias": ident.alias,
                    "fp": ident.fp,
                    "sign_pub": b64e(ident.sign_pub),
                    "dh_pub": b64e(ident.dh_pub),
                }))

                while True:
                    msg = json.loads(await ws.recv())
                    if msg.get("type") == "challenge":
                        chall = b64d(msg["challenge"])
                        break
                    if msg.get("type") == "error":
                        self.notify(f"Server error: {msg.get('error')}", severity="error")
                        return

                sig = ident.sign_sk.sign(chall).signature
                await ws.send(json.dumps({"type": "auth", "sig": b64e(sig)}))

                auth_res = json.loads(await ws.recv())
                if auth_res.get("type") != "ok":
                    self.notify(f"Server auth failed: {auth_res}", severity="error")
                    return

                await ws.send(json.dumps({"type": "delete_account"}))

                try:
                    del_res = json.loads(await ws.recv())
                    if del_res.get("type") == "delete_account" and del_res.get("ok"):
                        self.notify(self._t("account_deleted_server"))
                except:
                    pass

            self.delete_account_local(alias, db_path)

        except Exception as e:
            self.notify(self._t("err_server_delete", e=str(e)), severity="error")
        finally:
            if db is not None:
                try:
                    db.close()
                except Exception:
                    pass

    def on_mount(self) -> None:
        self.refresh_accounts()
        self.load_settings_ui()

    def load_settings_ui(self):
        conf = self.global_config["network"]

        mode = conf.get("mode", "direct")
        if mode == "direct": self.query_one("#mode_direct", RadioButton).value = True
        elif mode == "proxy" or mode == "tor": self.query_one("#mode_proxy", RadioButton).value = True

        self.query_one("#inp_proxy_host", Input).value = str(conf.get("proxy_host", "127.0.0.1"))

        port = conf.get("proxy_port", conf.get("tor_socks_port", 9050))
        self.query_one("#inp_proxy_port", Input).value = str(port)

        try:
            ui_lang = (self.global_config.get("ui", {}) or {}).get("language", self.lang) or self.lang
            ui_lang = ui_lang if ui_lang in LAUNCHER_I18N else "en"
            self.query_one(f"#lang_{ui_lang}", RadioButton).value = True
        except Exception:
            pass

        fconf = self.global_config.get("files", {})
        try:
            self.query_one("#chk_seeding_enabled", Checkbox).value = bool(fconf.get("seeding_enabled", True))
        except Exception:
            pass
        try:
            legacy = float(fconf.get("upload_limit_mbit", 0) or 0)
        except Exception:
            legacy = 0.0

        try:
            seed_limit = float(fconf.get("seed_upload_limit_mbit", legacy) or 0)
        except Exception:
            seed_limit = legacy
        try:
            cloud_limit = float(fconf.get("cloud_upload_limit_mbit", legacy) or 0)
        except Exception:
            cloud_limit = legacy

        try:
            self.query_one("#inp_seed_upload_mbit", Input).value = str(seed_limit)
        except Exception:
            self.query_one("#inp_seed_upload_mbit", Input).value = "0"
        try:
            self.query_one("#inp_cloud_upload_mbit", Input).value = str(cloud_limit)
        except Exception:
            self.query_one("#inp_cloud_upload_mbit", Input).value = "0"
        self.query_one("#inp_seed_dir", Input).value = str(fconf.get("seed_dir", str(APP_DIR / "files")))
        self.query_one("#inp_downloads_dir", Input).value = str(fconf.get("downloads_dir", str(APP_DIR / "downloads")))
        try:
            self.query_one("#inp_max_seed_mb", Input).value = str(int(fconf.get("max_seed_mb", 0)))
        except Exception:
            self.query_one("#inp_max_seed_mb", Input).value = "0"

    def save_settings_ui(self):
        conf = self.global_config["network"]

        rs_mode = self.query_one("#rs_mode", RadioSet)
        if rs_mode.pressed_button:
            if rs_mode.pressed_button.id == "mode_direct": conf["mode"] = "direct"
            elif rs_mode.pressed_button.id == "mode_proxy": conf["mode"] = "proxy"

        conf["proxy_host"] = self.query_one("#inp_proxy_host", Input).value
        try:
            conf["proxy_port"] = int(self.query_one("#inp_proxy_port", Input).value)
        except:
            conf["proxy_port"] = 9050

        if "tor_socks_port" in conf:
            del conf["tor_socks_port"]

        conf["kill_switch"] = True

        fconf = self.global_config.setdefault("files", {})
        try:
            fconf["seeding_enabled"] = bool(self.query_one("#chk_seeding_enabled", Checkbox).value)
        except Exception:
            fconf["seeding_enabled"] = True
        def _read_mbit(inp_id: str) -> float:
            try:
                raw = (self.query_one(inp_id, Input).value or "0").strip()
                return max(0.0, float(raw))
            except Exception:
                return 0.0

        seed_upload_mbit = _read_mbit("#inp_seed_upload_mbit")
        cloud_upload_mbit = _read_mbit("#inp_cloud_upload_mbit")
        fconf["seed_upload_limit_mbit"] = float(seed_upload_mbit)
        fconf["cloud_upload_limit_mbit"] = float(cloud_upload_mbit)

        fconf["upload_limit_mbit"] = float(seed_upload_mbit)

        seed_dir = self.query_one("#inp_seed_dir", Input).value.strip() or str(APP_DIR / "files")
        downloads_dir = self.query_one("#inp_downloads_dir", Input).value.strip() or str(APP_DIR / "downloads")
        try:
            max_seed_mb = int((self.query_one("#inp_max_seed_mb", Input).value or "0").strip())
        except Exception:
            max_seed_mb = 0

        try:
            pathlib.Path(seed_dir).mkdir(parents=True, exist_ok=True)
        except Exception:
            pass
        try:
            pathlib.Path(downloads_dir).mkdir(parents=True, exist_ok=True)
        except Exception:
            pass

        fconf["seed_dir"] = str(seed_dir)
        fconf["downloads_dir"] = str(downloads_dir)
        fconf["max_seed_mb"] = max(0, int(max_seed_mb))

        new_lang = self._lang_from_settings_ui()
        self.global_config.setdefault("ui", {})["language"] = new_lang

        save_global_config(self.global_config)

        self._apply_language(new_lang)
        self.notify(self._t("settings_saved"))
        self.show_select()

    def show_select(self):
        self.query_one("#view_select").remove_class("hidden")
        self.query_one("#view_new").add_class("hidden")
        self.query_one("#view_server").add_class("hidden")
        self.query_one("#view_settings").add_class("hidden")
        self.previous_view = "view_select"

    def show_settings(self):

        if not self.query_one("#view_select").has_class("hidden"):
            self.previous_view = "view_select"
        elif not self.query_one("#view_new").has_class("hidden"):
            self.previous_view = "view_new"
        elif not self.query_one("#view_server").has_class("hidden"):
            self.previous_view = "view_server"

        self.query_one("#view_select").add_class("hidden")
        self.query_one("#view_new").add_class("hidden")
        self.query_one("#view_server").add_class("hidden")
        self.query_one("#view_settings").remove_class("hidden")
        self.load_settings_ui()

    def refresh_accounts(self):
        lv = self.query_one("#account_list", ListView)
        lv.clear()

        files = list(APP_DIR.glob("client_*.db"))
        if not files:
            self.show_new()
            return

        for p in files:

            name = p.stem
            if name.startswith("client_"):
                alias = name[7:]

                try:
                    encrypted = DB.has_passphrase_file(p)
                except Exception:
                    encrypted = False
                lv.append(AccountItem(alias, str(p), encrypted=encrypted))

        self.show_select()

    def show_new(self):
        self.query_one("#view_select").add_class("hidden")
        self.query_one("#view_new").remove_class("hidden")
        self.query_one("#view_server").add_class("hidden")
        self.query_one("#view_settings").add_class("hidden")
        self.query_one("#inp_alias").focus()
        self.previous_view = "view_new"

    def on_button_pressed(self, event: Button.Pressed) -> None:
        bid = event.button.id
        if bid == "btn_new":
            self.show_new()
        elif bid in ("btn_settings", "btn_settings_new", "btn_settings_server"):
            self.show_settings()
        elif bid == "btn_back":
            self.refresh_accounts()
        elif bid == "btn_create":
            self.create_account()
        elif bid == "btn_connect":
            self.connect_existing()
        elif bid == "btn_back_server":
            self.refresh_accounts()
        elif bid == "btn_save_settings":
            self.save_settings_ui()
        elif bid == "btn_back_settings":

            self.query_one("#view_settings").add_class("hidden")
            self.query_one(f"#{self.previous_view}").remove_class("hidden")

    def create_account(self):
        alias = self.query_one("#inp_alias", Input).value.strip()
        server = self.query_one("#inp_server", Input).value.strip()
        passphrase = self.query_one("#inp_passphrase", Input).value.strip()
        passphrase_confirm = self.query_one("#inp_passphrase_confirm", Input).value.strip()

        if not alias or not server:
            self.notify(self._t("err_alias_server_required"), severity="error")
            return

        if not re.match(r"^[a-zA-Z0-9_]+$", alias):
            self.notify(self._t("err_invalid_alias"), severity="error")
            return

        if passphrase or passphrase_confirm:
            if passphrase != passphrase_confirm:
                self.notify(self._t("err_passphrase_mismatch"), severity="error")
                return
            if len(passphrase) < 4:
                self.notify(self._t("err_passphrase_short"), severity="error")
                return

        if not server.startswith("ws://") and not server.startswith("wss://"):
            server = "ws://" + server

        db_path = APP_DIR / f"client_{alias}.db"
        if db_path.exists():
             self.notify(self._t("err_account_exists"), severity="error")
             return

        db = DB(db_path)
        try:
            db.set_setting("server", server)

            if passphrase:
                db.set_passphrase(passphrase)
        finally:
            db.close()

        self.query_one("#inp_passphrase", Input).value = ""
        self.query_one("#inp_passphrase_confirm", Input).value = ""

        self.check_security_and_login(alias, server, db_path, passphrase if passphrase else None)

    def on_list_view_selected(self, event: ListView.Selected) -> None:

        item = event.item
        if isinstance(item, AccountItem):
            self.select_account(item.alias, item.db_path, edit_mode=False)

    def select_account(self, alias: str, db_path_str: str, edit_mode: bool = False, passphrase: str = None):
        db_path = pathlib.Path(db_path_str)

        try:
            needs_passphrase = DB.has_passphrase_file(db_path)
        except Exception:
            needs_passphrase = False

        if needs_passphrase and not passphrase:

            def _on_passphrase(p):
                if p:
                    self.select_account(alias, db_path_str, edit_mode, passphrase=p)

            try:
                login_pass = self.query_one("#inp_login_passphrase", Input).value.strip()
                if login_pass:
                    passphrase = login_pass
                    self.query_one("#inp_login_passphrase", Input).value = ""
                else:
                    self.push_screen(PassphraseInputScreen(alias, lang=self.lang), _on_passphrase)
                    return
            except Exception:
                self.push_screen(PassphraseInputScreen(alias, lang=self.lang), _on_passphrase)
                return

        db = None
        try:
            db = DB(db_path, passphrase=passphrase)
        except ValueError as e:
            self.notify(self._t("invalid_passphrase", e=str(e)), severity="error")
            return
        except Exception as e:
            self.notify(f"Error: {e}", severity="error")
            return

        try:
            server = db.get_setting("server") or "wss://antch.lama2923.com"
        finally:
            db.close()

        self.pending_alias = alias
        self.pending_db_path = db_path
        self.pending_passphrase = passphrase

        if edit_mode:

            self.query_one("#inp_alias_readonly", Input).value = alias
            self.query_one("#inp_server_existing", Input).value = server

            self.query_one("#view_select").add_class("hidden")
            self.query_one("#view_server").remove_class("hidden")
            self.query_one("#inp_server_existing").focus()
            self.previous_view = "view_server"
        else:

            self.check_security_and_login(alias, server, db_path, passphrase)

    def check_security_and_login(self, alias, server, db_path, passphrase=None):

        net_conf = self.global_config.get("network", {})
        mode = net_conf.get("mode", "direct")
        suppress_warn = net_conf.get("suppress_proxy_warning", False)

        if mode == "direct" and not suppress_warn:
            self.push_screen(SecurityWarningScreen(lang=self.lang), self.on_security_warning_result)

            self.pending_login = {"alias": alias, "server": server, "db_path": db_path, "passphrase": passphrase}
        else:
            self.exit({"alias": alias, "server": server, "db_path": db_path, "passphrase": passphrase})

    def on_security_warning_result(self, result: bool):
        if result:

            p = getattr(self, "pending_login", None)
            if p:
                self.exit(p)
        else:

            self.show_settings()

    def connect_existing(self):
        server = self.query_one("#inp_server_existing", Input).value.strip()
        if not server:
            self.notify(self._t("server_required"), severity="error")
            return

        if not server.startswith("ws://") and not server.startswith("wss://"):
            server = "ws://" + server

        passphrase = getattr(self, "pending_passphrase", None)

        db = DB(self.pending_db_path, passphrase=passphrase)
        try:
            db.set_setting("server", server)
        finally:
            db.close()

        self.check_security_and_login(self.pending_alias, server, self.pending_db_path, passphrase)
