import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:crypto/crypto.dart' as mcrypto;
import 'package:pyramid/core/fcm_push_service.dart';
import 'package:pyramid/core/notification_service.dart' show openBatteryOptimizationSettings;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart' show compute;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:http/http.dart' as http;
import 'package:matrix/encryption/ssss.dart';
import 'package:matrix/encryption/utils/bootstrap.dart';
import 'package:matrix/encryption/utils/key_verification.dart' show KeyVerification, KeyVerificationEmoji, KeyVerificationState;
import 'package:matrix/matrix.dart';
import 'package:pretty_qr_code/pretty_qr_code.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pointycastle/export.dart' as pc;
import 'package:pyramid/core/app_state.dart';
import 'package:pyramid/core/e2ee_diagnostics.dart';
import 'package:pyramid/core/livekit_call_manager.dart';
import 'package:pyramid/core/matrix_client.dart';
import 'package:pyramid/core/voip_manager.dart';
import 'package:pyramid/core/settings_prefs.dart';
import 'package:pyramid/core/theme.dart';
import 'package:pyramid/core/update_checker.dart';
import 'package:pyramid/widgets/update_download_dialog.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:pyramid/widgets/banner_crop_dialog.dart';
import 'package:pyramid/widgets/hover_region.dart';
import 'package:pyramid/widgets/mxc_image.dart';
import 'package:pyramid/widgets/pyramid_loader.dart';
import 'package:shared_preferences/shared_preferences.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>;
}

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

class SettingsModal extends ConsumerStatefulWidget {
  final VoidCallback onClose;
  const SettingsModal({super.key, required this.onClose});

  @override
  ConsumerState<SettingsModal> createState() => _SettingsModalState();
}

class _SettingsModalState extends ConsumerState<SettingsModal> {
  // null = show the category list (mobile). On desktop it falls back to the
  // first section so content is always visible.
  String? _section;

  // Categories shared by the desktop sidebar and the mobile list/detail.
  static const List<(String, String, IconData)> _categories = [
    ('profile', 'Mein Profil', Icons.person_outline_rounded),
    ('notifications', 'Benachrichtigungen', Icons.notifications_none_rounded),
    ('appearance', 'Erscheinungsbild', Icons.palette_outlined),
    ('voice', 'Sprache & Video', Icons.headphones_rounded),
    ('keys', 'Tastaturkürzel', Icons.keyboard_rounded),
    ('privacy', 'Datenschutz', Icons.lock_outline_rounded),
    ('sessions', 'Sitzungen', Icons.devices_rounded),
    ('encryption', 'Verschlüsselung', Icons.shield_outlined),
    ('about', 'Über Pyramid', Icons.info_outline_rounded),
  ];

  String _titleFor(String id) =>
      _categories.firstWhere((c) => c.$1 == id, orElse: () => (id, id, Icons.settings)).$2;

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final isDark = ref.watch(themeModeProvider);
    final accentIdx = ref.watch(accentProvider);
    final radius = ref.watch(radiusProvider);
    final density = ref.watch(densityProvider);
    final motion = ref.watch(motionProvider);

    final size = MediaQuery.sizeOf(context);
    final isMobile = size.width < 700;

    return GestureDetector(
      onTap: widget.onClose,
      child: Container(
        color: Colors.black.withAlpha(150),
        child: Center(
          child: GestureDetector(
            onTap: () {},
            child: Container(
              constraints: const BoxConstraints(maxWidth: 820, maxHeight: 660),
              width: isMobile ? size.width * 0.97 : 740,
              height: isMobile ? size.height * 0.92 : 560,
              decoration: BoxDecoration(
                color: pt.bg1,
                borderRadius: BorderRadius.circular(pt.rXl),
                border: Border.all(color: pt.border),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withAlpha(130),
                    blurRadius: 40,
                    offset: const Offset(0, 12),
                  ),
                ],
              ),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(pt.rXl),
                child: isMobile
                    ? _mobileLayout(pt, isDark, accentIdx, radius, density, motion)
                    : _desktopLayout(pt, isDark, accentIdx, radius, density, motion),
              ),
            ),
          ),
        ),
      ),
    );
  }

  // ── Desktop: sidebar + content side by side ──────────────────────────────
  Widget _desktopLayout(PyramidTheme pt, bool isDark, int accentIdx,
      double radius, double density, double motion) {
    final active = _section ?? 'appearance';
    return Row(
      children: [
        Container(
          width: 200,
          decoration: BoxDecoration(
            color: pt.bg2,
            border: Border(right: BorderSide(color: pt.border)),
          ),
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const SizedBox(height: 16),
                _NavSection('Account', pt),
                _NavItem('profile', 'Mein Profil', Icons.person_outline_rounded, active, pt, () => setState(() => _section = 'profile')),
                _NavItem('notifications', 'Benachrichtigungen', Icons.notifications_none_rounded, active, pt, () => setState(() => _section = 'notifications')),
                const SizedBox(height: 14),
                _NavSection('App', pt),
                _NavItem('appearance', 'Erscheinungsbild', Icons.palette_outlined, active, pt, () => setState(() => _section = 'appearance')),
                _NavItem('voice', 'Sprache & Video', Icons.headphones_rounded, active, pt, () => setState(() => _section = 'voice')),
                _NavItem('keys', 'Tastaturkürzel', Icons.keyboard_rounded, active, pt, () => setState(() => _section = 'keys')),
                const SizedBox(height: 14),
                _NavSection('Sicherheit', pt),
                _NavItem('privacy', 'Datenschutz', Icons.lock_outline_rounded, active, pt, () => setState(() => _section = 'privacy')),
                _NavItem('sessions', 'Sitzungen', Icons.devices_rounded, active, pt, () => setState(() => _section = 'sessions')),
                _NavItem('encryption', 'Verschlüsselung', Icons.shield_outlined, active, pt, () => setState(() => _section = 'encryption')),
                const SizedBox(height: 14),
                _NavItem('about', 'Über Pyramid', Icons.info_outline_rounded, active, pt, () => setState(() => _section = 'about')),
                const Divider(height: 24, indent: 12, endIndent: 12),
                _LogoutNavButton(pt: pt, isMobile: false, onClose: widget.onClose),
                const SizedBox(height: 16),
              ],
            ),
          ),
        ),
        Expanded(
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.all(28),
                child: _buildContent(pt, isDark, accentIdx, radius, density, motion, active),
              ),
              Positioned(
                top: 8,
                right: 8,
                child: PyrIconBtn(
                  icon: const Icon(Icons.close_rounded, size: 20),
                  tooltip: 'Schließen',
                  onPressed: widget.onClose,
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  // ── Mobile: full-width category list → detail with a back arrow ───────────
  Widget _mobileLayout(PyramidTheme pt, bool isDark, int accentIdx,
      double radius, double density, double motion) {
    final section = _section;
    if (section == null) {
      // Category list
      return Column(
        children: [
          _MobileHeader(title: 'Einstellungen', pt: pt, onClose: widget.onClose),
          Expanded(
            child: ListView(
              padding: const EdgeInsets.symmetric(vertical: 8),
              children: [
                for (final c in _categories)
                  _MobileCategoryTile(
                    label: c.$2,
                    icon: c.$3,
                    pt: pt,
                    onTap: () => setState(() => _section = c.$1),
                  ),
                const Divider(height: 24, indent: 16, endIndent: 16),
                _LogoutNavButton(pt: pt, isMobile: false, onClose: widget.onClose),
                const SizedBox(height: 16),
              ],
            ),
          ),
        ],
      );
    }
    // Detail view with a back arrow
    return Column(
      children: [
        _MobileHeader(
          title: _titleFor(section),
          pt: pt,
          onBack: () => setState(() => _section = null),
          onClose: widget.onClose,
        ),
        Expanded(
          child: Padding(
            padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
            child: _buildContent(pt, isDark, accentIdx, radius, density, motion, section),
          ),
        ),
      ],
    );
  }

  Widget _buildContent(PyramidTheme pt, bool isDark, int accentIdx,
      double radius, double density, double motion, String section) {
    return switch (section) {
      'appearance' => _AppearanceSection(
          pt: pt,
          isDark: isDark,
          accentIdx: accentIdx,
          radius: radius,
          density: density,
          motion: motion,
          ref: ref),
      'profile' => _ProfileSection(pt: pt),
      'notifications' => _NotificationsSection(pt: pt),
      'voice' => _VoiceSection(pt: pt),
      'keys' => _KeysSection(pt: pt),
      'privacy' => _PrivacySection(pt: pt),
      'sessions' => _SessionsSection(pt: pt),
      'encryption' => _EncryptionSection(pt: pt, ref: ref),
      'about' => _AboutSection(pt: pt),
      _ => _AppearanceSection(
          pt: pt,
          isDark: isDark,
          accentIdx: accentIdx,
          radius: radius,
          density: density,
          motion: motion,
          ref: ref),
    };
  }
}

// ── Mobile helpers ──────────────────────────────────────────────────────────

class _MobileHeader extends StatelessWidget {
  final String title;
  final PyramidTheme pt;
  final VoidCallback? onBack;
  final VoidCallback onClose;
  const _MobileHeader({required this.title, required this.pt, this.onBack, required this.onClose});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 52,
      padding: const EdgeInsets.symmetric(horizontal: 8),
      decoration: BoxDecoration(
        color: pt.bg2,
        border: Border(bottom: BorderSide(color: pt.border)),
      ),
      child: Row(
        children: [
          if (onBack != null)
            IconButton(
              onPressed: onBack,
              icon: Icon(Icons.arrow_back_rounded, size: 20, color: pt.fg),
              tooltip: 'Zurück',
            )
          else
            const SizedBox(width: 8),
          Expanded(
            child: Text(
              title,
              style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w600),
              overflow: TextOverflow.ellipsis,
            ),
          ),
          IconButton(
            onPressed: onClose,
            icon: Icon(Icons.close_rounded, size: 20, color: pt.fgMuted),
            tooltip: 'Schließen',
          ),
        ],
      ),
    );
  }
}

class _MobileCategoryTile extends StatelessWidget {
  final String label;
  final IconData icon;
  final PyramidTheme pt;
  final VoidCallback onTap;
  const _MobileCategoryTile({required this.label, required this.icon, required this.pt, required this.onTap});

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
        child: Row(
          children: [
            Icon(icon, size: 21, color: pt.fgMuted),
            const SizedBox(width: 16),
            Expanded(
              child: Text(label, style: TextStyle(color: pt.fg, fontSize: 15)),
            ),
            Icon(Icons.chevron_right_rounded, size: 20, color: pt.fgDim),
          ],
        ),
      ),
    );
  }
}

// ── Nav helpers ───────────────────────────────────────────────────────────────

class _NavSection extends StatelessWidget {
  final String title;
  final PyramidTheme pt;
  const _NavSection(this.title, this.pt);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(12, 0, 12, 4),
      child: Text(
        title.toUpperCase(),
        style: TextStyle(
          color: pt.fgDim,
          fontSize: 11,
          fontWeight: FontWeight.w600,
          letterSpacing: 0.04 * 11,
        ),
      ),
    );
  }
}

class _NavItem extends StatefulWidget {
  final String id;
  final String label;
  final IconData icon;
  final String active;
  final PyramidTheme pt;
  final VoidCallback onTap;

  const _NavItem(this.id, this.label, this.icon, this.active, this.pt, this.onTap);

  @override
  State<_NavItem> createState() => _NavItemState();
}

class _NavItemState extends State<_NavItem> {
  bool _hovered = false;

