import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
var MAXIMUM_BROWSER_SCROLLBAR_WIDTH = 20;

/*
  Calculates the position of the floating toolbar relative to the selection.
  This is a re-implementation which closely matches the behaviour on Confluence renderer.
  The main difference is the popup is always above the selection.
  Things to consider:
  - popup is always above the selection
  - coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
  - popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
  - we use the toolbarRect to center align toolbar
  - use wrapperBounds to clamp values
  - editorView.dom bounds differ to wrapperBounds, convert at the end
*/
export var calculateToolbarPositionAboveSelection = function calculateToolbarPositionAboveSelection(toolbarTitle) {
  return function (editorView, nextPos) {
    var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
    if (!toolbar) {
      return nextPos;
    }
    // scroll wrapper for full page, fall back to document body
    // Ignored via go/ees007
    // eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format
    // TODO: look into using getScrollGutterOptions()
    var scrollWrapper = editorView.dom.closest('.fabric-editor-popup-scroll-parent') || document.body;
    var wrapperBounds = scrollWrapper.getBoundingClientRect();
    var selection = window && window.getSelection();
    var range = selection && !selection.isCollapsed && selection.getRangeAt(0);
    if (!range) {
      return nextPos;
    }
    var toolbarRect = toolbar.getBoundingClientRect();
    var _editorView$state$sel = editorView.state.selection,
      head = _editorView$state$sel.head,
      anchor = _editorView$state$sel.anchor;
    var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
    var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
    var top = (topCoords.top || 0) - toolbarRect.height * 1.5;
    var left = 0;
    // If not on the same line
    if (topCoords.top !== bottomCoords.top) {
      // selecting downwards
      if (head > anchor) {
        left = Math.max(topCoords.right, bottomCoords.right);
      } else {
        left = Math.min(topCoords.left, bottomCoords.left);
      }
      /*
           short selection above a long paragraph
           eg. short {<}heading
           The purpose of this text is to show the selection range{>}.
           The horizontal positioning should center around "heading",
           not where it ends at "range".
           Note: if it was "head<b>ing</b>" then it would only center
           around "head". Undesireable but matches the current renderer.
         */
      var cliffPosition = range.getClientRects()[0];
      if (cliffPosition.right < left) {
        left = cliffPosition.left + cliffPosition.width / 2;
      }
    } else {
      // Otherwise center on the single line selection
      left = topCoords.left + (bottomCoords.right - topCoords.left) / 2;
    }
    left -= toolbarRect.width / 2;
    // Place toolbar below selection if not sufficient space above
    if (top < wrapperBounds.top) {
      var _getCoordsBelowSelect = getCoordsBelowSelection(bottomCoords, toolbarRect);
      top = _getCoordsBelowSelect.top;
      left = _getCoordsBelowSelect.left;
    }
    // remap positions from browser document to wrapperBounds
    return {
      top: top - wrapperBounds.top + scrollWrapper.scrollTop,
      left: Math.max(0, left - wrapperBounds.left)
    };
  };
};
var findContainingElement = function findContainingElement(editorView) {
  if (expValEquals('platform_editor_sel_toolbar_scroll_pos_fix_exp', 'isEnabled', true)) {
    // Traverse DOM Tree upwards looking for scroll parents with "overflow: scroll"
    // or fixed/absolute positioned containers.
    var parent = editorView.dom;

    // Ignored via go/ees005
    // eslint-disable-next-line no-cond-assign
    while (parent = parent.parentElement) {
      var style = window.getComputedStyle(parent);

      // Check for explicit scroll parent class
      if (parent.classList.contains('fabric-editor-popup-scroll-parent')) {
        return {
          container: parent,
          isFixed: false
        };
      }

      // Check for overflow scroll containers
      if (style.overflow === 'scroll' || style.overflowX === 'scroll' || style.overflowY === 'scroll' || style.overflow === 'auto' || style.overflowX === 'auto' || style.overflowY === 'auto') {
        return {
          container: parent,
          isFixed: false
        };
      }

      // Check for fixed or absolute positioned containers (modal wrappers, sidebars)
      if (style.position === 'fixed' || style.position === 'absolute') {
        return {
          container: parent,
          isFixed: style.position === 'fixed'
        };
      }

      // Stop at body
      if (parent === document.body) {
        break;
      }
    }

    // Fall back to document.body if no suitable container found
    return {
      container: document.body,
      isFixed: false
    };
  }

  // Original logic
  var scrollParent = editorView.dom.closest('.fabric-editor-popup-scroll-parent');
  if (scrollParent) {
    return {
      container: scrollParent,
      isFixed: false
    };
  } else {
    var fixedParent = editorView.dom.parentElement;
    while (fixedParent && fixedParent !== document.body) {
      var computedStyle = window.getComputedStyle(fixedParent);
      if (computedStyle.position === 'fixed') {
        return {
          container: fixedParent,
          isFixed: true
        };
      }
      fixedParent = fixedParent.parentElement;
    }
  }

  // Fall back to document.body if no fixed parent found
  return {
    container: document.body,
    isFixed: false
  };
};
export var calculateToolbarPositionTrackHeadOld = function calculateToolbarPositionTrackHeadOld(toolbarTitle) {
  return function (editorView, nextPos) {
    var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
    if (!toolbar) {
      return nextPos;
    }
    // scroll wrapper for full page, fall back to document body
    // Ignored via go/ees007
    // eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format
    // TODO: look into using getScrollGutterOptions()
    var scrollWrapper = editorView.dom.closest('.fabric-editor-popup-scroll-parent') || document.body;
    var wrapperBounds = scrollWrapper.getBoundingClientRect();
    var selection = window && window.getSelection();
    var moreRovoOptionsButton = document.querySelector('button[aria-label="More Rovo options"], [aria-label="More Rovo options"]');
    var isMoreRovoOptionsButtonVisible = !!moreRovoOptionsButton && moreRovoOptionsButton instanceof HTMLElement && !!moreRovoOptionsButton.offsetParent;
    var range = null;
    if (isMoreRovoOptionsButtonVisible) {
      if (selection && selection.getRangeAt && selection.rangeCount > 0) {
        var maybeRange = selection.getRangeAt(0);
        if (maybeRange instanceof Range) {
          range = maybeRange;
        }
      }
    } else {
      if (selection && !selection.isCollapsed && selection.getRangeAt && selection.rangeCount > 0) {
        var _maybeRange = selection.getRangeAt(0);
        if (_maybeRange instanceof Range) {
          range = _maybeRange;
        }
      }
    }
    if (!range) {
      return nextPos;
    }
    var toolbarRect = toolbar.getBoundingClientRect();
    var _editorView$state$sel2 = editorView.state.selection,
      head = _editorView$state$sel2.head,
      anchor = _editorView$state$sel2.anchor;
    var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
    var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
    var top;
    // If not the same line, display toolbar below.
    if (head > anchor && topCoords.top !== bottomCoords.top) {
      // We are taking the previous pos to the maxium, so avoid end of line positions
      // returning the next line's rect.
      top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
    } else {
      top = (topCoords.top || 0) - toolbarRect.height * 1.5;
    }
    var left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;
    // Place toolbar below selection if not sufficient space above
    if (top < wrapperBounds.top) {
      var _getCoordsBelowSelect2 = getCoordsBelowSelection(bottomCoords, toolbarRect);
      top = _getCoordsBelowSelect2.top;
      left = _getCoordsBelowSelect2.left;
    }
    var leftCoord = Math.max(0, left - wrapperBounds.left);
    if (leftCoord + toolbarRect.width > wrapperBounds.width) {
      var _scrollbarWidth = MAXIMUM_BROWSER_SCROLLBAR_WIDTH;
      leftCoord = Math.max(0, wrapperBounds.width - (toolbarRect.width + _scrollbarWidth));
    }

    // remap positions from browser document to wrapperBounds
    return {
      top: top - wrapperBounds.top + scrollWrapper.scrollTop,
      left: leftCoord
    };
  };
};

