import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
import { removeBlockMarks } from './editor-core-utils';

/**
 * This function creates a new transaction that wraps the current selection
 * in the specified node type if it results in a valid transaction.
 * If not valid, it performs a safe insert operation.
 *
 * Example of when wrapping might not be valid is when attempting to wrap
 * content that is already inside a panel with another panel
 */
export function createWrapSelectionTransaction({
  state,
  type,
  nodeAttributes
}) {
  let {
    tr
  } = state;
  const {
    alignment,
    indentation
  } = state.schema.marks;

  /** Alignment or Indentation is not valid inside block types */
  const removeAlignTr = removeBlockMarks(state, [alignment, indentation]);
  tr = removeAlignTr || tr;

  /**Get range and wrapping needed for the selection*/
  const {
    range,
    wrapping
  } = getWrappingOptions(state, type, nodeAttributes);
  if (wrapping) {
    tr.wrap(range, wrapping).scrollIntoView();
  } else {
    /** We always want to append a block type */
    safeInsert(type.createAndFill(nodeAttributes))(tr).scrollIntoView();
  }
  return tr;
}
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
export function getWrappingOptions(state, type,
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
nodeAttributes) {
  const {
    $from,
    $to,
    from
  } = state.selection;

  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let range = $from.blockRange($to);
  const isMediaSelection = state.selection instanceof NodeSelection && state.selection.node.type === state.schema.nodes.media;

  /**
   * To wrap a media group we need to start the range from one position
   * before a media selection's from position.
   */
  if (isMediaSelection) {
    const prev = from - 1;
    range = state.doc.resolve(from > 0 ? prev : from).blockRange($to);
  }
  let isAllowedChild = true;
  /**
   * Added a check to avoid wrapping codeblock
   */
  if (state.selection.empty) {
    state.doc.nodesBetween($from.pos, $to.pos, node => {
      if (!isAllowedChild) {
        return false;
      }
      return isAllowedChild = node.type !== state.schema.nodes.codeBlock;
    });
  }
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const wrapping = isAllowedChild && range && findWrapping(range, type, nodeAttributes);
  return {
    range,
    wrapping
  };
}