  @override
  Widget build(BuildContext context) {
    final isActive = widget.id == widget.active;
    final pt = widget.pt;

    return MouseRegion(
      cursor: SystemMouseCursors.click,
      onEnter: (_) => setState(() => _hovered = true),
      onExit: (_) => setState(() => _hovered = false),
      child: GestureDetector(
        onTap: widget.onTap,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 150),
          margin: const EdgeInsets.fromLTRB(8, 1, 8, 1),
          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
          decoration: BoxDecoration(
            color: isActive
                ? pt.accentSoft
                : _hovered
                    ? pt.bgHover
                    : Colors.transparent,
            borderRadius: BorderRadius.circular(pt.rSm),
          ),
          child: Row(
            mainAxisAlignment: widget.label.isEmpty
                ? MainAxisAlignment.center
                : MainAxisAlignment.start,
            children: [
              Icon(
                widget.icon,
                size: 14,
                color: isActive ? pt.accent : _hovered ? pt.fg : pt.fgMuted,
              ),
              if (widget.label.isNotEmpty) ...[
                const SizedBox(width: 8),
                Text(
                  widget.label,
                  style: TextStyle(
                    color: isActive ? pt.fg : _hovered ? pt.fg : pt.fgMuted,
                    fontSize: 13,
                    fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
                  ),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

class _LogoutNavButton extends ConsumerStatefulWidget {
  final PyramidTheme pt;
  final bool isMobile;
  final VoidCallback onClose;
  const _LogoutNavButton({required this.pt, required this.isMobile, required this.onClose});

  @override
  ConsumerState<_LogoutNavButton> createState() => _LogoutNavButtonState();
}

class _LogoutNavButtonState extends ConsumerState<_LogoutNavButton> {
  bool _hovered = false;
  bool _loading = false;

  Future<void> _logout() async {
    final pt = widget.pt;
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: pt.bg2,
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
        title: Text('Abmelden?',
            style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
        content: Text('Du wirst von diesem Gerät abgemeldet.',
            style: TextStyle(color: pt.fgMuted, fontSize: 13)),
        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('Abmelden'),
          ),
        ],
      ),
    );
    if (confirmed != true || !mounted) return;
    setState(() => _loading = true);
    try {
      widget.onClose();
      final client = await ref.read(matrixClientProvider.future);
      await client.logout();
    } catch (_) {}
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return MouseRegion(
      cursor: SystemMouseCursors.click,
      onEnter: (_) => setState(() => _hovered = true),
      onExit: (_) => setState(() => _hovered = false),
      child: GestureDetector(
        onTap: _loading ? null : _logout,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 150),
          margin: const EdgeInsets.fromLTRB(8, 1, 8, 1),
          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
          decoration: BoxDecoration(
            color: _hovered ? pt.danger.withAlpha(30) : Colors.transparent,
            borderRadius: BorderRadius.circular(pt.rSm),
          ),
          child: Row(
            mainAxisAlignment: widget.isMobile ? MainAxisAlignment.center : MainAxisAlignment.start,
            children: [
              _loading
                  ? SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: pt.danger))
                  : Icon(Icons.logout_rounded, size: 14, color: pt.danger),
              if (!widget.isMobile) ...[
                const SizedBox(width: 8),
                Text('Abmelden', style: TextStyle(color: pt.danger, fontSize: 13)),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

// ── Shared UI primitives ──────────────────────────────────────────────────────

class _SectionTitle extends StatelessWidget {
  final String title;
  final String subtitle;
  final PyramidTheme pt;
  const _SectionTitle({required this.title, required this.subtitle, required this.pt});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(title,
            style: TextStyle(color: pt.fg, fontSize: 20, fontWeight: FontWeight.w700)),
        const SizedBox(height: 4),
        Text(subtitle, style: TextStyle(color: pt.fgMuted, fontSize: 13)),
        const SizedBox(height: 24),
      ],
    );
  }
}

class _SettingsGroup extends StatelessWidget {
  final String title;
  final PyramidTheme pt;
  final List<Widget> children;

  const _SettingsGroup({required this.title, required this.pt, required this.children});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title.toUpperCase(),
          style: TextStyle(
              color: pt.fgDim,
              fontSize: 11,
              fontWeight: FontWeight.w600,
              letterSpacing: 0.04 * 11),
        ),
        const SizedBox(height: 10),
        Container(
          decoration: BoxDecoration(
            color: pt.bg2,
            borderRadius: BorderRadius.circular(10),
            border: Border.all(color: pt.border),
          ),
          child: Column(children: children),
        ),
      ],
    );
  }
}

class _SettingsRow extends StatelessWidget {
  final String title;
  final String? desc;
  final PyramidTheme pt;
  final Widget? control;
  final VoidCallback? onTap;
  final bool destructive;
  final bool showArrow;

  const _SettingsRow({
    required this.title,
    required this.pt,
    this.desc,
    this.control,
    this.onTap,
    this.destructive = false,
    this.showArrow = false,
  });

  @override
  Widget build(BuildContext context) {
    Widget content = Padding(
      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: TextStyle(
                    color: destructive ? pt.danger : pt.fg,
                    fontSize: 13,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                if (desc != null) ...[
                  const SizedBox(height: 2),
                  Text(desc!, style: TextStyle(color: pt.fgDim, fontSize: 12)),
                ],
              ],
            ),
          ),
          ?control,
          if (showArrow)
            Icon(Icons.chevron_right_rounded, size: 16, color: pt.fgDim),
        ],
      ),
    );

    if (onTap != null) {
      return Material(
        color: Colors.transparent,
        child: InkWell(
          onTap: onTap,
          borderRadius: BorderRadius.circular(10),
          child: content,
        ),
      );
    }
    return content;
  }
}

class _Divider extends StatelessWidget {
  final PyramidTheme pt;
  const _Divider({required this.pt});

  @override
  Widget build(BuildContext context) {
    return Container(height: 1, color: pt.border, margin: const EdgeInsets.only(left: 14));
  }
}

class _Toggle extends StatelessWidget {
  final bool value;
  final ValueChanged<bool> onChanged;
  final PyramidTheme pt;
  const _Toggle({required this.value, required this.onChanged, required this.pt});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => onChanged(!value),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        width: 42,
        height: 24,
        decoration: BoxDecoration(
          color: value ? pt.accent : pt.bg3,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: value ? pt.accent : pt.borderStrong),
        ),
        child: Padding(
          padding: const EdgeInsets.all(2),
          child: AnimatedAlign(
            duration: const Duration(milliseconds: 200),
            alignment: value ? Alignment.centerRight : Alignment.centerLeft,
            child: Container(
              width: 18,
              height: 18,
              decoration: BoxDecoration(
                color: Colors.white,
                shape: BoxShape.circle,
                boxShadow: [BoxShadow(color: Colors.black.withAlpha(60), blurRadius: 4)],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _SegControl extends StatelessWidget {
  final List<String> options;
  final int selected;
  final PyramidTheme pt;
  final ValueChanged<int> onSelect;

  const _SegControl(
      {required this.options, required this.selected, required this.pt, required this.onSelect});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: pt.bg3,
        borderRadius: BorderRadius.circular(pt.rSm),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: options.asMap().entries.map((e) {
          final isActive = e.key == selected;
          return GestureDetector(
            onTap: () => onSelect(e.key),
            child: AnimatedContainer(
              duration: const Duration(milliseconds: 150),
              padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
              decoration: BoxDecoration(
                color: isActive ? pt.accent : Colors.transparent,
                borderRadius: BorderRadius.circular(pt.rSm - 2),
              ),
              child: Text(
                e.value,
                style: TextStyle(
                  color: isActive ? pt.accentFg : pt.fgMuted,
                  fontSize: 13,
                  fontWeight: isActive ? FontWeight.w600 : FontWeight.w400,
                ),
              ),
            ),
          );
        }).toList(),
      ),
    );
  }
}

class _PrimaryBtn extends StatelessWidget {
  final String label;
  final IconData? icon;
  final VoidCallback? onPressed;
  final PyramidTheme pt;
  final bool loading;

  const _PrimaryBtn({
    required this.label,
    required this.pt,
    this.icon,
    this.onPressed,
    this.loading = false,
  });

  @override
  Widget build(BuildContext context) {
    final bg = pt.accent;
    final fg = pt.accentFg;
    return ElevatedButton.icon(
      style: ElevatedButton.styleFrom(
        backgroundColor: bg,
        foregroundColor: fg,
        disabledBackgroundColor: bg.withAlpha(100),
        padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
        elevation: 0,
      ),
      onPressed: loading ? null : onPressed,
      icon: loading
          ? SizedBox(
              width: 14, height: 14, child: CircularProgressIndicator(color: fg, strokeWidth: 2))
          : (icon != null ? Icon(icon, size: 16) : const SizedBox.shrink()),
      label: Text(label),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// PROFILE
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_ProfileSection> createState() => _ProfileSectionState();
}

class _ProfileSectionState extends ConsumerState<_ProfileSection> {
  late TextEditingController _nameCtrl;
  late TextEditingController _statusCtrl;
  bool _saving = false;
  bool _saved = false;
  String? _error;
  Uint8List? _pendingAvatar;
  Uri? _avatarUri;
  Uint8List? _pendingBanner;
  String? _bannerMxcUri;
  Uint8List? _pendingServerBanner;
  bool _serverBannerSaving = false;

  @override
  void initState() {
    super.initState();
    _nameCtrl = TextEditingController();
    _statusCtrl = TextEditingController();
    _loadProfile();
  }

  @override
  void dispose() {
    _nameCtrl.dispose();
    _statusCtrl.dispose();
    super.dispose();
  }

  Future<void> _loadProfile() async {
    final client = await ref.read(matrixClientProvider.future);
    if (!mounted) return;

    // 1) SOFORT aus dem lokalen Cache füllen — kein Warten auf den Server.
    _nameCtrl.text = client.userID ?? '';
    for (final room in client.rooms) {
      final u = room.unsafeGetUserFromMemoryOrFallback(client.userID!);
      if (u.displayName != null || u.avatarUrl != null) {
        if (u.displayName != null) _nameCtrl.text = u.displayName!;
        _avatarUri = u.avatarUrl;
        break;
      }
    }
    try {
      final bannerData = client.accountData['com.pyramid.profile.banner'];
      _bannerMxcUri = bannerData?.content['url'] as String?;
    } catch (_) {}
    setState(() {});

    // 2) Server-Daten PARALLEL nachladen, UI aktualisiert sich pro Antwort.
    client
        .getProfileFromUserId(client.userID!)
        .then((profile) {
      if (!mounted) return;
      setState(() {
        if (profile.displayName != null) _nameCtrl.text = profile.displayName!;
        _avatarUri = profile.avatarUrl ?? _avatarUri;
      });
    }).catchError((_) {});

    () async {
      try {
        final apiUri = client.homeserver!.replace(
          path:
              '/_matrix/client/v3/profile/${Uri.encodeComponent(client.userID!)}',
        );
        final resp = await client.httpClient.get(
          apiUri,
          headers: {'Authorization': 'Bearer ${client.accessToken}'},
        );
        if (resp.statusCode == 200 && mounted) {
          final data = jsonDecode(resp.body) as Map<String, dynamic>;
          final url = data['io.pyramid.profile.banner_url'] as String?;
          if (url != null) setState(() => _bannerMxcUri = url);
        }
      } catch (_) {}
    }();
  }

  Future<void> _pickAvatar() async {
    final result = await FilePicker.platform.pickFiles(type: FileType.image, withData: true);
    final file = result?.files.first;
    if (file == null) return;
    final bytes = file.bytes ?? await file.xFile.readAsBytes();
    setState(() => _pendingAvatar = bytes);
  }

  Future<void> _pickBanner() async {
    final result = await FilePicker.platform.pickFiles(type: FileType.image, withData: true);
    final file = result?.files.first;
    if (file == null) return;
    final raw = file.bytes ?? await file.xFile.readAsBytes();
    if (!mounted) return;
    final cropped = await showDialog<Uint8List>(
      context: context,
      builder: (_) => BannerCropDialog(imageBytes: raw, aspectRatio: 3.0),
    );
    if (cropped == null || !mounted) return;
    setState(() => _pendingBanner = cropped);
  }

  Future<void> _pickServerBanner() async {
    final result = await FilePicker.platform.pickFiles(type: FileType.image, withData: true);
    final file = result?.files.first;
    if (file == null) return;
    final raw = file.bytes ?? await file.xFile.readAsBytes();
    if (!mounted) return;
    final cropped = await showDialog<Uint8List>(
      context: context,
      builder: (_) => BannerCropDialog(imageBytes: raw, aspectRatio: 2.4),
    );
    if (cropped == null || !mounted) return;
    setState(() => _serverBannerSaving = true);
    try {
      final client = await ref.read(matrixClientProvider.future);
      final uri = await client.uploadContent(cropped, filename: 'server_banner.png', contentType: 'image/png');
      await ref.read(serverBannerProvider.notifier).set(client.homeserver.toString(), uri.toString());
      if (mounted) setState(() { _pendingServerBanner = cropped; _serverBannerSaving = false; });
    } catch (_) {
      if (mounted) setState(() => _serverBannerSaving = false);
    }
  }

  Future<void> _removeServerBanner() async {
    final client = ref.read(matrixClientProvider).valueOrNull;
    if (client == null) return;
    await ref.read(serverBannerProvider.notifier).set(client.homeserver.toString(), null);
    if (mounted) setState(() => _pendingServerBanner = null);
  }

  Future<void> _save() async {
    setState(() { _saving = true; _error = null; _saved = false; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      final name = _nameCtrl.text.trim();
      if (name.isNotEmpty) {
        await client.setProfileField(
          client.userID!,
          'displayname',
          {'displayname': name},
        );
      }
      if (_pendingAvatar != null) {
        await client.setAvatar(MatrixFile(
          bytes: _pendingAvatar!,
          name: 'avatar.jpg',
          mimeType: 'image/jpeg',
        ));
        setState(() => _pendingAvatar = null);
      }
      if (_pendingBanner != null) {
        final uri = await client.uploadContent(
            _pendingBanner!, filename: 'banner.png', contentType: 'image/png');
        final uriStr = uri.toString();
        // Store as a profile custom field so other users can see it
        await client.setProfileField(
          client.userID!,
          'io.pyramid.profile.banner_url',
          {'io.pyramid.profile.banner_url': uriStr},
        );
        // Also keep in account data as legacy fallback
        await client.setAccountData(client.userID!, 'com.pyramid.profile.banner',
            {'url': uriStr});
        setState(() { _bannerMxcUri = uriStr; _pendingBanner = null; });
      }
      final status = _statusCtrl.text.trim();
      await client.setPresence(
        client.userID!,
        PresenceType.online,
        statusMsg: status.isEmpty ? null : status,
      );
      if (mounted) setState(() { _saving = false; _saved = true; });
      await Future.delayed(const Duration(seconds: 2));
      if (mounted) setState(() => _saved = false);
    } catch (e) {
      if (mounted) setState(() { _saving = false; _error = e.toString().split('\n').first; });
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    final clientAsync = ref.watch(matrixClientProvider);
    final client = clientAsync.valueOrNull;

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Mein Profil',
              subtitle: 'Wie du in Pyramid erscheinst.',
              pt: pt),
          // Banner
          GestureDetector(
            onTap: _pickBanner,
            child: Stack(
              children: [
                Container(
                  height: 100,
                  decoration: BoxDecoration(
                    color: pt.bg3,
                    borderRadius: BorderRadius.circular(pt.rBase),
                    border: Border.all(color: pt.border),
                  ),
                  clipBehavior: Clip.hardEdge,
                  child: _pendingBanner != null
                      ? Image.memory(_pendingBanner!, fit: BoxFit.cover, width: double.infinity)
                      : (client != null && _bannerMxcUri != null
                          ? MxcImage(
                              mxcUri: _bannerMxcUri!,
                              client: client,
                              fit: BoxFit.cover,
                              width: double.infinity,
                            )
                          : Center(
                              child: Column(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(Icons.add_photo_alternate_outlined, size: 28, color: pt.fgDim),
                                  const SizedBox(height: 4),
                                  Text('Profilbanner auswählen', style: TextStyle(color: pt.fgDim, fontSize: 12)),
                                ],
                              ),
                            )),
                ),
                Positioned(
                  right: 8,
                  bottom: 8,
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                    decoration: BoxDecoration(
                      color: pt.bg0.withAlpha(200),
                      borderRadius: BorderRadius.circular(6),
                      border: Border.all(color: pt.border),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Icon(Icons.edit_rounded, size: 11, color: pt.fgMuted),
                        const SizedBox(width: 4),
                        Text('Banner ändern', style: TextStyle(color: pt.fgMuted, fontSize: 11)),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 16),
          // Avatar row
          Row(
            children: [
              GestureDetector(
                onTap: _pickAvatar,
                child: Stack(
                  children: [
                    Container(
                      width: 72,
                      height: 72,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        border: Border.all(color: pt.border, width: 2),
                      ),
                      child: ClipOval(
                        child: _pendingAvatar != null
                            ? Image.memory(_pendingAvatar!, fit: BoxFit.cover)
                            : (client != null && _avatarUri != null
                                ? MxcAvatar(
                                    mxcUri: _avatarUri,
                                    client: client,
                                    size: 72,
                                    placeholder: (_) => _AvatarFallback(
                                        name: _nameCtrl.text, size: 72, pt: pt),
                                  )
                                : _AvatarFallback(
                                    name: _nameCtrl.text, size: 72, pt: pt)),
                      ),
                    ),
                    Positioned(
                      right: 0,
                      bottom: 0,
                      child: Container(
                        width: 24,
                        height: 24,
                        decoration: BoxDecoration(
                          color: pt.accent,
                          shape: BoxShape.circle,
                          border: Border.all(color: pt.bg1, width: 2),
                        ),
                        child: Icon(Icons.edit_rounded, size: 12, color: pt.accentFg),
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(width: 20),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      _nameCtrl.text.isNotEmpty ? _nameCtrl.text : (client?.userID ?? ''),
                      style: TextStyle(
                          color: pt.fg, fontSize: 16, fontWeight: FontWeight.w600),
                    ),
                    const SizedBox(height: 2),
                    SelectableText(
                      client?.userID ?? '',
                      style: TextStyle(color: pt.fgDim, fontSize: 12),
                    ),
                    const SizedBox(height: 6),
                    if (_pendingAvatar != null)
                      Container(
                        padding:
                            const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                        decoration: BoxDecoration(
                          color: pt.accent.withAlpha(40),
                          borderRadius: BorderRadius.circular(6),
                        ),
                        child: Text('Neues Bild ausgewählt',
                            style: TextStyle(color: pt.accent, fontSize: 11)),
                      ),
                  ],
                ),
              ),
            ],
          ),
          const SizedBox(height: 24),
          _SettingsGroup(title: 'Name & Status', pt: pt, children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(14, 14, 14, 4),
              child: _PrefTextField(
                controller: _nameCtrl,
                label: 'Anzeigename',
                pt: pt,
              ),
            ),
            const SizedBox(height: 2),
            Padding(
              padding: const EdgeInsets.fromLTRB(14, 4, 14, 14),
              child: _PrefTextField(
                controller: _statusCtrl,
                label: 'Statusnachricht (optional)',
                pt: pt,
                maxLength: 120,
              ),
            ),
          ]),
          const SizedBox(height: 20),
          if (_error != null)
            Padding(
              padding: const EdgeInsets.only(bottom: 12),
              child: _StatusCard(
                  pt: pt, color: pt.danger, icon: Icons.error_outline_rounded, text: _error!),
            ),
          if (_saved)
            Padding(
              padding: const EdgeInsets.only(bottom: 12),
              child: _StatusCard(
                  pt: pt,
                  color: PyramidColors.success,
                  icon: Icons.check_circle_outline_rounded,
                  text: 'Profil gespeichert.'),
            ),
          _PrimaryBtn(
            label: _saving ? 'Speichern…' : 'Speichern',
            icon: Icons.save_outlined,
            pt: pt,
            loading: _saving,
            onPressed: _save,
          ),
          const SizedBox(height: 24),

          // ── Server-Banner ─────────────────────────────────────────────────
          _SectionTitle(
            title: 'Server-Darstellung',
            subtitle: 'Banner für ${client?.homeserver?.host ?? 'diesen Server'}',
            pt: pt,
          ),
          Consumer(builder: (context, ref, _) {
            final serverBanners = ref.watch(serverBannerProvider).valueOrNull ?? {};
            final homeserver = client?.homeserver.toString();
            final existingMxc = homeserver != null ? serverBanners[homeserver] : null;
            final hasBanner = _pendingServerBanner != null || existingMxc != null;

            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                GestureDetector(
                  onTap: _serverBannerSaving ? null : _pickServerBanner,
                  child: Container(
                    height: 90,
                    decoration: BoxDecoration(
                      color: pt.bg3,
                      borderRadius: BorderRadius.circular(pt.rBase),
                      border: Border.all(color: pt.border),
                    ),
                    clipBehavior: Clip.hardEdge,
                    child: Stack(
                      fit: StackFit.expand,
                      children: [
                        if (_pendingServerBanner != null)
                          Image.memory(_pendingServerBanner!, fit: BoxFit.cover,
                              errorBuilder: (_, _, _) => const SizedBox.shrink())
                        else if (existingMxc != null && client != null)
                          MxcImage(mxcUri: existingMxc, client: client, fit: BoxFit.cover)
                        else
                          Center(
                            child: Column(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Icon(Icons.add_photo_alternate_outlined, size: 24, color: pt.fgDim),
                                const SizedBox(height: 4),
                                Text('Server-Banner auswählen', style: TextStyle(color: pt.fgDim, fontSize: 12)),
                              ],
                            ),
                          ),
                        if (_serverBannerSaving)
                          Container(
                            color: Colors.black38,
                            child: const Center(child: CircularProgressIndicator(strokeWidth: 2)),
                          ),
                        if (hasBanner)
                          Positioned(
                            right: 8,
                            bottom: 8,
                            child: Container(
                              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                              decoration: BoxDecoration(
                                color: pt.bg0.withAlpha(200),
                                borderRadius: BorderRadius.circular(6),
                                border: Border.all(color: pt.border),
                              ),
                              child: Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(Icons.edit_rounded, size: 11, color: pt.fgMuted),
                                  const SizedBox(width: 4),
                                  Text('Banner ändern', style: TextStyle(color: pt.fgMuted, fontSize: 11)),
                                ],
                              ),
                            ),
                          ),
                      ],
                    ),
                  ),
                ),
                if (hasBanner) ...[
                  const SizedBox(height: 8),
                  GestureDetector(
                    onTap: _removeServerBanner,
                    child: Text(
                      'Banner entfernen',
                      style: TextStyle(color: pt.danger, fontSize: 12),
                    ),
                  ),
                ],
              ],
            );
          }),

          const SizedBox(height: 24),
          _SettingsGroup(title: 'Konto', pt: pt, children: [
            _SettingsRow(
              title: 'Passwort ändern',
              desc: 'Aktuelles Passwort zum Ändern erforderlich',
              pt: pt,
              showArrow: true,
              onTap: _changePassword,
            ),
          ]),
          const SizedBox(height: 12),
          _SettingsGroup(title: 'Gefahrenbereich', pt: pt, children: [
            _SettingsRow(
              title: 'Konto löschen',
              desc: 'Konto dauerhaft deaktivieren – nicht rückgängig zu machen',
              pt: pt,
              showArrow: true,
              destructive: true,
              onTap: _deleteAccount,
            ),
          ]),
        ],
      ),
    );
  }

  Future<void> _changePassword() async {
    await showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (ctx) => _ChangePasswordDialog(pt: widget.pt),
    );
  }

  Future<void> _deleteAccount() async {
    await showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (ctx) => _DeleteAccountDialog(pt: widget.pt),
    );
  }
}

