import 'dart:collection';

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

ShortcutEvent insertHorizontalRule = ShortcutEvent(
  key: 'Horizontal rule',
  command: 'Minus',
  handler: _insertHorzaontalRule,
);

ShortcutEventHandler _insertHorzaontalRule = (editorState, event) {
  final selection = editorState.service.selectionService.currentSelection.value;
  final textNodes = editorState.service.selectionService.currentSelectedNodes
      .whereType<TextNode>();
  if (textNodes.length != 1 || selection == null) {
    return KeyEventResult.ignored;
  }
  final textNode = textNodes.first;
  if (textNode.toPlainText() == '--') {
    final transaction = editorState.transaction
      ..deleteText(textNode, 0, 2)
      ..insertNode(
        textNode.path,
        Node(
          type: 'horizontal_rule',
          children: LinkedList(),
          attributes: {},
        ),
      )
      ..afterSelection =
          Selection.single(path: textNode.path.next, startOffset: 0);
    editorState.apply(transaction);
    return KeyEventResult.handled;
  }
  return KeyEventResult.ignored;
};

SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
  name: 'Horizontal rule',
  icon: (editorState, onSelected) => Icon(
    Icons.horizontal_rule,
    color: onSelected
        ? editorState.editorStyle.selectionMenuItemSelectedIconColor
        : editorState.editorStyle.selectionMenuItemIconColor,
    size: 18.0,
  ),
  keywords: ['horizontal rule'],
  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;
    if (textNode.toPlainText().isEmpty) {
      final transaction = editorState.transaction
        ..insertNode(
          textNode.path,
          Node(
            type: 'horizontal_rule',
            children: LinkedList(),
            attributes: {},
          ),
        )
        ..afterSelection =
            Selection.single(path: textNode.path.next, startOffset: 0);
      editorState.apply(transaction);
    } else {
      final transaction = editorState.transaction
        ..insertNode(
          selection.end.path.next,
          TextNode(
            children: LinkedList(),
            attributes: {
              'subtype': 'horizontal_rule',
            },
            delta: Delta()..insert('---'),
          ),
        )
        ..afterSelection = selection;
      editorState.apply(transaction);
    }
  },
);

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

  @override
  NodeValidator<Node> get nodeValidator => (node) {
        return true;
      };
}

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

  final Node node;
  final EditorState editorState;

  @override
  State<_HorizontalRuleWidget> createState() => __HorizontalRuleWidgetState();
}

class __HorizontalRuleWidgetState extends State<_HorizontalRuleWidget>
    with SelectableMixin {
  RenderBox get _renderBox => context.findRenderObject() as RenderBox;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10),
      child: Container(
        height: 1,
        color: Colors.grey,
      ),
    );
  }

  @override
  Position start() => Position(path: widget.node.path, offset: 0);

  @override
  Position end() => Position(path: widget.node.path, offset: 1);

  @override
  Position getPositionInOffset(Offset start) => end();

  @override
  bool get shouldCursorBlink => false;

  @override
  CursorStyle get cursorStyle => CursorStyle.borderLine;

  @override
  Rect? getCursorRectInPosition(Position position) {
    final size = _renderBox.size;
    return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height);
  }

  @override
  List<Rect> getRectsInSelection(Selection selection) =>
      [Offset.zero & _renderBox.size];

  @override
  Selection getSelectionInRange(Offset start, Offset end) => Selection.single(
        path: widget.node.path,
        startOffset: 0,
        endOffset: 1,
      );

  @override
  Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset);
}
