import 'dart:typed_data';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart' hide Visibility;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:matrix/matrix.dart';
import 'package:pyramid/core/app_state.dart';
import 'package:pyramid/core/matrix_client.dart';
import 'package:pyramid/core/theme.dart';
import 'package:pyramid/widgets/banner_crop_dialog.dart';
import 'package:pyramid/widgets/mxc_image.dart';

const _kSpaceBannerEvent = 'io.pyramid.space.banner';

/// Ändert die Power-Levels auf Basis des aktuellen SERVER-Stands (nicht des
/// lokalen Caches!). Das SDK-`setPower` und ein lokal fehlendes
/// power_levels-Event haben sonst zur Folge, dass die users-Map des Servers
/// mit einem Minimal-Event überschrieben wird — und Admins sich selbst
/// aussperren (genau das ist hier bereits passiert).
Future<void> updatePowerLevelsSafely(
  Room room,
  void Function(Map<String, dynamic> content) mutate,
) async {
  Map<String, dynamic> content;
  try {
    content = Map<String, dynamic>.from(
      await room.client
          .getRoomStateWithKey(room.id, EventTypes.RoomPowerLevels, ''),
    );
  } on MatrixException catch (e) {
    if (e.errcode == 'M_NOT_FOUND') {
      content = <String, dynamic>{};
    } else {
      rethrow;
    }
  }
  final users = content['users'];
  content['users'] =
      users is Map ? Map<String, dynamic>.from(users) : <String, dynamic>{};
  mutate(content);

  // Selbst-Aussperr-Schutz: Nach der Änderung muss der eigene Account immer
  // noch hoch genug stehen, um Power-Levels erneut zu ändern — sonst gäbe es
  // kein Zurück mehr. Greift bei versehentlicher Selbst-Degradierung ODER
  // wenn ein neu geschriebenes Event den eigenen Eintrag nicht enthält.
  final myId = room.client.userID;
  if (myId != null) {
    int asInt(Object? v, int fallback) => v is int ? v : fallback;
    final usersMap = content['users'] as Map<String, dynamic>;
    final myLevel = asInt(usersMap[myId], asInt(content['users_default'], 0));
    final events = content['events'];
    final neededForPl = (events is Map && events['m.room.power_levels'] != null)
        ? asInt(events['m.room.power_levels'], 50)
        : asInt(content['state_default'], 50);
    if (myLevel < neededForPl) {
      throw Exception(
        'Abgebrochen, um eine Selbst-Aussperrung zu verhindern: '
        'Diese Änderung würde dein Level auf $myLevel senken, '
        'aber zum Verwalten der Berechtigungen sind $neededForPl nötig.',
      );
    }
  }

  await room.client
      .setRoomStateWithKey(room.id, EventTypes.RoomPowerLevels, '', content);
}

// Power level labels
const _kPowerAdmin = 100;
const _kPowerMod = 50;
const _kPowerMember = 0;

String _powerLabel(int level) {
  if (level >= _kPowerAdmin) return 'Admin';
  if (level >= _kPowerMod) return 'Moderator';
  return 'Mitglied';
}

Color _powerColor(int level, PyramidTheme pt) {
  if (level >= _kPowerAdmin) return const Color(0xFFED4245);
  if (level >= _kPowerMod) return const Color(0xFFF59E0B);
  return pt.fgDim;
}

class SpaceAdminDialog extends ConsumerStatefulWidget {
  final Room space;
  const SpaceAdminDialog({super.key, required this.space});

  @override
  ConsumerState<SpaceAdminDialog> createState() => _SpaceAdminDialogState();
}

class _SpaceAdminDialogState extends ConsumerState<SpaceAdminDialog>
    with SingleTickerProviderStateMixin {
  late TabController _tabs;

  @override
  void initState() {
    super.initState();
    _tabs = TabController(length: 4, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    return Dialog(
      backgroundColor: pt.bg1,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(pt.rLg)),
      insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 560, maxHeight: 680),
        child: Column(
          children: [
            _DialogHeader(space: widget.space, pt: pt, tabs: _tabs),
            Expanded(
              child: TabBarView(
                controller: _tabs,
                children: [
                  _OverviewTab(space: widget.space),
                  _MembersTab(space: widget.space),
                  _PermissionsTab(space: widget.space),
                  _ChannelsTab(space: widget.space),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ─── Header + TabBar ─────────────────────────────────────────────────────────

class _DialogHeader extends ConsumerWidget {
  final Room space;
  final PyramidTheme pt;
  final TabController tabs;
  const _DialogHeader({required this.space, required this.pt, required this.tabs});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final client = ref.watch(matrixClientProvider).valueOrNull;
    final name = space.getLocalizedDisplayname();
    final avatarUri = space.avatar;
    final letter = name.isNotEmpty ? name[0].toUpperCase() : '?';

    Widget avatar;
    if (client != null && avatarUri != null) {
      avatar = MxcAvatar(
        mxcUri: avatarUri,
        client: client,
        size: 40,
        borderRadius: BorderRadius.circular(pt.rBase),
        placeholder: (_) => _InitialAvatar(letter: letter, pt: pt, size: 40),
      );
    } else {
      avatar = _InitialAvatar(letter: letter, pt: pt, size: 40);
    }

    return Container(
      decoration: BoxDecoration(
        border: Border(bottom: BorderSide(color: pt.border)),
      ),
      child: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(20, 20, 12, 12),
            child: Row(
              children: [
                avatar,
                const SizedBox(width: 12),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(name,
                          style: TextStyle(
                              color: pt.fg,
                              fontSize: 17,
                              fontWeight: FontWeight.w700)),
                      Text('Space-Einstellungen',
                          style: TextStyle(color: pt.fgDim, fontSize: 12)),
                    ],
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.close, color: pt.fgDim, size: 20),
                  onPressed: () => Navigator.of(context).pop(),
                ),
              ],
            ),
          ),
          TabBar(
            controller: tabs,
            labelColor: pt.accent,
            unselectedLabelColor: pt.fgDim,
            indicatorColor: pt.accent,
            indicatorSize: TabBarIndicatorSize.label,
            tabs: const [
              Tab(text: 'Übersicht'),
              Tab(text: 'Mitglieder'),
              Tab(text: 'Berechtigungen'),
              Tab(text: 'Kanäle'),
            ],
          ),
        ],
      ),
    );
  }
}

// ─── Overview Tab ─────────────────────────────────────────────────────────────

class _OverviewTab extends ConsumerStatefulWidget {
  final Room space;
  const _OverviewTab({required this.space});

  @override
  ConsumerState<_OverviewTab> createState() => _OverviewTabState();
}

class _OverviewTabState extends ConsumerState<_OverviewTab> {
  late TextEditingController _nameCtrl;
  late TextEditingController _topicCtrl;
  bool _saving = false;
  String? _error;
  String? _success;
  Uint8List? _pendingBanner;
  String? _bannerMxcUri;
  Uint8List? _pendingAvatar;

