import 'package:flutter/material.dart';

import '../../theming/app_theme.dart';
import 'pm_callout_view.dart';
import 'pm_image_view.dart';
import 'pm_node.dart';
import 'pm_render_scope.dart';
import 'pm_rich_text.dart';
import 'pm_table_view.dart';

/// Read-only renderer for a parsed ProseMirror document.
///
/// Content is centered and width-capped for comfortable line lengths on
/// tablets. All styling derives from `Theme.of(context)`; the only hard-coded
/// colors are the callout accents.
class PmDocumentView extends StatelessWidget {
  final PmNode doc;
  final void Function(String href)? onLinkTap;
  final double maxContentWidth;

  const PmDocumentView({
    required this.doc,
    this.onLinkTap,
    this.maxContentWidth = 720,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return PmRenderScope(
      onLinkTap: onLinkTap,
      child: Center(
        child: ConstrainedBox(
          constraints: BoxConstraints(maxWidth: maxContentWidth),
          child: PmNodeView(node: doc),
        ),
      ),
    );
  }
}

typedef PmNodeBuilder = Widget Function(PmNode node);

/// Dispatches a node to its registered widget, falling back to
/// [FallbackNodeWidget] for anything unregistered (including [PmUnknown]).
class PmNodeView extends StatelessWidget {
  final PmNode node;

  const PmNodeView({required this.node, super.key});

  static final Map<Type, PmNodeBuilder> registry = {
    PmParagraph: (n) => PmParagraphView(node: n as PmParagraph),
    PmHeading: (n) => PmHeadingView(node: n as PmHeading),
    PmBulletList: (n) => PmBulletListView(node: n as PmBulletList),
    PmOrderedList: (n) => PmOrderedListView(node: n as PmOrderedList),
    PmBlockquote: (n) => PmBlockquoteView(node: n as PmBlockquote),
    PmCodeBlock: (n) => PmCodeBlockView(node: n as PmCodeBlock),
    PmHorizontalRule: (n) => const PmHorizontalRuleView(),
    PmCallout: (n) => PmCalloutView(node: n as PmCallout),
    PmImage: (n) => PmImageView(node: n as PmImage),
    PmTable: (n) => PmTableView(node: n as PmTable),
  };

  @override
  Widget build(BuildContext context) {
    final builder = registry[node.runtimeType];
    if (builder != null) return builder(node);
    return FallbackNodeWidget(node: node);
  }
}

/// Renders a node's children as a vertical block stack. Used for the document
/// root, unknown nodes, and any container whose type has no dedicated widget.
class FallbackNodeWidget extends StatelessWidget {
  final PmNode node;

  const FallbackNodeWidget({required this.node, super.key});

  @override
  Widget build(BuildContext context) {
    if (node.children.isEmpty) return const SizedBox.shrink();
    return pmBlocks(node.children);
  }
}

/// Lays out block-level nodes in a column, evenly spaced.
Widget pmBlocks(List<PmNode> nodes, {double gap = AppSpacing.sm}) {
  final children = <Widget>[];
  for (var i = 0; i < nodes.length; i++) {
    if (i > 0) children.add(SizedBox(height: gap));
    children.add(PmNodeView(node: nodes[i]));
  }
  return Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    mainAxisSize: MainAxisSize.min,
    children: children,
  );
}

class PmParagraphView extends StatelessWidget {
  final PmParagraph node;

  const PmParagraphView({required this.node, super.key});

  @override
  Widget build(BuildContext context) {
    final base = Theme.of(context).textTheme.bodyMedium ?? const TextStyle();
    return PmRichText(
      inlines: node.children,
      baseStyle: base,
      textAlign: node.align ?? TextAlign.start,
    );
  }
}

class PmHeadingView extends StatelessWidget {
  final PmHeading node;

  const PmHeadingView({required this.node, super.key});

  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;
    final base = switch (node.level) {
      1 => textTheme.headlineSmall,
      2 => textTheme.titleLarge,
      3 => textTheme.titleMedium,
      _ => textTheme.titleSmall,
    };
    return PmRichText(
      inlines: node.children,
      baseStyle: base ?? const TextStyle(),
      textAlign: node.align ?? TextAlign.start,
    );
  }
}

class PmBulletListView extends StatelessWidget {
  final PmBulletList node;

  const PmBulletListView({required this.node, super.key});

  @override
  Widget build(BuildContext context) =>
      _ListLayout(items: node.children, markerFor: (_) => '•');
}

class PmOrderedListView extends StatelessWidget {
  final PmOrderedList node;

  const PmOrderedListView({required this.node, super.key});

  @override
  Widget build(BuildContext context) => _ListLayout(
    items: node.children,
    markerFor: (index) => '${node.start + index}.',
  );
}

class _ListLayout extends StatelessWidget {
  final List<PmNode> items;
  final String Function(int index) markerFor;

  const _ListLayout({required this.items, required this.markerFor});

  @override
  Widget build(BuildContext context) {
    final base = Theme.of(context).textTheme.bodyMedium ?? const TextStyle();
    final rows = <Widget>[];
    for (var i = 0; i < items.length; i++) {
      if (i > 0) rows.add(const SizedBox(height: AppSpacing.xs));
      rows.add(
        Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            SizedBox(
              width: 24,
              child: Text(
                markerFor(i),
                textAlign: TextAlign.right,
                style: base,
              ),
            ),
            const SizedBox(width: AppSpacing.sm),
            Expanded(child: pmBlocks(items[i].children, gap: AppSpacing.xs)),
          ],
        ),
      );
    }
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      mainAxisSize: MainAxisSize.min,
      children: rows,
    );
  }
}

class PmBlockquoteView extends StatelessWidget {
  final PmBlockquote node;

  const PmBlockquoteView({required this.node, super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Container(
      padding: const EdgeInsets.only(left: AppSpacing.md),
      decoration: BoxDecoration(
        border: Border(
          left: BorderSide(color: theme.colorScheme.outlineVariant, width: 4),
        ),
      ),
      child: pmBlocks(node.children),
    );
  }
}

class PmCodeBlockView extends StatelessWidget {
  final PmCodeBlock node;

  const PmCodeBlockView({required this.node, super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final code = node.children.whereType<PmText>().map((t) => t.text).join();
    return DecoratedBox(
      decoration: BoxDecoration(
        color: theme.colorScheme.surfaceContainerHighest,
        borderRadius: BorderRadius.circular(8),
      ),
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Padding(
          padding: const EdgeInsets.all(AppSpacing.md),
          child: Text(
            code,
            style: theme.textTheme.bodySmall?.copyWith(fontFamily: 'monospace'),
          ),
        ),
      ),
    );
  }
}

class PmHorizontalRuleView extends StatelessWidget {
  const PmHorizontalRuleView({super.key});

  @override
  Widget build(BuildContext context) => const Divider();
}
