import { createDOMRange } from '@lexical/selection';
import { $getEditor, $getRoot, TextNode } from 'lexical';

import $findNodeAndOffsetAtCharacterPosition from '@/components/lyricsCowriteModal/$findNodeAndOffsetAtCharacterPosition';

import createScrollWrapperRelativeRectsFromDOMRange from './createScrollWrapperRelativeRectsFromDOMRange';

export default function $getCharacterSpanRects(
  startChars: number,
  endChars: number
): DOMRect[] {
  const editor = $getEditor();
  const root = $getRoot();
  const startPoint = $findNodeAndOffsetAtCharacterPosition(root, startChars);
  const endPoint = $findNodeAndOffsetAtCharacterPosition(root, endChars);
  if (
    startPoint?.node instanceof TextNode &&
    endPoint?.node instanceof TextNode
  ) {
    const range = createDOMRange(
      editor,
      startPoint.node,
      startPoint.offset,
      endPoint.node,
      endPoint.offset
    );
    if (!range) return [];
    const rects = createScrollWrapperRelativeRectsFromDOMRange(
      editor,
      range
    ).filter((rect) => rect.width > 0.02 && rect.height > 0);

    const groupedRects = (() => {
      const groups = new Map<string, DOMRect[]>();

      for (const rect of rects) {
        const key = `${rect.y}-${rect.height}`;
        if (!groups.has(key)) {
          groups.set(key, []);
        }
        groups.get(key)!.push(rect);
      }

      const merged: DOMRect[] = [];

      groups.forEach((group) => {
        group.sort((a, b) => a.x - b.x);
        let current = group[0];

        for (let i = 1; i < group.length; i++) {
          const next = group[i];
          const currentEnd = current.x + current.width;

          if (next.x <= currentEnd) {
            const newEnd = Math.max(currentEnd, next.x + next.width);
            current = new DOMRect(
              current.x,
              current.y,
              newEnd - current.x,
              current.height
            );
          } else {
            merged.push(current);
            current = next;
          }
        }
        merged.push(current);
      });

      return merged;
    })();

    return groupedRects;
  } else {
    return [];
  }
}
