part of '../settings_modal.dart';

// ── Megolm key export / import – top-level so compute() can use them ─────────

const _kMegolmHeader = '-----BEGIN MEGOLM SESSION DATA-----';
const _kMegolmFooter = '-----END MEGOLM SESSION DATA-----';

// PBKDF2-HMAC-SHA512.  args: {pass: Uint8List, salt: Uint8List, iter: int}
Uint8List _runPbkdf2(Map<String, dynamic> args) {
  final pass = args['pass'] as Uint8List;
  final salt = args['salt'] as Uint8List;
  final iterations = args['iter'] as int;
  const blockLen = 64; // SHA-512 digest size
  const dkLen = 64;

  final hmac = mcrypto.Hmac(mcrypto.sha512, pass);
  final blockCount = (dkLen / blockLen).ceil();
  final dk = Uint8List(blockCount * blockLen);

  for (var i = 1; i <= blockCount; i++) {
    final saltI = Uint8List(salt.length + 4);
    saltI.setRange(0, salt.length, salt);
    ByteData.sublistView(saltI, salt.length).setUint32(0, i);

    var u = Uint8List.fromList(hmac.convert(saltI).bytes);
    final block = Uint8List.fromList(u);

    for (var j = 1; j < iterations; j++) {
      u = Uint8List.fromList(hmac.convert(u).bytes);
      for (var k = 0; k < blockLen; k++) { block[k] ^= u[k]; }
    }
    dk.setRange((i - 1) * blockLen, i * blockLen, block);
  }
  return dk.sublist(0, dkLen);
}

Uint8List _aesCtr(Uint8List key, Uint8List iv, Uint8List data) {
  final cipher = pc.SICStreamCipher(pc.AESEngine())
    ..init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv));
  final out = Uint8List(data.length);
  cipher.processBytes(data, 0, data.length, out, 0);
  return out;
}

// args: {passphrase: String, sessions: List<dynamic>}  →  encrypted String
String _encryptMegolmKeys(Map<String, dynamic> args) {
  final pass = args['passphrase'] as String;
  final sessions = args['sessions'] as List<dynamic>;

  final rng = Random.secure();
  final salt = Uint8List.fromList(List.generate(16, (_) => rng.nextInt(256)));
  // IV: 8 random bytes as nonce, last 8 bytes 0x00 (counter starts at 0)
  final iv = Uint8List(16);
  for (var i = 0; i < 8; i++) { iv[i] = rng.nextInt(256); }

  const iterations = 100000;
  final dk = _runPbkdf2({
    'pass': Uint8List.fromList(utf8.encode(pass)),
    'salt': salt,
    'iter': iterations,
  });
  final aesKey = dk.sublist(0, 32);
  final hmacKey = dk.sublist(32, 64);

  final plaintext = Uint8List.fromList(utf8.encode(jsonEncode(sessions)));
  final encrypted = _aesCtr(aesKey, iv, plaintext);

  final iterBe = Uint8List(4)..buffer.asByteData().setUint32(0, iterations);

  // header bytes: 0x01 | salt(16) | iv(16) | iterBe(4) | encrypted(n)
  final header = Uint8List(1 + 16 + 16 + 4 + encrypted.length);
  var off = 0;
  header[off++] = 0x01;
  header.setRange(off, off += 16, salt);
  header.setRange(off, off += 16, iv);
  header.setRange(off, off += 4, iterBe);
  header.setRange(off, off + encrypted.length, encrypted);

  final mac = Uint8List.fromList(
    mcrypto.Hmac(mcrypto.sha256, hmacKey).convert(header).bytes,
  );

  final full = Uint8List(header.length + 32);
  full.setRange(0, header.length, header);
  full.setRange(header.length, full.length, mac);

  return '$_kMegolmHeader\n${base64.encode(full)}\n$_kMegolmFooter';
}

// args: {passphrase: String, data: List<int>}  →  List<dynamic> sessions
List<dynamic> _decryptMegolmKeys(Map<String, dynamic> args) {
  final pass = args['passphrase'] as String;
  final raw = Uint8List.fromList(args['data'] as List<int>);

  if (raw.length < 1 + 16 + 16 + 4 + 32 || raw[0] != 0x01) {
    throw Exception('Ungültiges Dateiformat (Version ${raw.isEmpty ? "?" : raw[0]}).');
  }

  final salt = raw.sublist(1, 17);
  final iv = raw.sublist(17, 33);
  final iterations = raw.buffer.asByteData().getUint32(33);
  final encrypted = raw.sublist(37, raw.length - 32);
  final storedMac = raw.sublist(raw.length - 32);

  if (iterations < 1 || iterations > 10000000) {
    throw Exception('Iterationsanzahl außerhalb des erlaubten Bereichs.');
  }

  final dk = _runPbkdf2({
    'pass': Uint8List.fromList(utf8.encode(pass)),
    'salt': Uint8List.fromList(salt),
    'iter': iterations,
  });
  final aesKey = dk.sublist(0, 32);
  final hmacKey = dk.sublist(32, 64);

  // Verify MAC over all bytes except the appended MAC itself
  final toMac = raw.sublist(0, raw.length - 32);
  final expectedMac = mcrypto.Hmac(mcrypto.sha256, hmacKey).convert(toMac).bytes;
  var macOk = true;
  for (var i = 0; i < 32; i++) {
    if (expectedMac[i] != storedMac[i]) { macOk = false; break; }
  }
  if (!macOk) throw Exception('Falsches Passwort oder beschädigte Datei.');

  final decrypted = _aesCtr(aesKey, Uint8List.fromList(iv), Uint8List.fromList(encrypted));
  return jsonDecode(utf8.decode(decrypted)) as List<dynamic>;
}

// ─────────────────────────────────────────────────────────────────────────────

enum _EncState { idle, loading, needsKey, unlocking, done, error }