class _AvatarFallback extends StatelessWidget {
  final String name;
  final double size;
  final PyramidTheme pt;
  const _AvatarFallback({required this.name, required this.size, required this.pt});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: size,
      height: size,
      color: pt.accent,
      child: Center(
        child: Text(
          name.isNotEmpty ? name[0].toUpperCase() : '?',
          style:
              TextStyle(color: pt.accentFg, fontSize: size * 0.4, fontWeight: FontWeight.w700),
        ),
      ),
    );
  }
}

class _PrefTextField extends StatelessWidget {
  final TextEditingController controller;
  final String label;
  final PyramidTheme pt;
  final int? maxLength;

  const _PrefTextField({
    required this.controller,
    required this.label,
    required this.pt,
    this.maxLength,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      maxLength: maxLength,
      style: TextStyle(color: pt.fg, fontSize: 13),
      decoration: InputDecoration(
        labelText: label,
        labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
        counterText: '',
        filled: true,
        fillColor: pt.bg3,
        isDense: true,
        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: pt.border)),
        focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
      ),
      cursorColor: pt.accent,
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// NOTIFICATIONS
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final desktop = ref.watch(notifDesktopProvider);
    final sound = ref.watch(notifSoundProvider);
    final preview = ref.watch(notifPreviewProvider);
    final dmMentionOnly = ref.watch(notifMentionOnlyDmProvider);

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Benachrichtigungen',
              subtitle: 'Bestimme, wann und wie du benachrichtigt wirst.',
              pt: pt),
          _SettingsGroup(title: 'Desktop', pt: pt, children: [
            _SettingsRow(
              title: 'Desktop-Benachrichtigungen',
              desc: 'Zeige Systembenachrichtigungen bei neuen Nachrichten',
              pt: pt,
              control: _Toggle(
                value: desktop,
                pt: pt,
                onChanged: (v) => ref.read(notifDesktopProvider.notifier).set(v),
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Benachrichtigungston',
              desc: 'Sound abspielen wenn eine Nachricht eingeht',
              pt: pt,
              control: _Toggle(
                value: sound,
                pt: pt,
                onChanged: (v) => ref.read(notifSoundProvider.notifier).set(v),
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Nachrichtenvorschau',
              desc: 'Nachrichteninhalt in Benachrichtigung anzeigen',
              pt: pt,
              control: _Toggle(
                value: preview,
                pt: pt,
                onChanged: (v) => ref.read(notifPreviewProvider.notifier).set(v),
              ),
            ),
          ]),
          const SizedBox(height: 20),
          _SettingsGroup(title: 'Filter', pt: pt, children: [
            _SettingsRow(
              title: 'Nur Erwähnungen (DMs)',
              desc: 'Benachrichtigung nur bei direkten @Erwähnungen',
              pt: pt,
              control: _Toggle(
                value: dmMentionOnly,
                pt: pt,
                onChanged: (v) => ref.read(notifMentionOnlyDmProvider.notifier).set(v),
              ),
            ),
          ]),
          const SizedBox(height: 20),
          Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: pt.bg2,
              borderRadius: BorderRadius.circular(10),
              border: Border.all(color: pt.border),
            ),
            child: Row(
              children: [
                Icon(Icons.info_outline_rounded, size: 16, color: pt.fgDim),
                const SizedBox(width: 10),
                Expanded(
                  child: Text(
                    'Push-Benachrichtigungen für Android/iOS sind in Vorbereitung.',
                    style: TextStyle(color: pt.fgMuted, fontSize: 12),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// APPEARANCE
// ─────────────────────────────────────────────────────────────────────────────

class _AppearanceSection extends StatelessWidget {
  final PyramidTheme pt;
  final bool isDark;
  final int accentIdx;
  final double radius;
  final double density;
  final double motion;
  final WidgetRef ref;

  const _AppearanceSection({
    required this.pt,
    required this.isDark,
    required this.accentIdx,
    required this.radius,
    required this.density,
    required this.motion,
    required this.ref,
  });

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Erscheinungsbild',
              subtitle: 'Gestalte Pyramid nach deinem Geschmack.',
              pt: pt),
          _SettingsGroup(title: 'Theme', pt: pt, children: [
            _SettingsRow(
              title: 'Farbmodus',
              desc: 'Hell oder dunkel',
              pt: pt,
              control: _SegControl(
                options: const ['Dunkel', 'Hell'],
                selected: isDark ? 0 : 1,
                pt: pt,
                onSelect: (i) {
                  final dark = i == 0;
                  ref.read(themeModeProvider.notifier).state = dark;
                  saveThemePrefs(isDark: dark);
                },
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Akzentfarbe',
              desc: 'Farbe für Hervorhebungen und Buttons',
              pt: pt,
              control: Row(
                children: accentPresets.asMap().entries.map((e) {
                  final i = e.key;
                  final preset = e.value;
                  final isActive = accentIdx == i;
                  return GestureDetector(
                    onTap: () {
                      ref.read(accentProvider.notifier).state = i;
                      saveThemePrefs(accentIdx: i);
                    },
                    child: AnimatedContainer(
                      duration: const Duration(milliseconds: 150),
                      width: 22,
                      height: 22,
                      margin: const EdgeInsets.only(right: 6),
                      decoration: BoxDecoration(
                        color: preset.color,
                        shape: BoxShape.circle,
                        border: isActive ? Border.all(color: Colors.white, width: 2) : null,
                        boxShadow: isActive
                            ? [BoxShadow(color: preset.color.withAlpha(100), blurRadius: 8)]
                            : null,
                      ),
                    ),
                  );
                }).toList(),
              ),
            ),
          ]),
          const SizedBox(height: 20),
          _SettingsGroup(title: 'Form & Bewegung', pt: pt, children: [
            _SettingsRow(
              title: 'Eckenrundung',
              desc: '${radius.round()} px',
              pt: pt,
              control: SizedBox(
                width: 150,
                child: SliderTheme(
                  data: SliderThemeData(
                    activeTrackColor: pt.accent,
                    thumbColor: pt.accent,
                    inactiveTrackColor: pt.border,
                    trackHeight: 3,
                    thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6),
                  ),
                  child: Slider(
                    value: radius,
                    min: 4,
                    max: 20,
                    onChanged: (v) {
                      ref.read(radiusProvider.notifier).state = v;
                      saveThemePrefs(radius: v);
                    },
                  ),
                ),
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Animationen',
              desc: '${(motion * 100).round()}%',
              pt: pt,
              control: SizedBox(
                width: 150,
                child: SliderTheme(
                  data: SliderThemeData(
                    activeTrackColor: pt.accent,
                    thumbColor: pt.accent,
                    inactiveTrackColor: pt.border,
                    trackHeight: 3,
                    thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6),
                  ),
                  child: Slider(
                    value: motion,
                    min: 0,
                    max: 1,
                    onChanged: (v) {
                      ref.read(motionProvider.notifier).state = v;
                      saveThemePrefs(motion: v);
                    },
                  ),
                ),
              ),
            ),
          ]),
          const SizedBox(height: 20),
          _SettingsGroup(title: 'Vorschau', pt: pt, children: [
            Padding(
              padding: const EdgeInsets.all(16),
              child: Row(
                children: [
                  Expanded(
                    child: Column(
                      children: [
                        Container(
                          height: 100,
                          decoration: BoxDecoration(
                            color: pt.bg3,
                            borderRadius: BorderRadius.circular(pt.rBase),
                            border: Border.all(color: pt.border),
                          ),
                          child: const Center(child: PyramidLoader(size: 70, useLottie: true)),
                        ),
                        const SizedBox(height: 6),
                        Text('Lottie', style: TextStyle(color: pt.fgMuted, fontSize: 11)),
                      ],
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: Column(
                      children: [
                        Container(
                          height: 100,
                          decoration: BoxDecoration(
                            color: pt.bg3,
                            borderRadius: BorderRadius.circular(pt.rBase),
                            border: Border.all(color: pt.border),
                          ),
                          child: const Center(child: PyramidLoader(size: 70, useLottie: false)),
                        ),
                        const SizedBox(height: 6),
                        Text('SVG', style: TextStyle(color: pt.fgMuted, fontSize: 11)),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ]),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// VOICE & VIDEO
// ─────────────────────────────────────────────────────────────────────────────

class _VoiceSection extends StatefulWidget {
  final PyramidTheme pt;
  const _VoiceSection({required this.pt});

  @override
  State<_VoiceSection> createState() => _VoiceSectionState();
}

class _VoiceSectionState extends State<_VoiceSection> {
  List<MediaDeviceInfo> _audioInputs = [];
  List<MediaDeviceInfo> _audioOutputs = [];
  List<MediaDeviceInfo> _videoInputs = [];
  String? _selectedMic;
  String? _selectedSpeaker;
  String? _selectedCamera;
  double _outputVolume = 1.0;
  bool _loading = true;
  String? _loadError;

  static const _kMic = 'voice_mic_device';
  static const _kSpeaker = 'voice_speaker_device';
  static const _kCamera = 'voice_camera_device';
  static const _kOutputVolume = 'voice_output_volume';

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

  Future<void> _init() async {
    final prefs = await SharedPreferences.getInstance();
    List<MediaDeviceInfo> devices = [];
    String? loadError;
    try {
      devices = await navigator.mediaDevices.enumerateDevices();
    } catch (_) {
      devices = [];
    }
    // Windows (und teils Android) liefert eine LEERE Liste ohne Fehler,
    // solange in diesem Prozess noch nie ein Media-Stream geöffnet wurde.
    // Deshalb nicht nur bei Exception, sondern auch bei leerer Liste einmal
    // kurz getUserMedia anfordern und erneut enumerieren — und zwar SOLANGE
    // der Stream noch offen ist (nach stop() melden manche Systeme wieder 0).
    final hasAudioInput =
        devices.any((d) => d.kind?.toLowerCase() == 'audioinput');
    if (devices.isEmpty || !hasAudioInput) {
      try {
        final stream = await navigator.mediaDevices
            .getUserMedia({'audio': true, 'video': false});
        try {
          devices = await navigator.mediaDevices.enumerateDevices();
        } finally {
          for (final t in stream.getTracks()) {
            t.stop();
          }
          await stream.dispose();
        }
        if (devices.isEmpty) {
          // Bekanntes libwebrtc-Problem auf manchen Windows-Systemen: das
          // Audio-Device-Module meldet 0 Geräte, obwohl Aufnahme/Wiedergabe
          // über das Standardgerät einwandfrei funktionieren.
          loadError =
              'Die Audiotreiber melden keine Geräteliste an WebRTC '
              '(bekanntes libwebrtc-Problem auf manchen Windows-Systemen). '
              'Anrufe funktionieren trotzdem — es wird das Windows-Standardgerät verwendet.';
        }
      } catch (e2) {
        loadError = 'getUserMedia: ${e2.toString().split('\n').first}';
      }
    }
    if (!mounted) return;
    setState(() {
      _audioInputs = devices.where((d) => d.kind?.toLowerCase() == 'audioinput').toList();
      _audioOutputs = devices.where((d) => d.kind?.toLowerCase() == 'audiooutput').toList();
      _videoInputs = devices.where((d) => d.kind?.toLowerCase() == 'videoinput').toList();
      _selectedMic = prefs.getString(_kMic);
      _selectedSpeaker = prefs.getString(_kSpeaker);
      _selectedCamera = prefs.getString(_kCamera);
      _outputVolume = prefs.getDouble(_kOutputVolume) ?? 1.0;
      _loadError = loadError;
      _loading = false;
    });
  }

  Future<void> _setOutputVolume(double v) async {
    setState(() => _outputVolume = v);
    final prefs = await SharedPreferences.getInstance();
    await prefs.setDouble(_kOutputVolume, v);
    // Live auf laufende Calls anwenden.
    LiveKitCallManager.instance.setOutputVolume(v).catchError((_) {});
    try {
      PyramidVoipManager.instance.applyOutputVolume(v).catchError((_) {});
    } catch (_) {} // Instanz existiert evtl. noch nicht
  }

  Future<void> _save(String key, String? value) async {
    final prefs = await SharedPreferences.getInstance();
    if (value == null || value.isEmpty) {
      await prefs.remove(key);
    } else {
      await prefs.setString(key, value);
    }
  }

  Widget _deviceDropdown({
    required List<MediaDeviceInfo> devices,
    required String? selected,
    required String placeholder,
    required IconData icon,
    required ValueChanged<String?> onChanged,
  }) {
    final pt = widget.pt;
    if (devices.isEmpty) {
      return Container(
        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
        decoration: BoxDecoration(
          color: pt.bg3,
          borderRadius: BorderRadius.circular(6),
          border: Border.all(color: pt.border),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 13, color: pt.fgDim),
            const SizedBox(width: 6),
            Text('Kein Gerät', style: TextStyle(color: pt.fgDim, fontSize: 12)),
          ],
        ),
      );
    }

    final validSelected = devices.any((d) => d.deviceId == selected) ? selected : null;

    return ConstrainedBox(
      constraints: const BoxConstraints(maxWidth: 220),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
          color: pt.bg3,
          borderRadius: BorderRadius.circular(6),
          border: Border.all(color: pt.border),
        ),
        child: DropdownButtonHideUnderline(
          child: DropdownButton<String?>(
            value: validSelected,
            hint: Row(children: [
              Icon(icon, size: 13, color: pt.fgDim),
              const SizedBox(width: 6),
              Text(placeholder, style: TextStyle(color: pt.fgDim, fontSize: 12)),
            ]),
            dropdownColor: pt.bg2,
            iconEnabledColor: pt.fgDim,
            isDense: true,
            style: TextStyle(color: pt.fg, fontSize: 12),
            onChanged: onChanged,
            items: devices.map((d) {
              final label = d.label.isNotEmpty ? d.label : 'Gerät ${d.deviceId.substring(0, 6)}';
              return DropdownMenuItem<String?>(
                value: d.deviceId,
                child: Text(label, overflow: TextOverflow.ellipsis),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Sprache & Video',
              subtitle: 'Mikrofon, Kamera und Anrufeinstellungen.',
              pt: pt),
          if (_loading)
            const Center(child: Padding(padding: EdgeInsets.all(32), child: CircularProgressIndicator.adaptive()))
          else ...[
            if (_loadError != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 16),
                child: Container(
                  padding: const EdgeInsets.all(12),
                  decoration: BoxDecoration(
                    color: pt.bg2,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: pt.border),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(children: [
                        Icon(Icons.warning_amber_rounded, size: 16, color: pt.away),
                        const SizedBox(width: 8),
                        Text('Geräteliste nicht verfügbar',
                            style: TextStyle(color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500)),
                      ]),
                      const SizedBox(height: 6),
                      Text(_loadError!, style: TextStyle(color: pt.fgDim, fontSize: 11)),
                      const SizedBox(height: 6),
                      Text(
                        'Mikrofon und Lautsprecher legst du in diesem Fall über die '
                        'Windows-Soundeinstellungen (Standardgerät) fest. '
                        'Falls dort alles stimmt: Windows-Einstellungen → Datenschutz → Mikrofon prüfen.',
                        style: TextStyle(color: pt.fgMuted, fontSize: 11),
                      ),
                    ],
                  ),
                ),
              ),
            _SettingsGroup(title: 'Geräte', pt: pt, children: [
              _SettingsRow(
                title: 'Mikrofon',
                desc: _audioInputs.isEmpty ? 'Kein Gerät gefunden' : '${_audioInputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _audioInputs,
                  selected: _selectedMic,
                  placeholder: 'Standard',
                  icon: Icons.mic_rounded,
                  onChanged: (v) {
                    setState(() => _selectedMic = v);
                    _save(_kMic, v);
                  },
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Lautsprecher',
                desc: _audioOutputs.isEmpty ? 'Kein Gerät gefunden' : '${_audioOutputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _audioOutputs,
                  selected: _selectedSpeaker,
                  placeholder: 'Standard',
                  icon: Icons.volume_up_rounded,
                  onChanged: (v) {
                    setState(() => _selectedSpeaker = v);
                    _save(_kSpeaker, v);
                  },
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Kamera',
                desc: _videoInputs.isEmpty ? 'Kein Gerät gefunden' : '${_videoInputs.length} Gerät(e) gefunden',
                pt: pt,
                control: _deviceDropdown(
                  devices: _videoInputs,
                  selected: _selectedCamera,
                  placeholder: 'Standard',
                  icon: Icons.videocam_rounded,
                  onChanged: (v) {
                    setState(() => _selectedCamera = v);
                    _save(_kCamera, v);
                  },
                ),
              ),
            ]),
            const SizedBox(height: 16),
            _SettingsGroup(title: 'Pegel', pt: pt, children: [
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Icon(Icons.volume_up_rounded, size: 15, color: pt.fgMuted),
                        const SizedBox(width: 8),
                        Text('Ausgabelautstärke',
                            style: TextStyle(
                                color: pt.fg,
                                fontSize: 13,
                                fontWeight: FontWeight.w500)),
                        const Spacer(),
                        Text('${(_outputVolume * 100).round()} %',
                            style: TextStyle(
                                color: pt.fgMuted,
                                fontSize: 12,
                                fontFamily: 'monospace')),
                      ],
                    ),
                    SliderTheme(
                      data: SliderThemeData(
                        activeTrackColor: pt.accent,
                        inactiveTrackColor: pt.bg3,
                        thumbColor: pt.accent,
                        overlayColor: pt.accent.withAlpha(30),
                        trackHeight: 3,
                        thumbShape: const RoundSliderThumbShape(
                            enabledThumbRadius: 7),
                      ),
                      child: Slider(
                        value: _outputVolume.clamp(0.0, 1.5),
                        min: 0.0,
                        max: 1.5,
                        divisions: 30,
                        onChanged: _setOutputVolume,
                      ),
                    ),
                    Text(
                      'Lautstärke der anderen Teilnehmer in Calls. '
                      'Wirkt sofort auf laufende Anrufe.',
                      style: TextStyle(color: pt.fgDim, fontSize: 11),
                    ),
                  ],
                ),
              ),
            ]),
            const SizedBox(height: 16),
            _TurnUsageCard(pt: pt),
            const SizedBox(height: 12),
            TextButton.icon(
              style: TextButton.styleFrom(
                foregroundColor: pt.fgMuted,
                padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              ),
              onPressed: () { setState(() { _loading = true; _loadError = null; }); _init(); },
              icon: const Icon(Icons.refresh_rounded, size: 14),
              label: const Text('Geräte neu laden', style: TextStyle(fontSize: 12)),
            ),
          ],
        ],
      ),
    );
  }
}

// ── TURN-Verbrauch (Cloudflare Free Tier, vom Pi überwacht) ──────────────────

/// Zeigt den TURN-Relay-Verbrauch des laufenden Monats an. Die Daten kommen
/// aus der turn-status.json, die der Guard-Cron auf dem Pi stündlich schreibt.
/// Solange der Endpoint nicht existiert (Analytics-Token fehlt), bleibt die
/// Karte unsichtbar.
class _TurnUsageCard extends StatefulWidget {
  final PyramidTheme pt;
  const _TurnUsageCard({required this.pt});

  @override
  State<_TurnUsageCard> createState() => _TurnUsageCardState();
}

class _TurnUsageCardState extends State<_TurnUsageCard> {
  Map<String, dynamic>? _status;

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

  Future<void> _load() async {
    try {
      final resp = await http
          .get(Uri.parse(
              'https://steggi-matrix.work/.well-known/pyramid/turn-status.json'))
          .timeout(const Duration(seconds: 5));
      if (resp.statusCode == 200 && mounted) {
        setState(
            () => _status = jsonDecode(resp.body) as Map<String, dynamic>);
      }
    } catch (_) {
      // Endpoint (noch) nicht vorhanden — Karte einfach nicht anzeigen.
    }
  }

  @override
  Widget build(BuildContext context) {
    final s = _status;
    if (s == null) return const SizedBox.shrink();
    final pt = widget.pt;
    final used = (s['egress_gb'] as num?)?.toDouble() ?? 0;
    final limit = (s['limit_gb'] as num?)?.toDouble() ?? 990;
    final pct = limit > 0 ? (used / limit).clamp(0.0, 1.0) : 0.0;
    final active = s['turn_active'] != false;
    final barColor = !active
        ? pt.danger
        : pct > 0.8
            ? pt.away
            : pt.accent;

    return _SettingsGroup(title: 'Call-Relay (TURN)', pt: pt, children: [
      Padding(
        padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(children: [
              Icon(active ? Icons.cloud_done_rounded : Icons.cloud_off_rounded,
                  size: 15, color: active ? pt.online : pt.danger),
              const SizedBox(width: 8),
              Text(
                  active
                      ? 'Relay aktiv'
                      : 'Relay deaktiviert (Limit erreicht)',
                  style: TextStyle(
                      color: pt.fg,
                      fontSize: 13,
                      fontWeight: FontWeight.w500)),
              const Spacer(),
              Text(
                  '${used.toStringAsFixed(1)} / ${limit.toStringAsFixed(0)} GB',
                  style: TextStyle(
                      color: pt.fgMuted,
                      fontSize: 12,
                      fontFamily: 'monospace')),
            ]),
            const SizedBox(height: 8),
            ClipRRect(
              borderRadius: BorderRadius.circular(99),
              child: LinearProgressIndicator(
                value: pct,
                minHeight: 5,
                backgroundColor: pt.bg3,
                color: barColor,
              ),
            ),
            const SizedBox(height: 6),
            Text(
              active
                  ? 'Cloudflare-TURN-Traffic diesen Monat (Free Tier: ${limit.toStringAsFixed(0)} GB). '
                      'Bei Erreichen des Limits laufen Calls bis Monatsende ohne Relay weiter.'
                  : 'Monatslimit erreicht — Calls nutzen bis Monatsende nur direkte Verbindungen (STUN).',
              style: TextStyle(color: pt.fgDim, fontSize: 11),
            ),
          ],
        ),
      ),
    ]);
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// KEYBINDS
// ─────────────────────────────────────────────────────────────────────────────

class _KeysSection extends StatelessWidget {
  final PyramidTheme pt;
  const _KeysSection({required this.pt});

  @override
  Widget build(BuildContext context) {
    final keys = [
      ('Schnellsuche', 'Strg+K'),
      ('Raum als gelesen markieren', 'Shift+Esc'),
      ('Zu ungelesen springen', 'Strg+J'),
      ('Linke Leiste ein-/ausblenden', 'Strg+B'),
      ('Mitglieder ein-/ausblenden', 'Strg+Shift+U'),
      ('Nachrichten durchsuchen', 'Strg+F'),
      ('Antworten', 'R'),
      ('Auswahl aufheben', 'Esc'),
    ];

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Tastaturkürzel',
              subtitle: 'Schnelle Aktionen per Tastatur.',
              pt: pt),
          _SettingsGroup(
            title: 'Navigieren',
            pt: pt,
            children: keys.asMap().entries.map((entry) {
              final (action, binding) = entry.value;
              return Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                    child: Row(
                      children: [
                        Expanded(
                            child: Text(action,
                                style: TextStyle(color: pt.fg, fontSize: 13))),
                        Container(
                          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
                          decoration: BoxDecoration(
                            color: pt.bg3,
                            borderRadius: BorderRadius.circular(6),
                            border: Border.all(color: pt.border),
                          ),
                          child: Text(binding,
                              style: TextStyle(
                                  color: pt.fgMuted,
                                  fontSize: 12,
                                  fontFamily: 'monospace')),
                        ),
                      ],
                    ),
                  ),
                  if (entry.key < keys.length - 1) _Divider(pt: pt),
                ],
              );
            }).toList(),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// PRIVACY
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_PrivacySection> createState() => _PrivacySectionState();
}

class _PrivacySectionState extends ConsumerState<_PrivacySection> {
  List<String> _ignoredUsers = [];
  final Set<String> _unblocking = {};

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

  Future<void> _loadIgnoredUsers() async {
    final client = await ref.read(matrixClientProvider.future);
    if (mounted) setState(() => _ignoredUsers = List.from(client.ignoredUsers));
  }

  Future<void> _unblockUser(String userId) async {
    setState(() => _unblocking.add(userId));
    try {
      final client = await ref.read(matrixClientProvider.future);
      await client.unignoreUser(userId);
      await _loadIgnoredUsers();
    } catch (e) {
      // ignore — list will be stale but not crash
    } finally {
      if (mounted) setState(() => _unblocking.remove(userId));
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    final presence = ref.watch(privacyPresenceProvider);
    final readReceipts = ref.watch(privacyReadReceiptsProvider);
    final typing = ref.watch(privacyTypingProvider);

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Datenschutz',
              subtitle: 'Steuere, was andere über dich erfahren.',
              pt: pt),
          _SettingsGroup(title: 'Sichtbarkeit', pt: pt, children: [
            _SettingsRow(
              title: 'Online-Status anzeigen',
              desc: 'Anderen anzeigen, wenn du aktiv bist',
              pt: pt,
              control: _Toggle(
                value: presence,
                pt: pt,
                onChanged: (v) async {
                  ref.read(privacyPresenceProvider.notifier).set(v);
                  final client = await ref.read(matrixClientProvider.future);
                  await client.setPresence(
                    client.userID!,
                    v ? PresenceType.online : PresenceType.offline,
                  ).catchError((_) {});
                },
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Lesebestätigungen senden',
              desc: 'Anderen zeigen, dass du ihre Nachrichten gelesen hast',
              pt: pt,
              control: _Toggle(
                value: readReceipts,
                pt: pt,
                onChanged: (v) => ref.read(privacyReadReceiptsProvider.notifier).set(v),
              ),
            ),
            _Divider(pt: pt),
            _SettingsRow(
              title: 'Tipp-Indikator senden',
              desc: '„schreibt…" Anzeige aktivieren',
              pt: pt,
              control: _Toggle(
                value: typing,
                pt: pt,
                onChanged: (v) => ref.read(privacyTypingProvider.notifier).set(v),
              ),
            ),
          ]),
          const SizedBox(height: 20),
          // ── Blocked users ──────────────────────────────────────────────────
          _SettingsGroup(title: 'Blockierte Nutzer (${_ignoredUsers.length})', pt: pt, children: [
            if (_ignoredUsers.isEmpty)
              Padding(
                padding: const EdgeInsets.all(14),
                child: Text('Keine blockierten Nutzer.',
                    style: TextStyle(color: pt.fgMuted, fontSize: 13)),
              )
            else
              ..._ignoredUsers.asMap().entries.map((entry) {
                final i = entry.key;
                final userId = entry.value;
                final isUnblocking = _unblocking.contains(userId);
                return Column(children: [
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
                    child: Row(children: [
                      Expanded(
                        child: Text(userId,
                            style: TextStyle(color: pt.fg, fontSize: 13),
                            overflow: TextOverflow.ellipsis),
                      ),
                      if (isUnblocking)
                        const SizedBox(width: 16, height: 16, child: CircularProgressIndicator.adaptive(strokeWidth: 2))
                      else
                        TextButton(
                          style: TextButton.styleFrom(
                            foregroundColor: pt.accent,
                            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
                            minimumSize: Size.zero,
                            tapTargetSize: MaterialTapTargetSize.shrinkWrap,
                          ),
                          onPressed: () => _unblockUser(userId),
                          child: const Text('Entblocken', style: TextStyle(fontSize: 12)),
                        ),
                    ]),
                  ),
                  if (i < _ignoredUsers.length - 1) _Divider(pt: pt),
                ]);
              }),
          ]),
          const SizedBox(height: 20),
          Container(
            padding: const EdgeInsets.all(12),
            decoration: BoxDecoration(
              color: pt.accentSoft,
              borderRadius: BorderRadius.circular(10),
              border: Border.all(color: pt.accent.withAlpha(60)),
            ),
            child: Row(
              children: [
                Icon(Icons.shield_outlined, size: 16, color: pt.accent),
                const SizedBox(width: 10),
                Expanded(
                  child: Text(
                    'Alle Nachrichten sind Ende-zu-Ende-verschlüsselt.',
                    style: TextStyle(color: pt.fg, fontSize: 12),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// SESSIONS
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_SessionsSection> createState() => _SessionsSectionState();
}

class _SessionsSectionState extends ConsumerState<_SessionsSection> {
  List<DeviceKeys> _devices = [];
  bool _loading = true;
  String? _currentDeviceId;
  final Set<String> _revoking = {};
  bool _logoutAllLoading = false;
  String? _error;

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

  Future<void> _load() async {
    setState(() { _loading = true; _error = null; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      _currentDeviceId = client.deviceID;
      await client.updateUserDeviceKeys(additionalUsers: {client.userID!});
      final keys = client.userDeviceKeys[client.userID];
      if (mounted) {
        setState(() {
          _devices = keys?.deviceKeys.values.toList() ?? [];
          _loading = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _loading = false;
          _error = e.toString().split('\n').first;
        });
      }
    }
  }

  Future<void> _revokeDevice(String deviceId) async {
    setState(() => _revoking.add(deviceId));
    try {
      final client = await ref.read(matrixClientProvider.future);
      await client.deleteDevice(deviceId);
      await _load();
    } catch (e) {
      if (mounted) {
        setState(() {
          _revoking.remove(deviceId);
          _error = 'Abmelden fehlgeschlagen: ${e.toString().split('\n').first}';
        });
      }
    }
  }

  Future<void> _renameCurrentDevice(BuildContext context) async {
    final client = await ref.read(matrixClientProvider.future);
    if (!context.mounted) return;
    final currentName = _devices
        .where((d) => d.deviceId == _currentDeviceId)
        .map((d) => d.unsigned?['device_display_name'] as String? ?? d.deviceId ?? '')
        .firstOrNull ?? '';

    final ctrl = TextEditingController(text: currentName);
    final pt = widget.pt;
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: pt.bg2,
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(pt.rXl),
            side: BorderSide(color: pt.border)),
        title: Text('Gerät umbenennen',
            style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
        content: TextField(
          controller: ctrl,
          autofocus: true,
          style: TextStyle(color: pt.fg, fontSize: 13),
          decoration: InputDecoration(
            labelText: 'Gerätename',
            labelStyle: TextStyle(color: pt.fgMuted, 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: pt.border)),
            focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
          ),
          cursorColor: pt.accent,
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx, false), 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: () => Navigator.pop(ctx, true),
            child: const Text('Umbenennen'),
          ),
        ],
      ),
    );
    ctrl.dispose();
    if (confirmed != true || _currentDeviceId == null) return;
    try {
      await client.updateDevice(_currentDeviceId!, displayName: ctrl.text.trim());
      await _load();
    } catch (e) {
      if (mounted) setState(() => _error = 'Umbenennen fehlgeschlagen: ${e.toString().split('\n').first}');
    }
  }

  Future<void> _logoutAllOtherDevices(BuildContext context) async {
    final pt = widget.pt;
    final otherIds = _devices
        .where((d) => d.deviceId != _currentDeviceId && d.deviceId != null)
        .map((d) => d.deviceId!)
        .toList();
    if (otherIds.isEmpty) return;

    final confirm = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: pt.bg2,
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
        title: Text('Alle anderen Sitzungen abmelden?',
            style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
        content: Text(
          '${otherIds.length} Gerät${otherIds.length == 1 ? '' : 'e'} werden abgemeldet. Nicht gespeicherte Nachrichten auf diesen Geräten können verloren gehen.',
          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('Alle abmelden'),
          ),
        ],
      ),
    );
    if (confirm != true || !mounted) return;
    setState(() { _logoutAllLoading = true; _error = null; });

    final client = await ref.read(matrixClientProvider.future);
    try {
      await client.deleteDevices(otherIds);
    } on MatrixException catch (e) {
      if (e.requireAdditionalAuthentication && context.mounted) {
        final password = await _askPassword(context, pt);
        if (password == null || !mounted) { setState(() => _logoutAllLoading = false); return; }
        try {
          await client.deleteDevices(otherIds, auth: AuthenticationData(
            type: AuthenticationTypes.password,
            session: e.session,
            additionalFields: {
              'identifier': {'type': 'm.id.user', 'user': client.userID},
              'password': password,
            },
          ));
        } catch (e2) {
          if (mounted) setState(() { _logoutAllLoading = false; _error = 'Abmelden fehlgeschlagen: ${e2.toString().split('\n').first}'; });
          return;
        }
      } else {
        if (mounted) setState(() { _logoutAllLoading = false; _error = 'Abmelden fehlgeschlagen: ${e.toString().split('\n').first}'; });
        return;
      }
    } catch (e) {
      if (mounted) setState(() { _logoutAllLoading = false; _error = 'Abmelden fehlgeschlagen: ${e.toString().split('\n').first}'; });
      return;
    }
    await _load();
    if (mounted) setState(() => _logoutAllLoading = false);
  }

  Future<String?> _askPassword(BuildContext context, PyramidTheme pt) {
    final ctrl = TextEditingController();
    return showDialog<String>(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: pt.bg2,
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
        title: Text('Passwort bestätigen', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w700)),
        content: TextField(
          controller: ctrl,
          autofocus: true,
          obscureText: true,
          style: TextStyle(color: pt.fg, fontSize: 13),
          decoration: InputDecoration(
            labelText: 'Passwort',
            labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
            filled: true, fillColor: pt.bg3,
            contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
            border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
            enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
            focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
          ),
          cursorColor: pt.accent,
          onSubmitted: (_) => Navigator.pop(ctx, ctrl.text),
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx, null), 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: () => Navigator.pop(ctx, ctrl.text),
            child: const Text('Bestätigen'),
          ),
        ],
      ),
    ).then((v) { ctrl.dispose(); return v; });
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(
              title: 'Sitzungen',
              subtitle: 'Geräte, auf denen du angemeldet bist.',
              pt: pt),
          if (_loading)
            const Center(
                child: Padding(padding: EdgeInsets.all(32), child: CircularProgressIndicator.adaptive()))
          else if (_error != null)
            _StatusCard(
                pt: pt, color: pt.danger, icon: Icons.error_outline_rounded, text: _error!)
          else ...[
            _SettingsGroup(
              title: 'Aktive Sitzungen (${_devices.length})',
              pt: pt,
              children: _devices.isEmpty
                  ? [
                      Padding(
                        padding: const EdgeInsets.all(16),
                        child: Text('Keine Sitzungen gefunden.',
                            style: TextStyle(color: pt.fgMuted, fontSize: 13)),
                      )
                    ]
                  : _devices.asMap().entries.map((entry) {
                      final i = entry.key;
                      final device = entry.value;
                      final isCurrent = device.deviceId == _currentDeviceId;
                      final isRevoking = _revoking.contains(device.deviceId);
                      final isVerified = device.verified;
                      final displayName = device.unsigned?['device_display_name'] as String? ??
                          device.deviceId ??
                          'Unbekanntes Gerät';

                      return Column(
                        children: [
                          Padding(
                            padding: const EdgeInsets.symmetric(
                                horizontal: 14, vertical: 12),
                            child: Row(
                              children: [
                                Container(
                                  width: 36,
                                  height: 36,
                                  decoration: BoxDecoration(
                                    color: isCurrent
                                        ? pt.accentSoft
                                        : pt.bg3,
                                    borderRadius: BorderRadius.circular(8),
                                  ),
                                  child: Icon(
                                    Icons.devices_rounded,
                                    size: 18,
                                    color: isCurrent ? pt.accent : pt.fgMuted,
                                  ),
                                ),
                                const SizedBox(width: 12),
                                Expanded(
                                  child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: [
                                      Row(
                                        children: [
                                          Expanded(
                                            child: Text(
                                              displayName,
                                              style: TextStyle(
                                                color: pt.fg,
                                                fontSize: 13,
                                                fontWeight: FontWeight.w500,
                                              ),
                                            ),
                                          ),
                                          if (isCurrent) ...[
                                            Container(
                                              padding: const EdgeInsets.symmetric(
                                                  horizontal: 6, vertical: 2),
                                              decoration: BoxDecoration(
                                                color: pt.accentSoft,
                                                borderRadius: BorderRadius.circular(4),
                                              ),
                                              child: Text('Dieses Gerät',
                                                  style: TextStyle(
                                                      color: pt.accent,
                                                      fontSize: 10,
                                                      fontWeight: FontWeight.w600)),
                                            ),
                                            const SizedBox(width: 4),
                                            GestureDetector(
                                              onTap: () => _renameCurrentDevice(context),
                                              child: Tooltip(
                                                message: 'Gerät umbenennen',
                                                child: Icon(Icons.edit_rounded, size: 13, color: pt.fgDim),
                                              ),
                                            ),
                                          ],
                                          if (isVerified == true && !isCurrent)
                                            Icon(Icons.verified_rounded,
                                                size: 14, color: PyramidColors.success),
                                          if (isVerified == false && !isCurrent)
                                            Icon(Icons.warning_amber_rounded,
                                                size: 14, color: pt.away),
                                        ],
                                      ),
                                      const SizedBox(height: 2),
                                      SelectableText(
                                        device.deviceId ?? '',
                                        style: TextStyle(
                                            color: pt.fgDim,
                                            fontSize: 11,
                                            fontFamily: 'monospace'),
                                      ),
                                    ],
                                  ),
                                ),
                                if (!isCurrent) ...[
                                  const SizedBox(width: 8),
                                  if (isVerified == false)
                                    GestureDetector(
                                      onTap: () => _verifyDevice(context, device, displayName),
                                      child: Tooltip(
                                        message: 'Verifizieren',
                                        child: Icon(Icons.verified_user_outlined,
                                            size: 16, color: pt.accent),
                                      ),
                                    ),
                                  const SizedBox(width: 6),
                                  if (isRevoking)
                                    SizedBox(
                                      width: 16,
                                      height: 16,
                                      child: CircularProgressIndicator(
                                          strokeWidth: 2, color: pt.danger),
                                    )
                                  else
                                    GestureDetector(
                                      onTap: () => _confirmRevoke(context, device, displayName),
                                      child: Tooltip(
                                        message: 'Abmelden',
                                        child: Icon(Icons.logout_rounded,
                                            size: 16, color: pt.fgDim),
                                      ),
                                    ),
                                ],
                              ],
                            ),
                          ),
                          if (i < _devices.length - 1) _Divider(pt: pt),
                        ],
                      );
                    }).toList(),
            ),
            const SizedBox(height: 12),
            Row(
              children: [
                TextButton.icon(
                  style: TextButton.styleFrom(
                    foregroundColor: pt.fgMuted,
                    padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                  ),
                  onPressed: _load,
                  icon: const Icon(Icons.refresh_rounded, size: 14),
                  label: const Text('Aktualisieren', style: TextStyle(fontSize: 12)),
                ),
                const Spacer(),
                if (_logoutAllLoading)
                  const SizedBox(width: 16, height: 16, child: CircularProgressIndicator.adaptive(strokeWidth: 2))
                else if (_devices.where((d) => d.deviceId != _currentDeviceId).isNotEmpty)
                  TextButton.icon(
                    style: TextButton.styleFrom(
                      foregroundColor: pt.danger,
                      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                    ),
                    onPressed: () => _logoutAllOtherDevices(context),
                    icon: const Icon(Icons.logout_rounded, size: 14),
                    label: const Text('Alle anderen abmelden', style: TextStyle(fontSize: 12)),
                  ),
              ],
            ),
          ],
        ],
      ),
    );
  }

  Future<void> _verifyDevice(
      BuildContext context, DeviceKeys device, String displayName) async {
    await showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (ctx) => _SasVerificationDialog(
        pt: widget.pt,
        deviceName: displayName,
        device: device,
      ),
    );
    await _load();
  }

  Future<void> _confirmRevoke(
      BuildContext context, DeviceKeys device, String displayName) 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('Gerät abmelden?',
              style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
          content: Text(
            '"$displayName" wird abgemeldet und alle lokalen Schlüssel werden gelöscht.',
            style: TextStyle(color: pt.fgMuted, fontSize: 13),
          ),
          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('Abmelden'),
            ),
          ],
        );
      },
    );
    if (confirm == true && device.deviceId != null) {
      await _revokeDevice(device.deviceId!);
    }
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// SAS VERIFICATION DIALOG
// ─────────────────────────────────────────────────────────────────────────────

