part of '../chat_view.dart';

class _ChatHeader extends ConsumerWidget {
  final Room? room;
  final String roomId;
  final PyramidTheme pt;
  final bool membersOpen;
  final bool pinnedOpen;
  final VoidCallback onToggleMembers;
  final VoidCallback onOpenSearch;
  final VoidCallback onTogglePinned;

  const _ChatHeader({
    required this.room,
    required this.roomId,
    required this.pt,
    required this.membersOpen,
    required this.pinnedOpen,
    required this.onToggleMembers,
    required this.onOpenSearch,
    required this.onTogglePinned,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final name = room?.getLocalizedDisplayname() ?? roomId;
    final isDm = room?.isDirectChat ?? false;
    final isEncrypted = room?.encrypted ?? false;
    final topic = room?.topic ?? (isDm ? '' : 'Welcome to #$name');
    final client = ref.watch(matrixClientProvider).valueOrNull;
    final size = MediaQuery.sizeOf(context);
    final isMobile = size.width < 900 || size.height < 500;

    return Container(
      height: 52,
      padding: const EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(
        color: pt.bg1,
        border: Border(bottom: BorderSide(color: pt.border)),
      ),
      child: Row(
        children: [
          // Back button on mobile
          if (isMobile) ...[
            GestureDetector(
              onTap: () => ref.read(activeRoomIdProvider.notifier).state = null,
              child: Icon(Icons.arrow_back_rounded, size: 22, color: pt.fg),
            ),
            const SizedBox(width: 12),
          ],
          // Room icon / avatar
          if (isDm && client != null) ...[
            _DmHeaderAvatar(room: room, name: name, client: client),
          ] else if (!isDm) ...[
            Icon(
              isEncrypted ? Icons.lock_outline_rounded : Icons.tag_rounded,
              size: 16,
              color: pt.fgDim,
            ),
          ],
          const SizedBox(width: 8),
          // Name + presence for DMs
          if (isDm)
            Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    name,
                    style: TextStyle(
                        color: pt.fg,
                        fontSize: 15,
                        fontWeight: FontWeight.w600),
                    overflow: TextOverflow.ellipsis,
                  ),
                  if (room != null && client != null)
                    _PresenceLine(
                      userId: room!.directChatMatrixID,
                      client: client,
                      pt: pt,
                    ),
                ],
              ),
            )
          else
            Text(
              name,
              style: TextStyle(
                  color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600),
            ),
          const SizedBox(width: 12),
          if (!isDm && topic.isNotEmpty) ...[
            Container(width: 1, height: 18, color: pt.border),
            const SizedBox(width: 12),
            Expanded(
              child: Text(
                topic,
                style: TextStyle(color: pt.fgMuted, fontSize: 13),
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ] else if (!isDm)
            const Spacer(),
          // Right actions
          Row(
            children: [
              if (isDm) ...[
                PyrIconBtn(
                  size: 28,
                  icon: const Icon(Icons.phone_rounded),
                  tooltip: 'Voice call',
                  onPressed: () => _startCall(ref, room, cam: false),
                ),
                PyrIconBtn(
                  size: 28,
                  icon: const Icon(Icons.videocam_rounded),
                  tooltip: 'Video call',
                  onPressed: () => _startCall(ref, room, cam: true),
                ),
                if (size.width > 700)
                  Container(
                    width: 1,
                    height: 20,
                    margin: const EdgeInsets.symmetric(horizontal: 4),
                    color: pt.border,
                  ),
              ],
              if (size.width > 800) ...[
                PyrIconBtn(
                  size: 28,
                  icon: const Icon(Icons.inbox_rounded),
                  tooltip: 'Threads',
                  onPressed: () {},
                ),
                PyrIconBtn(
                  size: 28,
                  icon: const Icon(Icons.push_pin_rounded),
                  tooltip: 'Angepinnte Nachrichten',
                  active: pinnedOpen,
                  onPressed: onTogglePinned,
                ),
              ],
              PyrIconBtn(
                size: 28,
                icon: const Icon(Icons.search_rounded),
                tooltip: 'Search',
                onPressed: onOpenSearch,
              ),
              PyrIconBtn(
                size: 28,
                icon: const Icon(Icons.group_rounded),
                tooltip: 'Members',
                active: membersOpen,
                onPressed: onToggleMembers,
              ),
            ],
          ),
        ],
      ),
    );
  }

  void _startCall(WidgetRef ref, Room? room, {required bool cam}) async {
    if (room == null) return;
    
    if (room.isDirectChat) {
      // Use native Matrix VoIP for 1:1 DMs
      ref.read(viewModeProvider.notifier).state = ViewMode.voice;
      await PyramidVoipManager.instance.startCall(room.id, video: cam);
      return;
    }

    // Use LiveKit for group voice rooms
    ref.read(viewModeProvider.notifier).state = ViewMode.voice;
    await ref.read(callStateProvider).startCall(
      roomName: roomId,
      roomDisplayName: room.getLocalizedDisplayname(),
      identity: 'user_${DateTime.now().millisecondsSinceEpoch}',
      audioOnly: !cam,
    );
  }
}