  @override
  void initState() {
    super.initState();
    _nameCtrl = TextEditingController(
        text: widget.space.getLocalizedDisplayname());
    _topicCtrl = TextEditingController(text: widget.space.topic);
    // Prefer local override (set on last save) over room state event
    final overrides = ref.read(spaceBannerOverrideProvider);
    _bannerMxcUri = overrides.containsKey(widget.space.id)
        ? overrides[widget.space.id]
        : widget.space.getState(_kSpaceBannerEvent)?.content['url'] as String?;
  }

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

  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: 2.4),
    );
    if (cropped == null || !mounted) return;
    setState(() => _pendingBanner = cropped);
  }

  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 raw = file.bytes ?? await file.xFile.readAsBytes();
    if (!mounted) return;
    setState(() => _pendingAvatar = raw);
  }

  Future<void> _removeAvatar() async {
    setState(() => _pendingAvatar = null);
    try {
      await widget.space.client.setRoomStateWithKey(
          widget.space.id, EventTypes.RoomAvatar, '', {});
    } catch (_) {}
  }

  Future<void> _removeBanner() async {
    setState(() { _pendingBanner = null; _bannerMxcUri = null; });
    try {
      await widget.space.client.setRoomStateWithKey(
          widget.space.id, _kSpaceBannerEvent, '', {});
    } catch (_) {}
  }

  Future<void> _save() async {
    setState(() { _saving = true; _error = null; _success = null; });
    // Welcher Schritt gerade läuft — für präzise Fehlermeldungen statt eines
    // pauschalen "keine Berechtigung" (das auch von Upload-Fehlern kam).
    var step = 'Speichern';
    try {
      final client = widget.space.client;
      final newName = _nameCtrl.text.trim();
      final newTopic = _topicCtrl.text.trim();
      if (newName.isNotEmpty &&
          newName != widget.space.getLocalizedDisplayname()) {
        step = 'Name ändern';
        await widget.space.setName(newName);
      }
      if (newTopic != widget.space.topic) {
        step = 'Beschreibung ändern';
        await widget.space.setDescription(newTopic);
      }
      if (_pendingAvatar != null) {
        step = 'Avatar hochladen';
        final uri = await client.uploadContent(
          _pendingAvatar!,
          filename: 'space_avatar.jpg',
          contentType: 'image/jpeg',
        );
        step = 'Avatar setzen';
        await _setStateWithRepair(
          client,
          EventTypes.RoomAvatar,
          {'url': uri.toString()},
        );
        setState(() => _pendingAvatar = null);
      }
      if (_pendingBanner != null) {
        step = 'Banner hochladen';
        final uri = await client.uploadContent(
          _pendingBanner!,
          filename: 'space_banner.jpg',
          contentType: 'image/jpeg',
        );
        step = 'Banner setzen';
        await _setStateWithRepair(
          client,
          _kSpaceBannerEvent,
          {'url': uri.toString()},
        );
        final uriStr = uri.toString();
        // Update local override immediately so header updates before next sync
        ref.read(spaceBannerOverrideProvider.notifier).update(
              (m) => {...m, widget.space.id: uriStr},
            );
        setState(() {
          _bannerMxcUri = uriStr;
          _pendingBanner = null;
        });
      }
      setState(() { _success = 'Gespeichert'; });
    } catch (e) {
      // Server-Fehlertext anzeigen (MatrixException liefert lesbare Meldung) —
      // nicht pauschal auf "Admin-Level erforderlich" mappen.
      final raw = e is MatrixException
          ? '${e.errcode}: ${e.errorMessage}'
          : e.toString().split('\n').first;
      var detail = '';
      if (e is MatrixException && e.errcode == 'M_FORBIDDEN') {
        detail = '\n${_permDiagnostics()}';
        // Zusätzlich die SERVER-Sicht der Power-Levels holen — die lokale
        // Kopie kann von der des Servers abweichen.
        try {
          final pl = await widget.space.client.getRoomStateWithKey(
              widget.space.id, EventTypes.RoomPowerLevels, '');
          detail +=
              '\nServer-PL: users=${pl['users']}, state_default=${pl['state_default']}, events=${pl['events']}';
        } on MatrixException catch (se) {
          detail += '\nServer-PL: ${se.errcode} (Event fehlt auch serverseitig)';
        } catch (_) {}
      }
      setState(() {
        _error = '$step fehlgeschlagen – $raw$detail';
      });
    } finally {
      setState(() { _saving = false; });
    }
  }

  /// Sendet ein State-Event; bei M_FORBIDDEN in einem Raum OHNE
  /// power_levels-Event (Continuwuity-Sonderfall: Server verweigert dann
  /// Custom-State trotz Creator-Rechten) werden einmalig Standard-Power-Levels
  /// mit uns als Admin angelegt und der Vorgang wiederholt.
  Future<void> _setStateWithRepair(
    Client client,
    String type,
    Map<String, Object?> content,
  ) async {
    try {
      await client.setRoomStateWithKey(widget.space.id, type, '', content);
    } on MatrixException catch (e) {
      if (e.errcode != 'M_FORBIDDEN') rethrow;
      final repaired = await _ensurePowerLevels(client);
      if (!repaired) rethrow;
      await client.setRoomStateWithKey(widget.space.id, type, '', content);
    }
  }

  /// Legt Standard-Power-Levels an, falls der Raum keine hat.
  /// Gibt true zurück, wenn das Event neu angelegt wurde.
  Future<bool> _ensurePowerLevels(Client client) async {
    try {
      await client.getRoomStateWithKey(
          widget.space.id, EventTypes.RoomPowerLevels, '');
      return false; // existiert bereits — Ablehnung hat andere Ursache
    } catch (_) {
      // fehlt (M_NOT_FOUND) → anlegen
    }
    try {
      await client.setRoomStateWithKey(
        widget.space.id,
        EventTypes.RoomPowerLevels,
        '',
        {
          'users': {client.userID!: 100},
          'users_default': 0,
          'events': const <String, Object?>{},
          'events_default': 0,
          'state_default': 50,
          'ban': 50,
          'kick': 50,
          'redact': 50,
          'invite': 0,
          'notifications': {'room': 50},
        },
      );
      return true;
    } catch (_) {
      return false;
    }
  }

  /// Kompakte Berechtigungs-Diagnose für M_FORBIDDEN-Fehler: zeigt, wie der
  /// Server die Lage vermutlich sieht (Raumversion, Creator, Power-Level).
  String _permDiagnostics() {
    try {
      final space = widget.space;
      final create = space.getState(EventTypes.RoomCreate);
      final version = create?.content['room_version'] ?? '1';
      final creator = create?.senderId ?? '?';
      final own = space.ownPowerLevel;
      final needed = space.powerForChangingStateEvent(_kSpaceBannerEvent);
      final hasPl = space.getState(EventTypes.RoomPowerLevels) != null;
      return 'Diagnose: dein Level $own, benötigt $needed, '
          'Raumversion $version, Creator $creator'
          '${hasPl ? '' : ', kein power_levels-Event vorhanden!'}';
    } catch (_) {
      return '';
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final client = ref.watch(matrixClientProvider).valueOrNull;
    // SDK-Berechtigungsprüfung (berücksichtigt events-Overrides und
    // state_default) statt hartcodiertem Admin-Level 100.
    final canEdit = widget.space.canChangeStateEvent(EventTypes.RoomName) ||
        widget.space.ownPowerLevel >= _kPowerAdmin;
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // ── Banner ────────────────────────────────────────────────────────
          _SectionLabel('Banner', pt),
          const SizedBox(height: 6),
          GestureDetector(
            onTap: canEdit ? _pickBanner : null,
            child: Stack(
              children: [
                AspectRatio(
                  aspectRatio: 2.4,
                  child: Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    color: pt.bg0,
                    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)
                      : (_bannerMxcUri != null && client != null
                          ? MxcImage(
                              mxcUri: _bannerMxcUri!,
                              client: client,
                              fit: BoxFit.cover,
                              width: double.infinity,
                              placeholder: Container(color: pt.bg0),
                            )
                          : Center(
                              child: Column(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(Icons.add_photo_alternate_outlined,
                                      size: 24, color: pt.fgDim),
                                  const SizedBox(height: 4),
                                  Text('Banner hinzufügen',
                                      style: TextStyle(
                                          color: pt.fgDim, fontSize: 12)),
                                ],
                              ),
                            )),
                  ), // Container
                ), // AspectRatio
                if (canEdit)
                  Positioned(
                    right: 6,
                    bottom: 6,
                    child: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        if (_bannerMxcUri != null || _pendingBanner != null)
                          GestureDetector(
                            onTap: _removeBanner,
                            child: Container(
                              margin: const EdgeInsets.only(right: 4),
                              padding: const EdgeInsets.symmetric(
                                  horizontal: 7, vertical: 3),
                              decoration: BoxDecoration(
                                color: const Color(0xFFED4245).withAlpha(200),
                                borderRadius: BorderRadius.circular(5),
                              ),
                              child: const Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(Icons.delete_outline,
                                      size: 11, color: Colors.white),
                                  SizedBox(width: 3),
                                  Text('Entfernen',
                                      style: TextStyle(
                                          color: Colors.white, fontSize: 11)),
                                ],
                              ),
                            ),
                          ),
                        Container(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 7, vertical: 3),
                          decoration: BoxDecoration(
                            color: pt.bg0.withAlpha(200),
                            borderRadius: BorderRadius.circular(5),
                            border: Border.all(color: pt.border),
                          ),
                          child: Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Icon(Icons.edit_rounded,
                                  size: 11, color: pt.fgMuted),
                              const SizedBox(width: 3),
                              Text('Banner ändern',
                                  style: TextStyle(
                                      color: pt.fgMuted, fontSize: 11)),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
              ],
            ),
          ),
          const SizedBox(height: 16),
          // ── Avatar ───────────────────────────────────────────────────────
          _SectionLabel('Serverbild', pt),
          const SizedBox(height: 8),
          _AvatarEditor(
            space: widget.space,
            pendingAvatar: _pendingAvatar,
            canEdit: canEdit,
            onPick: _pickAvatar,
            onRemove: _removeAvatar,
            pt: pt,
            client: client,
          ),
          const SizedBox(height: 16),
          // ── Name / Topic ─────────────────────────────────────────────────
          _SectionLabel('Name', pt),
          const SizedBox(height: 6),
          _Field(controller: _nameCtrl, hint: 'Space-Name', pt: pt),
          const SizedBox(height: 16),
          _SectionLabel('Beschreibung', pt),
          const SizedBox(height: 6),
          _Field(
              controller: _topicCtrl,
              hint: 'Worum geht es in diesem Space?',
              maxLines: 4,
              pt: pt),
          const SizedBox(height: 8),
          _SpaceVisibilityToggle(space: widget.space),
          const SizedBox(height: 20),
          if (_error != null)
            Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: Text(_error!,
                  style: const TextStyle(color: Color(0xFFED4245), fontSize: 12)),
            ),
          if (_success != null)
            Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: Text(_success!,
                  style: const TextStyle(color: Color(0xFF57F287), fontSize: 12)),
            ),
          SizedBox(
            width: double.infinity,
            child: FilledButton(
              onPressed: _saving ? null : _save,
              style: FilledButton.styleFrom(
                backgroundColor: pt.accent,
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(pt.rBase)),
              ),
              child: _saving
                  ? SizedBox(
                      width: 18,
                      height: 18,
                      child: CircularProgressIndicator(
                          strokeWidth: 2, color: pt.bg0))
                  : const Text('Speichern',
                      style: TextStyle(
                          color: Colors.white, fontWeight: FontWeight.w600)),
            ),
          ),
          const SizedBox(height: 24),
          _DangerZone(space: widget.space),
        ],
      ),
    );
  }
}

