part of '../message_group.dart';

String _formatMessageTime(DateTime t) {
  final h = t.hour.toString().padLeft(2, '0');
  final m = t.minute.toString().padLeft(2, '0');
  return '$h:$m';
}

class MessageGroup extends StatefulWidget {
  final List<Event> events;
  final String currentUserId;
  final Room? room;
  final Timeline? timeline;
  final VoidCallback? onReply;
  final Set<String> selectedIds;
  final ValueChanged<String>? onToggleSelect;
  final bool showStatusIndicator;
  /// Event IDs that another member has read up to (their last-read position).
  /// A ✓✓ is rendered at any of this group's events contained here.
  final Set<String> readEventIds;
  /// When non-null and pointing to a non-last event in this group, an inline
  /// "Neue Nachrichten" divider is rendered after that event's row.
  final String? fullyReadId;
  /// Jump-to-message (from search): the row matching [jumpEventId] gets
  /// [jumpKey] attached (for ensureVisible) and [highlightEventId] is tinted.
  final String? jumpEventId;
  final GlobalKey? jumpKey;
  final String? highlightEventId;
  final bool highlightOn;

  const MessageGroup({
    super.key,
    required this.events,
    required this.currentUserId,
    this.room,
    this.timeline,
    this.onReply,
    this.selectedIds = const {},
    this.onToggleSelect,
    this.showStatusIndicator = false,
    this.readEventIds = const {},
    this.fullyReadId,
    this.jumpEventId,
    this.jumpKey,
    this.highlightEventId,
    this.highlightOn = false,
  });

  @override
  State<MessageGroup> createState() => _MessageGroupState();
}

class _MessageGroupState extends State<MessageGroup> {
  // Per-message hover state (desktop): which event row the pointer is over, and
  // which row currently has its reaction-picker overlay open.
  String? _hoveredEventId;
  String? _overlayEventId;
  double _swipeOffset = 0;
  static const _swipeThreshold = 56.0;
  bool _swipeTriggered = false;

  bool get _isMobile => Platform.isAndroid || Platform.isIOS;

