import 'package:cryptography/cryptography.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:chepuhagram/data/models/contact_model.dart';
import 'dart:async';
import 'package:pointycastle/export.dart' as pc;
import 'dart:io';

class CryptoService {
  final _storage = const FlutterSecureStorage();
  final algorithm = X25519();
  final aesGcm = AesGcm.with256bits();

  Future<Map<String, String>> initAccountSecurity(String masterPassword) async {
    // Генерируем пару X25519 ключей
    final keyPair = await algorithm.newKeyPair();
    final publicKey = await keyPair.extractPublicKey();
    final privateKeyBytes = await keyPair.extractPrivateKeyBytes();

    // Сохраняем приватный ключ в Secure Storage
    await _storage.write(
      key: 'private_key',
      value: base64Encode(privateKeyBytes),
    );

    // Шифруем приватный ключ с мастер-паролем (AES-GCM)
    final masterKey = await _deriveKeyFromPassword(masterPassword);
    final nonce = aesGcm.newNonce();
    final encrypted = await aesGcm.encrypt(
      privateKeyBytes,
      secretKey: masterKey,
      nonce: nonce,
    );

    // Комбинируем nonce и зашифрованные данные
    final encryptedData = nonce + encrypted.mac.bytes + encrypted.cipherText;
    final encryptedPrivateKey = base64Encode(encryptedData);

    final publicKeyBase64 = base64Encode(publicKey.bytes);

    return {
      'public_key': publicKeyBase64,
      'encrypted_private_key': encryptedPrivateKey,
    };
  }

  Future<String> decryptPrivateKey(
    String encryptedPrivateKey,
    String masterPassword,
  ) async {
    try {
      final encryptedData = base64Decode(encryptedPrivateKey);

      // Разделяем nonce и зашифрованные данные
      final nonce = encryptedData.sublist(0, 12); // GCM nonce = 12 bytes
      final macBytes = encryptedData.sublist(12, 28);
      final cipherText = encryptedData.sublist(28);

      final masterKey = await _deriveKeyFromPassword(masterPassword);

      final decrypted = await aesGcm.decrypt(
        SecretBox(cipherText, nonce: nonce, mac: Mac(macBytes)),
        secretKey: masterKey,
      );

      return base64Encode(decrypted);
    } catch (e) {
      throw Exception('Неверный мастер-пароль или поврежденные данные');
    }
  }

  Future<String> encryptPrivateKeyWithPassword(
    String privateKeyBase64,
    String masterPassword,
  ) async {
    final privateKeyBytes = base64Decode(privateKeyBase64);
    final masterKey = await _deriveKeyFromPassword(masterPassword);
    final nonce = aesGcm.newNonce();
    final encrypted = await aesGcm.encrypt(
      privateKeyBytes,
      secretKey: masterKey,
      nonce: nonce,
    );

    final encryptedData = nonce + encrypted.mac.bytes + encrypted.cipherText;
    return base64Encode(encryptedData);
  }

  Future<SecretKey> _deriveKeyFromPassword(String password) async {
    final pbkdf2 = Pbkdf2(
      macAlgorithm: Hmac.sha256(),
      iterations: 10000,
      bits: 256,
    );

    final salt = utf8.encode('chepuhagram_salt');
    return await pbkdf2.deriveKeyFromPassword(password: password, nonce: salt);
  }

  Future<SecretKey> deriveSharedSecret(
    String myPrivateKeyBase64,
    String theirPublicKeyBase64,
  ) async {
    final myKeyPair = await algorithm.newKeyPairFromSeed(
      base64Decode(myPrivateKeyBase64),
    );
    final theirPublicKey = SimplePublicKey(
      base64Decode(theirPublicKeyBase64),
      type: KeyPairType.x25519,
    );

    return await algorithm.sharedSecretKey(
      keyPair: myKeyPair,
      remotePublicKey: theirPublicKey,
    );
  }

  Future<String> encryptMessage(String text, SecretKey sharedKey) async {
    final nonce = aesGcm.newNonce();
    final encrypted = await aesGcm.encrypt(
      utf8.encode(text),
      secretKey: sharedKey,
      nonce: nonce,
    );
    // Сохраняем Nonce + MAC + CipherText для передачи
    return base64Encode(nonce + encrypted.mac.bytes + encrypted.cipherText);
  }

