import 'package:cryptography/cryptography.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
import 'package:chepuhagram/data/models/contact_model.dart';

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,
          ),
        );
      } catch (e) {
        result.add(contact.copyWith(lastMessage: '[не удалось расшифровать: $e]', isLastMsgDecrypted: true));
      }
    }
    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<(List<int>, String)?> encryptImage(
    List<int> fileBytes,
    SecretKey sharedKey,
  ) async {
    try {
      final SecretKey fileSecretKey = await aesGcm.newSecretKey();
      final List<int> fileSecretKeyBytes = await fileSecretKey.extractBytes();

      final SecretBox secretBox = await aesGcm.encrypt(
        fileBytes,
        secretKey: fileSecretKey,
      );
      final List<int> dataToUpload = secretBox.concatenation();

      final encryptedKeyBox = await aesGcm.encrypt(
        fileSecretKeyBytes,
        secretKey: sharedKey,
      );

      final String encryptedKeyForServer = base64Encode(
        encryptedKeyBox.concatenation(),
      );

      return (dataToUpload, encryptedKeyForServer);
    } catch (e) {
      print("Ошибка шифрования медиа: $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');
  }
}
