import 'package:flutter/material.dart';
import 'package:tasq/utils/app_time.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import '../../models/office.dart';
import '../../models/profile.dart';
import '../../models/task.dart';
import '../../models/ticket.dart';
import '../../models/ticket_message.dart';
import '../../providers/notifications_provider.dart';
import '../../providers/profile_provider.dart';
import '../../providers/tasks_provider.dart';
import '../../providers/tickets_provider.dart';
import '../../providers/typing_provider.dart';
import '../../widgets/app_breakpoints.dart';
import '../../widgets/mono_text.dart';
import '../../widgets/responsive_body.dart';
import '../../widgets/status_pill.dart';
import '../../widgets/task_assignment_section.dart';
import '../../widgets/typing_dots.dart';
import '../../theme/app_surfaces.dart';

class TicketDetailScreen extends ConsumerStatefulWidget {
  const TicketDetailScreen({super.key, required this.ticketId});

  final String ticketId;

  @override
  ConsumerState<TicketDetailScreen> createState() => _TicketDetailScreenState();
}

class _TicketDetailScreenState extends ConsumerState<TicketDetailScreen> {
  final _messageController = TextEditingController();
  static const List<String> _statusOptions = ['pending', 'promoted', 'closed'];
  String? _mentionQuery;
  int? _mentionStart;
  List<Profile> _mentionResults = [];

  @override
  void initState() {
    super.initState();
    Future.microtask(
      () => ref
          .read(notificationsControllerProvider)
          .markReadForTicket(widget.ticketId),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    final ticket = _findTicket(ref, widget.ticketId);
    final messagesAsync = ref.watch(ticketMessagesProvider(widget.ticketId));
    final profilesAsync = ref.watch(profilesProvider);
    final officesAsync = ref.watch(officesProvider);
    final currentProfileAsync = ref.watch(currentProfileProvider);
    final tasksAsync = ref.watch(tasksProvider);
    final typingState = ref.watch(typingIndicatorProvider(widget.ticketId));
    final canPromote = currentProfileAsync.maybeWhen(
      data: (profile) => profile != null && _canPromote(profile.role),
      orElse: () => false,
    );
    final canSendMessages = ticket != null && ticket.status != 'closed';
    final canAssign = currentProfileAsync.maybeWhen(
      data: (profile) => profile != null && _canAssignStaff(profile.role),
      orElse: () => false,
    );
    final showAssign = canAssign && ticket?.status != 'closed';
    final taskForTicket = ticket == null
        ? null
        : _findTaskForTicket(tasksAsync, ticket.id);
    final hasStaffMessage = _hasStaffMessage(
      messagesAsync.valueOrNull ?? const [],
      profilesAsync.valueOrNull ?? const [],
    );
    final effectiveRespondedAt = ticket?.promotedAt != null && !hasStaffMessage
        ? ticket!.promotedAt
        : ticket?.respondedAt;

    return ResponsiveBody(
      child: LayoutBuilder(
        builder: (context, constraints) {
          if (ticket == null) {
            return const Center(child: Text('Ticket not found.'));
          }

          final isWide = constraints.maxWidth >= AppBreakpoints.desktop;
          final detailsContent = Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Align(
                alignment: Alignment.center,
                child: Text(
                  ticket.subject,
                  textAlign: TextAlign.center,
                  style: Theme.of(
                    context,
                  ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w700),
                ),
              ),
              const SizedBox(height: 6),
              Align(
                alignment: Alignment.center,
                child: Text(
                  _filedByLabel(profilesAsync, ticket),
                  textAlign: TextAlign.center,
                  style: Theme.of(context).textTheme.bodySmall,
                ),
              ),
              const SizedBox(height: 12),
              Wrap(
                spacing: 12,
                runSpacing: 8,
                crossAxisAlignment: WrapCrossAlignment.center,
                children: [
                  _buildStatusChip(context, ref, ticket, canPromote),
                  _MetaBadge(
                    label: 'Office',
                    value: _officeLabel(officesAsync, ticket),
                  ),
                  _MetaBadge(
                    label: 'Ticket ID',
                    value: ticket.id,
                    isMono: true,
                  ),
                ],
              ),
              const SizedBox(height: 12),
              Text(ticket.description),
              const SizedBox(height: 12),
              _buildTatRow(context, ticket, effectiveRespondedAt),
              if (taskForTicket != null) ...[
                const SizedBox(height: 16),
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: TaskAssignmentSection(
                        taskId: taskForTicket.id,
                        canAssign: showAssign,
                      ),
                    ),
                    const SizedBox(width: 8),
                    IconButton(
                      tooltip: 'Open task',
                      onPressed: () => context.go('/tasks/${taskForTicket.id}'),
                      icon: const Icon(Icons.open_in_new),
                    ),
                  ],
                ),
              ],
            ],
          );

