import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_math_fork/flutter_math.dart';

const String kMathEquationType = 'math_equation';
const String kMathEquationAttr = 'math_equation';

SelectionMenuItem mathEquationMenuItem = SelectionMenuItem(
  name: 'Math Equation',
  icon: (editorState, onSelected) => Icon(
    Icons.text_fields_rounded,
    color: onSelected
        ? editorState.editorStyle.selectionMenuItemSelectedIconColor
        : editorState.editorStyle.selectionMenuItemIconColor,
    size: 18.0,
  ),
  keywords: ['tex, latex, katex', 'math equation'],
  handler: (editorState, _, __) {
    final selection =
        editorState.service.selectionService.currentSelection.value;
    final textNodes = editorState.service.selectionService.currentSelectedNodes
        .whereType<TextNode>();
    if (selection == null || textNodes.isEmpty) {
      return;
    }
    final textNode = textNodes.first;
    final Path mathEquationNodePath;
    if (textNode.toPlainText().isEmpty) {
      mathEquationNodePath = selection.end.path;
    } else {
      mathEquationNodePath = selection.end.path.next;
    }
    // insert the math equation node
    final transaction = editorState.transaction
      ..insertNode(
        mathEquationNodePath,
        Node(type: kMathEquationType, attributes: {kMathEquationAttr: ''}),
      )
      ..afterSelection = selection;
    editorState.apply(transaction);

    // tricy to show the editing dialog.
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final mathEquationState = editorState.document
          .nodeAtPath(mathEquationNodePath)
          ?.key
          .currentState;
      if (mathEquationState != null &&
          mathEquationState is _MathEquationNodeWidgetState) {
        mathEquationState.showEditingDialog();
      }
    });
  },
);

class MathEquationNodeWidgetBuidler extends NodeWidgetBuilder<Node>
    with ActionProvider<Node> {
  @override
  Widget build(NodeWidgetContext<Node> context) {
    return _MathEquationNodeWidget(
      key: context.node.key,
      node: context.node,
      editorState: context.editorState,
    );
  }

  @override
  NodeValidator<Node> get nodeValidator =>
      (node) => node.attributes[kMathEquationAttr] is String;

  @override
  List<ActionMenuItem> actions(NodeWidgetContext<Node> context) {
    return [
      ActionMenuItem.svg(
        name: "delete",
        onPressed: () {
          final transaction = context.editorState.transaction
            ..deleteNode(context.node);
          context.editorState.apply(transaction);
        },
      ),
    ];
  }
}

class _MathEquationNodeWidget extends StatefulWidget {
  const _MathEquationNodeWidget({
    Key? key,
    required this.node,
    required this.editorState,
  }) : super(key: key);

  final Node node;
  final EditorState editorState;

  @override
  State<_MathEquationNodeWidget> createState() =>
      _MathEquationNodeWidgetState();
}

class _MathEquationNodeWidgetState extends State<_MathEquationNodeWidget> {
  String get _mathEquation =>
      widget.node.attributes[kMathEquationAttr] as String;
  bool _isHover = false;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onHover: (value) {
        setState(() {
          _isHover = value;
        });
      },
      onTap: () {
        showEditingDialog();
      },
      child: Stack(
        children: [
          _buildMathEquation(context),
        ],
      ),
    );
  }

  Widget _buildMathEquation(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width,
      constraints: const BoxConstraints(minHeight: 50),
      padding: const EdgeInsets.symmetric(vertical: 20),
      decoration: BoxDecoration(
        borderRadius: const BorderRadius.all(Radius.circular(8.0)),
        color: _isHover || _mathEquation.isEmpty
            ? Colors.grey[200]
            : Colors.transparent,
      ),
      child: Center(
        child: _mathEquation.isEmpty
            ? Text(
                'Add a Math Equation',
                style: widget.editorState.editorStyle.placeholderTextStyle,
              )
            : Math.tex(
                _mathEquation,
                textStyle: const TextStyle(fontSize: 20),
                mathStyle: MathStyle.display,
              ),
      ),
    );
  }

  void showEditingDialog() {
    showDialog(
      context: context,
      builder: (context) {
        final controller = TextEditingController(text: _mathEquation);
        return AlertDialog(
          title: const Text('Edit Math Equation'),
          content: RawKeyboardListener(
            focusNode: FocusNode(),
            onKey: (key) {
              if (key is! RawKeyDownEvent) return;
              if (key.logicalKey == LogicalKeyboardKey.enter &&
                  !key.isShiftPressed) {
                _updateMathEquation(controller.text, context);
              } else if (key.logicalKey == LogicalKeyboardKey.escape) {
                _dismiss(context);
              }
            },
            child: TextField(
              autofocus: true,
              controller: controller,
              maxLines: null,
              decoration: const InputDecoration(
                border: OutlineInputBorder(),
                hintText: 'E = MC^2',
              ),
            ),
          ),
          actions: [
            TextButton(
              onPressed: () => _dismiss(context),
              child: const Text('Cancel'),
            ),
            TextButton(
              onPressed: () => _updateMathEquation(controller.text, context),
              child: const Text('Done'),
            ),
          ],
        );
      },
    );
  }

  void _updateMathEquation(String mathEquation, BuildContext context) {
    if (mathEquation == _mathEquation) {
      _dismiss(context);
      return;
    }
    final transaction = widget.editorState.transaction;
    transaction.updateNode(
      widget.node,
      {
        kMathEquationAttr: mathEquation,
      },
    );
    widget.editorState.apply(transaction);
    _dismiss(context);
  }

  void _dismiss(BuildContext context) {
    Navigator.of(context).pop();
  }
}
