import {
  ExcalidrawElement,
  ExcalidrawImageElement,
  ExcalidrawTextElement,
} from "@zsviczian/excalidraw/types/element/src/types";
import { REGEX_LINK, REG_LINKINDEX_HYPERLINK } from "../shared/ExcalidrawData";
import { TextMode } from "src/shared/TextMode";
import type ExcalidrawView from "src/view/ExcalidrawView";
import { rotatedDimensions } from "./utils";
import { getBoundTextElementId } from "src/utils/excalidrawViewHelpers";

type PointerPoint = {
  x: number;
  y: number;
};

export const getElementsAtPointer = (
  pointer: PointerPoint,
  elements: readonly ExcalidrawElement[],
  type?: string,
): ExcalidrawElement[] => {
  return elements
    .filter((e: ExcalidrawElement) => {
      if (type && e.type !== type) {
        return false;
      }
      if (e.locked) {
        return false;
      }
      const [x, y, w, h] = rotatedDimensions(e);
      return (
        x <= pointer.x &&
        x + w >= pointer.x &&
        y <= pointer.y &&
        y + h >= pointer.y
      );
    })
    .reverse();
};

export const getTextElementAtPointer = (
  pointer: PointerPoint,
  view: ExcalidrawView,
) => {
  const api = view.excalidrawAPI;
  if (!api) {
    return { id: null, text: null };
  }
  const elements = getElementsAtPointer(
    pointer,
    api.getSceneElements(),
    "text",
  ) as ExcalidrawTextElement[];
  if (elements.length == 0) {
    return { id: null, text: null };
  }
  if (elements.length === 1) {
    return { id: elements[0].id, text: elements[0].text };
  }
  //if more than 1 text elements are at the location, look for one that has a link
  const elementsWithLinks = elements.filter((e: ExcalidrawTextElement) => {
    const text: string =
      view.textMode === TextMode.parsed
        ? view.excalidrawData.getRawText(e.id)
        : e.text;
    if (!text) {
      return false;
    }
    if (text.match(REG_LINKINDEX_HYPERLINK)) {
      return true;
    }
    const parts = REGEX_LINK.getRes(text).next();
    if (!parts.value) {
      return false;
    }
    return true;
  });
  //if there are no text elements with links, return the first element without a link
  if (elementsWithLinks.length == 0) {
    return { id: elements[0].id, text: elements[0].text };
  }
  //if there are still multiple text elements with links on top of each other, return the first
  return { id: elementsWithLinks[0].id, text: elementsWithLinks[0].text };
};

export const getImageElementAtPointer = (
  pointer: PointerPoint,
  view: ExcalidrawView,
) => {
  const api = view.excalidrawAPI;
  if (!api) {
    return;
  }
  const elements = getElementsAtPointer(
    pointer,
    api.getSceneElements(),
    "image",
  ) as ExcalidrawImageElement[];
  if (elements.length === 0) {
    return { id: null, fileId: null };
  }
  if (elements.length >= 1) {
    return { id: elements[0].id, fileId: elements[0].fileId };
  }
  //if more than 1 image elements are at the location, return the first
};

export const getElementWithLinkAtPointer = (
  pointer: PointerPoint,
  view: ExcalidrawView,
) => {
  const api = view.excalidrawAPI;
  if (!api) {
    return;
  }
  let elements = getElementsAtPointer(pointer, api.getSceneElements()).filter(
    (el) => el.link,
  );

  //as a fallback let's check if any of the elements at pointer are containers with a text element that has a link.
  if (elements.length === 0) {
    const textElIDs = (
      getElementsAtPointer(
        pointer,
        api.getSceneElements(),
      ) as ExcalidrawImageElement[]
    ).map((el) => getBoundTextElementId(el));
    elements = view
      .getViewElements()
      .filter(
        (el) => el.type === "text" && el.link && textElIDs.includes(el.id),
      );
  }

  if (elements.length === 0) {
    return { id: null, text: null };
  }

  if (elements.length >= 1) {
    return { id: elements[0].id, text: elements[0].link };
  }
};