enum _RecoveryStep { loading, showKey, confirmKey, applying, done, error }

class _EncryptionSection extends ConsumerStatefulWidget {
  final PyramidTheme pt;
  final WidgetRef ref;
  const _EncryptionSection({required this.pt, required this.ref});

  @override
  ConsumerState<_EncryptionSection> createState() => _EncryptionSectionState();
}

class _EncryptionSectionState extends ConsumerState<_EncryptionSection> {
  _EncState _state = _EncState.idle;
  Bootstrap? _bootstrap;
  final _keyCtrl = TextEditingController();
  String? _error;

  // Status loaded from encryption object
  bool? _crossSigningEnabled;
  bool? _crossSigningCached;
  bool? _keyBackupEnabled;
  bool _statusLoaded = false;

  // Key file export / import
  bool _exportLoading = false;
  bool _importLoading = false;
  String? _keyFileMsg;
  bool _keyFileMsgIsError = false;
  bool _diagUploading = false;
  String? _diagMsg;
  bool _diagMsgIsError = false;

  @override
  void initState() {
    super.initState();
    _loadStatus();
  }

  @override
  void dispose() {
    _keyCtrl.dispose();
    super.dispose();
  }

  Future<void> _loadStatus() async {
    try {
      final client = await ref.read(matrixClientProvider.future);
      final enc = client.encryption;
      if (enc == null) return;
      final cached = await enc.crossSigning.isCached().catchError((_) => false);
      if (!mounted) return;
      setState(() {
        _crossSigningEnabled = enc.crossSigning.enabled;
        _crossSigningCached = cached;
        _keyBackupEnabled = enc.keyManager.enabled;
        _statusLoaded = true;
      });
    } catch (_) {}
  }

  Future<void> _start() async {
    setState(() { _state = _EncState.loading; _error = null; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      if (client.encryption == null) {
        setState(() { _state = _EncState.error; _error = 'Verschlüsselung nicht verfügbar.'; });
        return;
      }
      final enc = client.encryption!;
      final cached = await enc.keyManager.isCached().catchError((_) => false);
      if (cached) {
        await enc.keyManager.loadAllKeys().catchError((_) {});
        if (mounted) setState(() { _state = _EncState.done; _error = null; });
        await _loadStatus();
        return;
      }

      await client.roomsLoading;
      await client.accountDataLoading;
      await client.userDeviceKeysLoading;

      final completer = Completer<bool>();
      _bootstrap = enc.bootstrap(onUpdate: (bs) {
        if (!mounted) return;
        switch (bs.state) {
          case BootstrapState.askWipeSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.wipeSsss(false));
          case BootstrapState.askBadSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.ignoreBadSecrets(true));
          case BootstrapState.askUseExistingSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.useExistingSsss(true));
          case BootstrapState.openExistingSsss:
            if (!completer.isCompleted) completer.complete(true);
          case BootstrapState.askNewSsss:
            if (!completer.isCompleted) completer.complete(false);
          case BootstrapState.error:
            if (!completer.isCompleted) completer.complete(false);
            if (mounted) setState(() { _state = _EncState.error; _error = 'Bootstrap-Fehler.'; });
          default:
            break;
        }
      });