class _SpaceVisibilityToggle extends ConsumerStatefulWidget {
  final Room space;
  const _SpaceVisibilityToggle({required this.space});

  @override
  ConsumerState<_SpaceVisibilityToggle> createState() =>
      _SpaceVisibilityToggleState();
}

class _SpaceVisibilityToggleState
    extends ConsumerState<_SpaceVisibilityToggle> {
  bool? _isPublic;
  bool _loading = true;

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

  Future<void> _load() async {
    try {
      final joinRules = widget.space.joinRules;
      setState(() {
        _isPublic = joinRules == JoinRules.public;
        _loading = false;
      });
    } catch (_) {
      setState(() => _loading = false);
    }
  }

  Future<void> _toggle(bool value) async {
    setState(() => _isPublic = value);
    try {
      await widget.space.setJoinRules(
          value ? JoinRules.public : JoinRules.invite);
    } catch (_) {
      setState(() => _isPublic = !value);
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    if (_loading) return const SizedBox.shrink();
    return Row(
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Öffentlicher Space',
                  style: TextStyle(
                      color: pt.fg,
                      fontSize: 13,
                      fontWeight: FontWeight.w600)),
              Text('Jeder kann diesem Space beitreten',
                  style: TextStyle(color: pt.fgDim, fontSize: 12)),
            ],
          ),
        ),
        Switch(
          value: _isPublic ?? false,
          onChanged: _toggle,
          activeThumbColor: pt.accent,
        ),
      ],
    );
  }
}

class _DangerZone extends ConsumerWidget {
  final Room space;
  const _DangerZone({required this.space});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final pt = PyramidTheme.of(context);
    return Container(
      decoration: BoxDecoration(
        border: Border.all(color: const Color(0xFFED4245).withAlpha(80)),
        borderRadius: BorderRadius.circular(pt.rBase),
      ),
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('Gefahrenzone',
              style: TextStyle(
                  color: const Color(0xFFED4245),
                  fontSize: 13,
                  fontWeight: FontWeight.w700)),
          const SizedBox(height: 12),
          Row(
            children: [
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Space verlassen',
                        style: TextStyle(
                            color: pt.fg,
                            fontSize: 13,
                            fontWeight: FontWeight.w600)),
                    Text('Du verlässt diesen Space',
                        style: TextStyle(color: pt.fgDim, fontSize: 12)),
                  ],
                ),
              ),
              OutlinedButton(
                onPressed: () => _confirmLeave(context, ref),
                style: OutlinedButton.styleFrom(
                  foregroundColor: const Color(0xFFED4245),
                  side: const BorderSide(color: Color(0xFFED4245)),
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(8)),
                ),
                child: const Text('Verlassen'),
              ),
            ],
          ),
        ],
      ),
    );
  }

  void _confirmLeave(BuildContext context, WidgetRef ref) {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        title: const Text('Space verlassen?'),
        content: Text(
            'Möchtest du "${space.getLocalizedDisplayname()}" wirklich verlassen?'),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Abbrechen')),
          TextButton(
            onPressed: () async {
              Navigator.pop(context);
              Navigator.pop(context);
              await space.leave();
            },
            child: const Text('Verlassen',
                style: TextStyle(color: Color(0xFFED4245))),
          ),
        ],
      ),
    );
  }
}

