import 'package:chepuhagram/presentation/screens/contacts_screen.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../logic/auth_provider.dart'; import '../../domain/services/crypto_service.dart'; import '../../domain/services/api_service.dart'; import 'account_setup_screen.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; import '../../core/constants.dart'; class KeyRecoveryScreen extends StatefulWidget { const KeyRecoveryScreen({super.key}); @override State createState() => _KeyRecoveryScreenState(); } class _KeyRecoveryScreenState extends State { bool _isLoading = false; String? _errorMessage; final _passwordController = TextEditingController(); final _formKey = GlobalKey(); Future _startFresh() async { setState(() { _isLoading = true; _errorMessage = null; }); try { final authProvider = context.read(); // Удаляем все сообщения пользователя try { final api = ApiService(); await api.deleteAllMessages(); } catch (e) { print('Ошибка при удалении сообщений: $e'); // Продолжаем даже если удаление сообщений не удалось } // Удаляем старые ключи и создаем новые await authProvider.resetKeys(); // Переходим на экран настройки для создания новых ключей if (mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const AccountSetupScreen()), ); } } catch (e) { if (mounted) { setState(() { _errorMessage = 'Ошибка: ${e.toString()}'; _isLoading = false; }); } } } Future _recoverKeys() async { if (!_formKey.currentState!.validate()) return; setState(() { _isLoading = true; _errorMessage = null; }); try { final authProvider = context.read(); final apiService = ApiService(); final cryptoService = CryptoService(); // Получаем токен final token = await apiService.getAccessToken(); if (token == null) throw Exception('Не авторизован'); // Скачиваем зашифрованный приватный ключ с сервера final response = await http.get( Uri.parse('${AppConstants.baseUrl}/users/me'), headers: {'Authorization': 'Bearer $token'}, ); if (response.statusCode != 200) { throw Exception('Не удалось получить данные пользователя'); } final data = jsonDecode(utf8.decode(response.bodyBytes)) as Map; final encryptedPrivateKey = data['encrypted_private_key']; if (encryptedPrivateKey == null || encryptedPrivateKey.isEmpty) { throw Exception('Зашифрованный ключ не найден на сервере'); } // Расшифровываем приватный ключ final decryptedPrivateKey = await cryptoService.decryptPrivateKey( encryptedPrivateKey, _passwordController.text, ); // Сохраняем расшифрованный ключ локально await cryptoService.savePrivateKey(decryptedPrivateKey); // Обновляем статус в AuthProvider await authProvider.tryAutoLogin(); if (mounted) { // Возвращаемся на главный экран Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const ContactsScreen()), ); } } catch (e) { if (mounted) { setState(() { _errorMessage = 'Ошибка восстановления: ${e.toString().replaceAll('Exception: ', '')}'; _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Восстановление ключей'), centerTitle: true, elevation: 0, ), body: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 32), Icon( Icons.security_outlined, size: 80, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 24), Text( 'Восстановление ключей шифрования', textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 16), Text( 'Вы переустановили приложение или используете новый девайс. У вас есть два варианта:', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 32), // Вариант 1: Начать с чистого листа Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.restart_alt_outlined, color: Theme.of(context).colorScheme.primary, size: 28, ), const SizedBox(width: 12), Expanded( child: Text( 'Начать с чистого листа', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 12), Text( 'Создаются новые ключи шифрования. Старые сообщения не будут расшифрованы.', style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isLoading ? null : _startFresh, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Продолжить'), ), ), ], ), ), ), const SizedBox(height: 24), // Вариант 2: Восстановить из облака Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.cloud_download_outlined, color: Theme.of(context).colorScheme.primary, size: 28, ), const SizedBox(width: 12), Expanded( child: Text( 'Восстановить из облака', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 12), Text( 'Введите мастер-пароль для восстановления ключей шифрования', style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, obscureText: true, decoration: const InputDecoration( labelText: 'Мастер-пароль', border: OutlineInputBorder(), ), validator: (value) { if (value == null || value.isEmpty) { return 'Введите мастер-пароль'; } return null; }, ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isLoading ? null : _recoverKeys, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Восстановить'), ), ), ], ), ), ), ), const SizedBox(height: 24), // Сообщение об ошибке if (_errorMessage != null) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).colorScheme.error.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Text( _errorMessage!, style: TextStyle( color: Theme.of(context).colorScheme.error, ), ), ), ], ), ), ); } @override void dispose() { _passwordController.dispose(); super.dispose(); } }