  @override
  Widget build(BuildContext context) {
    final pt = PyramidTheme.of(context);
    final firstEvent = widget.events.first;
    final sender = firstEvent.senderFromMemoryOrFallback;
    final name = sender.calcDisplayname();
    final initial = name.isNotEmpty ? name[0].toUpperCase() : '?';
    final color = _colorFromId(firstEvent.senderId);
    final time = _formatMessageTime(firstEvent.originServerTs);
    final avatarUri = sender.avatarUrl;

    // Build a FLAT column of rows — each message at the same indentation level.
    // First event row: avatar + name + time + content
    // Continuation rows: timestamp-slot + content
    // This prevents the nested-Row double-indent bug.
    final rows = <Widget>[];

    // ── First event row ──────────────────────────────────
    // Each message row is INDIVIDUALLY selectable (see _selectableRow) so two
    // consecutive messages from the same sender aren't selected as one block.
    rows.add(_selectableRow(firstEvent, pt, Padding(
      padding: EdgeInsets.only(top: pt.msgGap / 2, left: 20, right: 20, bottom: 2),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Avatar
          SizedBox(
            width: 38,
            child: Column(children: [
              const SizedBox(height: 2),
              _Avatar(initial: initial, color: color, pt: pt, size: 38, avatarUri: avatarUri),
            ]),
          ),
          const SizedBox(width: 14),
          // Name + time + message
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(children: [
                  Text(name, style: TextStyle(color: pt.fg, fontSize: 14, fontWeight: FontWeight.w600)),
                  const SizedBox(width: 8),
                  Text(time, style: TextStyle(color: pt.fgDim, fontSize: 11)),
                ]),
                const SizedBox(height: 2),
                _MessageBody(
                  event: firstEvent,
                  pt: pt,
                  timeline: widget.timeline,
                  room: widget.room,
                  currentUserId: widget.currentUserId,
                ),
              ],
            ),
          ),
        ],
      ),
    )));
    if (widget.readEventIds.contains(firstEvent.eventId)) rows.add(_ReadMarker(pt: pt));
    // Inline unread divider after the first event when it's the fullyRead
    // marker and there are more events in this group.
    if (widget.fullyReadId == firstEvent.eventId && widget.events.length > 1) {
      rows.add(_InlineUnreadDivider(pt: pt));
    }

    // ── Continuation event rows (siblings, same indentation) ──
    for (var j = 1; j < widget.events.length; j++) {
      final event = widget.events[j];
      rows.add(_selectableRow(event, pt, Padding(
        padding: const EdgeInsets.only(top: 8, left: 20, right: 20),
        child: _ContinuationMessage(
          event: event,
          pt: pt,
          timeline: widget.timeline,
          room: widget.room,
          currentUserId: widget.currentUserId,
        ),
      )));
      if (widget.readEventIds.contains(event.eventId)) rows.add(_ReadMarker(pt: pt));
      // Inline divider after this event if it's the fullyRead and not the last.
      if (widget.fullyReadId == event.eventId && j < widget.events.length - 1) {
        rows.add(_InlineUnreadDivider(pt: pt));
      }
    }

    // Sending/sent indicator on the newest own message — only when no read ✓✓
    // is already shown for it (the ✓✓ implies it was delivered + read).
    if (widget.showStatusIndicator &&
        firstEvent.senderId == widget.currentUserId &&
        widget.room != null &&
        !widget.readEventIds.contains(widget.events.last.eventId)) {
      rows.add(Padding(
        padding: const EdgeInsets.only(right: 24, bottom: 1),
        child: Align(
          alignment: Alignment.centerRight,
          child: _MessageStatus(
            lastEvent: widget.events.last,
            pt: pt,
          ),
        ),
      ));
    }
    rows.add(const SizedBox(height: 2));

    // Hover highlight + actions are now per-row (see _selectableRow), so the
    // group is just the column of rows.
    Widget content = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: rows,
    );

    if (!_isMobile) return content;

    return GestureDetector(
      onHorizontalDragUpdate: (d) {
        if (d.delta.dx > 0) return; // Only allow right-to-left swipe for reply
        setState(() {
          _swipeOffset = (_swipeOffset + d.delta.dx).clamp(-_swipeThreshold, 0.0);
          if (_swipeOffset <= -_swipeThreshold && !_swipeTriggered) {
            _swipeTriggered = true;
            HapticFeedback.mediumImpact();
          }
        });
      },
      onHorizontalDragEnd: (_) {
        if (_swipeTriggered) widget.onReply?.call();
        setState(() { _swipeOffset = 0; _swipeTriggered = false; });
      },
      child: Stack(
        children: [
          Transform.translate(
            offset: Offset(_swipeOffset, 0),
            child: content,
          ),
          if (_swipeOffset < -8)
            Positioned(
              right: 12,
              top: 0,
              bottom: 0,
              child: Center(
                child: AnimatedOpacity(
                  opacity: (-_swipeOffset / _swipeThreshold).clamp(0.0, 1.0),
                  duration: const Duration(milliseconds: 60),
                  child: Icon(Icons.reply_rounded, size: 20, color: pt.fgMuted),
                ),
              ),
            ),
        ],
      ),
    );
  }

  // Wraps a single message row so it can be selected independently of the rest
  // of the sender's group. Long-press enters selection mode; in selection mode a
  // full-row tap overlay toggles this message's selection.
  Widget _selectableRow(Event event, PyramidTheme pt, Widget child) {
    final selected = widget.selectedIds.contains(event.eventId);
    final inSel = widget.selectedIds.isNotEmpty;
    final showActions = !_isMobile && !inSel &&
        (_hoveredEventId == event.eventId || _overlayEventId == event.eventId);
    final isJumpTarget = widget.jumpEventId == event.eventId;
    final isHighlighted = widget.highlightEventId == event.eventId;

    Widget row = GestureDetector(
      key: isJumpTarget ? widget.jumpKey : null,
      onLongPress: () => widget.onToggleSelect?.call(event.eventId),
      child: Stack(
        children: [
          AnimatedContainer(
            duration: const Duration(milliseconds: 100),
            color: selected
                ? pt.accent.withAlpha(40)
                : (showActions ? pt.bg2 : Colors.transparent),
            child: child,
          ),
          // Jump-from-search highlight — fades out via AnimatedOpacity.
          if (isHighlighted)
            Positioned.fill(
              child: IgnorePointer(
                child: AnimatedOpacity(
                  opacity: widget.highlightOn ? 1.0 : 0.0,
                  duration: const Duration(milliseconds: 900),
                  curve: Curves.easeOut,
                  child: Container(color: pt.accent.withAlpha(48)),
                ),
              ),
            ),
          if (selected)
            Positioned(
              left: 0,
              top: 0,
              bottom: 0,
              child: Container(width: 3, color: pt.accent),
            ),
          if (showActions)
            Positioned(
              top: 0,
              right: 32,
              child: _HoverActions(
                pt: pt,
                event: event,
                events: [event],
                room: widget.room,
                currentUserId: widget.currentUserId,
                onReply: widget.onReply,
                onOverlayStateChanged: (active) => setState(
                    () => _overlayEventId = active ? event.eventId : null),
              ),
            ),
          if (inSel)
            Positioned.fill(
              child: GestureDetector(
                behavior: HitTestBehavior.opaque,
                onTap: () => widget.onToggleSelect?.call(event.eventId),
              ),
            ),
        ],
      ),
    );

    if (_isMobile) return row;

    // Desktop: per-row hover highlight + actions.
    return MouseRegion(
      onEnter: (_) => setState(() => _hoveredEventId = event.eventId),
      onExit: (_) => setState(() {
        if (_hoveredEventId == event.eventId) _hoveredEventId = null;
      }),
      child: row,
    );
  }

  Color _colorFromId(String id) {
    const colors = [
      Color(0xFF06B6D4), Color(0xFFA855F7), Color(0xFF10B981),
      Color(0xFFF43F5E), Color(0xFF3B82F6), Color(0xFFF59E0B),
      Color(0xFFFF6B9D),
    ];
    if (id.isEmpty) return colors[0];
    return colors[id.codeUnitAt(0) % colors.length];
  }
}