// ─── Members Tab ──────────────────────────────────────────────────────────────

class _MembersTab extends ConsumerStatefulWidget {
  final Room space;
  const _MembersTab({required this.space});

  @override
  ConsumerState<_MembersTab> createState() => _MembersTabState();
}

class _MembersTabState extends ConsumerState<_MembersTab> {
  List<User>? _members;
  bool _loading = true;
  String? _error;
  String _filter = '';
  final _filterCtrl = TextEditingController();

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

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

  Future<void> _load() async {
    try {
      final members =
          await widget.space.requestParticipants([Membership.join, Membership.invite]);
      if (mounted) setState(() { _members = members; _loading = false; });
    } catch (e) {
      if (mounted) setState(() { _error = e.toString(); _loading = false; });
    }
  }

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

    if (_loading) {
      return Center(
          child: CircularProgressIndicator(color: pt.accent, strokeWidth: 2));
    }
    if (_error != null) {
      return Center(
          child: Text(_error!, style: TextStyle(color: pt.fgDim, fontSize: 13)));
    }

    final members = _members ?? [];
    final filtered = _filter.isEmpty
        ? members
        : members
            .where((m) =>
                (m.displayName ?? m.id)
                    .toLowerCase()
                    .contains(_filter.toLowerCase()) ||
                m.id.toLowerCase().contains(_filter.toLowerCase()))
            .toList()
      ..sort((a, b) => (b.powerLevel).compareTo(a.powerLevel));

    final ownPower = widget.space.ownPowerLevel;

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
          child: Row(
            children: [
              Expanded(
                child: _SearchField(
                  controller: _filterCtrl,
                  hint: 'Mitglieder suchen…',
                  pt: pt,
                  onChanged: (v) => setState(() => _filter = v),
                ),
              ),
              if (ownPower >= _kPowerMod) ...[
                const SizedBox(width: 8),
                _InviteButton(space: widget.space),
              ],
            ],
          ),
        ),
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.symmetric(horizontal: 8),
            itemCount: filtered.length,
            itemBuilder: (context, i) {
              final member = filtered[i];
              return _MemberTile(
                member: member,
                space: widget.space,
                ownPower: ownPower,
                onChanged: _load,
              );
            },
          ),
        ),
      ],
    );
  }
}

class _InviteButton extends ConsumerStatefulWidget {
  final Room space;
  const _InviteButton({required this.space});

  @override
  ConsumerState<_InviteButton> createState() => _InviteButtonState();
}

class _InviteButtonState extends ConsumerState<_InviteButton> {
  bool _inviting = false;
  String? _inviteError;

  void _showInvite() {
    final pt = PyramidTheme.of(context);
    final ctrl = TextEditingController();
    showDialog(
      context: context,
      builder: (ctx) => StatefulBuilder(
        builder: (ctx, setS) => AlertDialog(
          backgroundColor: pt.bg1,
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(pt.rLg)),
          title: Text('Zu Space einladen',
              style: TextStyle(color: pt.fg, fontWeight: FontWeight.w700)),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _Field(controller: ctrl, hint: '@user:server.org', pt: pt),
              const SizedBox(height: 6),
              Text('Lädt zu Space und allen Kanälen ein.',
                  style: TextStyle(color: pt.fgDim, fontSize: 11)),
              if (_inviteError != null) ...[
                const SizedBox(height: 6),
                Text(_inviteError!,
                    style: TextStyle(color: pt.danger, fontSize: 12)),
              ],
            ],
          ),
          actions: [
            TextButton(
                onPressed: () => Navigator.pop(ctx),
                child: const Text('Abbrechen')),
            FilledButton(
              style: FilledButton.styleFrom(backgroundColor: pt.accent),
              onPressed: _inviting
                  ? null
                  : () async {
                      final userId = ctrl.text.trim();
                      if (userId.isEmpty) return;
                      setS(() { _inviting = true; _inviteError = null; });
                      try {
                        // 1. Invite to space itself
                        await widget.space.invite(userId);
                        // 2. Invite to every joined child room
                        final client = widget.space.client;
                        for (final child in widget.space.spaceChildren) {
                          final roomId = child.roomId;
                          if (roomId == null) continue;
                          final room = client.getRoomById(roomId);
                          if (room == null || room.membership == Membership.leave) continue;
                          try {
                            await room.invite(userId);
                          } catch (_) {}
                        }
                        if (ctx.mounted) Navigator.pop(ctx);
                      } catch (e) {
                        setS(() { _inviteError = e.toString().split('\n').first; });
                      } finally {
                        setS(() => _inviting = false);
                      }
                    },
              child: _inviting
                  ? const SizedBox(
                      width: 16, height: 16,
                      child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white))
                  : const Text('Einladen',
                      style: TextStyle(color: Colors.white)),
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    return IconButton(
      icon: Icon(Icons.person_add_outlined, color: pt.accent, size: 20),
      tooltip: 'Einladen',
      onPressed: _showInvite,
    );
  }
}

class _MemberTile extends ConsumerStatefulWidget {
  final User member;
  final Room space;
  final int ownPower;
  final VoidCallback onChanged;

  const _MemberTile({
    required this.member,
    required this.space,
    required this.ownPower,
    required this.onChanged,
  });

  @override
  ConsumerState<_MemberTile> createState() => _MemberTileState();
}

class _MemberTileState extends ConsumerState<_MemberTile> {
  bool _hovered = false;

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final member = widget.member;
    final power = member.powerLevel;
    final name = member.displayName ?? member.id;
    final client = ref.watch(matrixClientProvider).valueOrNull;
    final avatarUri = member.avatarUrl;
    final isMe = member.id == widget.space.client.userID;
    final canManage = widget.ownPower > power && !isMe;

    Widget avatar;
    final letter = name.isNotEmpty ? name[0].toUpperCase() : '?';
    if (client != null && avatarUri != null) {
      avatar = MxcAvatar(
        mxcUri: avatarUri,
        client: client,
        size: 32,
        borderRadius: BorderRadius.circular(16),
        placeholder: (_) => _InitialAvatar(letter: letter, pt: pt, size: 32),
      );
    } else {
      avatar = _InitialAvatar(letter: letter, pt: pt, size: 32);
    }