class _DmExpandingHeader extends ConsumerStatefulWidget {
  final Room room;
  final String roomId;
  final PyramidTheme pt;
  final bool expanded;
  final VoidCallback onToggle;
  final bool membersOpen;
  final bool pinnedOpen;
  final VoidCallback onToggleMembers;
  final VoidCallback onOpenSearch;
  final VoidCallback onTogglePinned;

  const _DmExpandingHeader({
    super.key,
    required this.room,
    required this.roomId,
    required this.pt,
    required this.expanded,
    required this.onToggle,
    required this.membersOpen,
    required this.pinnedOpen,
    required this.onToggleMembers,
    required this.onOpenSearch,
    required this.onTogglePinned,
  });

  @override
  ConsumerState<_DmExpandingHeader> createState() =>
      _DmExpandingHeaderState();
}

class _DmExpandingHeaderState extends ConsumerState<_DmExpandingHeader> {
  static const _animDuration = Duration(milliseconds: 280);
  static const _animCurve = Curves.easeOutCubic;

  Profile? _profile;
  String? _bannerMxcUri;
  String? _statusMsg;
  bool _online = false;

  String? get _partnerId => widget.room.directChatMatrixID;

  @override
  void initState() {
    super.initState();
    // Direkt laden, damit das Banner beim ersten Aufklappen schon da ist.
    _load();
  }

  @override
  void didUpdateWidget(_DmExpandingHeader old) {
    super.didUpdateWidget(old);
    if (old.room.id != widget.room.id) {
      _profile = null;
      _bannerMxcUri = null;
      _statusMsg = null;
      _online = false;
      _load();
    }
  }

  Future<void> _load() async {
    final userId = _partnerId;
    if (userId == null) return;
    final client = widget.room.client;
    // Race-Guard: Beim schnellen DM-Wechsel dürfen verspätete Antworten des
    // VORHERIGEN Partners das Profil des aktuellen nicht überschreiben.
    bool stillCurrent() => mounted && _partnerId == userId;
    try {
      final p = await client.getProfileFromUserId(userId);
      if (stillCurrent()) setState(() => _profile = p);
    } catch (_) {}
    try {
      final p = await client.fetchCurrentPresence(userId);
      final last = p.lastActiveTimestamp;
      final fresh = p.currentlyActive == true ||
          (last != null &&
              DateTime.now().difference(last) < const Duration(minutes: 5));
      if (stillCurrent()) {
        setState(() {
          _online = p.presence == PresenceType.online && fresh;
          _statusMsg = p.statusMsg;
        });
      }
    } catch (_) {}
    // Banner aus dem Profil-Custom-Field (io.pyramid.profile.banner_url).
    try {
      final apiUri = client.homeserver!.replace(
        path: '/_matrix/client/v3/profile/${Uri.encodeComponent(userId)}',
      );
      final resp = await client.httpClient.get(
        apiUri,
        headers: {'Authorization': 'Bearer ${client.accessToken}'},
      );
      if (resp.statusCode == 200) {
        final data = jsonDecode(resp.body) as Map<String, dynamic>;
        final url = data['io.pyramid.profile.banner_url'] as String?;
        if (url != null && stillCurrent()) {
          setState(() => _bannerMxcUri = url);
        }
      }
    } catch (_) {}
  }