class _SasVerificationDialog extends ConsumerStatefulWidget {
  final PyramidTheme pt;
  final String deviceName;
  final DeviceKeys device;

  const _SasVerificationDialog({
    required this.pt,
    required this.deviceName,
    required this.device,
  });

  @override
  ConsumerState<_SasVerificationDialog> createState() => _SasVerificationDialogState();
}

class _SasVerificationDialogState extends ConsumerState<_SasVerificationDialog> {
  KeyVerification? _verification;
  KeyVerificationState _state = KeyVerificationState.waitingAccept;
  bool _starting = true;
  String? _error;

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

  @override
  void dispose() {
    final v = _verification;
    if (v != null && !v.isDone) {
      v.cancel('m.user').catchError((_) {});
    }
    super.dispose();
  }

  Future<void> _startVerification() async {
    try {
      final client = await ref.read(matrixClientProvider.future);
      final deviceId = widget.device.deviceId;
      if (deviceId == null) throw Exception('Keine Geräte-ID');

      final deviceKeys = client.userDeviceKeys[client.userID]?.deviceKeys[deviceId];
      if (deviceKeys == null) throw Exception('Gerät nicht gefunden');

      final verification = await deviceKeys.startVerification();
      _verification = verification;

      verification.onUpdate = () {
        if (!mounted) return;
        setState(() => _state = verification.state);
      };

      if (mounted) {
        setState(() {
          _state = verification.state;
          _starting = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _error = e.toString().split('\n').first;
          _starting = false;
        });
      }
    }
  }