    return MouseRegion(
      onEnter: (_) => setState(() => _hovered = true),
      onExit: (_) => setState(() => _hovered = false),
      child: Container(
        margin: const EdgeInsets.symmetric(vertical: 1),
        decoration: BoxDecoration(
          color: _hovered ? pt.bg2 : Colors.transparent,
          borderRadius: BorderRadius.circular(pt.rSm),
        ),
        child: ListTile(
          dense: true,
          contentPadding: const EdgeInsets.symmetric(horizontal: 8),
          leading: avatar,
          title: Text(name,
              style: TextStyle(
                  color: pt.fg,
                  fontSize: 13,
                  fontWeight: FontWeight.w500),
              overflow: TextOverflow.ellipsis),
          subtitle: Text(member.id,
              style: TextStyle(color: pt.fgDim, fontSize: 11),
              overflow: TextOverflow.ellipsis),
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
                decoration: BoxDecoration(
                  color: _powerColor(power, pt).withAlpha(30),
                  borderRadius: BorderRadius.circular(6),
                ),
                child: Text(_powerLabel(power),
                    style: TextStyle(
                        color: _powerColor(power, pt),
                        fontSize: 11,
                        fontWeight: FontWeight.w600)),
              ),
              if (canManage) ...[
                const SizedBox(width: 4),
                _MemberActions(
                    member: member,
                    space: widget.space,
                    ownPower: widget.ownPower,
                    onChanged: widget.onChanged),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

class _MemberActions extends ConsumerWidget {
  final User member;
  final Room space;
  final int ownPower;
  final VoidCallback onChanged;

  const _MemberActions({
    required this.member,
    required this.space,
    required this.ownPower,
    required this.onChanged,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final pt = PyramidTheme.of(context);
    return PopupMenuButton<_MemberAction>(
      icon: Icon(Icons.more_vert, color: pt.fgDim, size: 18),
      color: pt.bg2,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(pt.rBase)),
      itemBuilder: (_) => [
        if (ownPower >= _kPowerAdmin && member.powerLevel < _kPowerMod)
          const PopupMenuItem(
            value: _MemberAction.makeMod,
            child: Text('Zum Moderator machen'),
          ),
        if (ownPower >= _kPowerAdmin && member.powerLevel == _kPowerMod)
          const PopupMenuItem(
            value: _MemberAction.removeMod,
            child: Text('Moderator-Rechte entziehen'),
          ),
        if (ownPower >= _kPowerAdmin && member.powerLevel < _kPowerAdmin)
          const PopupMenuItem(
            value: _MemberAction.makeAdmin,
            child: Text('Zum Admin machen'),
          ),
        const PopupMenuItem(
          value: _MemberAction.kick,
          child: Text('Kicken', style: TextStyle(color: Color(0xFFF59E0B))),
        ),
        const PopupMenuItem(
          value: _MemberAction.ban,
          child: Text('Bannen', style: TextStyle(color: Color(0xFFED4245))),
        ),
      ],
      onSelected: (action) => _handleAction(context, action),
    );
  }

  Future<void> _handleAction(BuildContext context, _MemberAction action) async {
    switch (action) {
      case _MemberAction.makeMod:
        await _setPower(context, _kPowerMod);
      case _MemberAction.removeMod:
        await _setPower(context, _kPowerMember);
      case _MemberAction.makeAdmin:
        _confirmAdmin(context);
      case _MemberAction.kick:
        _confirmKick(context);
      case _MemberAction.ban:
        _confirmBan(context);
    }
  }

  Future<void> _setPower(BuildContext context, int level) async {
    try {
      // NICHT space.setPower() — das merged nur mit dem lokalen Cache und
      // kann die users-Map des Servers zerstören (Admin-Aussperrung).
      await updatePowerLevelsSafely(space, (content) {
        (content['users'] as Map<String, dynamic>)[member.id] = level;
      });
      onChanged();
    } catch (e) {
      if (context.mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(e.toString())));
      }
    }
  }

  void _confirmAdmin(BuildContext context) {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        title: const Text('Admin machen?'),
        content: Text(
            '${member.displayName ?? member.id} zum Admin befördern? '
            'Admin-Rechte können nicht einfach entzogen werden.'),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Abbrechen')),
          TextButton(
            onPressed: () async {
              Navigator.pop(context);
              await _setPower(context, _kPowerAdmin);
            },
            child: const Text('Admin machen',
                style: TextStyle(color: Color(0xFFED4245))),
          ),
        ],
      ),
    );
  }

  void _confirmKick(BuildContext context) {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        title: const Text('Mitglied kicken?'),
        content: Text('${member.displayName ?? member.id} aus dem Space entfernen?'),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Abbrechen')),
          TextButton(
            onPressed: () async {
              Navigator.pop(context);
              try {
                await space.kick(member.id);
                onChanged();
              } catch (_) {}
            },
            child: const Text('Kicken',
                style: TextStyle(color: Color(0xFFF59E0B))),
          ),
        ],
      ),
    );
  }

  void _confirmBan(BuildContext context) {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        title: const Text('Mitglied bannen?'),
        content: Text(
            '${member.displayName ?? member.id} dauerhaft aus dem Space ausschließen?'),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Abbrechen')),
          TextButton(
            onPressed: () async {
              Navigator.pop(context);
              try {
                await space.ban(member.id);
                onChanged();
              } catch (_) {}
            },
            child: const Text('Bannen',
                style: TextStyle(color: Color(0xFFED4245))),
          ),
        ],
      ),
    );
  }
}

enum _MemberAction { makeMod, removeMod, makeAdmin, kick, ban }

// ─── Permissions Tab ──────────────────────────────────────────────────────────

class _PermissionsTab extends ConsumerStatefulWidget {
  final Room space;
  const _PermissionsTab({required this.space});

  @override
  ConsumerState<_PermissionsTab> createState() => _PermissionsTabState();
}

class _PermissionsTabState extends ConsumerState<_PermissionsTab> {
  bool _saving = false;
  String? _status;

  // Current power level values (loaded from room state)
  late Map<String, int> _levels;

  static const _defaults = {
    'events_default': 0,
    'state_default': 50,
    'invite': 0,
    'kick': 50,
    'ban': 50,
    'redact': 50,
  };

  static const _labels = {
    'events_default': 'Nachrichten senden',
    'state_default': 'Raum-Einstellungen ändern',
    'invite': 'Mitglieder einladen',
    'kick': 'Mitglieder kicken',
    'ban': 'Mitglieder bannen',
    'redact': 'Nachrichten löschen',
  };

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

  void _load() {
    final content = widget.space
            .getState(EventTypes.RoomPowerLevels)
            ?.content ??
        {};
    _levels = {
      for (final key in _defaults.keys)
        key: (content[key] as int?) ?? _defaults[key]!,
    };
  }

  Future<void> _save() async {
    setState(() { _saving = true; _status = null; });
    try {
      // Server-Stand holen und nur die geänderten Schlüssel mergen — der
      // lokale Cache kann unvollständig sein und würde users/events wipen.
      await updatePowerLevelsSafely(widget.space, (content) {
        for (final entry in _levels.entries) {
          content[entry.key] = entry.value;
        }
      });
      setState(() => _status = 'Gespeichert');
    } catch (e) {
      final msg = e is MatrixException
          ? '${e.errcode}: ${e.errorMessage}'
          : e.toString().split('\n').first;
      setState(() => _status = 'Fehler: $msg');
    } finally {
      setState(() => _saving = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final ownPower = widget.space.ownPowerLevel;
    final canEdit = ownPower >= _kPowerAdmin;

    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('Mindestberechtigungen',
              style: TextStyle(
                  color: pt.fgDim,
                  fontSize: 11,
                  fontWeight: FontWeight.w700,
                  letterSpacing: 0.8)),
          const SizedBox(height: 4),
          Text(
              'Lege fest, welche Stufe ein Mitglied mindestens haben muss, um diese Aktion auszuführen.',
              style: TextStyle(color: pt.fgDim, fontSize: 12)),
          const SizedBox(height: 16),
          ...(_labels.entries.map((e) => _PermissionRow(
                label: e.value,
                value: _levels[e.key]!,
                enabled: canEdit,
                pt: pt,
                onChanged: (v) => setState(() => _levels[e.key] = v),
              ))),
          if (canEdit) ...[
            const SizedBox(height: 20),
            if (_status != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 8),
                child: Text(_status!,
                    style: TextStyle(
                        color: _status!.startsWith('F')
                            ? const Color(0xFFED4245)
                            : const Color(0xFF57F287),
                        fontSize: 12)),
              ),
            SizedBox(
              width: double.infinity,
              child: FilledButton(
                onPressed: _saving ? null : _save,
                style: FilledButton.styleFrom(
                  backgroundColor: pt.accent,
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(pt.rBase)),
                ),
                child: _saving
                    ? SizedBox(
                        width: 18,
                        height: 18,
                        child: CircularProgressIndicator(
                            strokeWidth: 2, color: pt.bg0))
                    : const Text('Speichern',
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.w600)),
              ),
            ),
          ],
        ],
      ),
    );
  }
}

