import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';

import '../../../../utils/url_opener.dart';
import '../data/talk_markdown.dart';

/// Renders a Talk chat-message body as Markdown, matching the subset the
/// official Nextcloud Talk clients render.
///
/// Supported (the intentional "Rahmen", kept in sync with Talk's GFM-based
/// renderer): bold, italic, strikethrough, inline code, links (explicit and
/// bare autolinks), headings, ordered/unordered/task lists, fenced code
/// blocks, block quotes, horizontal rules and tables.
///
/// Deliberately unsupported, mirroring Talk's restrictions: raw HTML is shown
/// literally (never interpreted) and images are not fetched/rendered — only
/// their alt text (or URL) is shown, so a message can never pull a remote
/// resource on display.
class MessageMarkdown extends StatelessWidget {
  final String data;
  final TextStyle? style;

  const MessageMarkdown({required this.data, this.style, super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final base =
        style ??
        theme.textTheme.bodyMedium ??
        DefaultTextStyle.of(context).style;
    final baseSize = base.fontSize ?? 14;
    final muted = (base.color ?? theme.colorScheme.onSurface).withValues(
      alpha: 0.55,
    );
    final codeBackground = theme.colorScheme.surfaceContainerHighest.withValues(
      alpha: 0.6,
    );
    final codeStyle = base.copyWith(
      fontFamily: 'monospace',
      fontFamilyFallback: const ['monospace'],
      fontSize: baseSize * 0.92,
    );

    final styleSheet = MarkdownStyleSheet.fromTheme(theme).copyWith(
      p: base,
      a: base.copyWith(
        color: Colors.blue,
        decoration: TextDecoration.underline,
      ),
      em: base.copyWith(fontStyle: FontStyle.italic),
      strong: base.copyWith(fontWeight: FontWeight.bold),
      del: base.copyWith(decoration: TextDecoration.lineThrough),
      // Talk levels headings down so they stay readable inside a chat bubble;
      // we cap them at modest multiples of the body size instead of the theme's
      // display sizes.
      h1: base.copyWith(fontSize: baseSize * 1.5, fontWeight: FontWeight.bold),
      h2: base.copyWith(fontSize: baseSize * 1.4, fontWeight: FontWeight.bold),
      h3: base.copyWith(fontSize: baseSize * 1.3, fontWeight: FontWeight.bold),
      h4: base.copyWith(fontSize: baseSize * 1.15, fontWeight: FontWeight.bold),
      h5: base.copyWith(fontSize: baseSize * 1.05, fontWeight: FontWeight.bold),
      h6: base.copyWith(fontWeight: FontWeight.bold),
      code: codeStyle.copyWith(backgroundColor: codeBackground),
      codeblockPadding: const EdgeInsets.all(8),
      codeblockDecoration: BoxDecoration(
        color: codeBackground,
        borderRadius: BorderRadius.circular(6),
      ),
      blockquote: base.copyWith(color: muted),
      blockquotePadding: const EdgeInsets.only(left: 12, top: 2, bottom: 2),
      blockquoteDecoration: BoxDecoration(
        border: Border(left: BorderSide(color: muted, width: 4)),
      ),
      listBullet: base,
      checkbox: base,
      horizontalRuleDecoration: BoxDecoration(
        border: Border(top: BorderSide(color: muted)),
      ),
      tableHead: base.copyWith(fontWeight: FontWeight.bold),
      tableBody: base,
      tableBorder: TableBorder.all(color: muted, width: 0.8),
      tableCellsPadding: const EdgeInsets.symmetric(
        horizontal: 6,
        vertical: 3,
      ),
      // Keep multi-block messages compact; the default spacing is too airy for
      // a chat bubble.
      blockSpacing: baseSize * 0.5,
    );

    return MarkdownBody(
      data: data,
      styleSheet: styleSheet,
      extensionSet: talkMarkdownExtensionSet,
      // Talk turns a single newline into a line break (`breaks: true`);
      // standard Markdown would collapse it into a space.
      softLineBreak: true,
      onTapLink: (text, href, title) {
        if (href != null && href.isNotEmpty) unawaited(UrlOpener.openUrl(href));
      },
      // No remote fetch: render the alt text (or URL) instead of the image.
      imageBuilder: (uri, title, alt) => Text(
        (alt != null && alt.isNotEmpty) ? alt : uri.toString(),
        style: base,
      ),
      checkboxBuilder: (checked) => Padding(
        padding: const EdgeInsets.only(right: 4),
        child: Icon(
          checked ? Icons.check_box_outlined : Icons.check_box_outline_blank,
          size: baseSize + 2,
          color: base.color,
        ),
      ),
    );
  }
}