class _SendFailedRow extends StatelessWidget {
  final Event event;
  final PyramidTheme pt;
  const _SendFailedRow({required this.event, required this.pt});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 3, bottom: 1),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.error_outline_rounded, size: 13, color: pt.danger),
          const SizedBox(width: 4),
          Text('Nicht gesendet', style: TextStyle(color: pt.danger, fontSize: 12)),
          const SizedBox(width: 10),
          _link('Wiederholen', pt, () async {
            try { await event.sendAgain(); } catch (_) {}
          }),
          const SizedBox(width: 10),
          _link('Löschen', pt, () async {
            try { await event.cancelSend(); } catch (_) {}
          }),
        ],
      ),
    );
  }

  Widget _link(String label, PyramidTheme pt, VoidCallback onTap) => GestureDetector(
        onTap: onTap,
        child: Text(
          label,
          style: TextStyle(
            color: pt.accent,
            fontSize: 12,
            decoration: TextDecoration.underline,
            decorationColor: pt.accent,
          ),
        ),
      );
}

class _MessageBody extends StatelessWidget {
  final Event event;
  final PyramidTheme pt;
  final Timeline? timeline;
  final Room? room;
  final String currentUserId;

  const _MessageBody({
    required this.event,
    required this.pt,
    this.timeline,
    this.room,
    required this.currentUserId,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        _MessageContent(event: event, pt: pt, timeline: timeline),
        if (event.status.isError) _SendFailedRow(event: event, pt: pt),
        if (timeline != null) _ReactionsRow(event: event, timeline: timeline!, room: room, currentUserId: currentUserId, pt: pt),
      ],
    );
  }
}

class _ContinuationMessage extends StatefulWidget {
  final Event event;
  final PyramidTheme pt;
  final Timeline? timeline;
  final Room? room;
  final String currentUserId;

  const _ContinuationMessage({
    required this.event,
    required this.pt,
    this.timeline,
    this.room,
    required this.currentUserId,
  });

  @override
  State<_ContinuationMessage> createState() => _ContinuationMessageState();
}

class _ContinuationMessageState extends State<_ContinuationMessage> {
  bool _hovered = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) => setState(() => _hovered = true),
      onExit: (_) => setState(() => _hovered = false),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Timestamp on hover (in avatar column space)
          SizedBox(
            width: 38,
            child: Padding(
              padding: const EdgeInsets.only(top: 4),
              child: AnimatedOpacity(
                opacity: _hovered ? 1 : 0,
                duration: const Duration(milliseconds: 120),
                child: Text(
                  _formatMessageTime(widget.event.originServerTs),
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: widget.pt.fgDim,
                    fontSize: 10,
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                _MessageContent(event: widget.event, pt: widget.pt, timeline: widget.timeline),
                if (widget.event.status.isError) _SendFailedRow(event: widget.event, pt: widget.pt),
                if (widget.timeline != null)
                  _ReactionsRow(
                    event: widget.event,
                    timeline: widget.timeline!,
                    room: widget.room,
                    currentUserId: widget.currentUserId,
                    pt: widget.pt,
                  ),
              ],
            ),
          ),
        ],
      ),
    );
  }

}