  @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)),
      contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 0),
      title: Row(
        children: [
          Icon(Icons.verified_user_outlined, size: 20, color: pt.accent),
          const SizedBox(width: 10),
          Expanded(
            child: Text(
              'Gerät verifizieren',
              style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700),
            ),
          ),
        ],
      ),
      content: SizedBox(
        width: 380,
        child: _buildContent(pt),
      ),
      actions: _buildActions(pt),
    );
  }

  Widget _buildContent(PyramidTheme pt) {
    if (_error != null) {
      return Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: _StatusCard(
            pt: pt,
            icon: Icons.error_outline_rounded,
            color: pt.danger,
            text: _error!),
      );
    }

    if (_starting) {
      return const Padding(
        padding: EdgeInsets.symmetric(vertical: 24),
        child: Center(child: CircularProgressIndicator.adaptive()),
      );
    }

    final v = _verification;

    return switch (_state) {
      KeyVerificationState.waitingAccept || KeyVerificationState.askAccept => Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const CircularProgressIndicator.adaptive(),
            const SizedBox(height: 16),
            Text(
              'Warte auf Bestätigung von „${widget.deviceName}"…',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgMuted, fontSize: 13),
            ),
            const SizedBox(height: 8),
            Text(
              'Öffne Pyramid auf dem anderen Gerät und bestätige die Verifikationsanfrage.',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgDim, fontSize: 12),
            ),
          ],
        ),
      ),
      KeyVerificationState.askChoice => Padding(
        padding: const EdgeInsets.only(bottom: 8),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'Das andere Gerät hat die Anfrage akzeptiert.\nWähle eine Verifikationsmethode:',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgMuted, fontSize: 13),
            ),
            const SizedBox(height: 16),
            ElevatedButton.icon(
              style: ElevatedButton.styleFrom(
                backgroundColor: pt.accent,
                foregroundColor: pt.accentFg,
                elevation: 0,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                padding: const EdgeInsets.symmetric(vertical: 12),
              ),
              onPressed: () => v?.continueVerification('m.sas.v1').catchError((_) {}),
              icon: const Icon(Icons.tag_faces_rounded, size: 18),
              label: const Text('Emoji-Verifikation'),
            ),
            const SizedBox(height: 8),
            ElevatedButton.icon(
              style: ElevatedButton.styleFrom(
                backgroundColor: pt.bg3,
                foregroundColor: pt.fg,
                elevation: 0,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                padding: const EdgeInsets.symmetric(vertical: 12),
              ),
              onPressed: () => v?.continueVerification('m.qr_code.show.v1').catchError((_) {}),
              icon: const Icon(Icons.qr_code_rounded, size: 18),
              label: const Text('QR-Code anzeigen'),
            ),
            const SizedBox(height: 8),
          ],
        ),
      ),
      KeyVerificationState.confirmQRScan => _OutgoingQrCodeDisplay(
        pt: pt,
        request: v,
      ),
      KeyVerificationState.showQRSuccess => Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: _StatusCard(
            pt: pt,
            icon: Icons.verified_rounded,
            color: PyramidColors.success,
            text: 'QR-Code erfolgreich gescannt. Verifikation abgeschlossen.'),
      ),
      KeyVerificationState.askSas => _SasEmojiContent(
        pt: pt,
        verification: v!,
        onConfirm: () => v.acceptSas().catchError((_) {}),
        onReject: () {
          v.rejectSas().catchError((_) {});
          Navigator.of(context).pop();
        },
      ),
      KeyVerificationState.waitingSas => Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const CircularProgressIndicator.adaptive(),
            const SizedBox(height: 16),
            Text(
              'Warte auf Bestätigung des anderen Geräts…',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgMuted, fontSize: 13),
            ),
          ],
        ),
      ),
      KeyVerificationState.done => Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: _StatusCard(
            pt: pt,
            icon: Icons.verified_rounded,
            color: PyramidColors.success,
            text: '„${widget.deviceName}" wurde erfolgreich verifiziert.'),
      ),
      _ => Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: _StatusCard(
            pt: pt,
            icon: Icons.error_outline_rounded,
            color: pt.danger,
            text: 'Verifizierung fehlgeschlagen oder abgebrochen.'),
      ),
    };
  }

  List<Widget> _buildActions(PyramidTheme pt) {
    if (_state == KeyVerificationState.done ||
        _state == KeyVerificationState.showQRSuccess ||
        _error != null ||
        _state == KeyVerificationState.error) {
      return [
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: pt.accent,
              foregroundColor: pt.accentFg,
              elevation: 0,
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
          onPressed: () => Navigator.pop(context),
          child: const Text('Schließen'),
        ),
      ];
    }
    if (_state == KeyVerificationState.confirmQRScan) {
      return [
        TextButton(
          onPressed: () {
            _verification?.cancel('m.user').catchError((_) {});
            Navigator.pop(context);
          },
          child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)),
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: const Color(0xFF22C55E),
              foregroundColor: Colors.white,
              elevation: 0,
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
          onPressed: () => _verification?.acceptQRScanConfirmation().catchError((_) {}),
          child: const Text('Gescannt bestätigen'),
        ),
      ];
    }
    if (_state == KeyVerificationState.askSas || _state == KeyVerificationState.askChoice) {
      return [];
    }
    return [
      TextButton(
        onPressed: () {
          _verification?.cancel('m.user').catchError((_) {});
          Navigator.pop(context);
        },
        child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)),
      ),
    ];
  }
}