      final needsKey = await completer.future
          .timeout(const Duration(seconds: 30), onTimeout: () => false);
      if (!mounted) return;
      setState(() => _state = needsKey ? _EncState.needsKey : _EncState.done);
      await _loadStatus();
    } catch (e) {
      if (mounted) setState(() { _state = _EncState.error; _error = e.toString().split('\n').first; });
    }
  }

  Future<void> _unlock() async {
    final bs = _bootstrap;
    if (bs == null || bs.newSsssKey == null) return;
    final key = _keyCtrl.text.trim();
    if (key.isEmpty) { setState(() => _error = 'Bitte Wiederherstellungsschlüssel eingeben.'); return; }
    setState(() { _state = _EncState.unlocking; _error = null; });
    try {
      await bs.newSsssKey!.unlock(keyOrPassphrase: key);
      await bs.openExistingSsss();
      final client = await ref.read(matrixClientProvider.future);
      if (bs.encryption.crossSigning.enabled) {
        await client.encryption!.crossSigning.selfSign(recoveryKey: key);
      }
      await client.encryption!.keyManager.loadAllKeys();
      if (mounted) setState(() => _state = _EncState.done);
      await _loadStatus();
    } on InvalidPassphraseException catch (_) {
      if (mounted) setState(() { _state = _EncState.needsKey; _error = 'Falscher Schlüssel.'; });
    } on FormatException catch (_) {
      if (mounted) setState(() { _state = _EncState.needsKey; _error = 'Ungültiges Format.'; });
    } catch (e) {
      if (mounted) setState(() { _state = _EncState.error; _error = e.toString().split('\n').first; });
    }
  }

  Future<void> _resetSecurity() async {
    final confirm = await showDialog<bool>(
      context: context,
      builder: (ctx) {
        final pt = widget.pt;
        return AlertDialog(
          backgroundColor: pt.bg2,
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(pt.rXl),
              side: BorderSide(color: pt.border)),
          title: Text('Sicherheit zurücksetzen?',
              style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
          content: Text(
            'Dies löscht deine aktuellen Cross-Signing-Schlüssel und den Online-Schlüssel-Backup. Nicht gesicherte Sitzungen können Nachrichten möglicherweise nicht mehr entschlüsseln.',
            style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(ctx, false),
              child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)),
            ),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                  backgroundColor: pt.danger, foregroundColor: Colors.white, elevation: 0,
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
              onPressed: () => Navigator.pop(ctx, true),
              child: const Text('Zurücksetzen'),
            ),
          ],
        );
      },
    );
    if (confirm != true || !mounted) return;
    setState(() { _state = _EncState.loading; _error = null; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      final enc = client.encryption;
      if (enc == null) throw Exception('Verschlüsselung nicht verfügbar');
      await client.roomsLoading;
      await client.accountDataLoading;
      await client.userDeviceKeysLoading;
      final completer = Completer<void>();
      enc.bootstrap(onUpdate: (bs) {
        if (!mounted) return;
        switch (bs.state) {
          case BootstrapState.askWipeSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.wipeSsss(true));
          case BootstrapState.askBadSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.ignoreBadSecrets(true));
          case BootstrapState.askUseExistingSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.useExistingSsss(false));
          case BootstrapState.askNewSsss:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.newSsss());
          case BootstrapState.askWipeCrossSigning:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.wipeCrossSigning(true));
          case BootstrapState.askSetupCrossSigning:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.askSetupCrossSigning(
              setupMasterKey: true, setupSelfSigningKey: true, setupUserSigningKey: true));
          case BootstrapState.askWipeOnlineKeyBackup:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.wipeOnlineKeyBackup(true));
          case BootstrapState.askSetupOnlineKeyBackup:
            WidgetsBinding.instance.addPostFrameCallback((_) => bs.askSetupOnlineKeyBackup(true));
          case BootstrapState.done:
            if (!completer.isCompleted) completer.complete();
          case BootstrapState.error:
            if (!completer.isCompleted) completer.completeError('Bootstrap-Fehler');
          default:
            break;
        }
      });
      await completer.future.timeout(const Duration(seconds: 60));
      if (mounted) setState(() => _state = _EncState.done);
      await _loadStatus();
    } catch (e) {
      if (mounted) setState(() { _state = _EncState.error; _error = e.toString().split('\n').first; });
    }
  }

  Future<String?> _showPassphraseDialog({required bool forExport}) {
    return showDialog<String>(
      context: context,
      builder: (ctx) => _PassphraseDialog(pt: widget.pt, forExport: forExport),
    );
  }

  Future<void> _exportKeys() async {
    final passphrase = await _showPassphraseDialog(forExport: true);
    if (passphrase == null || !mounted) return;

    setState(() { _exportLoading = true; _keyFileMsg = null; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      final db = client.database;

      final stored = await db.getAllInboundGroupSessions();
      final sessions = <Map<String, dynamic>>[];

      for (final s in stored) {
        try {
          final content = jsonDecode(s.content) as Map<String, dynamic>;
          final sessionKey = content['session_key'] as String?;
          if (sessionKey == null || sessionKey.isEmpty) continue;

          final claimedKeys = s.senderClaimedKeys.isNotEmpty
              ? (jsonDecode(s.senderClaimedKeys) as Map<String, dynamic>)
                  .map((k, v) => MapEntry(k, v.toString()))
              : <String, String>{};

          sessions.add({
            'algorithm': 'm.megolm.v1.aes-sha2',
            'forwarding_curve25519_key_chain': <dynamic>[],
            'room_id': s.roomId,
            'sender_key': s.senderKey,
            'sender_claimed_keys': claimedKeys,
            'session_id': s.sessionId,
            'session_key': sessionKey,
          });
        } catch (_) { continue; }
      }

      if (sessions.isEmpty) throw Exception('Keine exportierbaren Sitzungsschlüssel gefunden.');

      final encrypted = await compute(_encryptMegolmKeys, {
        'passphrase': passphrase,
        'sessions': sessions,
      });

      final dir = await getApplicationDocumentsDirectory();
      final now = DateTime.now();
      final stamp = '${now.year}'
          '${now.month.toString().padLeft(2, '0')}'
          '${now.day.toString().padLeft(2, '0')}';
      final file = File('${dir.path}/pyramid_keys_$stamp.txt');
      await file.writeAsString(encrypted);

      if (mounted) {
        setState(() {
          _exportLoading = false;
          _keyFileMsgIsError = false;
          _keyFileMsg = '${sessions.length} Schlüssel exportiert → ${file.path}';
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _exportLoading = false;
          _keyFileMsgIsError = true;
          _keyFileMsg = 'Export fehlgeschlagen: ${e.toString().split('\n').first}';
        });
      }
    }
  }

  Future<void> _uploadDiagnostics() async {
    setState(() { _diagUploading = true; _diagMsg = null; });
    try {
      final notifier = ref.read(e2eeDiagnosticsProvider.notifier);
      final msg = await notifier.upload(
        serverUrl: 'https://dashboard.steggi-matrix.work',
        token: 'J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI',
      );
      if (mounted) setState(() { _diagUploading = false; _diagMsgIsError = false; _diagMsg = msg; });
    } catch (e) {
      if (mounted) setState(() { _diagUploading = false; _diagMsgIsError = true; _diagMsg = e.toString(); });
    }
  }

  Future<void> _importKeys() async {
    final result = await FilePicker.platform.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['txt', 'key', 'keys'],
      allowMultiple: false,
    );
    if (result == null || result.files.isEmpty || result.files.first.path == null) return;
    if (!mounted) return;

    final passphrase = await _showPassphraseDialog(forExport: false);
    if (passphrase == null || !mounted) return;

    setState(() { _importLoading = true; _keyFileMsg = null; });
    try {
      final fileContent = await File(result.files.first.path!).readAsString();
      final trimmed = fileContent.trim();

      if (!trimmed.startsWith(_kMegolmHeader) || !trimmed.endsWith(_kMegolmFooter)) {
        throw Exception('Ungültiges Dateiformat – kein Megolm-Schlüsseldatei-Header.');
      }

      final lines = trimmed.split('\n');
      // Strip \r so Windows line endings (\r\n) don't corrupt the base64 string.
      final b64 = lines.skip(1).take(lines.length - 2).join('').replaceAll(RegExp(r'\s'), '');
      final padded = b64 + '=' * ((4 - b64.length % 4) % 4);
      final dataBytes = base64.decode(padded);

      final sessions = await compute(_decryptMegolmKeys, {
        'passphrase': passphrase,
        'data': dataBytes.toList(),
      });

      final client = await ref.read(matrixClientProvider.future);
      final km = client.encryption?.keyManager;
      if (km == null) throw Exception('Verschlüsselung nicht initialisiert.');

      var imported = 0;
      for (final raw in sessions) {
        try {
          final session = raw as Map<String, dynamic>;
          final roomId = session['room_id'] as String?;
          final sessionId = session['session_id'] as String?;
          final senderKey = session['sender_key'] as String?;
          if (roomId == null || sessionId == null || senderKey == null) continue;

          final claimedKeys = (session['sender_claimed_keys'] as Map?)
              ?.map((k, v) => MapEntry(k.toString(), v.toString())) ??
              <String, String>{};

          await km.setInboundGroupSession(
            roomId, sessionId, senderKey,
            Map<String, dynamic>.from(session),
            forwarded: true,
            senderClaimedKeys: claimedKeys,
          );
          imported++;
        } catch (_) { continue; }
      }

      if (mounted) {
        setState(() {
          _importLoading = false;
          _keyFileMsgIsError = false;
          _keyFileMsg = '$imported von ${sessions.length} Schlüssel importiert.';
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _importLoading = false;
          _keyFileMsgIsError = true;
          _keyFileMsg = 'Import fehlgeschlagen: ${e.toString().split('\n').first}';
        });
      }
    }
  }

  Future<void> _changeRecoveryKey() async {
    final changed = await showDialog<bool>(
      context: context,
      barrierDismissible: false,
      builder: (ctx) => _ChangeRecoveryKeyDialog(pt: widget.pt),
    );
    if (changed == true && mounted) {
      await _loadStatus();
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Verschlüsselung',
              subtitle: 'Ende-zu-Ende-Verschlüsselung & Sicherheit verwalten.',
              pt: pt),

          // ── Status overview ───────────────────────────────────────────────
          if (_statusLoaded) ...[
            _SettingsGroup(title: 'Sicherheitsstatus', pt: pt, children: [
              _SettingsRow(
                title: 'Cross-Signing',
                desc: _crossSigningEnabled == true
                    ? (_crossSigningCached == true ? 'Eingerichtet & verifiziert' : 'Eingerichtet, Schlüssel nicht im Cache')
                    : 'Nicht eingerichtet',
                pt: pt,
                control: Icon(
                  _crossSigningEnabled == true && _crossSigningCached == true
                      ? Icons.verified_rounded
                      : (_crossSigningEnabled == true ? Icons.warning_amber_rounded : Icons.cancel_outlined),
                  size: 16,
                  color: _crossSigningEnabled == true && _crossSigningCached == true
                      ? PyramidColors.success
                      : (_crossSigningEnabled == true ? pt.away : pt.danger),
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Online-Schlüsselbackup',
                desc: _keyBackupEnabled == true ? 'Aktiv – Sitzungsschlüssel gesichert' : 'Nicht aktiviert',
                pt: pt,
                control: Icon(
                  _keyBackupEnabled == true ? Icons.cloud_done_rounded : Icons.cloud_off_rounded,
                  size: 16,
                  color: _keyBackupEnabled == true ? PyramidColors.success : pt.fgDim,
                ),
              ),
            ]),
            const SizedBox(height: 20),
            _FingerprintGroup(pt: pt),
            const SizedBox(height: 20),
          ],

          // ── Key recovery / unlock ─────────────────────────────────────────
          _SettingsGroup(title: 'Wiederherstellung', pt: pt, children: [
            _SettingsRow(
              title: 'Schlüssel wiederherstellen',
              desc: 'Nachrichten mit dem Wiederherstellungsschlüssel entschlüsseln',
              pt: pt,
              showArrow: true,
              onTap: _state == _EncState.loading ? null : _start,
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Wiederherstellungsschlüssel ändern',
              desc: 'Neuen Schlüssel generieren, notieren und aktivieren',
              pt: pt,
              showArrow: true,
              onTap: _state == _EncState.loading ? null : _changeRecoveryKey,
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Sicherheit neu einrichten',
              desc: 'Cross-Signing & Backup zurücksetzen und neu aufsetzen',
              pt: pt,
              showArrow: true,
              destructive: true,
              onTap: _state == _EncState.loading ? null : _resetSecurity,
            ),
          ]),
          const SizedBox(height: 20),

          // ── Key file export / import ───────────────────────────────────────
          _SettingsGroup(title: 'Schlüsseldatei', pt: pt, children: [
            _SettingsRow(
              title: 'Schlüssel exportieren',
              desc: 'Sitzungsschlüssel als verschlüsselte Datei sichern (kompatibel mit Element)',
              pt: pt,
              showArrow: !_exportLoading,
              control: _exportLoading
                  ? const SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator.adaptive(strokeWidth: 2),
                    )
                  : null,
              onTap: (_exportLoading || _importLoading) ? null : _exportKeys,
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Schlüssel importieren',
              desc: 'Sitzungsschlüssel aus exportierter Datei laden',
              pt: pt,
              showArrow: !_importLoading,
              control: _importLoading
                  ? const SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator.adaptive(strokeWidth: 2),
                    )
                  : null,
              onTap: (_exportLoading || _importLoading) ? null : _importKeys,
            ),
          ]),

          if (_keyFileMsg != null) ...[
            const SizedBox(height: 12),
            _StatusCard(
              pt: pt,
              icon: _keyFileMsgIsError
                  ? Icons.error_outline_rounded
                  : Icons.check_circle_outline_rounded,
              color: _keyFileMsgIsError ? pt.danger : PyramidColors.success,
              text: _keyFileMsg!,
            ),
          ],
          const SizedBox(height: 20),

          // ── E2EE Diagnostics ──────────────────────────────────────────────
          Consumer(builder: (context, ref, _) {
            final count = ref.watch(e2eeDiagnosticsProvider).length;
            return _SettingsGroup(title: 'Diagnose', pt: pt, children: [
              _SettingsRow(
                title: 'Entschlüsselungsfehler hochladen',
                desc: count == 0
                    ? 'Keine Fehler gesammelt'
                    : '$count Fehler gesammelt – zum Server hochladen',
                pt: pt,
                showArrow: !_diagUploading && count > 0,
                control: _diagUploading
                    ? const SizedBox(
                        width: 16,
                        height: 16,
                        child: CircularProgressIndicator.adaptive(strokeWidth: 2),
                      )
                    : count > 0
                        ? Container(
                            padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
                            decoration: BoxDecoration(
                              color: pt.danger.withAlpha(30),
                              borderRadius: BorderRadius.circular(10),
                            ),
                            child: Text('$count', style: TextStyle(color: pt.danger, fontSize: 12)),
                          )
                        : null,
                onTap: (_diagUploading || count == 0) ? null : _uploadDiagnostics,
              ),
            ]);
          }),

          if (_diagMsg != null) ...[
            const SizedBox(height: 12),
            _StatusCard(
              pt: pt,
              icon: _diagMsgIsError
                  ? Icons.error_outline_rounded
                  : Icons.cloud_done_rounded,
              color: _diagMsgIsError ? pt.danger : PyramidColors.success,
              text: _diagMsg!,
            ),
          ],
          const SizedBox(height: 20),

          // ── Inline state UI ───────────────────────────────────────────────
          if (_state == _EncState.loading)
            const Center(child: Padding(padding: EdgeInsets.all(24), child: CircularProgressIndicator.adaptive())),

          if (_state == _EncState.done) ...[
            _StatusCard(
                pt: pt,
                icon: Icons.check_circle_outline_rounded,
                color: PyramidColors.success,
                text: 'Alle Schlüssel importiert. Nachrichten werden entschlüsselt.'),
            const SizedBox(height: 12),
          ],

          if (_state == _EncState.needsKey || _state == _EncState.unlocking) ...[
            _StatusCard(
                pt: pt,
                icon: Icons.lock_outline_rounded,
                color: pt.accent,
                text: 'Wiederherstellungsschlüssel erforderlich.'),
            const SizedBox(height: 16),
            TextField(
              controller: _keyCtrl,
              readOnly: _state == _EncState.unlocking,
              autofocus: true,
              autocorrect: false,
              style: TextStyle(color: pt.fg, fontSize: 13, fontFamily: 'monospace'),
              decoration: InputDecoration(
                hintText: 'EsXX XXXX XXXX XXXX …',
                hintStyle: TextStyle(color: pt.fgDim, fontSize: 12),
                labelText: 'Wiederherstellungsschlüssel',
                labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
                errorText: _error,
                errorMaxLines: 2,
                filled: true,
                fillColor: pt.bg2,
                prefixIcon: Icon(Icons.vpn_key_outlined, color: pt.fgDim, size: 18),
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
                enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
                focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
                isDense: true,
                contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
              ),
              cursorColor: pt.accent,
              onSubmitted: (_) => _unlock(),
            ),
            const SizedBox(height: 12),
            SizedBox(
              width: double.infinity,
              child: _PrimaryBtn(
                label: _state == _EncState.unlocking ? 'Entschlüssele…' : 'Nachrichten entschlüsseln',
                icon: Icons.lock_open_outlined,
                pt: pt,
                loading: _state == _EncState.unlocking,
                onPressed: _unlock,
              ),
            ),
            const SizedBox(height: 12),
          ],

          if (_state == _EncState.error) ...[
            _StatusCard(
                pt: pt, icon: Icons.error_outline_rounded, color: PyramidColors.danger,
                text: _error ?? 'Unbekannter Fehler.'),
            const SizedBox(height: 12),
            OutlinedButton.icon(
              style: OutlinedButton.styleFrom(
                foregroundColor: pt.fg,
                side: BorderSide(color: pt.border),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
              ),
              onPressed: _start,
              icon: const Icon(Icons.refresh_rounded, size: 16),
              label: const Text('Erneut versuchen'),
            ),
          ],

          if (_state == _EncState.idle && !_statusLoaded)
            _StatusCard(
                pt: pt,
                icon: Icons.shield_outlined,
                color: pt.fgMuted,
                text: 'Lade Sicherheitsstatus…'),
        ],
      ),
    );
  }
}