  static Future<String> decryptInIsolate(
    String base64Data,
    SecretKey sharedKey,
  ) async {
    final data = base64Decode(base64Data);
    final aesGcm = AesGcm.with256bits();

    final nonce = data.sublist(0, 12);
    final mac = data.sublist(12, 28);
    final cipherText = data.sublist(28);

    final decrypted = await aesGcm.decrypt(
      SecretBox(cipherText, nonce: nonce, mac: Mac(mac)),
      secretKey: sharedKey,
    );

    return utf8.decode(decrypted);
  }

  static Future<List<Contact>> bulkDecryptContacts(
    Map<String, dynamic> data,
  ) async {
    final List<Contact> contacts = data['contacts'];
    final String privKey = data['privKey'];
    final Map<int, SecretKey> cache = data['cache'];

    final x25519 = X25519();
    final aesGcm = AesGcm.with256bits();
    final List<Contact> result = [];

    // Вычисляем свою пару один раз
    final myKeyPair = await x25519.newKeyPairFromSeed(base64Decode(privKey));

    for (var contact in contacts) {
      if (contact.lastMessage == null || contact.publicKey == null) {
        result.add(contact);
        continue;
      }

      try {
        SecretKey sharedKey;
        if (cache.containsKey(contact.id)) {
          sharedKey = cache[contact.id]!;
        } else {
          final theirPubKey = SimplePublicKey(
            base64Decode(contact.publicKey!),
            type: KeyPairType.x25519,
          );
          sharedKey = await x25519.sharedSecretKey(
            keyPair: myKeyPair,
            remotePublicKey: theirPubKey,
          );
        }

        // Дешифровка AES-GCM
        final msgData = base64Decode(contact.lastMessage!);
        final decrypted = await aesGcm.decrypt(
          SecretBox(
            msgData.sublist(28),
            nonce: msgData.sublist(0, 12),
            mac: Mac(msgData.sublist(12, 28)),
          ),
          secretKey: sharedKey,
        );

        result.add(
          contact.copyWith(
            lastMessage: utf8.decode(decrypted),
            isLastMsgDecrypted: true,
            avatarFileId: contact.avatarFileId,
            avatarUrl: contact.avatarUrl,
          ),
        );
      } catch (e) {
        result.add(
          contact.copyWith(
            lastMessage: '[не удалось расшифровать: $e]',
            isLastMsgDecrypted: true,
            avatarFileId: contact.avatarFileId,
            avatarUrl: contact.avatarUrl,
          ),
        );
      }
    }
    return result;
  }

  static Future<Map<int, List<int>>> computeSharedKeysTask(
    Map<String, dynamic> params,
  ) async {
    final Map<int, String> isolateKeysMap = params['keysMap'];
    final String isolatePrivKey = params['privKey'];

    final x25519 = X25519();
    final Map<int, List<int>> result = {};

    final myKeyPair = await x25519.newKeyPairFromSeed(
      base64Decode(isolatePrivKey),
    );

    for (var entry in isolateKeysMap.entries) {
      try {
        final theirPubKey = SimplePublicKey(
          base64Decode(entry.value),
          type: KeyPairType.x25519,
        );

        final sharedKey = await x25519.sharedSecretKey(
          keyPair: myKeyPair,
          remotePublicKey: theirPubKey,
        );

        result[entry.key] = await sharedKey.extractBytes();
      } catch (_) {
        continue;
      }
    }
    return result;
  }

  Future<(Stream<List<int>>, String)> encryptFileStream(
    Stream<List<int>> fileStream,
    SecretKey sharedKey, {
    void Function(int processed, int total)? onProgress,
    int? totalSize,
  }) async {
    // 1. Генерируем уникальный ключ для конкретного файла
    final SecretKey fileKey = await aesGcm.newSecretKey();
    final List<int> fileKeyBytes = await fileKey.extractBytes();

    // 2. Шифруем этот ключ файла на общем ключе чата (sharedKey)
    final keyNonce = aesGcm.newNonce();
    final encryptedKeyBox = await aesGcm.encrypt(
      fileKeyBytes,
      secretKey: sharedKey,
      nonce: keyNonce,
    );

    // Кодируем зашифрованный ключ в Base64 для сервера
    final String encryptedKeyForServer = base64Encode(
      encryptedKeyBox.concatenation(),
    );
    int processedBytes = 0;
    final int total = totalSize ?? 0;
    
    // 3. Создаем асинхронный генератор для поблочного шифрования самого файла
    Stream<List<int>> processEncryption() async* {
      final List<int> buffer = [];
      const int chunkSize = 64 * 1024; // 64 KB

      await for (final chunk in fileStream) {
        buffer.addAll(chunk);

        while (buffer.length >= chunkSize) {
          final plainBlock = Uint8List.fromList(buffer.sublist(0, chunkSize));
          buffer.removeRange(0, chunkSize);

          final blockNonce = aesGcm.newNonce();
          final secretBox = await aesGcm.encrypt(
            plainBlock,
            secretKey: fileKey,
            nonce: blockNonce,
          );

          final payload = secretBox.concatenation();
          final header = ByteData(4)..setUint32(0, payload.length);

          yield header.buffer.asUint8List();
          yield payload;
          processedBytes += chunkSize;
          if (onProgress != null) {
            onProgress(processedBytes, total);
          }
        }
      }

      // Дозаписываем остаток файла (хвост), если он есть
      if (buffer.isNotEmpty) {
        final plainBlock = Uint8List.fromList(buffer);
        final blockNonce = aesGcm.newNonce();
        final secretBox = await aesGcm.encrypt(
          plainBlock,
          secretKey: fileKey,
          nonce: blockNonce,
        );

        final payload = secretBox.concatenation();
        final header = ByteData(4)..setUint32(0, payload.length);

        yield header.buffer.asUint8List();
        yield payload;
      }
    }

    // Возвращаем кортеж (Record): очищенный зашифрованный поток данных и ключ для сервера
    return (processEncryption(), encryptedKeyForServer);
  }