class _OutgoingQrCodeDisplay extends StatelessWidget {
  final PyramidTheme pt;
  final KeyVerification? request;

  const _OutgoingQrCodeDisplay({required this.pt, required this.request});

  @override
  Widget build(BuildContext context) {
    final qrCode = request?.qrCode;
    if (qrCode == null) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const CircularProgressIndicator.adaptive(),
            const SizedBox(height: 12),
            Text('QR-Code wird generiert…', style: TextStyle(color: pt.fgMuted, fontSize: 13)),
          ],
        ),
      );
    }

    try {
      final rawBytes = Uint8List.fromList(qrCode.qrDataRawBytes.toList());
      final qr = QrCode.fromUint8List(data: rawBytes, errorCorrectLevel: QrErrorCorrectLevel.L);
      final qrImage = QrImage(qr);

      return Padding(
        padding: const EdgeInsets.only(bottom: 8),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              'Scanne diesen QR-Code mit dem anderen Gerät',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgMuted, fontSize: 13),
            ),
            const SizedBox(height: 16),
            Center(
              child: Container(
                width: 220,
                height: 220,
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: PrettyQrView(qrImage: qrImage),
              ),
            ),
            const SizedBox(height: 12),
            Text(
              'Sobald das andere Gerät den Code gescannt hat, tippe auf „Gescannt bestätigen".',
              textAlign: TextAlign.center,
              style: TextStyle(color: pt.fgDim, fontSize: 12),
            ),
            const SizedBox(height: 8),
          ],
        ),
      );
    } catch (_) {
      return Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: _StatusCard(
          pt: pt,
          icon: Icons.error_outline_rounded,
          color: pt.danger,
          text: 'QR-Code konnte nicht generiert werden.',
        ),
      );
    }
  }
}

