content: import 'dart:math';
  
  import 'package:flutter/material.dart';
  
  import 'package:matrix/matrix.dart';
  
  import 'package:fluffychat/l10n/l10n.dart';
  import 'package:fluffychat/pages/chat/events/video_player.dart';
  import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
  import 'package:fluffychat/utils/date_time_extension.dart';
  import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
  import 'package:fluffychat/widgets/avatar.dart';
  import 'package:fluffychat/widgets/matrix.dart';
  import '../../../config/app_config.dart';
  import '../../../utils/event_checkbox_extension.dart';
  import '../../../utils/platform_infos.dart';
  import '../../../utils/url_launcher.dart';
  import '../../bootstrap/bootstrap_dialog.dart';
  import 'audio_player.dart';
  import 'cute_events.dart';
  import 'html_message.dart';
  import 'image_bubble.dart';
  import 'map_bubble.dart';
  import 'message_download_content.dart';
  
  class MessageContent extends StatelessWidget {
    final Event event;
    final Color textColor;
    final Color linkColor;
    final void Function(Event)? onInfoTab;
    final BorderRadius borderRadius;
    final Timeline timeline;
    final bool selected;
  
    const MessageContent(
      this.event, {
      this.onInfoTab,
      super.key,
      required this.timeline,
      required this.textColor,
      required this.linkColor,
      required this.borderRadius,
      required this.selected,
    });
  
    void _verifyOrRequestKey(BuildContext context) async {
      final l10n = L10n.of(context);
      if (event.content['can_request_session'] != true) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(
              event.calcLocalizedBodyFallback(MatrixLocals(l10n)),
            ),
          ),
        );
        return;
      }
      final client = Matrix.of(context).client;
      if (client.isUnknownSession && client.encryption!.crossSigning.enabled) {
        final success = await BootstrapDialog(
          client: Matrix.of(context).client,
        ).show(context);
        if (success != true) return;
      }
      event.requestKey();
      final sender = event.senderFromMemoryOrFallback;
      await showAdaptiveBottomSheet(
        context: context,
        builder: (context) => Scaffold(
          appBar: AppBar(
            leading: CloseButton(onPressed: Navigator.of(context).pop),
            title: Text(
              l10n.whyIsThisMessageEncrypted,
              style: const TextStyle(fontSize: 16),
            ),
          ),
          body: SafeArea(
            child: ListView(
              padding: const EdgeInsets.all(16),
              children: [
                ListTile(
                  contentPadding: EdgeInsets.zero,
                  leading: Avatar(
                    mxContent: sender.avatarUrl,
                    name: sender.calcDisplayname(),
                    presenceUserId: sender.stateKey,
                    client: event.room.client,
                  ),
                  title: Text(sender.calcDisplayname()),
                  subtitle: Text(event.originServerTs.localizedTime(context)),
                  trailing: const Icon(Icons.lock_outlined),
                ),
                const Divider(),
                Text(
                  event.calcLocalizedBodyFallback(
                    MatrixLocals(l10n),
                  ),
                ),
              ],
            ),
          ),
        ),
      );
    }
  
    @override
    Widget build(BuildContext context) {
      final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
      final buttonTextColor = textColor;
      switch (event.type) {
        case EventTypes.Message:
        case EventTypes.Encrypted:
        case EventTypes.Sticker:
          switch (event.messageType) {
            case MessageTypes.Image:
            case MessageTypes.Sticker:
              if (event.redacted) continue textmessage;
              const maxSize = 256.0;
              final w = event.content
                  .tryGetMap<String, Object?>('info')
                  ?.tryGet<int>('w');
              final h = event.content
                  .tryGetMap<String, Object?>('info')
                  ?.tryGet<int>('h');
              var width = maxSize;
              var height = maxSize;
              var fit = event.messageType == MessageTypes.Sticker
                  ? BoxFit.contain
                  : BoxFit.cover;
              if (w != null && h != null) {
                fit = BoxFit.contain;
                if (w > h) {
                  width = maxSize;
                  height = max(32, maxSize * (h / w));
                } else {
                  height = maxSize;
                  width = max(32, maxSize * (w / h));
                }
              }
              return ImageBubble(
                event,
                width: width,
                height: height,
                fit: fit,
                borderRadius: borderRadius,
                timeline: timeline,
                textColor: textColor,
              );
            case CuteEventContent.eventType:
              return CuteContent(event);
            case MessageTypes.Audio:
              if (PlatformInfos.isMobile ||
                      PlatformInfos.isMacOS ||
                      PlatformInfos.isWeb
                  // Disabled until https://github.com/bleonard252/just_audio_mpv/issues/3
                  // is fixed
                  //   || PlatformInfos.isLinux
                  ) {
                return AudioPlayerWidget(
                  event,
                  color: textColor,
                  linkColor: linkColor,
                  fontSize: fontSize,
                );
              }
              return MessageDownloadContent(
                event,
                textColor: textColor,
                linkColor: linkColor,
              );
            case MessageTypes.Video:
              return EventVideoPlayer(
                event,
                textColor: textColor,
                linkColor: linkColor,
                timeline: timeline,
              );
            case MessageTypes.File:
              return MessageDownloadContent(
                event,
                textColor: textColor,
                linkColor: linkColor,
              );
            case MessageTypes.BadEncrypted:
            case EventTypes.Encrypted:
              return _ButtonContent(
                textColor: buttonTextColor,
                onPressed: () => _verifyOrRequestKey(context),
                icon: '🔒',
                label: L10n.of(context).encrypted,
                fontSize: fontSize,
              );
            case MessageTypes.Location:
              final geoUri =
                  Uri.tryParse(event.content.tryGet<String>('geo_uri')!);
              if (geoUri != null && geoUri.scheme == 'geo') {
                final latlong = geoUri.path
                    .split(';')
                    .first
                    .split(',')
                    .map((s) => double.tryParse(s))
                    .toList();
                if (latlong.length == 2 &&
                    latlong.first != null &&
                    latlong.last != null) {
                  return Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      MapBubble(
                        latitude: latlong.first!,
                        longitude: latlong.last!,
                      ),
                      const SizedBox(height: 6),
                      OutlinedButton.icon(
                        icon: Icon(Icons.location_on_outlined, color: textColor),
                        onPressed:
                            UrlLauncher(context, geoUri.toString()).launchUrl,
                        label: Text(
                          L10n.of(context).openInMaps,
                          style: TextStyle(color: textColor),
                        ),
                      ),
                    ],
                  );
                }
              }
              continue textmessage;
            case MessageTypes.Text:
            case MessageTypes.Notice:
            case MessageTypes.Emote:
            case MessageTypes.None:
            textmessage:
            default:
              if (event.redacted) {
                return FutureBuilder<User?>(
                  future: event.redactedBecause?.fetchSenderUser(),
                  builder: (context, snapshot) {
                    final reason =
                        event.redactedBecause?.content.tryGet<String>('reason');
                    final redactedBy = snapshot.data?.calcDisplayname() ??
                        event.redactedBecause?.senderId.localpart ??
                        L10n.of(context).user;
                    return _ButtonContent(
                      label: reason == null
                          ? L10n.of(context).redactedBy(redactedBy)
                          : L10n.of(context).redactedByBecause(
                              redactedBy,
                              reason,
                            ),
                      icon: '🗑️',
                      textColor: buttonTextColor.withAlpha(128),
                      onPressed: () => onInfoTab!(event),
                      fontSize: fontSize,
                    );
                  },
                );
              }
              var html = AppConfig.renderHtml && event.isRichMessage
                  ? event.formattedText
                  : event.body;
              if (event.messageType == MessageTypes.Emote) {
                html = '* $html';
              }
  
              final bigEmotes = event.onlyEmotes &&
                  event.numberEmotes > 0 &&
                  event.numberEmotes <= 3;
              return Padding(
                padding: const EdgeInsets.symmetric(
                  horizontal: 16,
                  vertical: 8,
                ),
                child: HtmlMessage(
                  html: html,
                  textColor: textColor,
                  room: event.room,
                  fontSize: AppConfig.fontSizeFactor *
                      AppConfig.messageFontSize *
                      (bigEmotes ? 5 : 1),
                  limitHeight: !selected,
                  linkStyle: TextStyle(
                    color: linkColor,
                    fontSize:
                        AppConfig.fontSizeFactor * AppConfig.messageFontSize,
                    decoration: TextDecoration.underline,
                    decorationColor: linkColor,
                  ),
                  onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
                  eventId: event.eventId,
                  checkboxCheckedEvents: event.aggregatedEvents(
                    timeline,
                    EventCheckboxRoomExtension.relationshipType,
                  ),
                ),
              );
          }
        case EventTypes.CallInvite:
          return FutureBuilder<User?>(
            future: event.fetchSenderUser(),
            builder: (context, snapshot) {
              return _ButtonContent(
                label: L10n.of(context).startedACall(
                  snapshot.data?.calcDisplayname() ??
                      event.senderFromMemoryOrFallback.calcDisplayname(),
                ),
                icon: '📞',
                textColor: buttonTextColor,
                onPressed: () => onInfoTab!(event),
                fontSize: fontSize,
              );
            },
          );
        default:
          return FutureBuilder<User?>(
            future: event.fetchSenderUser(),
            builder: (context, snapshot) {
              return _ButtonContent(
                label: L10n.of(context).userSentUnknownEvent(
                  snapshot.data?.calcDisplayname() ??
                      event.senderFromMemoryOrFallback.calcDisplayname(),
                  event.type,
                ),
                icon: 'ℹ️',
                textColor: buttonTextColor,
                onPressed: () => onInfoTab!(event),
                fontSize: fontSize,
              );
            },
          );
      }
    }
  }
  
  class _ButtonContent extends StatelessWidget {
    final void Function() onPressed;
    final String label;
    final String icon;
    final Color? textColor;
    final double fontSize;
  
    const _ButtonContent({
      required this.label,
      required this.icon,
      required this.textColor,
      required this.onPressed,
      required this.fontSize,
    });
  
    @override
    Widget build(BuildContext context) {
      return Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: 16,
          vertical: 8,
        ),
        child: InkWell(
          onTap: onPressed,
          child: Text(
            '$icon  $label',
            style: TextStyle(
              color: textColor,
              fontSize: fontSize,
            ),
          ),
        ),
      );
    }
  }
  