  Stream<List<int>> decryptFileStream(
    Stream<List<int>> encryptedStream,
    SecretKey sharedKey,
    String encryptedFileKey, {
    int? totalBytes,
    void Function(int processed, int total)? onProgress,
  }) async* {
    try {
      // 1. Дешифруем ключ файла с помощью общего ключа чата
      final encryptedKeyBytes = base64Decode(encryptedFileKey);
      final keySecretBox = SecretBox.fromConcatenation(
        encryptedKeyBytes,
        nonceLength: 12,
        macLength: 16,
      );
      final fileKeyBytes = await aesGcm.decrypt(
        keySecretBox,
        secretKey: sharedKey,
      );
      final fileKey = SecretKey(fileKeyBytes);

      final List<int> buffer = [];
      int blocksDecrypted = 0;
      int totalProcessedBytes = 0;

      // 2. Потоковая дешифровка блоков файла
      await for (final chunk in encryptedStream) {
        buffer.addAll(chunk);

        while (true) {
          if (buffer.length < 4) break;

          final headerBytes = Uint8List.fromList(buffer.sublist(0, 4));
          final int payloadLength = ByteData.sublistView(
            headerBytes,
          ).getUint32(0);

          // Проверяем: если длина чанка подозрительно огромная (из-за неверного формата файла)
          if (payloadLength > 500 * 1024 || payloadLength <= 0) {
            print(
              "ОШИБКА: Неверный заголовок длины чанка: $payloadLength байт. Возможно, файл зашифрован старым методом!",
            );
            throw Exception("Неверный формат зашифрованного блока");
          }

          if (buffer.length < 4 + payloadLength) break;

          final encryptedBlockBytes = Uint8List.fromList(
            buffer.sublist(4, 4 + payloadLength),
          );
          buffer.removeRange(0, 4 + payloadLength);

          final blockSecretBox = SecretBox.fromConcatenation(
            encryptedBlockBytes,
            nonceLength: 12,
            macLength: 16,
          );

          final decryptedBlock = await aesGcm.decrypt(
            blockSecretBox,
            secretKey: fileKey,
          );
          blocksDecrypted++;

          if (blocksDecrypted % 10 == 0 || payloadLength < 64 * 1024) {
            print(
              "Дешифровано блоков: $blocksDecrypted. Текущий размер: ${decryptedBlock.length} байт. Всего обработано $totalProcessedBytes. Всего $totalBytes",
            );
          }

          // Увеличиваем счетчик обработанных зашифрованных байт
          totalProcessedBytes += 4 + payloadLength;

          // Вызываем колбэк прогресса
          if (onProgress != null) {
            // Передаем, сколько байт обработано, и общий размер (если totalBytes null, передаем -1)
            onProgress(totalProcessedBytes, totalBytes ?? -1);
          }

          yield decryptedBlock;
        }
      }
      print(
        "ПОТОК ДЕШИФРАЦИИ ЗАВЕРШЕН ПОЛНОСТЬЮ. Всего блоков: $blocksDecrypted",
      );
    } catch (e, stack) {
      print("КРИТИЧЕСКАЯ ОШИБКА ВНУТРИ КРИПТОСТРИМА: $e");
      print(stack);
      rethrow;
    }
  }