class _PermissionRow extends StatelessWidget {
  final String label;
  final int value;
  final bool enabled;
  final PyramidTheme pt;
  final ValueChanged<int> onChanged;

  const _PermissionRow({
    required this.label,
    required this.value,
    required this.enabled,
    required this.pt,
    required this.onChanged,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Row(
        children: [
          Expanded(
            child: Text(label,
                style: TextStyle(
                    color: pt.fg,
                    fontSize: 13,
                    fontWeight: FontWeight.w500)),
          ),
          if (enabled)
            DropdownButton<int>(
              value: value,
              dropdownColor: pt.bg2,
              underline: const SizedBox.shrink(),
              style: TextStyle(color: pt.fg, fontSize: 13),
              items: const [
                DropdownMenuItem(value: 0, child: Text('Mitglied (0)')),
                DropdownMenuItem(value: 50, child: Text('Moderator (50)')),
                DropdownMenuItem(value: 100, child: Text('Admin (100)')),
              ],
              onChanged: (v) => v != null ? onChanged(v) : null,
            )
          else
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
              decoration: BoxDecoration(
                color: _powerColor(value, pt).withAlpha(30),
                borderRadius: BorderRadius.circular(6),
              ),
              child: Text(_powerLabel(value),
                  style: TextStyle(
                      color: _powerColor(value, pt),
                      fontSize: 12,
                      fontWeight: FontWeight.w600)),
            ),
        ],
      ),
    );
  }
}

// ─── Channels Tab ─────────────────────────────────────────────────────────────

const _kSpaceChild = 'm.space.child';
const _kSpaceParent = 'm.space.parent';
const _kVoiceChannelType = 'io.pyramid.voice_channel';

class _ChannelsTab extends ConsumerStatefulWidget {
  final Room space;
  const _ChannelsTab({required this.space});

  @override
  ConsumerState<_ChannelsTab> createState() => _ChannelsTabState();
}

class _ChannelsTabState extends ConsumerState<_ChannelsTab> {
  bool _busy = false;

  List<({String roomId, Room? room, bool isVoice})> _getChildren(Client client) {
    final childStates = widget.space.states[_kSpaceChild] ?? {};
    final result = <({String roomId, Room? room, bool isVoice})>[];
    for (final entry in childStates.entries) {
      final content = entry.value.content;
      if (content.isEmpty) continue;
      final childRoomId = entry.key;
      final room = client.getRoomById(childRoomId);
      final createContent = room?.getState('m.room.create')?.content;
      final isVoice = createContent?['type'] == _kVoiceChannelType;
      result.add((roomId: childRoomId, room: room, isVoice: isVoice));
    }
    result.sort((a, b) {
      final nameA = a.room?.getLocalizedDisplayname() ?? a.roomId;
      final nameB = b.room?.getLocalizedDisplayname() ?? b.roomId;
      return nameA.compareTo(nameB);
    });
    return result;
  }

  Future<void> _removeChild(String roomId) async {
    final client = ref.read(matrixClientProvider).valueOrNull;
    if (client == null) return;
    setState(() => _busy = true);
    try {
      await client.setRoomStateWithKey(widget.space.id, _kSpaceChild, roomId, {});
      if (mounted) setState(() {});
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context)
            .showSnackBar(SnackBar(content: Text(e.toString())));
      }
    } finally {
      if (mounted) setState(() => _busy = false);
    }
  }

  Future<void> _addChild(String roomId) async {
    final client = ref.read(matrixClientProvider).valueOrNull;
    if (client == null) return;
    final serverName = client.userID?.split(':').last ?? '';
    try {
      await client.setRoomStateWithKey(
        widget.space.id,
        _kSpaceChild,
        roomId,
        {'via': [serverName], 'suggested': false},
      );
      // Best-effort: set parent in child room
      try {
        await client.setRoomStateWithKey(
          roomId,
          _kSpaceParent,
          widget.space.id,
          {'via': [serverName], 'canonical': true},
        );
      } catch (_) {}
      if (mounted) setState(() {});
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context)
            .showSnackBar(SnackBar(content: Text(e.toString())));
      }
    }
  }

  Future<void> _createAndAdd({
    required String name,
    required bool isVoice,
    required Client client,
  }) async {
    setState(() => _busy = true);
    try {
      final serverName = client.userID?.split(':').last ?? '';
      final roomId = await client.createRoom(
        name: name,
        visibility: Visibility.private,
        preset: CreateRoomPreset.privateChat,
        creationContent: isVoice ? {'type': _kVoiceChannelType} : null,
        initialState: [
          StateEvent(
            type: 'm.room.join_rules',
            content: {'join_rule': 'restricted', 'allow': [
              {'type': 'm.room_membership', 'room_id': widget.space.id}
            ]},
          ),
        ],
      );
      await client.setRoomStateWithKey(
        widget.space.id,
        _kSpaceChild,
        roomId,
        {'via': [serverName], 'suggested': false},
      );
      try {
        await client.setRoomStateWithKey(
          roomId,
          _kSpaceParent,
          widget.space.id,
          {'via': [serverName], 'canonical': true},
        );
      } catch (_) {}
      if (mounted) setState(() {});
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context)
            .showSnackBar(SnackBar(content: Text(e.toString())));
      }
    } finally {
      if (mounted) setState(() => _busy = false);
    }
  }

  void _showCreateDialog(BuildContext context, Client client) {
    showDialog(
      context: context,
      builder: (_) => _CreateChannelDialog(
        onConfirm: (name, isVoice) =>
            _createAndAdd(name: name, isVoice: isVoice, client: client),
      ),
    );
  }

  void _showAddExistingDialog(BuildContext context, Client client) {
    final childStates = widget.space.states[_kSpaceChild] ?? {};
    final existingIds = {
      for (final e in childStates.entries)
        if (e.value.content.isNotEmpty) e.key
    };
    final available = client.rooms
        .where((r) => !r.isSpace && !existingIds.contains(r.id))
        .toList()
      ..sort((a, b) => a.getLocalizedDisplayname()
          .compareTo(b.getLocalizedDisplayname()));

    showDialog(
      context: context,
      builder: (_) => _AddExistingDialog(
        rooms: available,
        client: client,
        onConfirm: (roomId) => _addChild(roomId),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final client = ref.watch(matrixClientProvider).valueOrNull;
    final ownPower = widget.space.ownPowerLevel;
    final canManage = ownPower >= _kPowerMod;

    if (client == null) {
      return Center(
          child: CircularProgressIndicator(color: pt.accent, strokeWidth: 2));
    }

    final children = _getChildren(client);

    return Column(
      children: [
        if (canManage)
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
            child: Row(
              children: [
                Expanded(
                  child: OutlinedButton.icon(
                    icon: const Icon(Icons.add, size: 16),
                    label: const Text('Kanal erstellen'),
                    style: OutlinedButton.styleFrom(
                      foregroundColor: pt.accent,
                      side: BorderSide(color: pt.accent),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(pt.rSm)),
                    ),
                    onPressed: _busy
                        ? null
                        : () => _showCreateDialog(context, client),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: OutlinedButton.icon(
                    icon: const Icon(Icons.link, size: 16),
                    label: const Text('Raum hinzufügen'),
                    style: OutlinedButton.styleFrom(
                      foregroundColor: pt.fgDim,
                      side: BorderSide(color: pt.border),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(pt.rSm)),
                    ),
                    onPressed: _busy
                        ? null
                        : () => _showAddExistingDialog(context, client),
                  ),
                ),
              ],
            ),
          ),
        if (children.isEmpty)
          Expanded(
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.forum_outlined, color: pt.fgDim, size: 40),
                  const SizedBox(height: 8),
                  Text('Noch keine Kanäle',
                      style: TextStyle(color: pt.fgDim, fontSize: 13)),
                ],
              ),
            ),
          )
        else
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              itemCount: children.length,
              itemBuilder: (context, i) {
                final child = children[i];
                final name = child.room?.getLocalizedDisplayname() ??
                    child.roomId;
                return _ChannelItem(
                  name: name,
                  isVoice: child.isVoice,
                  roomId: child.roomId,
                  pt: pt,
                  canRemove: canManage,
                  onRemove: () => _removeChild(child.roomId),
                );
              },
            ),
          ),
      ],
    );
  }
}