  @override
  Widget build(BuildContext context) {
    final pt = widget.pt;
    final expanded = widget.expanded;
    final size = MediaQuery.sizeOf(context);
    final isMobile = size.width < 900 || size.height < 500;
    final client = ref.watch(matrixClientProvider).valueOrNull;

    final userId = _partnerId ?? '';
    final member = widget.room.unsafeGetUserFromMemoryOrFallback(userId);
    final name = _profile?.displayName ??
        member.displayName ??
        widget.room.getLocalizedDisplayname();
    final avatarUrl =
        _profile?.avatarUrl ?? member.avatarUrl ?? widget.room.avatar;
    final initial = name.isNotEmpty ? name[0].toUpperCase() : '?';

    // Im Querformat (wenig Höhe) kompakter aufklappen.
    final expandedHeight = size.height < 500 ? 140.0 : 190.0;
    final avatarSize = expanded
        ? (size.height < 500 ? 72.0 : 96.0)
        : 32.0;

    return AnimatedContainer(
      duration: _animDuration,
      curve: _animCurve,
      height: expanded ? expandedHeight : 52,
      decoration: BoxDecoration(
        color: pt.bg1,
        border: Border(bottom: BorderSide(color: pt.border)),
      ),
      clipBehavior: Clip.hardEdge,
      child: Stack(
        fit: StackFit.expand,
        children: [
          // Banner als Hintergrund — blendet beim Aufklappen ein.
          if (_bannerMxcUri != null && client != null)
            Positioned.fill(
              child: AnimatedOpacity(
                opacity: expanded ? 1 : 0,
                duration: _animDuration,
                curve: Curves.easeOut,
                child: MxcImage(
                  mxcUri: _bannerMxcUri!,
                  client: client,
                  fit: BoxFit.cover,
                  placeholder: const SizedBox.shrink(),
                ),
              ),
            ),
          // Scrim: links dunkel, damit Avatar/Text lesbar bleiben (Mockup).
          if (_bannerMxcUri != null)
            Positioned.fill(
              child: AnimatedOpacity(
                opacity: expanded ? 1 : 0,
                duration: _animDuration,
                child: DecoratedBox(
                  decoration: BoxDecoration(
                    gradient: LinearGradient(
                      begin: Alignment.centerLeft,
                      end: Alignment.centerRight,
                      colors: [
                        pt.bg1,
                        pt.bg1.withAlpha(210),
                        pt.bg1.withAlpha(0),
                      ],
                      stops: const [0.0, 0.32, 0.72],
                    ),
                  ),
                ),
              ),
            ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              children: [
                if (isMobile) ...[
                  GestureDetector(
                    onTap: () =>
                        ref.read(activeRoomIdProvider.notifier).state = null,
                    child:
                        Icon(Icons.arrow_back_rounded, size: 22, color: pt.fg),
                  ),
                  const SizedBox(width: 12),
                ],
                // Klickbarer Profilbereich: Avatar + Name + Details
                Expanded(
                  child: MouseRegion(
                    cursor: SystemMouseCursors.click,
                    child: GestureDetector(
                      behavior: HitTestBehavior.opaque,
                      onTap: widget.onToggle,
                      child: Row(
                        children: [
                          // Avatar — skaliert mit (rund, wie im Mockup)
                          AnimatedContainer(
                            duration: _animDuration,
                            curve: _animCurve,
                            width: avatarSize,
                            height: avatarSize,
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              color: pt.bg3,
                            ),
                            clipBehavior: Clip.antiAlias,
                            child: avatarUrl != null && client != null
                                ? MxcImage(
                                    mxcUri: avatarUrl.toString(),
                                    client: client,
                                    fit: BoxFit.cover,
                                    placeholder: _initialBox(initial, pt),
                                  )
                                : _initialBox(initial, pt),
                          ),
                          AnimatedContainer(
                            duration: _animDuration,
                            curve: _animCurve,
                            width: expanded ? 20 : 10,
                          ),
                          Expanded(
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                // Name skaliert mit; Status blendet daneben ein
                                Row(
                                  crossAxisAlignment: CrossAxisAlignment.center,
                                  children: [
                                    Flexible(
                                      child: AnimatedDefaultTextStyle(
                                        duration: _animDuration,
                                        curve: _animCurve,
                                        style: TextStyle(
                                          color: pt.fg,
                                          fontSize: expanded ? 24 : 15,
                                          fontWeight: expanded
                                              ? FontWeight.w700
                                              : FontWeight.w600,
                                        ),
                                        child: Text(
                                          name,
                                          maxLines: 1,
                                          overflow: TextOverflow.ellipsis,
                                        ),
                                      ),
                                    ),
                                    AnimatedOpacity(
                                      opacity: expanded ? 1 : 0,
                                      duration: _animDuration,
                                      child: Padding(
                                        padding:
                                            const EdgeInsets.only(left: 8),
                                        child: Text(
                                          _online ? 'online' : 'offline',
                                          style: TextStyle(
                                            color: _online
                                                ? pt.online
                                                : pt.fgMuted,
                                            fontSize: 13,
                                            fontWeight: FontWeight.w500,
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                                // Eingeklappt: "zuletzt online vor…",
                                // aufgeklappt: Handle + Biographie.
                                AnimatedCrossFade(
                                  duration: _animDuration,
                                  sizeCurve: _animCurve,
                                  crossFadeState: expanded
                                      ? CrossFadeState.showSecond
                                      : CrossFadeState.showFirst,
                                  firstChild: client != null
                                      ? _PresenceLine(
                                          userId: _partnerId,
                                          client: client,
                                          pt: pt,
                                        )
                                      : const SizedBox.shrink(),
                                  secondChild: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      const SizedBox(height: 4),
                                      Text(
                                        userId,
                                        style: TextStyle(
                                            color: pt.fgMuted, fontSize: 13),
                                        maxLines: 1,
                                        overflow: TextOverflow.ellipsis,
                                      ),
                                      if (_statusMsg != null &&
                                          _statusMsg!.isNotEmpty) ...[
                                        const SizedBox(height: 8),
                                        Text(
                                          _statusMsg!,
                                          style: TextStyle(
                                              color: pt.fgMuted,
                                              fontSize: 13,
                                              height: 1.35),
                                          maxLines: 2,
                                          overflow: TextOverflow.ellipsis,
                                        ),
                                      ],
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
                const SizedBox(width: 12),
                // Aktionen — bleiben immer sichtbar
                Row(
                  children: [
                    PyrIconBtn(
                      size: 28,
                      icon: const Icon(Icons.phone_rounded),
                      tooltip: 'Voice call',
                      onPressed: () => _startCall(cam: false),
                    ),
                    PyrIconBtn(
                      size: 28,
                      icon: const Icon(Icons.videocam_rounded),
                      tooltip: 'Video call',
                      onPressed: () => _startCall(cam: true),
                    ),
                    if (size.width > 700)
                      Container(
                        width: 1,
                        height: 20,
                        margin: const EdgeInsets.symmetric(horizontal: 4),
                        color: pt.border,
                      ),
                    if (size.width > 800)
                      PyrIconBtn(
                        size: 28,
                        icon: const Icon(Icons.push_pin_rounded),
                        tooltip: 'Angepinnte Nachrichten',
                        active: widget.pinnedOpen,
                        onPressed: widget.onTogglePinned,
                      ),
                    PyrIconBtn(
                      size: 28,
                      icon: const Icon(Icons.search_rounded),
                      tooltip: 'Search',
                      onPressed: widget.onOpenSearch,
                    ),
                    PyrIconBtn(
                      size: 28,
                      icon: const Icon(Icons.group_rounded),
                      tooltip: 'Members',
                      active: widget.membersOpen,
                      onPressed: widget.onToggleMembers,
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _initialBox(String initial, PyramidTheme pt) => Center(
        child: FittedBox(
          fit: BoxFit.scaleDown,
          child: Padding(
            padding: const EdgeInsets.all(6),
            child: Text(
              initial,
              style: TextStyle(
                  color: pt.fg, fontSize: 28, fontWeight: FontWeight.w700),
            ),
          ),
        ),
      );

  void _startCall({required bool cam}) async {
    ref.read(viewModeProvider.notifier).state = ViewMode.voice;
    await PyramidVoipManager.instance.startCall(widget.room.id, video: cam);
  }
}

class _DmHeaderAvatar extends StatelessWidget {
  final Room? room;
  final String name;
  final Client client;

  const _DmHeaderAvatar({required this.room, required this.name, required this.client});

  @override
  Widget build(BuildContext context) {
    final initial = name.isNotEmpty ? name[0].toUpperCase() : '?';
    const size = 32.0;
    return MxcAvatar(
      mxcUri: room?.avatar,
      client: client,
      size: size,
      borderRadius: BorderRadius.circular(size / 2),
      placeholder: (_) => _InitialCircle(initial: initial, size: size),
    );
  }
}

class _InitialCircle extends StatelessWidget {
  final String initial;
  final double size;
  const _InitialCircle({required this.initial, required this.size});

  @override
  Widget build(BuildContext context) {
    const color = Color(0xFF06B6D4);
    return Container(
      width: size,
      height: size,
      decoration: const BoxDecoration(color: color, shape: BoxShape.circle),
      child: Center(
        child: Text(initial,
          style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600)),
      ),
    );
  }
}

class _PresenceLine extends StatefulWidget {
  final String? userId;
  final Client client;
  final PyramidTheme pt;

  const _PresenceLine({required this.userId, required this.client, required this.pt});

  @override
  State<_PresenceLine> createState() => _PresenceLineState();
}

class _PresenceLineState extends State<_PresenceLine> {
  CachedPresence? _presence;
  StreamSubscription<CachedPresence>? _sub;
  // Debounce timer: delay "online" updates 4s to filter out background-sync
  // false positives (push notifications briefly wake the recipient's client).
  Timer? _onlineDebounce;

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

  @override
  void didUpdateWidget(_PresenceLine oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.userId != widget.userId) {
      _sub?.cancel();
      _sub = null;
      _onlineDebounce?.cancel();
      setState(() => _presence = null);
      _load(widget.userId);
    }
  }

  @override
  void dispose() {
    _sub?.cancel();
    _onlineDebounce?.cancel();
    super.dispose();
  }

  // "online" only when the server's presence enum says online AND the last
  // activity is recent. Using currentlyActive (the old logic) reported online
  // long after the user left, because that flag lingers.
  static bool _isOnline(CachedPresence p) {
    if (p.presence != PresenceType.online) return false;
    final last = p.lastActiveTimestamp;
    return last == null || DateTime.now().difference(last).inSeconds < 90;
  }

  void _applyPresence(CachedPresence p) {
    _onlineDebounce?.cancel();
    if (_isOnline(p)) {
      // Wait 4 seconds before committing "online" to filter single-sync spikes
      // (a backgrounded client briefly reports online when a push wakes it).
      _onlineDebounce = Timer(const Duration(seconds: 4), () {
        if (mounted) setState(() => _presence = p);
      });
    } else {
      if (mounted) setState(() => _presence = p);
    }
  }

  Future<void> _load(String? userId) async {
    if (userId == null) return;
    // Never show our own presence in a DM header
    if (userId == widget.client.userID) return;
    try {
      final p = await widget.client.fetchCurrentPresence(userId);
      if (mounted) _applyPresence(p);
    } catch (_) {}
    _sub = widget.client.onPresenceChanged.stream
        .where((p) => p.userid == userId)
        .listen((p) => _applyPresence(p));
  }

  @override
  Widget build(BuildContext context) {
    final p = _presence;
    if (p == null) return const SizedBox.shrink();

    String text;
    Color color = widget.pt.fgDim;
    if (_isOnline(p)) {
      text = 'online';
      color = const Color(0xFF3BA55D);
    } else if (p.lastActiveTimestamp != null) {
      text = 'zuletzt online ${_relativeTime(p.lastActiveTimestamp!)}';
    } else {
      return const SizedBox.shrink();
    }

    return Text(
      text,
      style: TextStyle(color: color, fontSize: 11),
      maxLines: 1,
      overflow: TextOverflow.ellipsis,
    );
  }

  String _relativeTime(DateTime t) {
    final diff = DateTime.now().difference(t);
    if (diff.inMinutes < 1) return 'gerade eben';
    if (diff.inMinutes < 60) return 'vor ${diff.inMinutes}min';
    if (diff.inHours < 24) return 'vor ${diff.inHours}h';
    if (diff.inDays < 7) return 'vor ${diff.inDays}d';
    if (diff.inDays < 365) return 'vor ${(diff.inDays / 7).floor()}w';
    return 'vor ${(diff.inDays / 365).floor()}j';
  }
}
