import type { BlockComponent } from '@blocksuite/block-std';

import { getCurrentNativeRange } from '@blocksuite/affine-shared/utils';
import { Rect } from '@blocksuite/global/utils';

import type { AffineDragHandleWidget } from '../drag-handle.js';

import {
  DRAG_HANDLE_CONTAINER_WIDTH,
  DRAG_HOVER_RECT_PADDING,
} from '../config.js';
import {
  containBlock,
  getDragHandleLeftPadding,
  includeTextSelection,
} from '../utils.js';

export class RectHelper {
  private _getHoveredBlocks = (): BlockComponent[] => {
    if (!this.widget.isHoverDragHandleVisible || !this.widget.anchorBlockId)
      return [];

    const hoverBlock = this.widget.anchorBlockComponent.peek();
    if (!hoverBlock) return [];

    const selections = this.widget.selectionHelper.selectedBlocks;
    let blocks: BlockComponent[] = [];

    // When current selection is TextSelection, should cover all the blocks in native range
    if (selections.length > 0 && includeTextSelection(selections)) {
      const range = getCurrentNativeRange();
      if (!range) return [];
      const rangeManager = this.widget.std.range;
      if (!rangeManager) return [];
      blocks = rangeManager.getSelectedBlockComponentsByRange(range, {
        match: el => el.model.role === 'content',
        mode: 'highest',
      });
    } else {
      blocks = this.widget.selectionHelper.selectedBlockComponents;
    }

    if (
      containBlock(
        blocks.map(block => block.blockId),
        this.widget.anchorBlockId.peek()!
      )
    ) {
      return blocks;
    }

    return [hoverBlock];
  };

  getDraggingAreaRect = (): Rect | null => {
    const block = this.widget.anchorBlockComponent.value;
    if (!block) return null;

    // When hover block is in selected blocks, should show hover rect on the selected blocks
    // Top: the top of the first selected block
    // Left: the left of the first selected block
    // Right: the largest right of the selected blocks
    // Bottom: the bottom of the last selected block
    let { left, top, right, bottom } = block.getBoundingClientRect();

    const blocks = this._getHoveredBlocks();

    blocks.forEach(block => {
      left = Math.min(left, block.getBoundingClientRect().left);
      top = Math.min(top, block.getBoundingClientRect().top);
      right = Math.max(right, block.getBoundingClientRect().right);
      bottom = Math.max(bottom, block.getBoundingClientRect().bottom);
    });

    const offsetLeft = getDragHandleLeftPadding(blocks);

    const offsetParentRect =
      this.widget.dragHandleContainerOffsetParent.getBoundingClientRect();
    if (!offsetParentRect) return null;

    left -= offsetParentRect.left;
    right -= offsetParentRect.left;
    top -= offsetParentRect.top;
    bottom -= offsetParentRect.top;

    const scaleInNote = this.widget.scaleInNote.value;
    // Add padding to hover rect
    left -= (DRAG_HANDLE_CONTAINER_WIDTH + offsetLeft) * scaleInNote;
    top -= DRAG_HOVER_RECT_PADDING * scaleInNote;
    right += DRAG_HOVER_RECT_PADDING * scaleInNote;
    bottom += DRAG_HOVER_RECT_PADDING * scaleInNote;

    return new Rect(left, top, right, bottom);
  };

  constructor(readonly widget: AffineDragHandleWidget) {}
}