class _ChangeRecoveryKeyDialog extends ConsumerStatefulWidget {
  final PyramidTheme pt;
  const _ChangeRecoveryKeyDialog({required this.pt});

  @override
  ConsumerState<_ChangeRecoveryKeyDialog> createState() =>
      _ChangeRecoveryKeyDialogState();
}

class _ChangeRecoveryKeyDialogState
    extends ConsumerState<_ChangeRecoveryKeyDialog> {
  _RecoveryStep _step = _RecoveryStep.loading;
  String? _generatedKey;
  String? _error;
  bool _copied = false;
  // Completer signals when bootstrap reaches `done`
  final _doneCompleter = Completer<void>();

  @override
  void initState() {
    super.initState();
    _startBootstrap();
  }

  Future<void> _startBootstrap() async {
    try {
      final client = await ref.read(matrixClientProvider.future);
      final enc = client.encryption;
      if (enc == null) throw Exception('Verschlüsselung nicht verfügbar.');
      await client.roomsLoading;
      await client.accountDataLoading;
      await client.userDeviceKeysLoading;
      enc.bootstrap(onUpdate: (bs) {
        if (!mounted) return;
        _onBootstrapUpdate(bs);
      });
    } catch (e) {
      if (mounted) {
        setState(() {
          _step = _RecoveryStep.error;
          _error = e.toString().split('\n').first;
        });
      }
    }
  }

  // After newSsss() calls checkCrossSigning(), the bootstrap immediately
  // transitions to askWipeCrossSigning (or askSetupCrossSigning). All post-
  // newSsss states must therefore auto-advance unconditionally — guarding on
  // _step would leave them stuck.
  void _onBootstrapUpdate(Bootstrap bs) {
    switch (bs.state) {
      case BootstrapState.askWipeSsss:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askWipeSsss) bs.wipeSsss(true);
        });
      case BootstrapState.askBadSsss:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askBadSsss) bs.ignoreBadSecrets(true);
        });
      case BootstrapState.askUseExistingSsss:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askUseExistingSsss) bs.useExistingSsss(false);
        });
      case BootstrapState.askUnlockSsss:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askUnlockSsss) bs.unlockedSsss();
        });
      case BootstrapState.askNewSsss:
        // Only generate once
        if (_step == _RecoveryStep.loading) {
          WidgetsBinding.instance.addPostFrameCallback((_) async {
            if (bs.state != BootstrapState.askNewSsss) return;
            try {
              await bs.newSsss();
              // newSsss() internally calls checkCrossSigning() which already
              // transitions to askWipeCrossSigning; capture key after it returns.
              final key = bs.newSsssKey?.recoveryKey;
              if (mounted) setState(() { _generatedKey = key; _step = _RecoveryStep.showKey; });
            } catch (e) {
              if (mounted) setState(() { _step = _RecoveryStep.error; _error = e.toString().split('\n').first; });
            }
          });
        }
      case BootstrapState.askWipeCrossSigning:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askWipeCrossSigning) bs.wipeCrossSigning(false);
        });
      case BootstrapState.askSetupCrossSigning:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askSetupCrossSigning) {
            bs.askSetupCrossSigning(
              setupMasterKey: true,
              setupSelfSigningKey: true,
              setupUserSigningKey: true,
            );
          }
        });
      case BootstrapState.askWipeOnlineKeyBackup:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askWipeOnlineKeyBackup) bs.wipeOnlineKeyBackup(true);
        });
      case BootstrapState.askSetupOnlineKeyBackup:
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (bs.state == BootstrapState.askSetupOnlineKeyBackup) bs.askSetupOnlineKeyBackup(true);
        });
      case BootstrapState.done:
        if (!_doneCompleter.isCompleted) _doneCompleter.complete();
        // Only auto-advance to done if user already clicked through (applying).
        // If still on showKey/confirmKey, the user must finish reading first.
        if (mounted && _step == _RecoveryStep.applying) {
          setState(() => _step = _RecoveryStep.done);
        }
      case BootstrapState.error:
        if (!_doneCompleter.isCompleted) _doneCompleter.completeError('Bootstrap-Fehler');
        if (mounted) setState(() { _step = _RecoveryStep.error; _error = 'Fehler bei der Schlüsselgenerierung.'; });
      default:
        break;
    }
  }

  // Called when user taps "Schlüssel gespeichert – weiter" on the showKey screen.
  void _onKeySaved() {
    setState(() => _step = _RecoveryStep.confirmKey);
  }

  // Called when user submits the correct key confirmation.
  // Bootstrap may still be running (cross-signing + key backup); wait for done.
  Future<void> _onKeyConfirmed() async {
    setState(() => _step = _RecoveryStep.applying);
    try {
      await _doneCompleter.future.timeout(const Duration(seconds: 90));
      if (mounted) setState(() => _step = _RecoveryStep.done);
    } catch (e) {
      if (mounted) setState(() { _step = _RecoveryStep.error; _error = 'Timeout: Bootstrap abgebrochen.'; });
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    final canClose = _step != _RecoveryStep.loading && _step != _RecoveryStep.applying && _step != _RecoveryStep.confirmKey;

    return Dialog(
      backgroundColor: pt.bg2,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(pt.rXl),
        side: BorderSide(color: pt.border),
      ),
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 440, minWidth: 320),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(20, 16, 12, 12),
              child: Row(
                children: [
                  Icon(Icons.key_rounded, size: 18, color: pt.accent),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Text(
                      'Wiederherstellungsschlüssel',
                      style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600),
                    ),
                  ),
                  if (canClose)
                    IconButton(
                      icon: Icon(Icons.close_rounded, color: pt.fgDim, size: 16),
                      onPressed: () => Navigator.of(context).pop(_step == _RecoveryStep.done),
                      padding: EdgeInsets.zero,
                      constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
                    ),
                ],
              ),
            ),
            Divider(color: pt.border, height: 1),
            Padding(
              padding: const EdgeInsets.all(20),
              child: _buildBody(pt),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBody(PyramidTheme pt) {
    return switch (_step) {
      _RecoveryStep.loading => const Center(
        child: Padding(padding: EdgeInsets.all(24), child: CircularProgressIndicator.adaptive()),
      ),
      _RecoveryStep.showKey => _ShowKeyBody(
        pt: pt,
        generatedKey: _generatedKey ?? '',
        copied: _copied,
        onCopy: () {
          Clipboard.setData(ClipboardData(text: _generatedKey ?? ''));
          setState(() => _copied = true);
        },
        onKeySaved: _onKeySaved,
      ),
      _RecoveryStep.confirmKey => _ConfirmKeyBody(
        pt: pt,
        expectedKey: _generatedKey ?? '',
        onConfirmed: _onKeyConfirmed,
      ),
      _RecoveryStep.applying => Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const CircularProgressIndicator.adaptive(),
          const SizedBox(height: 16),
          Text(
            'Schlüssel wird aktiviert…\nCross-Signing und Backup werden eingerichtet.',
            textAlign: TextAlign.center,
            style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5),
          ),
        ],
      ),
      _RecoveryStep.done => Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.check_circle_outline_rounded, color: PyramidColors.success, size: 52),
          const SizedBox(height: 12),
          Text(
            'Wiederherstellungsschlüssel wurde erfolgreich geändert.',
            textAlign: TextAlign.center,
            style: TextStyle(color: pt.fg, fontSize: 14, fontWeight: FontWeight.w500),
          ),
          const SizedBox(height: 6),
          Text(
            'Bewahre deinen Schlüssel sicher auf – du brauchst ihn bei Geräteverlust.',
            textAlign: TextAlign.center,
            style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.4),
          ),
          const SizedBox(height: 20),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton(
              style: ElevatedButton.styleFrom(
                backgroundColor: pt.accent, foregroundColor: pt.accentFg,
                elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
              ),
              onPressed: () => Navigator.of(context).pop(true),
              child: const Text('Fertig'),
            ),
          ),
        ],
      ),
      _RecoveryStep.error => Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.error_outline_rounded, color: PyramidColors.danger, size: 48),
          const SizedBox(height: 12),
          Text(
            _error ?? 'Ein unbekannter Fehler ist aufgetreten.',
            textAlign: TextAlign.center,
            style: TextStyle(color: pt.fgMuted, fontSize: 13),
          ),
          const SizedBox(height: 20),
          SizedBox(
            width: double.infinity,
            child: OutlinedButton(
              style: OutlinedButton.styleFrom(
                foregroundColor: pt.fg, side: BorderSide(color: pt.border),
                padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
              ),
              onPressed: () => Navigator.of(context).pop(false),
              child: const Text('Schließen'),
            ),
          ),
        ],
      ),
    };
  }
}