class _ChannelItem extends StatefulWidget {
  final String name;
  final bool isVoice;
  final String roomId;
  final PyramidTheme pt;
  final bool canRemove;
  final VoidCallback onRemove;

  const _ChannelItem({
    required this.name,
    required this.isVoice,
    required this.roomId,
    required this.pt,
    required this.canRemove,
    required this.onRemove,
  });

  @override
  State<_ChannelItem> createState() => _ChannelItemState();
}

class _ChannelItemState extends State<_ChannelItem> {
  bool _hovered = false;

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    return MouseRegion(
      onEnter: (_) => setState(() => _hovered = true),
      onExit: (_) => setState(() => _hovered = false),
      child: Container(
        margin: const EdgeInsets.symmetric(vertical: 1),
        decoration: BoxDecoration(
          color: _hovered ? pt.bg2 : Colors.transparent,
          borderRadius: BorderRadius.circular(pt.rSm),
        ),
        child: ListTile(
          dense: true,
          contentPadding:
              const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
          leading: Icon(
            widget.isVoice
                ? Icons.volume_up_outlined
                : Icons.tag,
            color: pt.fgDim,
            size: 18,
          ),
          title: Text(
            widget.name,
            style: TextStyle(
                color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500),
            overflow: TextOverflow.ellipsis,
          ),
          subtitle: Text(
            widget.isVoice ? 'Sprachkanal' : 'Textkanal',
            style: TextStyle(color: pt.fgDim, fontSize: 11),
          ),
          trailing: widget.canRemove && _hovered
              ? IconButton(
                  icon: Icon(Icons.link_off,
                      color: const Color(0xFFED4245), size: 18),
                  tooltip: 'Aus Space entfernen',
                  onPressed: widget.onRemove,
                )
              : null,
        ),
      ),
    );
  }
}

class _CreateChannelDialog extends StatefulWidget {
  final void Function(String name, bool isVoice) onConfirm;
  const _CreateChannelDialog({required this.onConfirm});

  @override
  State<_CreateChannelDialog> createState() => _CreateChannelDialogState();
}

class _CreateChannelDialogState extends State<_CreateChannelDialog> {
  final _ctrl = TextEditingController();
  bool _isVoice = false;

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

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    return AlertDialog(
      backgroundColor: pt.bg1,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(pt.rLg)),
      title: Text('Neuen Kanal erstellen',
          style: TextStyle(color: pt.fg, fontWeight: FontWeight.w700)),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _SectionLabel('Name', pt),
          const SizedBox(height: 6),
          _Field(controller: _ctrl, hint: 'kanal-name', pt: pt),
          const SizedBox(height: 16),
          _SectionLabel('Typ', pt),
          const SizedBox(height: 8),
          Row(
            children: [
              Expanded(
                child: GestureDetector(
                  onTap: () => setState(() => _isVoice = false),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 12, vertical: 10),
                    decoration: BoxDecoration(
                      color: !_isVoice ? pt.accent.withAlpha(40) : pt.bg0,
                      border: Border.all(
                          color: !_isVoice ? pt.accent : pt.border),
                      borderRadius: BorderRadius.circular(pt.rSm),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.tag,
                            color: !_isVoice ? pt.accent : pt.fgDim,
                            size: 16),
                        const SizedBox(width: 6),
                        Text('Text',
                            style: TextStyle(
                                color: !_isVoice ? pt.accent : pt.fgDim,
                                fontSize: 13,
                                fontWeight: FontWeight.w600)),
                      ],
                    ),
                  ),
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: GestureDetector(
                  onTap: () => setState(() => _isVoice = true),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 12, vertical: 10),
                    decoration: BoxDecoration(
                      color: _isVoice ? pt.accent.withAlpha(40) : pt.bg0,
                      border: Border.all(
                          color: _isVoice ? pt.accent : pt.border),
                      borderRadius: BorderRadius.circular(pt.rSm),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.volume_up_outlined,
                            color: _isVoice ? pt.accent : pt.fgDim,
                            size: 16),
                        const SizedBox(width: 6),
                        Text('Sprache',
                            style: TextStyle(
                                color: _isVoice ? pt.accent : pt.fgDim,
                                fontSize: 13,
                                fontWeight: FontWeight.w600)),
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
      actions: [
        TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Abbrechen')),
        FilledButton(
          style: FilledButton.styleFrom(backgroundColor: pt.accent),
          onPressed: () {
            final name = _ctrl.text.trim();
            if (name.isEmpty) return;
            Navigator.pop(context);
            widget.onConfirm(name, _isVoice);
          },
          child: const Text('Erstellen',
              style: TextStyle(color: Colors.white)),
        ),
      ],
    );
  }
}

class _AddExistingDialog extends StatefulWidget {
  final List<Room> rooms;
  final Client client;
  final void Function(String roomId) onConfirm;

  const _AddExistingDialog({
    required this.rooms,
    required this.client,
    required this.onConfirm,
  });

  @override
  State<_AddExistingDialog> createState() => _AddExistingDialogState();
}

class _AddExistingDialogState extends State<_AddExistingDialog> {
  String _filter = '';
  final _ctrl = TextEditingController();

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

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final filtered = _filter.isEmpty
        ? widget.rooms
        : widget.rooms
            .where((r) => r
                .getLocalizedDisplayname()
                .toLowerCase()
                .contains(_filter.toLowerCase()))
            .toList();