/**
 * Same logic as calculateToolbarPositionTrackHeadOld, but with the following changes:
 * - Uses a cached container to avoid repeated DOM traversal and getComputedStyle calls
 * - Works when editor is nested within a fixed positioned parent, such as within a modal or sidebar
 */
export var calculateToolbarPositionTrackHeadNew = function calculateToolbarPositionTrackHeadNew(toolbarTitle) {
  // Cache the container to avoid repeated DOM traversal and getComputedStyle calls
  var cachedContainer = null;
  var isFixedContainer = false;
  var cachedEditorDom = null;
  return function (editorView, nextPos) {
    var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
    if (!toolbar) {
      return nextPos;
    }

    // Find and cache the container (only recalculates if editor DOM changed)
    if (cachedEditorDom !== editorView.dom) {
      cachedEditorDom = editorView.dom;
      var _findContainingElemen = findContainingElement(editorView),
        _container = _findContainingElemen.container,
        isFixed = _findContainingElemen.isFixed;
      cachedContainer = _container;
      isFixedContainer = isFixed;
    }
    if (!cachedContainer) {
      return nextPos;
    }
    var container = cachedContainer;
    var containerBounds = container.getBoundingClientRect();
    var selection = window && window.getSelection();
    var moreRovoOptionsButton = document.querySelector('button[aria-label="More Rovo options"], [aria-label="More Rovo options"]');
    var isMoreRovoOptionsButtonVisible = !!moreRovoOptionsButton && moreRovoOptionsButton instanceof HTMLElement && !!moreRovoOptionsButton.offsetParent;
    var range = null;
    if (isMoreRovoOptionsButtonVisible) {
      if (selection && selection.getRangeAt && selection.rangeCount > 0) {
        var maybeRange = selection.getRangeAt(0);
        if (maybeRange instanceof Range) {
          range = maybeRange;
        }
      }
    } else {
      if (selection && !selection.isCollapsed && selection.getRangeAt && selection.rangeCount > 0) {
        var _maybeRange2 = selection.getRangeAt(0);
        if (_maybeRange2 instanceof Range) {
          range = _maybeRange2;
        }
      }
    }
    if (!range) {
      return nextPos;
    }
    var toolbarRect = toolbar.getBoundingClientRect();
    var _editorView$state$sel3 = editorView.state.selection,
      head = _editorView$state$sel3.head,
      anchor = _editorView$state$sel3.anchor;
    var top;
    var left;
    var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
    var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
    // If not the same line AND we are selecting downwards, display toolbar below.
    if (head > anchor && topCoords.top !== bottomCoords.top) {
      // We are taking the previous pos to the maxium, so avoid end of line positions
      // returning the next line's rect.
      top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
    } else {
      top = (topCoords.top || 0) - toolbarRect.height * 1.5;
    }
    left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;

    // Place toolbar below selection if not sufficient space above
    if (top < containerBounds.top) {
      var _getCoordsBelowSelect3 = getCoordsBelowSelection(bottomCoords, toolbarRect);
      top = _getCoordsBelowSelect3.top;
      left = _getCoordsBelowSelect3.left;
    }
    var leftCoord = Math.max(0, left - containerBounds.left);
    if (leftCoord + toolbarRect.width > containerBounds.width) {
      var _scrollbarWidth2 = MAXIMUM_BROWSER_SCROLLBAR_WIDTH;
      leftCoord = Math.max(0, containerBounds.width - (toolbarRect.width + _scrollbarWidth2));
    }

    // Apply scroll offset only for non-fixed containers
    // Fixed positioned elements don't scroll with the page
    var scrollOffset = isFixedContainer ? 0 : container.scrollTop;
    return {
      top: top - containerBounds.top + scrollOffset,
      left: leftCoord
    };
  };
};