class _SasEmojiContent extends StatelessWidget {
  final PyramidTheme pt;
  final KeyVerification verification; // ignore: library_private_types_in_public_api
  final VoidCallback onConfirm;
  final VoidCallback onReject;

  const _SasEmojiContent({
    required this.pt,
    required this.verification,
    required this.onConfirm,
    required this.onReject,
  });

  @override
  Widget build(BuildContext context) {
    final emojis = verification.sasEmojis;

    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            'Vergleiche die Emojis auf beiden Geräten',
            textAlign: TextAlign.center,
            style: TextStyle(color: pt.fgMuted, fontSize: 13),
          ),
          const SizedBox(height: 20),
          if (emojis.isNotEmpty)
            Wrap(
              spacing: 12,
              runSpacing: 12,
              alignment: WrapAlignment.center,
              children: emojis.map((e) => _SasEmojiChip(emoji: e, pt: pt)).toList(),
            )
          else
            Text('Keine Emojis verfügbar.', style: TextStyle(color: pt.fgDim, fontSize: 13)),
          const SizedBox(height: 24),
          Text(
            'Stimmen alle Emojis überein?',
            style: TextStyle(color: pt.fg, fontSize: 14, fontWeight: FontWeight.w600),
          ),
          const SizedBox(height: 16),
          Row(
            children: [
              Expanded(
                child: OutlinedButton.icon(
                  style: OutlinedButton.styleFrom(
                    foregroundColor: pt.danger,
                    side: BorderSide(color: pt.danger.withAlpha(120)),
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                    padding: const EdgeInsets.symmetric(vertical: 12),
                  ),
                  onPressed: onReject,
                  icon: const Icon(Icons.close_rounded, size: 16),
                  label: const Text('Stimmt nicht überein'),
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: ElevatedButton.icon(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: PyramidColors.success,
                    foregroundColor: Colors.white,
                    elevation: 0,
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                    padding: const EdgeInsets.symmetric(vertical: 12),
                  ),
                  onPressed: onConfirm,
                  icon: const Icon(Icons.check_rounded, size: 16),
                  label: const Text('Stimmt überein'),
                ),
              ),
            ],
          ),
          const SizedBox(height: 8),
        ],
      ),
    );
  }
}

class _SasEmojiChip extends StatelessWidget {
  final KeyVerificationEmoji emoji;
  final PyramidTheme pt;

  const _SasEmojiChip({required this.emoji, required this.pt});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 68,
      padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
      decoration: BoxDecoration(
        color: pt.bg3,
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: pt.border),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(emoji.emoji, style: const TextStyle(fontSize: 26)),
          const SizedBox(height: 4),
          Text(
            emoji.name,
            style: TextStyle(color: pt.fgMuted, fontSize: 10),
            textAlign: TextAlign.center,
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
          ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// ENCRYPTION
// ─────────────────────────────────────────────────────────────────────────────

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…'),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// CHANGE RECOVERY KEY DIALOG
// ─────────────────────────────────────────────────────────────────────────────

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'),
          ),
        ),
      ],
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// FINGERPRINT GROUP
// ─────────────────────────────────────────────────────────────────────────────

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,
              ),
            ),
          ],
        ),
      ),
    ]);
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// CHANGE PASSWORD DIALOG
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_ChangePasswordDialog> createState() => _ChangePasswordDialogState();
}

class _ChangePasswordDialogState extends ConsumerState<_ChangePasswordDialog> {
  final _oldCtrl = TextEditingController();
  final _newCtrl = TextEditingController();
  final _confirmCtrl = TextEditingController();
  bool _oldVisible = false;
  bool _newVisible = false;
  bool _loading = false;
  String? _error;
  bool _done = false;

  @override
  void dispose() {
    _oldCtrl.dispose();
    _newCtrl.dispose();
    _confirmCtrl.dispose();
    super.dispose();
  }

  Future<void> _submit() async {
    final oldPw = _oldCtrl.text;
    final newPw = _newCtrl.text;
    final confirm = _confirmCtrl.text;
    if (oldPw.isEmpty || newPw.isEmpty) {
      setState(() => _error = 'Bitte alle Felder ausfüllen.');
      return;
    }
    if (newPw != confirm) {
      setState(() => _error = 'Die neuen Passwörter stimmen nicht überein.');
      return;
    }
    if (newPw.length < 8) {
      setState(() => _error = 'Das neue Passwort muss mindestens 8 Zeichen lang sein.');
      return;
    }
    setState(() { _loading = true; _error = null; });
    try {
      final client = await ref.read(matrixClientProvider.future);
      await client.changePassword(newPw, oldPassword: oldPw);
      if (mounted) setState(() { _loading = false; _done = true; });
    } on MatrixException catch (e) {
      if (mounted) setState(() { _loading = false; _error = e.errorMessage; });
    } catch (e) {
      if (mounted) setState(() { _loading = false; _error = e.toString().split('\n').first; });
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return Dialog(
      backgroundColor: pt.bg2,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 420, minWidth: 300),
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: _done ? _buildDone(pt) : _buildForm(pt),
        ),
      ),
    );
  }

  Widget _buildDone(PyramidTheme pt) => Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      Icon(Icons.check_circle_outline_rounded, color: PyramidColors.success, size: 52),
      const SizedBox(height: 12),
      Text('Passwort geändert', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
      const SizedBox(height: 6),
      Text('Dein Passwort wurde erfolgreich aktualisiert.',
          textAlign: TextAlign.center, style: TextStyle(color: pt.fgMuted, fontSize: 13)),
      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(),
        child: const Text('Schließen'),
      )),
    ],
  );

  Widget _buildForm(PyramidTheme pt) => Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(children: [
        Icon(Icons.lock_outline_rounded, size: 18, color: pt.accent),
        const SizedBox(width: 8),
        Text('Passwort ändern', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
        const Spacer(),
        IconButton(
          icon: Icon(Icons.close_rounded, color: pt.fgDim, size: 16),
          onPressed: () => Navigator.of(context).pop(),
          padding: EdgeInsets.zero,
          constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
        ),
      ]),
      const SizedBox(height: 20),
      _PwField(ctrl: _oldCtrl, label: 'Aktuelles Passwort', pt: pt,
          visible: _oldVisible, onToggle: () => setState(() => _oldVisible = !_oldVisible)),
      const SizedBox(height: 12),
      _PwField(ctrl: _newCtrl, label: 'Neues Passwort', pt: pt,
          visible: _newVisible, onToggle: () => setState(() => _newVisible = !_newVisible)),
      const SizedBox(height: 12),
      _PwField(ctrl: _confirmCtrl, label: 'Neues Passwort bestätigen', pt: pt,
          visible: _newVisible, onToggle: null,
          onSubmitted: (_) => _submit()),
      if (_error != null) ...[
        const SizedBox(height: 10),
        _StatusCard(pt: pt, color: pt.danger, icon: Icons.error_outline_rounded, text: _error!),
      ],
      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: _loading ? null : _submit,
        child: _loading
            ? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator.adaptive(strokeWidth: 2))
            : const Text('Passwort ändern'),
      )),
    ],
  );
}

class _PwField extends StatelessWidget {
  final TextEditingController ctrl;
  final String label;
  final PyramidTheme pt;
  final bool visible;
  final VoidCallback? onToggle;
  final ValueChanged<String>? onSubmitted;

  const _PwField({
    required this.ctrl, required this.label, required this.pt,
    required this.visible, required this.onToggle, this.onSubmitted,
  });

  @override
  Widget build(BuildContext context) => TextField(
    controller: ctrl,
    obscureText: !visible,
    onSubmitted: onSubmitted,
    style: TextStyle(color: pt.fg, fontSize: 13),
    decoration: InputDecoration(
      labelText: label,
      labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
      filled: true, fillColor: pt.bg3,
      contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
      enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
      focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)),
      suffixIcon: onToggle != null
          ? IconButton(
              icon: Icon(visible ? Icons.visibility_off_rounded : Icons.visibility_rounded, size: 16, color: pt.fgDim),
              onPressed: onToggle,
            )
          : null,
    ),
    cursorColor: pt.accent,
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// DELETE ACCOUNT DIALOG
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_DeleteAccountDialog> createState() => _DeleteAccountDialogState();
}

enum _DeleteStep { confirm, auth, deleting, error }

class _DeleteAccountDialogState extends ConsumerState<_DeleteAccountDialog> {
  _DeleteStep _step = _DeleteStep.confirm;
  final _pwCtrl = TextEditingController();
  bool _pwVisible = false;
  bool _eraseData = true;
  String? _error;

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