    return Dialog(
      backgroundColor: pt.bg1,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(pt.rLg)),
      child: ConstrainedBox(
        constraints: const BoxConstraints(maxWidth: 400, maxHeight: 520),
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(20, 20, 20, 12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Expanded(
                        child: Text('Raum hinzufügen',
                            style: TextStyle(
                                color: pt.fg,
                                fontSize: 17,
                                fontWeight: FontWeight.w700)),
                      ),
                      IconButton(
                        icon: Icon(Icons.close, color: pt.fgDim, size: 20),
                        onPressed: () => Navigator.pop(context),
                      ),
                    ],
                  ),
                  const SizedBox(height: 12),
                  _SearchField(
                    controller: _ctrl,
                    hint: 'Raum suchen…',
                    pt: pt,
                    onChanged: (v) => setState(() => _filter = v),
                  ),
                ],
              ),
            ),
            const Divider(height: 1),
            if (filtered.isEmpty)
              Expanded(
                child: Center(
                  child: Text('Keine Räume gefunden',
                      style: TextStyle(color: pt.fgDim, fontSize: 13)),
                ),
              )
            else
              Expanded(
                child: ListView.builder(
                  padding: const EdgeInsets.symmetric(
                      horizontal: 8, vertical: 4),
                  itemCount: filtered.length,
                  itemBuilder: (context, i) {
                    final room = filtered[i];
                    final name = room.getLocalizedDisplayname();
                    final letter =
                        name.isNotEmpty ? name[0].toUpperCase() : '?';
                    final avatarUri = room.avatar;
                    final createContent =
                        room.getState('m.room.create')?.content;
                    final isVoice =
                        createContent?['type'] == _kVoiceChannelType;
                    return ListTile(
                      dense: true,
                      leading: avatarUri != null
                          ? MxcAvatar(
                              mxcUri: avatarUri,
                              client: widget.client,
                              size: 32,
                              borderRadius: BorderRadius.circular(pt.rSm),
                              placeholder: (_) => _InitialAvatar(
                                  letter: letter, pt: pt, size: 32),
                            )
                          : _InitialAvatar(letter: letter, pt: pt, size: 32),
                      title: Text(name,
                          style: TextStyle(
                              color: pt.fg,
                              fontSize: 13,
                              fontWeight: FontWeight.w500),
                          overflow: TextOverflow.ellipsis),
                      subtitle: Text(
                        isVoice ? 'Sprachkanal' : 'Textkanal',
                        style: TextStyle(color: pt.fgDim, fontSize: 11),
                      ),
                      trailing:
                          Icon(isVoice ? Icons.volume_up_outlined : Icons.tag,
                              color: pt.fgDim, size: 16),
                      onTap: () {
                        Navigator.pop(context);
                        widget.onConfirm(room.id);
                      },
                    );
                  },
                ),
              ),
          ],
        ),
      ),
    );
  }
}

// ─── Shared helpers ───────────────────────────────────────────────────────────

class _SectionLabel extends StatelessWidget {
  final String text;
  final PyramidTheme pt;
  const _SectionLabel(this.text, this.pt);

  @override
  Widget build(BuildContext context) {
    return Text(text,
        style: TextStyle(
            color: pt.fgDim,
            fontSize: 11,
            fontWeight: FontWeight.w700,
            letterSpacing: 0.8));
  }
}

class _Field extends StatelessWidget {
  final TextEditingController controller;
  final String hint;
  final int maxLines;
  final PyramidTheme pt;

  const _Field({
    required this.controller,
    required this.hint,
    this.maxLines = 1,
    required this.pt,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      maxLines: maxLines,
      style: TextStyle(color: pt.fg, fontSize: 13),
      decoration: InputDecoration(
        hintText: hint,
        hintStyle: TextStyle(color: pt.fgDim),
        filled: true,
        fillColor: pt.bg0,
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.border),
        ),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.border),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.accent),
        ),
      ),
    );
  }
}

class _SearchField extends StatelessWidget {
  final TextEditingController controller;
  final String hint;
  final PyramidTheme pt;
  final ValueChanged<String> onChanged;

  const _SearchField({
    required this.controller,
    required this.hint,
    required this.pt,
    required this.onChanged,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      onChanged: onChanged,
      style: TextStyle(color: pt.fg, fontSize: 13),
      decoration: InputDecoration(
        hintText: hint,
        hintStyle: TextStyle(color: pt.fgDim),
        prefixIcon: Icon(Icons.search, color: pt.fgDim, size: 18),
        filled: true,
        fillColor: pt.bg0,
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        isDense: true,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.border),
        ),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.border),
        ),
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(pt.rSm),
          borderSide: BorderSide(color: pt.accent),
        ),
      ),
    );
  }
}

// ─── Avatar Editor ───────────────────────────────────────────────────────────

class _AvatarEditor extends StatelessWidget {
  final Room space;
  final Uint8List? pendingAvatar;
  final bool canEdit;
  final VoidCallback onPick;
  final VoidCallback onRemove;
  final PyramidTheme pt;
  final dynamic client;

  const _AvatarEditor({
    required this.space,
    required this.pendingAvatar,
    required this.canEdit,
    required this.onPick,
    required this.onRemove,
    required this.pt,
    required this.client,
  });

  @override
  Widget build(BuildContext context) {
    final letter = space.getLocalizedDisplayname().isNotEmpty
        ? space.getLocalizedDisplayname()[0].toUpperCase()
        : '?';
    final avatarUri = space.avatar;
    final hasAvatar = pendingAvatar != null || avatarUri != null;

    Widget avatarWidget;
    if (pendingAvatar != null) {
      avatarWidget = Image.memory(pendingAvatar!, fit: BoxFit.cover,
          width: 64, height: 64);
    } else if (avatarUri != null && client != null) {
      avatarWidget = MxcAvatar(
        mxcUri: avatarUri,
        client: client,
        size: 64,
        borderRadius: BorderRadius.circular(32),
        placeholder: (_) => _InitialAvatar(letter: letter, pt: pt, size: 64),
      );
    } else {
      avatarWidget = _InitialAvatar(letter: letter, pt: pt, size: 64);
    }

    return Row(
      children: [
        GestureDetector(
          onTap: canEdit ? onPick : null,
          child: Stack(
            clipBehavior: Clip.none,
            children: [
              Container(
                width: 64,
                height: 64,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  border: Border.all(color: pt.border),
                ),
                clipBehavior: Clip.hardEdge,
                child: avatarWidget,
              ),
              if (canEdit)
                Positioned(
                  right: -2,
                  bottom: -2,
                  child: Container(
                    padding: const EdgeInsets.all(4),
                    decoration: BoxDecoration(
                      color: pt.accent,
                      shape: BoxShape.circle,
                      border: Border.all(color: pt.bg1, width: 2),
                    ),
                    child: const Icon(Icons.edit, size: 10, color: Colors.white),
                  ),
                ),
            ],
          ),
        ),
        const SizedBox(width: 12),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              canEdit ? 'Klicken zum Ändern' : 'Serverbild',
              style: TextStyle(color: pt.fg, fontSize: 13,
                  fontWeight: FontWeight.w500),
            ),
            if (canEdit && hasAvatar)
              GestureDetector(
                onTap: onRemove,
                child: Text('Entfernen',
                    style: TextStyle(
                        color: const Color(0xFFED4245), fontSize: 12)),
              ),
          ],
        ),
      ],
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        color: pt.bg3,
        borderRadius: BorderRadius.circular(size / 2),
      ),
      child: Center(
        child: Text(letter,
            style: TextStyle(
                color: pt.fg,
                fontSize: size * 0.4,
                fontWeight: FontWeight.w700)),
      ),
    );
  }
}