class _ShowKeyBody extends StatelessWidget {
  final PyramidTheme pt;
  final String generatedKey;
  final bool copied;
  final VoidCallback onCopy;
  final VoidCallback onKeySaved;

  const _ShowKeyBody({
    required this.pt,
    required this.generatedKey,
    required this.copied,
    required this.onCopy,
    required this.onKeySaved,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'Dein neuer Wiederherstellungsschlüssel',
          style: TextStyle(color: pt.fg, fontSize: 14, fontWeight: FontWeight.w600),
        ),
        const SizedBox(height: 8),
        Text(
          'Speichere diesen Schlüssel sicher – du brauchst ihn, wenn du den Zugang zu allen Geräten verlierst.',
          style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5),
        ),
        const SizedBox(height: 16),
        Container(
          decoration: BoxDecoration(
            color: pt.bg3,
            borderRadius: BorderRadius.circular(8),
            border: Border.all(color: pt.border),
          ),
          child: Row(
            children: [
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
                  child: SelectableText(
                    generatedKey,
                    style: TextStyle(
                      color: pt.accent, fontSize: 13,
                      fontFamily: 'monospace', letterSpacing: 0.5, height: 1.6,
                    ),
                  ),
                ),
              ),
              IconButton(
                onPressed: onCopy,
                icon: Icon(
                  copied ? Icons.check_rounded : Icons.copy_rounded,
                  size: 18,
                  color: copied ? PyramidColors.success : pt.fgDim,
                ),
                tooltip: copied ? 'Kopiert!' : 'In Zwischenablage kopieren',
              ),
            ],
          ),
        ),
        const SizedBox(height: 12),
        Container(
          padding: const EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: Colors.orange.withAlpha(28),
            borderRadius: BorderRadius.circular(8),
            border: Border.all(color: Colors.orange.withAlpha(80)),
          ),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Icon(Icons.warning_amber_rounded, size: 16, color: Colors.orange.shade700),
              const SizedBox(width: 8),
              Expanded(
                child: Text(
                  'Schreibe diesen Schlüssel auf oder speichere ihn in einem Passwort-Manager. Er kann nicht wiederhergestellt werden.',
                  style: TextStyle(color: Colors.orange.shade800, fontSize: 12, height: 1.4),
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 20),
        SizedBox(
          width: double.infinity,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: pt.accent, foregroundColor: pt.accentFg,
              elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
            ),
            onPressed: onKeySaved,
            child: const Text('Schlüssel gespeichert – weiter'),
          ),
        ),
      ],
    );
  }
}