          final detailsCard = Card(
            child: Padding(
              padding: const EdgeInsets.all(20),
              child: SingleChildScrollView(child: detailsContent),
            ),
          );

          final messagesCard = Card(
            child: Padding(
              padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
              child: Column(
                children: [
                  Expanded(
                    child: messagesAsync.when(
                      data: (messages) {
                        if (messages.isEmpty) {
                          return const Center(child: Text('No messages yet.'));
                        }
                        final profileById = {
                          for (final profile in profilesAsync.valueOrNull ?? [])
                            profile.id: profile,
                        };
                        return ListView.builder(
                          reverse: true,
                          padding: const EdgeInsets.fromLTRB(0, 16, 0, 72),
                          itemCount: messages.length,
                          itemBuilder: (context, index) {
                            final message = messages[index];
                            final currentUserId =
                                Supabase.instance.client.auth.currentUser?.id;
                            final isMe =
                                currentUserId != null &&
                                message.senderId == currentUserId;
                            final senderName = message.senderId == null
                                ? 'System'
                                : profileById[message.senderId]?.fullName ??
                                      message.senderId!;
                            final bubbleColor = isMe
                                ? Theme.of(context).colorScheme.primaryContainer
                                : Theme.of(
                                    context,
                                  ).colorScheme.surfaceContainerHighest;
                            final textColor = isMe
                                ? Theme.of(
                                    context,
                                  ).colorScheme.onPrimaryContainer
                                : Theme.of(context).colorScheme.onSurface;

                            return Align(
                              alignment: isMe
                                  ? Alignment.centerRight
                                  : Alignment.centerLeft,
                              child: Column(
                                crossAxisAlignment: isMe
                                    ? CrossAxisAlignment.end
                                    : CrossAxisAlignment.start,
                                children: [
                                  if (!isMe)
                                    Padding(
                                      padding: const EdgeInsets.only(bottom: 4),
                                      child: Text(
                                        senderName,
                                        style: Theme.of(
                                          context,
                                        ).textTheme.labelSmall,
                                      ),
                                    ),
                                  Container(
                                    margin: const EdgeInsets.only(bottom: 12),
                                    padding: const EdgeInsets.all(12),
                                    constraints: const BoxConstraints(
                                      maxWidth: 520,
                                    ),
                                    decoration: BoxDecoration(
                                      color: bubbleColor,
                                      borderRadius: BorderRadius.only(
                                        topLeft: Radius.circular(
                                          AppSurfaces.of(context).cardRadius,
                                        ),
                                        topRight: Radius.circular(
                                          AppSurfaces.of(context).cardRadius,
                                        ),
                                        bottomLeft: Radius.circular(
                                          isMe
                                              ? AppSurfaces.of(
                                                  context,
                                                ).cardRadius
                                              : 4,
                                        ),
                                        bottomRight: Radius.circular(
                                          isMe
                                              ? 4
                                              : AppSurfaces.of(
                                                  context,
                                                ).cardRadius,
                                        ),
                                      ),
                                    ),
                                    child: _buildMentionText(
                                      message.content,
                                      textColor,
                                      profilesAsync.valueOrNull ?? [],
                                    ),
                                  ),
                                ],
                              ),
                            );
                          },
                        );
                      },
                      loading: () =>
                          const Center(child: CircularProgressIndicator()),
                      error: (error, _) => Center(
                        child: Text('Failed to load messages: $error'),
                      ),
                    ),
                  ),
                  SafeArea(
                    top: false,
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(0, 8, 0, 12),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          if (typingState.userIds.isNotEmpty)
                            Padding(
                              padding: const EdgeInsets.only(bottom: 6),
                              child: Container(
                                padding: const EdgeInsets.symmetric(
                                  horizontal: 10,
                                  vertical: 6,
                                ),
                                decoration: BoxDecoration(
                                  color: Theme.of(
                                    context,
                                  ).colorScheme.surfaceContainerHighest,
                                  borderRadius: BorderRadius.circular(16),
                                ),
                                child: Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    Text(
                                      _typingLabel(
                                        typingState.userIds,
                                        profilesAsync,
                                      ),
                                      style: Theme.of(
                                        context,
                                      ).textTheme.labelSmall,
                                    ),
                                    const SizedBox(width: 8),
                                    TypingDots(
                                      size: 8,
                                      color: Theme.of(
                                        context,
                                      ).colorScheme.primary,
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          if (_mentionQuery != null)
                            Padding(
                              padding: const EdgeInsets.only(bottom: 8),
                              child: _buildMentionList(profilesAsync),
                            ),
                          if (!canSendMessages)
                            Padding(
                              padding: const EdgeInsets.only(bottom: 8),
                              child: Text(
                                'Messaging is disabled for closed tickets.',
                                style: Theme.of(context).textTheme.labelMedium,
                              ),
                            ),
                          Row(
                            children: [
                              Expanded(
                                child: TextField(
                                  controller: _messageController,
                                  decoration: const InputDecoration(
                                    hintText: 'Message...',
                                  ),
                                  enabled: canSendMessages,
                                  textInputAction: TextInputAction.send,
                                  onChanged: canSendMessages
                                      ? (_) => _handleComposerChanged(
                                          profilesAsync.valueOrNull ?? [],
                                          Supabase
                                              .instance
                                              .client
                                              .auth
                                              .currentUser
                                              ?.id,
                                          canSendMessages,
                                        )
                                      : null,
                                  onSubmitted: canSendMessages
                                      ? (_) => _handleSendMessage(
                                          ref,
                                          profilesAsync.valueOrNull ?? [],
                                          Supabase
                                              .instance
                                              .client
                                              .auth
                                              .currentUser
                                              ?.id,
                                          canSendMessages,
                                        )
                                      : null,
                                ),
                              ),
                              const SizedBox(width: 12),
                              IconButton(
                                tooltip: 'Send',
                                onPressed: canSendMessages
                                    ? () => _handleSendMessage(
                                        ref,
                                        profilesAsync.valueOrNull ?? [],
                                        Supabase
                                            .instance
                                            .client
                                            .auth
                                            .currentUser
                                            ?.id,
                                        canSendMessages,
                                      )
                                    : null,
                                icon: const Icon(Icons.send),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ),
          );

          if (isWide) {
            return Row(
              children: [
                Expanded(flex: 2, child: detailsCard),
                const SizedBox(width: 16),
                Expanded(flex: 3, child: messagesCard),
              ],
            );
          }

          return Column(
            children: [
              detailsCard,
              const SizedBox(height: 12),
              Expanded(child: messagesCard),
            ],
          );
        },
      ),
    );
  }

  String _filedByLabel(AsyncValue<List<Profile>> profilesAsync, Ticket ticket) {
    final creatorId = ticket.creatorId;
    if (creatorId == null || creatorId.isEmpty) {
      return 'Filed by: Unknown';
    }
    final profile = profilesAsync.valueOrNull
        ?.where((item) => item.id == creatorId)
        .firstOrNull;
    final name = profile?.fullName.isNotEmpty == true
        ? profile!.fullName
        : creatorId;
    return 'Filed by: $name';
  }

  Ticket? _findTicket(WidgetRef ref, String ticketId) {
    final ticketsAsync = ref.watch(ticketsProvider);
    return ticketsAsync.maybeWhen(
      data: (tickets) =>
          tickets.where((ticket) => ticket.id == ticketId).firstOrNull,
      orElse: () => null,
    );
  }

  bool _hasStaffMessage(List<TicketMessage> messages, List<Profile> profiles) {
    if (messages.isEmpty || profiles.isEmpty) {
      return false;
    }
    final staffIds = profiles
        .where(
          (profile) =>
              profile.role == 'admin' ||
              profile.role == 'dispatcher' ||
              profile.role == 'it_staff',
        )
        .map((profile) => profile.id)
        .toSet();
    if (staffIds.isEmpty) {
      return false;
    }
    return messages.any(
      (message) =>
          message.senderId != null && staffIds.contains(message.senderId!),
    );
  }

  Task? _findTaskForTicket(AsyncValue<List<Task>> tasksAsync, String ticketId) {
    return tasksAsync.maybeWhen(
      data: (tasks) =>
          tasks.where((task) => task.ticketId == ticketId).firstOrNull,
      orElse: () => null,
    );
  }

  Future<void> _handleSendMessage(
    WidgetRef ref,
    List<Profile> profiles,
    String? currentUserId,
    bool canSendMessages,
  ) async {
    if (!canSendMessages) return;
    final content = _messageController.text.trim();
    if (content.isEmpty) return;

    _maybeTypingController(widget.ticketId)?.stopTyping();

    final message = await ref
        .read(ticketsControllerProvider)
        .sendTicketMessage(ticketId: widget.ticketId, content: content);
    final mentionUserIds = _extractMentionedUserIds(
      content,
      profiles,
      currentUserId,
    );
    if (mentionUserIds.isNotEmpty && currentUserId != null) {
      await ref
          .read(notificationsControllerProvider)
          .createMentionNotifications(
            userIds: mentionUserIds,
            actorId: currentUserId,
            ticketId: widget.ticketId,
            messageId: message.id,
          );
    }
    ref.invalidate(ticketMessagesProvider(widget.ticketId));
    if (mounted) {
      _messageController.clear();
      _clearMentions();
    }
  }

  List<String> _extractMentionedUserIds(
    String content,
    List<Profile> profiles,
    String? currentUserId,
  ) {
    final lower = content.toLowerCase();
    final mentioned = <String>{};
    for (final profile in profiles) {
      if (profile.id == currentUserId) continue;
      final label = profile.fullName.isEmpty ? profile.id : profile.fullName;
      if (label.isEmpty) continue;
      final token = '@${label.toLowerCase()}';
      if (lower.contains(token)) {
        mentioned.add(profile.id);
      }
    }
    return mentioned.toList();
  }

  void _handleComposerChanged(
    List<Profile> profiles,
    String? currentUserId,
    bool canSendMessages,
  ) {
    if (!canSendMessages) {
      _maybeTypingController(widget.ticketId)?.stopTyping();
      _clearMentions();
      return;
    }
    _maybeTypingController(widget.ticketId)?.userTyping();
    final text = _messageController.text;
    final cursor = _messageController.selection.baseOffset;
    if (cursor < 0) {
      _clearMentions();
      return;
    }
    final textBeforeCursor = text.substring(0, cursor);
    final atIndex = textBeforeCursor.lastIndexOf('@');
    if (atIndex == -1) {
      _clearMentions();
      return;
    }
    if (atIndex > 0 && !_isWhitespace(textBeforeCursor[atIndex - 1])) {
      _clearMentions();
      return;
    }
    final query = textBeforeCursor.substring(atIndex + 1);
    if (query.contains(RegExp(r'\s'))) {
      _clearMentions();
      return;
    }
    final normalizedQuery = query.toLowerCase();
    final candidates = profiles.where((profile) {
      if (profile.id == currentUserId) {
        return false;
      }
      final label = profile.fullName.isEmpty ? profile.id : profile.fullName;
      return label.toLowerCase().contains(normalizedQuery);
    }).toList();
    setState(() {
      _mentionQuery = query;
      _mentionStart = atIndex;
      _mentionResults = candidates.take(6).toList();
    });
  }

  void _clearMentions() {
    if (_mentionQuery == null && _mentionResults.isEmpty) {
      return;
    }
    setState(() {
      _mentionQuery = null;
      _mentionStart = null;
      _mentionResults = [];
    });
  }

  // Safely obtain the typing controller for [ticketId].
  TypingIndicatorController? _maybeTypingController(String ticketId) {
    try {
      final controller = ref.read(typingIndicatorProvider(ticketId).notifier);
      return controller.mounted ? controller : null;
    } on StateError {
      return null;
    } catch (_) {
      return null;
    }
  }

  bool _isWhitespace(String char) {
    return char.trim().isEmpty;
  }

  String _typingLabel(
    Set<String> userIds,
    AsyncValue<List<Profile>> profilesAsync,
  ) {
    final profileById = {
      for (final profile in profilesAsync.valueOrNull ?? [])
        profile.id: profile,
    };
    final names = userIds
        .map((id) => profileById[id]?.fullName ?? id)
        .where((name) => name.isNotEmpty)
        .toList();
    if (names.isEmpty) {
      return 'Someone is typing...';
    }
    if (names.length == 1) {
      return '${names.first} is typing...';
    }
    if (names.length == 2) {
      return '${names[0]} and ${names[1]} are typing...';
    }
    return '${names[0]}, ${names[1]} and others are typing...';
  }

  Widget _buildMentionList(AsyncValue<List<Profile>> profilesAsync) {
    if (_mentionResults.isEmpty) {
      return const SizedBox.shrink();
    }

    return Container(
      constraints: const BoxConstraints(maxHeight: 200),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceContainerHighest,
        borderRadius: BorderRadius.circular(
          AppSurfaces.of(context).compactCardRadius,
        ),
      ),
      child: ListView.separated(
        shrinkWrap: true,
        padding: const EdgeInsets.symmetric(vertical: 8),
        itemCount: _mentionResults.length,
        separatorBuilder: (context, index) => const SizedBox(height: 4),
        itemBuilder: (context, index) {
          final profile = _mentionResults[index];
          final label = profile.fullName.isEmpty
              ? profile.id
              : profile.fullName;
          return ListTile(
            dense: true,
            title: Text(label),
            onTap: () => _insertMention(profile),
          );
        },
      ),
    );
  }

  void _insertMention(Profile profile) {
    final start = _mentionStart;
    if (start == null) {
      _clearMentions();
      return;
    }
    final text = _messageController.text;
    final cursor = _messageController.selection.baseOffset;
    final end = cursor < 0 ? text.length : cursor;
    final label = profile.fullName.isEmpty ? profile.id : profile.fullName;
    final mentionText = '@$label ';
    final updated = text.replaceRange(start, end, mentionText);
    final newCursor = start + mentionText.length;
    _messageController.text = updated;
    _messageController.selection = TextSelection.collapsed(offset: newCursor);
    _clearMentions();
  }

  Widget _buildTatRow(
    BuildContext context,
    Ticket ticket,
    DateTime? respondedAtOverride,
  ) {
    final respondedAt = respondedAtOverride ?? ticket.respondedAt;
    final responseDuration = respondedAt?.difference(ticket.createdAt);
    final triageEnd = _earliestDate(ticket.promotedAt, ticket.closedAt);
    final triageStart = respondedAt;
    final triageDuration = triageStart == null || triageEnd == null
        ? null
        : triageEnd.difference(triageStart);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          'Response time: ${responseDuration == null ? 'Pending' : _formatDuration(responseDuration)}',
        ),
        const SizedBox(height: 8),
        Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Expanded(
              child: Text(
                'Triage duration: ${triageDuration == null ? 'Pending' : _formatDuration(triageDuration)}',
              ),
            ),
            IconButton(
              tooltip: 'View timeline',
              onPressed: () => _showTimelineDialog(context, ticket),
              icon: const Icon(Icons.access_time),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildMentionText(
    String text,
    Color baseColor,
    List<Profile> profiles,
  ) {
    final mentionColor = Theme.of(context).colorScheme.primary;
    final spans = _mentionSpans(text, baseColor, mentionColor, profiles);
    return RichText(
      text: TextSpan(
        children: spans,
        style: TextStyle(color: baseColor),
      ),
    );
  }

  List<TextSpan> _mentionSpans(
    String text,
    Color baseColor,
    Color mentionColor,
    List<Profile> profiles,
  ) {
    final mentionLabels = profiles
        .map(
          (profile) => profile.fullName.isEmpty ? profile.id : profile.fullName,
        )
        .where((label) => label.isNotEmpty)
        .map(_escapeRegExp)
        .toList();
    final pattern = mentionLabels.isEmpty
        ? r'@\S+'
        : '@(?:${mentionLabels.join('|')})';
    final matches = RegExp(pattern, caseSensitive: false).allMatches(text);
    if (matches.isEmpty) {
      return [
        TextSpan(
          text: text,
          style: TextStyle(color: baseColor),
        ),
      ];
    }

    final spans = <TextSpan>[];
    var lastIndex = 0;
    for (final match in matches) {
      if (match.start > lastIndex) {
        spans.add(
          TextSpan(
            text: text.substring(lastIndex, match.start),
            style: TextStyle(color: baseColor),
          ),
        );
      }
      spans.add(
        TextSpan(
          text: text.substring(match.start, match.end),
          style: TextStyle(color: mentionColor, fontWeight: FontWeight.w700),
        ),
      );
      lastIndex = match.end;
    }
    if (lastIndex < text.length) {
      spans.add(
        TextSpan(
          text: text.substring(lastIndex),
          style: TextStyle(color: baseColor),
        ),
      );
    }
    return spans;
  }

  String _escapeRegExp(String value) {
    return value.replaceAllMapped(
      RegExp(r'[\\^$.*+?()[\]{}|]'),
      (match) => '\\${match[0]}',
    );
  }

  DateTime? _earliestDate(DateTime? first, DateTime? second) {
    if (first == null) return second;
    if (second == null) return first;
    return first.isBefore(second) ? first : second;
  }

  String _officeLabel(AsyncValue<List<Office>> officesAsync, Ticket ticket) {
    final offices = officesAsync.valueOrNull ?? [];
    final office = offices
        .where((item) => item.id == ticket.officeId)
        .firstOrNull;
    return office?.name ?? ticket.officeId;
  }

  Future<void> _showTimelineDialog(BuildContext context, Ticket ticket) async {
    await showDialog<void>(
      context: context,
      builder: (dialogContext) {
        return AlertDialog(
          shape: AppSurfaces.of(context).dialogShape,
          title: const Text('Ticket Timeline'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _timelineRow('Created', ticket.createdAt),
              _timelineRow('Responded', ticket.respondedAt),
              _timelineRow('Promoted', ticket.promotedAt),
              _timelineRow('Closed', ticket.closedAt),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(dialogContext).pop(),
              child: const Text('Close'),
            ),
          ],
        );
      },
    );
  }

  Widget _timelineRow(String label, DateTime? value) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Text('$label: ${value == null ? '—' : AppTime.formatDate(value)}'),
    );
  }

  String _formatDuration(Duration duration) {
    if (duration.inSeconds < 60) {
      return 'Less than a minute';
    }
    final hours = duration.inHours;
    final minutes = duration.inMinutes.remainder(60);
    if (hours > 0) {
      return '${hours}h ${minutes}m';
    }
    return '${minutes}m';
  }

  Widget _buildStatusChip(
    BuildContext context,
    WidgetRef ref,
    Ticket ticket,
    bool canPromote,
  ) {
    final isLocked = ticket.status == 'promoted' || ticket.status == 'closed';
    final chip = StatusPill(
      label: _statusLabel(ticket.status),
      isEmphasized: ticket.status != 'pending',
    );

    if (isLocked) {
      return chip;
    }

    final availableStatuses = canPromote
        ? _statusOptions
        : _statusOptions.where((status) => status != 'promoted').toList();

    return PopupMenuButton<String>(
      onSelected: (value) async {
        // Rely on the realtime stream to propagate the status change.
        await ref
            .read(ticketsControllerProvider)
            .updateTicketStatus(ticketId: ticket.id, status: value);
      },
      itemBuilder: (context) => availableStatuses
          .map(
            (status) => PopupMenuItem(
              value: status,
              child: Text(_statusMenuLabel(status)),
            ),
          )
          .toList(),
      child: chip,
    );
  }

  String _statusLabel(String status) {
    return status.toUpperCase();
  }

  String _statusMenuLabel(String status) {
    return switch (status) {
      'pending' => 'Pending',
      'promoted' => 'Promote to Task',
      'closed' => 'Close',
      _ => status,
    };
  }

  bool _canPromote(String role) {
    return role == 'admin' || role == 'dispatcher' || role == 'it_staff';
  }

  bool _canAssignStaff(String role) {
    return role == 'admin' || role == 'dispatcher' || role == 'it_staff';
  }
}

class _MetaBadge extends StatelessWidget {
  const _MetaBadge({required this.label, required this.value, this.isMono});

  final String label;
  final String value;
  final bool? isMono;

  @override
  Widget build(BuildContext context) {
    final border = Theme.of(context).colorScheme.outlineVariant;
    final background = Theme.of(context).colorScheme.surfaceContainerLow;
    final textStyle = Theme.of(context).textTheme.labelSmall;
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(
        color: background,
        borderRadius: BorderRadius.circular(
          AppSurfaces.of(context).compactCardRadius,
        ),
        border: Border.all(color: border),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(label, style: textStyle),
          const SizedBox(width: 6),
          if (isMono == true)
            MonoText(value, style: textStyle)
          else
            Text(value, style: textStyle),
        ],
      ),
    );
  }
}

extension _FirstOrNull<T> on Iterable<T> {
  T? get firstOrNull => isEmpty ? null : first;
}
