import 'package:flutter/material.dart';

import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_matrix_html/flutter_html.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/widgets/matrix.dart';
import '../../../config/app_config.dart';
import '../../../config/setting_keys.dart';
import '../../../pages/image_viewer/image_viewer.dart';
import '../../../utils/matrix_sdk_extensions.dart/matrix_locals.dart';
import '../../../utils/url_launcher.dart';

class HtmlMessage extends StatelessWidget {
  final String html;
  final int? maxLines;
  final Room room;
  final TextStyle? defaultTextStyle;
  final TextStyle? linkStyle;
  final double? emoteSize;

  const HtmlMessage({
    Key? key,
    required this.html,
    this.maxLines,
    required this.room,
    this.defaultTextStyle,
    this.linkStyle,
    this.emoteSize,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // riot-web is notorious for creating bad reply fallback events from invalid messages which, if
    // not handled properly, can lead to impersination. As such, we strip the entire `<mx-reply>` tags
    // here already, to prevent that from happening.
    // We do *not* do this in an AST and just with simple regex here, as riot-web tends to create
    // miss-matching tags, and this way we actually correctly identify what we want to strip and, well,
    // strip it.
    final renderHtml = html.replaceAll(
        RegExp('<mx-reply>.*</mx-reply>',
            caseSensitive: false, multiLine: false, dotAll: true),
        '');

    // there is no need to pre-validate the html, as we validate it while rendering

    final matrix = Matrix.of(context);

    final themeData = Theme.of(context);
    return Html(
      data: renderHtml,
      defaultTextStyle: defaultTextStyle,
      emoteSize: emoteSize,
      linkStyle: linkStyle ??
          themeData.textTheme.bodyText2!.copyWith(
            color: themeData.colorScheme.secondary,
            decoration: TextDecoration.underline,
          ),
      shrinkToFit: true,
      maxLines: maxLines,
      onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
      onPillTap: (url) => UrlLauncher(context, url).launchUrl(),
      getMxcUrl: (String mxc, double? width, double? height,
          {bool? animated = false}) {
        final ratio = MediaQuery.of(context).devicePixelRatio;
        return Uri.parse(mxc)
            .getThumbnail(
              matrix.client,
              width: (width ?? 800) * ratio,
              height: (height ?? 800) * ratio,
              method: ThumbnailMethod.scale,
              animated: AppConfig.autoplayImages ? animated : false,
            )
            .toString();
      },
      onImageTap: (String mxc) => showDialog(
          context: Matrix.of(context).navigatorContext,
          useRootNavigator: false,
          builder: (_) => ImageViewer(Event(
              type: EventTypes.Message,
              content: <String, dynamic>{
                'body': mxc,
                'url': mxc,
                'msgtype': MessageTypes.Image,
              },
              senderId: room.client.userID!,
              originServerTs: DateTime.now(),
              eventId: 'fake_event',
              room: room))),
      setCodeLanguage: (String key, String value) async {
        await matrix.store.setItem('${SettingKeys.codeLanguage}.$key', value);
      },
      getCodeLanguage: (String key) async {
        return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
      },
      getPillInfo: (String url) async {
        final identityParts = url.parseIdentifierIntoParts();
        final identifier = identityParts?.primaryIdentifier;
        if (identifier == null) {
          return {};
        }
        if (identifier.sigil == '@') {
          // we have a user pill
          final user = room.getState('m.room.member', identifier);
          if (user != null) {
            return user.content;
          }
          // there might still be a profile...
          final profile = await room.client.getProfileFromUserId(identifier);
          return {
            'displayname': profile.displayName,
            'avatar_url': profile.avatarUrl.toString(),
          };
        }
        if (identifier.sigil == '#') {
          // we have an alias pill
          for (final r in room.client.rooms) {
            final state = r.getState('m.room.canonical_alias');
            if (state != null &&
                ((state.content['alias'] is String &&
                        state.content['alias'] == identifier) ||
                    (state.content['alt_aliases'] is List &&
                        state.content['alt_aliases'].contains(identifier)))) {
              // we have a room!
              return {
                'displayname':
                    r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
                'avatar_url': r.getState('m.room.avatar')?.content['url'],
              };
            }
          }
          return {};
        }
        if (identifier.sigil == '!') {
          // we have a room ID pill
          final r = room.client.getRoomById(identifier);
          if (r == null) {
            return {};
          }
          return {
            'displayname':
                r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
            'avatar_url': r.getState('m.room.avatar')?.content['url'],
          };
        }
        return {};
      },
    );
  }
}