class _ConfirmKeyBody extends StatefulWidget {
  final PyramidTheme pt;
  final String expectedKey;
  final VoidCallback onConfirmed;

  const _ConfirmKeyBody({
    required this.pt,
    required this.expectedKey,
    required this.onConfirmed,
  });

  @override
  State<_ConfirmKeyBody> createState() => _ConfirmKeyBodyState();
}

class _ConfirmKeyBodyState extends State<_ConfirmKeyBody> {
  final _controller = TextEditingController();
  bool _mismatch = false;

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _submit() {
    final entered = _controller.text.trim();
    if (entered == widget.expectedKey) {
      widget.onConfirmed();
    } else {
      setState(() => _mismatch = true);
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'Schlüssel bestätigen',
          style: TextStyle(color: pt.fg, fontSize: 14, fontWeight: FontWeight.w600),
        ),
        const SizedBox(height: 8),
        Text(
          'Gib deinen neuen Wiederherstellungsschlüssel zur Bestätigung ein.',
          style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5),
        ),
        const SizedBox(height: 16),
        TextField(
          controller: _controller,
          autofocus: true,
          style: TextStyle(
            color: pt.fg, fontSize: 13,
            fontFamily: 'monospace', letterSpacing: 0.4,
          ),
          decoration: InputDecoration(
            hintText: 'EsTX XXXX XXXX …',
            hintStyle: TextStyle(color: pt.fgDim, fontSize: 13),
            filled: true,
            fillColor: pt.bg3,
            contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(color: pt.border),
            ),
            enabledBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(color: _mismatch ? PyramidColors.danger : pt.border),
            ),
            focusedBorder: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
              borderSide: BorderSide(color: _mismatch ? PyramidColors.danger : pt.accent, width: 1.5),
            ),
            errorText: _mismatch ? 'Schlüssel stimmt nicht überein.' : null,
          ),
          onChanged: (_) { if (_mismatch) setState(() => _mismatch = false); },
          onSubmitted: (_) => _submit(),
        ),
        const SizedBox(height: 20),
        SizedBox(
          width: double.infinity,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: pt.accent, foregroundColor: pt.accentFg,
              elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
            ),
            onPressed: _submit,
            child: const Text('Aktivieren'),
          ),
        ),
      ],
    );
  }
}