  Future<void> _proceed() async {
    setState(() { _step = _DeleteStep.deleting; _error = null; });
    final client = await ref.read(matrixClientProvider.future);
    try {
      await client.deactivateAccount(erase: _eraseData);
    } on MatrixException catch (e) {
      if (e.requireAdditionalAuthentication && mounted) {
        // UIA required — try with password
        try {
          await client.deactivateAccount(
            erase: _eraseData,
            auth: AuthenticationData(
              type: AuthenticationTypes.password,
              session: e.session,
              additionalFields: {
                'identifier': {'type': 'm.id.user', 'user': client.userID},
                'password': _pwCtrl.text,
              },
            ),
          );
        } on MatrixException catch (e2) {
          if (mounted) setState(() { _step = _DeleteStep.error; _error = e2.errorMessage; });
          return;
        } catch (e2) {
          if (mounted) setState(() { _step = _DeleteStep.error; _error = e2.toString().split('\n').first; });
          return;
        }
      } else {
        if (mounted) setState(() { _step = _DeleteStep.error; _error = e.errorMessage; });
        return;
      }
    } catch (e) {
      if (mounted) setState(() { _step = _DeleteStep.error; _error = e.toString().split('\n').first; });
      return;
    }
    // Konto wurde gelöscht — ausloggen
    try { await client.logout(); } catch (_) {}
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    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: 300),
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: switch (_step) {
            _DeleteStep.confirm => _buildConfirm(pt),
            _DeleteStep.auth => _buildAuth(pt),
            _DeleteStep.deleting => const Center(
              child: Padding(padding: EdgeInsets.all(32), child: CircularProgressIndicator.adaptive()),
            ),
            _DeleteStep.error => _buildError(pt),
          },
        ),
      ),
    );
  }

  Widget _buildConfirm(PyramidTheme pt) => Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(children: [
        Icon(Icons.warning_rounded, size: 20, color: pt.danger),
        const SizedBox(width: 8),
        Text('Konto löschen', style: TextStyle(color: pt.danger, fontSize: 15, fontWeight: FontWeight.w700)),
        const Spacer(),
        IconButton(
          icon: Icon(Icons.close_rounded, color: pt.fgDim, size: 16),
          onPressed: () => Navigator.of(context).pop(),
          padding: EdgeInsets.zero,
          constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
        ),
      ]),
      const SizedBox(height: 16),
      Container(
        padding: const EdgeInsets.all(12),
        decoration: BoxDecoration(
          color: pt.danger.withAlpha(20),
          borderRadius: BorderRadius.circular(8),
          border: Border.all(color: pt.danger.withAlpha(60)),
        ),
        child: Text(
          'Diese Aktion ist unwiderruflich. Dein Konto, alle Sitzungen und '
          'deine Mitgliedschaft in Räumen werden dauerhaft gelöscht.',
          style: TextStyle(color: pt.fg, fontSize: 13, height: 1.5),
        ),
      ),
      const SizedBox(height: 16),
      GestureDetector(
        onTap: () => setState(() => _eraseData = !_eraseData),
        child: Row(children: [
          SizedBox(
            width: 18, height: 18,
            child: Checkbox(
              value: _eraseData,
              onChanged: (v) => setState(() => _eraseData = v ?? true),
              activeColor: pt.accent,
              side: BorderSide(color: pt.border),
              materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
            ),
          ),
          const SizedBox(width: 8),
          Expanded(child: Text('Nachrichten und Daten vom Server löschen',
              style: TextStyle(color: pt.fg, fontSize: 13))),
        ]),
      ),
      const SizedBox(height: 20),
      Row(children: [
        Expanded(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(),
          child: const Text('Abbrechen'),
        )),
        const SizedBox(width: 12),
        Expanded(child: ElevatedButton(
          style: ElevatedButton.styleFrom(backgroundColor: pt.danger, foregroundColor: Colors.white,
              elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
          onPressed: () => setState(() => _step = _DeleteStep.auth),
          child: const Text('Weiter'),
        )),
      ]),
    ],
  );

  Widget _buildAuth(PyramidTheme pt) => Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(children: [
        Icon(Icons.lock_outline_rounded, size: 18, color: pt.danger),
        const SizedBox(width: 8),
        Text('Konto löschen bestätigen', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
      ]),
      const SizedBox(height: 16),
      Text('Gib dein Passwort ein, um die Löschung zu bestätigen.',
          style: TextStyle(color: pt.fgMuted, fontSize: 13)),
      const SizedBox(height: 16),
      TextField(
        controller: _pwCtrl,
        autofocus: true,
        obscureText: !_pwVisible,
        style: TextStyle(color: pt.fg, fontSize: 13),
        decoration: InputDecoration(
          labelText: 'Passwort',
          labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
          filled: true, fillColor: pt.bg3,
          contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
          border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
          enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
          focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.danger)),
          suffixIcon: IconButton(
            icon: Icon(_pwVisible ? Icons.visibility_off_rounded : Icons.visibility_rounded, size: 16, color: pt.fgDim),
            onPressed: () => setState(() => _pwVisible = !_pwVisible),
          ),
        ),
        cursorColor: pt.danger,
        onSubmitted: (_) => _proceed(),
      ),
      const SizedBox(height: 20),
      Row(children: [
        Expanded(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: () => setState(() => _step = _DeleteStep.confirm),
          child: const Text('Zurück'),
        )),
        const SizedBox(width: 12),
        Expanded(child: ElevatedButton(
          style: ElevatedButton.styleFrom(backgroundColor: pt.danger, foregroundColor: Colors.white,
              elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
          onPressed: _proceed,
          child: const Text('Konto endgültig löschen'),
        )),
      ]),
    ],
  );

  Widget _buildError(PyramidTheme pt) => Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      Icon(Icons.error_outline_rounded, color: pt.danger, size: 48),
      const SizedBox(height: 12),
      Text(_error ?? 'Ein 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(),
        child: const Text('Schließen'),
      )),
    ],
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// ABOUT
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final clientAsync = ref.watch(matrixClientProvider);
    final client = clientAsync.valueOrNull;

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionTitle(title: 'Über Pyramid', subtitle: 'Version & Konto-Infos.', pt: pt),
          _SettingsGroup(title: 'Version', pt: pt, children: [
            _SettingsRow(
                title: 'Pyramid',
                desc: '0.1.0 · Flutter · Matrix SDK',
                pt: pt,
                control: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
                  decoration: BoxDecoration(
                      color: pt.accentSoft,
                      borderRadius: BorderRadius.circular(6)),
                  child: Text('Beta',
                      style: TextStyle(
                          color: pt.accent, fontSize: 10, fontWeight: FontWeight.w700)),
                )),
            _Divider(pt: pt),
            _SettingsRow(
                title: 'Matrix SDK',
                desc: 'matrix-dart-sdk ^6.2.0',
                pt: pt),
          ]),
          const SizedBox(height: 20),
          _UpdatesSettingsGroup(pt: pt),
          const SizedBox(height: 20),
          _NotificationsSettingsGroup(pt: pt),
          const SizedBox(height: 20),
          if (client != null)
            _SettingsGroup(title: 'Konto', pt: pt, children: [
              _SettingsRow(
                title: 'Matrix-ID',
                desc: client.userID ?? '',
                pt: pt,
                control: GestureDetector(
                  onTap: () => Clipboard.setData(ClipboardData(text: client.userID ?? '')),
                  child: Tooltip(
                    message: 'Kopieren',
                    child: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim),
                  ),
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Homeserver',
                desc: client.homeserver?.toString() ?? '',
                pt: pt,
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Gerät',
                desc: client.deviceID ?? '',
                pt: pt,
                control: GestureDetector(
                  onTap: () =>
                      Clipboard.setData(ClipboardData(text: client.deviceID ?? '')),
                  child: Tooltip(
                    message: 'Kopieren',
                    child: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim),
                  ),
                ),
              ),
            ]),
          const SizedBox(height: 20),
          _SettingsGroup(title: 'Konto-Aktionen', pt: pt, children: [
            _SettingsRow(
              title: 'Abmelden',
              desc: 'Auf diesem Gerät ausloggen',
              pt: pt,
              destructive: true,
              showArrow: true,
              onTap: () => _confirmLogout(context, ref),
            ),
          ]),
        ],
      ),
    );
  }

  Future<void> _confirmLogout(BuildContext context, WidgetRef ref) async {
    final confirm = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        backgroundColor: pt.bg2,
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(pt.rXl),
            side: BorderSide(color: pt.border)),
        title: Text('Abmelden?',
            style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)),
        content: Text(
          'Du wirst auf diesem Gerät abgemeldet. Deine Nachrichten bleiben erhalten.',
          style: TextStyle(color: pt.fgMuted, fontSize: 13),
        ),
        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('Abmelden'),
          ),
        ],
      ),
    );
    if (confirm == true) {
      final client = await ref.read(matrixClientProvider.future);
      await client.logout().catchError((_) {});
    }
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// NOTIFICATIONS SETTINGS GROUP
// ─────────────────────────────────────────────────────────────────────────────

class _NotificationsSettingsGroup extends ConsumerStatefulWidget {
  final PyramidTheme pt;
  const _NotificationsSettingsGroup({required this.pt});
  @override
  ConsumerState<_NotificationsSettingsGroup> createState() =>
      _NotificationsSettingsGroupState();
}

class _NotificationsSettingsGroupState
    extends ConsumerState<_NotificationsSettingsGroup> {
  bool _running = false;
  String? _result;
  bool _retryRunning = false;
  String? _retryResult;

  Future<void> _runDiag() async {
    setState(() { _running = true; _result = null; });
    final client = await ref.read(matrixClientProvider.future);
    final diag = await fcmDiagnostics(client);
    if (mounted) setState(() { _running = false; _result = diag; });
  }

  Future<void> _runRetry() async {
    setState(() { _retryRunning = true; _retryResult = null; });
    final client = await ref.read(matrixClientProvider.future);
    final result = await retryPusherRegistration(client);
    if (mounted) setState(() { _retryRunning = false; _retryResult = result; });
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    if (!Platform.isAndroid) return const SizedBox.shrink();
    return _SettingsGroup(title: 'Push-Benachrichtigungen', pt: pt, children: [
      _SettingsRow(
        title: 'Akku-Optimierung deaktivieren',
        desc: 'Samsung / andere OEMs unterdrücken sonst Benachrichtigungen bei geschlossener App.',
        pt: pt,
        showArrow: true,
        onTap: openBatteryOptimizationSettings,
        control: Icon(Icons.battery_saver_outlined, size: 16, color: pt.fgDim),
      ),
      _Divider(pt: pt),
      _SettingsRow(
        title: 'FCM Diagnose',
        desc: _result ?? 'Prüft ob FCM-Token & Pusher korrekt registriert sind.',
        pt: pt,
        onTap: _running ? null : _runDiag,
        control: _running
            ? SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2, color: pt.accent),
              )
            : Icon(Icons.bug_report_outlined, size: 16, color: pt.fgDim),
      ),
      _Divider(pt: pt),
      _SettingsRow(
        title: 'Pusher neu registrieren',
        desc: _retryResult ?? 'Pusher beim Homeserver erneut anmelden.',
        pt: pt,
        onTap: _retryRunning ? null : _runRetry,
        control: _retryRunning
            ? SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2, color: pt.accent),
              )
            : Icon(Icons.sync_outlined, size: 16, color: pt.fgDim),
      ),
    ]);
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// UPDATE SETTINGS GROUP
// ─────────────────────────────────────────────────────────────────────────────

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

  @override
  ConsumerState<_UpdatesSettingsGroup> createState() =>
      _UpdatesSettingsGroupState();
}

class _UpdatesSettingsGroupState extends ConsumerState<_UpdatesSettingsGroup> {
  bool _checking = false;

  Future<void> _checkNow() async {
    setState(() => _checking = true);
    await resetUpdateCheckTimer();
    ref.invalidate(updateInfoProvider);
    // Give the async provider a moment to resolve before resetting the spinner.
    await Future<void>.delayed(const Duration(seconds: 2));
    if (mounted) setState(() => _checking = false);
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    final updateAsync = ref.watch(updateInfoProvider);

    return _SettingsGroup(title: 'Updates', pt: pt, children: [
      updateAsync.when(
        loading: () => _SettingsRow(
          title: 'Nach Updates suchen…',
          desc: 'Gitea Releases wird geprüft',
          pt: pt,
          control: SizedBox(
            width: 16,
            height: 16,
            child: CircularProgressIndicator(
              strokeWidth: 2,
              color: pt.accent,
            ),
          ),
        ),
        error: (e, _) => _SettingsRow(
          title: 'Update-Prüfung fehlgeschlagen',
          desc: 'Keine Verbindung oder Gitea nicht erreichbar (nur im Heimnetz).',
          pt: pt,
          control: _checking
              ? SizedBox(
                  width: 16,
                  height: 16,
                  child: CircularProgressIndicator(
                      strokeWidth: 2, color: pt.accent),
                )
              : GestureDetector(
                  onTap: _checkNow,
                  child: Tooltip(
                    message: 'Erneut prüfen',
                    child: Icon(Icons.refresh_rounded,
                        size: 16, color: pt.fgDim),
                  ),
                ),
        ),
        data: (info) {
          if (info == null) {
            return _SettingsRow(
              title: 'Pyramid ist aktuell',
              desc: 'Du verwendest die neueste Version.',
              pt: pt,
              onTap: _checking ? null : _checkNow,
              control: _checking
                  ? SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator(
                          strokeWidth: 2, color: pt.accent),
                    )
                  : Icon(Icons.refresh_rounded, size: 16, color: pt.fgDim),
            );
          }
          // New version available
          return Column(
            children: [
              _SettingsRow(
                title: 'Update verfügbar: ${info.tagName}',
                desc: info.releaseName,
                pt: pt,
                showArrow: true,
                onTap: () async {
                  if (info.canDownload) {
                    await showUpdateDownloadDialog(context, info);
                  } else {
                    final uri = Uri.parse(info.htmlUrl);
                    if (await canLaunchUrl(uri)) {
                      await launchUrl(uri, mode: LaunchMode.externalApplication);
                    }
                  }
                },
                control: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
                  decoration: BoxDecoration(
                      color: pt.accent,
                      borderRadius: BorderRadius.circular(6)),
                  child: Text(
                    'Neu',
                    style: TextStyle(
                        color: pt.accentFg,
                        fontSize: 10,
                        fontWeight: FontWeight.w700),
                  ),
                ),
              ),
              _Divider(pt: pt),
              _SettingsRow(
                title: 'Diese Version überspringen',
                desc: '${info.tagName} nicht mehr anzeigen',
                pt: pt,
                onTap: () async {
                  await skipUpdateVersion(info.tagName);
                  ref.invalidate(updateInfoProvider);
                },
              ),
            ],
          );
        },
      ),
    ]);
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// Passphrase dialog (for key export/import)
// ─────────────────────────────────────────────────────────────────────────────

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'),
        ),
      ],
    );
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// Shared small widgets
// ─────────────────────────────────────────────────────────────────────────────

class _StatusCard extends StatelessWidget {
  final PyramidTheme pt;
  final IconData icon;
  final Color color;
  final String text;
  const _StatusCard(
      {required this.pt, required this.icon, required this.color, required this.text});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: pt.bg2,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: pt.border),
      ),
      child: Row(
        children: [
          Icon(icon, color: color, size: 18),
          const SizedBox(width: 10),
          Expanded(child: Text(text, style: TextStyle(color: pt.fgMuted, fontSize: 13))),
        ],
      ),
    );
  }
}