/*
  Calculates the position of the floating toolbar relative to the selection.
  This implementation works in multiple scenarios:
  - Standard scrollable containers with .fabric-editor-popup-scroll-parent
  - Fixed positioned parent containers
  - Falls back to document.body

  The function automatically detects the container type and applies the appropriate
  positioning logic and scroll offset handling.

  Things to consider:
  - stick as close to the head X release coordinates as possible
  - coordinates of head X and getBoundingClientRect() are absolute in client viewport
  - popup may appear in '.fabric-editor-popup-scroll-parent', fixed parent, or body
  - we use the toolbarRect to center align toolbar
  - use container bounds to clamp values
  - for fixed positioning, no scroll offsets are applied
  - for scrollable containers, scroll offsets are included
*/
export var calculateToolbarPositionTrackHead = function calculateToolbarPositionTrackHead(toolbarTitle) {
  var isSelToolbarFixEnabled = expValEquals('platform_editor_sel_toolbar_fix', 'isEnabled', true);
  return isSelToolbarFixEnabled ? calculateToolbarPositionTrackHeadNew(toolbarTitle) : calculateToolbarPositionTrackHeadOld(toolbarTitle);
};

/**
 * Returns the coordintes at the bottom the selection.
 */
var getCoordsBelowSelection = function getCoordsBelowSelection(bottomCoords, toolbarRect) {
  return {
    top: (bottomCoords.top || 0) + toolbarRect.height / 1.15,
    left: bottomCoords.right - toolbarRect.width / 2
  };
};
var cellSelectionToolbarOffsetTop = 10;
var scrollbarWidth = 20;
var offsetTopOnColumnSelection = 4;
export var calculateToolbarPositionOnCellSelection = function calculateToolbarPositionOnCellSelection(toolbarTitle) {
  return function (editorView, nextPos) {
    var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
    if (!toolbar) {
      return nextPos;
    }
    var selection = editorView.state.selection;
    if (!(selection instanceof CellSelection)) {
      return nextPos;
    }
    var $anchorCell = selection.$anchorCell,
      $headCell = selection.$headCell;
    var domAtPos = editorView.domAtPos.bind(editorView);
    var anchorCellDOM = findDomRefAtPos($anchorCell.pos, domAtPos);
    var headCellDOM = findDomRefAtPos($headCell.pos, domAtPos);
    if (!(anchorCellDOM instanceof HTMLElement) || !(headCellDOM instanceof HTMLElement)) {
      return nextPos;
    }
    var anchorCellRect = anchorCellDOM.getBoundingClientRect();
    var headCellRect = headCellDOM.getBoundingClientRect();
    var toolbarRect = toolbar.getBoundingClientRect();
    var top;
    if (headCellRect.top <= anchorCellRect.top) {
      // Display Selection toolbar at the top of the selection
      top = headCellRect.top - toolbarRect.height - cellSelectionToolbarOffsetTop;
    } else {
      // Display Selection toolbar at the bottom of the selection
      top = headCellRect.bottom + cellSelectionToolbarOffsetTop;
    }

    // scroll wrapper for full page, fall back to document body
    // Ignored via go/ees007
    // eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format
    // TODO: look into using getScrollGutterOptions()
    var scrollWrapper = editorView.dom.closest('.fabric-editor-popup-scroll-parent') || document.body;
    var wrapperBounds = scrollWrapper.getBoundingClientRect();
    // Place toolbar below selection if not sufficient space above
    if (top < wrapperBounds.top && headCellRect.top <= anchorCellRect.top) {
      top = anchorCellRect.bottom + cellSelectionToolbarOffsetTop;
    }
    var left;
    if (headCellRect.left < anchorCellRect.left) {
      left = headCellRect.left;
    } else if (headCellRect.left === anchorCellRect.left) {
      left = headCellRect.left + headCellRect.width / 2;
    } else {
      left = headCellRect.right;
    }

    // If a user selected multiple columns via clicking on a drag handle
    // (clicking first on the left column and then shift clicking on the right column),
    // the $headcell stays in place and $anchorcell changes position. If they clicked on the right column
    // and then shift clicked on the left, the $headCell will change while $anchor stays in place.
    // Where is no way to know if user was dragging to select the cells or clicking on the drag handle.
    // So if all cells in columns are selected, we will align the Text Formatting toolbar
    // relative to center of the selected area.
    if (selection.isColSelection()) {
      if (headCellRect.left < anchorCellRect.left) {
        left = headCellRect.left + (anchorCellRect.right - headCellRect.left) / 2;
      } else if (headCellRect.left === anchorCellRect.left) {
        left = left;
      } else {
        left = anchorCellRect.left + (headCellRect.right - anchorCellRect.left) / 2;
      }

      // When column is selected, adjust top position to ensure that the toolbar does not
      // touch column drag handle
      top = top - offsetTopOnColumnSelection;
    }
    var adjustedLeft = Math.max(0, left - toolbarRect.width / 2 - wrapperBounds.left);
    if (adjustedLeft + toolbarRect.width > wrapperBounds.width) {
      adjustedLeft = Math.max(0, wrapperBounds.width - (toolbarRect.width + scrollbarWidth));
    }

    // If the toolbar is to be placed at the bottom of the selection (it happens when
    // there is no sufficient space above) and the bottom of the selection is not in the view,
    // place the toolbar at the bottom of the visible part of selection
    if (selection.isColSelection() && top + toolbarRect.height > wrapperBounds.bottom) {
      top = top - (top - wrapperBounds.bottom) - (toolbarRect.height + 2 * cellSelectionToolbarOffsetTop);
    }
    return {
      top: top - wrapperBounds.top + scrollWrapper.scrollTop,
      left: adjustedLeft
    };
  };
};