class _FingerprintGroup extends ConsumerWidget {
  final PyramidTheme pt;
  const _FingerprintGroup({required this.pt});

  static String _fmt(String key) {
    final clean = key.replaceAll(RegExp(r'\s'), '');
    final groups = <String>[];
    for (var i = 0; i < clean.length; i += 4) {
      groups.add(clean.substring(i, (i + 4).clamp(0, clean.length)));
    }
    return groups.join(' ');
  }

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final clientAsync = ref.watch(matrixClientProvider);
    final client = clientAsync.valueOrNull;
    if (client == null) return const SizedBox.shrink();

    final fingerprint = _fmt(client.fingerprintKey);
    final deviceId = client.deviceID ?? '';

    return _SettingsGroup(title: 'Dieses Gerät', pt: pt, children: [
      _SettingsRow(
        title: 'Geräte-ID',
        desc: deviceId,
        pt: pt,
        control: IconButton(
          icon: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim),
          tooltip: 'Kopieren',
          padding: EdgeInsets.zero,
          constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
          onPressed: () => Clipboard.setData(ClipboardData(text: deviceId)),
        ),
      ),
      _Divider(pt: pt),
      Padding(
        padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text('Ed25519-Fingerabdruck',
                    style: TextStyle(color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500)),
                const Spacer(),
                IconButton(
                  icon: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim),
                  tooltip: 'Fingerabdruck kopieren',
                  padding: EdgeInsets.zero,
                  constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
                  onPressed: () => Clipboard.setData(ClipboardData(text: client.fingerprintKey)),
                ),
              ],
            ),
            const SizedBox(height: 4),
            SelectableText(
              fingerprint,
              style: TextStyle(
                color: pt.fgMuted, fontSize: 11,
                fontFamily: 'monospace', letterSpacing: 0.5, height: 1.6,
              ),
            ),
          ],
        ),
      ),
    ]);
  }
}

