import 'package:markdown/markdown.dart' as md;

/// The Markdown feature set the app renders for Talk messages — CommonMark plus
/// the GFM extensions the official Talk clients enable.
///
/// It deliberately omits `InlineHtmlSyntax` (raw HTML stays literal), footnotes,
/// alert blocks, color swatches and `:shortcode:` emoji — matching Talk's
/// restrictions. Shared by the renderer ([MessageMarkdown]) and the plain-text
/// stripper below so both agree on what counts as Markdown.
final md.ExtensionSet talkMarkdownExtensionSet = md.ExtensionSet(
  <md.BlockSyntax>[
    const md.FencedCodeBlockSyntax(),
    const md.TableSyntax(),
    const md.UnorderedListWithCheckboxSyntax(),
    const md.OrderedListWithCheckboxSyntax(),
  ],
  <md.InlineSyntax>[
    md.StrikethroughSyntax(),
    md.AutolinkExtensionSyntax(),
  ],
);

/// Block-level tags after which running text would otherwise glue together;
/// their boundaries become a single space in the flattened preview.
const Set<String> _blockTags = {
  'p',
  'li',
  'blockquote',
  'pre',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'br',
  'hr',
  'tr',
  'th',
  'td',
};

/// Flattens a Markdown [source] into a single line of readable plain text —
/// markers (`**`, `#`, `` ` ``, list bullets, link/image syntax …) removed,
/// links reduced to their label and images to their alt text. Used for the
/// one-line previews (reply quote, conversation list) where Talk also strips
/// formatting rather than rendering it.
String markdownToPlainText(String source) {
  final nodes = md.Document(
    extensionSet: talkMarkdownExtensionSet,
    encodeHtml: false,
  ).parse(source);

  final buffer = StringBuffer();
  _collect(nodes, buffer);
  return buffer.toString().replaceAll(RegExp(r'\s+'), ' ').trim();
}

void _collect(List<md.Node> nodes, StringBuffer buffer) {
  for (final node in nodes) {
    if (node is md.Text) {
      buffer.write(node.text);
    } else if (node is md.Element) {
      // Images carry their text in the `alt` attribute, not in child nodes.
      if (node.tag == 'img') {
        buffer.write(node.attributes['alt'] ?? '');
        continue;
      }
      final isBlock = _blockTags.contains(node.tag);
      if (isBlock) buffer.write(' ');
      final children = node.children;
      if (children != null) _collect(children, buffer);
      if (isBlock) buffer.write(' ');
    }
  }
}