  Future<Uint8List?> decryptAesKey(
    String encryptedKey,
    SecretKey sharedKey,
  ) async {
    try {
      final keyBytes = base64Decode(encryptedKey);
      final nonce = keyBytes.sublist(0, 12);
      final cipherText = keyBytes.sublist(12, keyBytes.length - 16);
      final mac = keyBytes.sublist(keyBytes.length - 16);

      final decrypted = await aesGcm.decrypt(
        SecretBox(cipherText, nonce: nonce, mac: Mac(mac)),
        secretKey: sharedKey,
      );
      return Uint8List.fromList(decrypted);
    } catch (e) {
      print('Ошибка дешифровки AES ключа: $e');
      return null;
    }
  }

  Future<String?> encryptAesKey(List<int> keyBytes, SecretKey sharedKey) async {
    try {
      final encrypted = await aesGcm.encrypt(keyBytes, secretKey: sharedKey);
      return base64Encode(encrypted.concatenation());
    } catch (e) {
      print('Ошибка шифрования AES ключа: $e');
      return null;
    }
  }

  Future<Uint8List?> decryptMedia(
    List<int> encryptedData,
    String encryptedKey,
    SecretKey sharedKey,
  ) async {
    try {
      final keyBytes = base64Decode(encryptedKey);
      final keyNonce = keyBytes.sublist(0, 12);
      final keyCipher = keyBytes.sublist(12, keyBytes.length - 16);
      final keyMac = keyBytes.sublist(keyBytes.length - 16);

      final decryptedFileKey = await aesGcm.decrypt(
        SecretBox(keyCipher, nonce: keyNonce, mac: Mac(keyMac)),
        secretKey: sharedKey,
      );

      final fileSecretKey = SecretKey(decryptedFileKey);
      final nonce = encryptedData.sublist(0, 12);
      final cipherText = encryptedData.sublist(12, encryptedData.length - 16);
      final mac = encryptedData.sublist(encryptedData.length - 16);

      final decryptedBytes = await aesGcm.decrypt(
        SecretBox(cipherText, nonce: nonce, mac: Mac(mac)),
        secretKey: fileSecretKey,
      );

      return Uint8List.fromList(decryptedBytes);
    } catch (e) {
      print('Ошибка дешифровки медиа: $e');
      return null;
    }
  }

  Future<SecretKey?> _decryptFileKey(
    String encryptedFileKey,
    SecretKey sharedKey,
  ) async {
    try {
      final keyBytes = base64Decode(encryptedFileKey);
      final nonce = keyBytes.sublist(0, 12);
      final macBytes = keyBytes.sublist(keyBytes.length - 16);
      final cipherText = keyBytes.sublist(12, keyBytes.length - 16);

      final decrypted = await aesGcm.decrypt(
        SecretBox(cipherText, nonce: nonce, mac: Mac(macBytes)),
        secretKey: sharedKey,
      );
      return SecretKey(decrypted);
    } catch (e) {
      print('Error decrypting file key: $e');
      return null;
    }
  }

  Future<String> decryptMessage(String base64Data, SecretKey sharedKey) async {
    final data = base64Decode(base64Data);

    final nonce = data.sublist(0, 12);
    final mac = data.sublist(12, 28);
    final cipherText = data.sublist(28);

    final decrypted = await aesGcm.decrypt(
      SecretBox(cipherText, nonce: nonce, mac: Mac(mac)),
      secretKey: sharedKey,
    );

    return utf8.decode(decrypted);
  }

  Future<String?> getPrivateKey() async {
    return await _storage.read(key: 'private_key');
  }

  Future<bool> hasPrivateKey() async {
    final key = await _storage.read(key: 'private_key');
    return key != null;
  }

  Future<void> savePrivateKey(String privateKey) async {
    await _storage.write(key: 'private_key', value: privateKey);
  }

  Future<void> deletePrivateKey() async {
    await _storage.delete(key: 'private_key');
  }

  SecretKey? _currentSharedKey;

  // Метод для установки ключа (вызывается при входе в чат)
  void setCurrentSharedKey(SecretKey key) {
    _currentSharedKey = key;
  }

  // Тот самый метод, который ищет ChatScreen
  Future<SecretKey> getSharedKey(String? chatId) async {
    if (_currentSharedKey == null) {
      // Если ключа нет, его нужно либо вычислить заново,
      // либо выбросить ошибку. Для теста можно вернуть ошибку:
      throw Exception("Shared key not initialized for chat $chatId");
    }
    return _currentSharedKey!;
  }
}