class _PassphraseDialog extends StatefulWidget {
  final PyramidTheme pt;
  final bool forExport;
  const _PassphraseDialog({required this.pt, required this.forExport});

  @override
  State<_PassphraseDialog> createState() => _PassphraseDialogState();
}

class _PassphraseDialogState extends State<_PassphraseDialog> {
  final _ctrl1 = TextEditingController();
  final _ctrl2 = TextEditingController();
  bool _obscure = true;
  String? _error;

  @override
  void dispose() {
    _ctrl1.dispose();
    _ctrl2.dispose();
    super.dispose();
  }

  void _submit() {
    final pass = _ctrl1.text.trim();
    if (pass.isEmpty) {
      setState(() => _error = 'Bitte ein Passwort eingeben.');
      return;
    }
    if (widget.forExport && pass != _ctrl2.text.trim()) {
      setState(() => _error = 'Passwörter stimmen nicht überein.');
      return;
    }
    Navigator.pop(context, pass);
  }

  InputDecoration _inputDeco(String label, PyramidTheme pt) => InputDecoration(
        labelText: label,
        labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
        errorText: _error,
        filled: true,
        fillColor: pt.bg3,
        prefixIcon: Icon(Icons.lock_outline_rounded, color: pt.fgDim, size: 18),
        border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
        enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
        focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
        isDense: true,
        errorMaxLines: 2,
        suffixIcon: IconButton(
          icon: Icon(
            _obscure ? Icons.visibility_off_outlined : Icons.visibility_outlined,
            size: 18,
            color: pt.fgDim,
          ),
          onPressed: () => setState(() => _obscure = !_obscure),
        ),
      );

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return AlertDialog(
      backgroundColor: pt.bg2,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(pt.rXl),
        side: BorderSide(color: pt.border),
      ),
      title: Text(
        widget.forExport ? 'Schlüssel exportieren' : 'Schlüssel importieren',
        style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700),
      ),
      content: SizedBox(
        width: 340,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              widget.forExport
                  ? 'Wähle ein Passwort zum Verschlüsseln der Schlüsseldatei.'
                  : 'Gib das Passwort ein, mit dem die Datei verschlüsselt wurde.',
              style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5),
            ),
            const SizedBox(height: 16),
            TextField(
              controller: _ctrl1,
              obscureText: _obscure,
              autofocus: true,
              style: TextStyle(color: pt.fg, fontSize: 13),
              decoration: _inputDeco('Passwort', pt).copyWith(
                // Only show error on confirm field if export, else here
                errorText: widget.forExport ? null : _error,
              ),
              cursorColor: pt.accent,
              onSubmitted: widget.forExport ? null : (_) => _submit(),
            ),
            if (widget.forExport) ...[
              const SizedBox(height: 12),
              TextField(
                controller: _ctrl2,
                obscureText: _obscure,
                style: TextStyle(color: pt.fg, fontSize: 13),
                decoration: _inputDeco('Passwort bestätigen', pt),
                cursorColor: pt.accent,
                onSubmitted: (_) => _submit(),
              ),
            ],
          ],
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)),
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(
            backgroundColor: pt.accent,
            foregroundColor: pt.accentFg,
            elevation: 0,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          ),
          onPressed: _submit,
          child: Text(widget.forExport ? 'Exportieren' : 'Importieren'),
        ),
      ],
    );
  }
}
