import { BoundingBox } from "@zsviczian/excalidraw/types/element/src";
import {
  ElementsMap,
  ElementsMapOrArray,
  ExcalidrawBindableElement,
  ExcalidrawElement,
  ExcalidrawFrameElement,
  ExcalidrawFrameLikeElement,
  ExcalidrawTextContainer,
  ExcalidrawTextElement,
  FontFamilyValues,
  FontString,
  NonDeleted,
  NonDeletedExcalidrawElement,
  OrderedExcalidrawElement,
  Theme,
} from "@zsviczian/excalidraw/types/element/src/types";
import {
  CombineBrandsIfNeeded,
  FontMetadata,
} from "@zsviczian/excalidraw/types/common/src";
import {
  AppState,
  BinaryFiles,
  DataURL,
  Zoom,
} from "@zsviczian/excalidraw/types/excalidraw/types";
import { Mutable } from "@zsviczian/excalidraw/types/common/src/utility-types";
import { GlobalPoint } from "@zsviczian/excalidraw/types/math/src/types";

type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];

type JsonObject = {
  [key: string]: JsonValue;
};

type MermaidToExcalidrawLibProps =
  import("@zsviczian/excalidraw/types/excalidraw/components/TTDDialog/types").MermaidToExcalidrawLibProps;

interface MermaidConfig {
  /**
   * Whether to start the diagram automatically when the page loads.
   * @default false
   */
  startOnLoad?: boolean;
  /**
   * The flowchart curve style.
   * @default "linear"
   */
  flowchart?: {
    curve?: "linear" | "basis";
  };
  /**
   * Theme variables
   * @default { fontSize: "25px" }
   */
  themeVariables?: {
    fontSize?: string;
  };
  /**
   * Maximum number of edges to be rendered.
   * @default 1000
   */
  maxEdges?: number;
  /**
   * Maximum number of characters to be rendered.
   * @default 1000
   */
  maxTextSize?: number;
}

type EmbeddedLink =
  | ({
      aspectRatio: { w: number; h: number };
      warning?: string;
    } & (
      | { type: "video" | "generic"; link: string }
      | { type: "document"; srcdoc: (theme: Theme) => string }
    ))
  | null;

declare namespace ExcalidrawLib {
  type ElementUpdate<TElement extends ExcalidrawElement> = Omit<
    Partial<TElement>,
    "id" | "updated"
  >;

  type ExportOpts = {
    elements: readonly NonDeleted<ExcalidrawElement>[];
    appState?: Partial<Omit<AppState, "offsetTop" | "offsetLeft">>;
    files: BinaryFiles | null;
    maxWidthOrHeight?: number;
    exportingFrame?: ExcalidrawFrameLikeElement | null;
    getDimensions?: (
      width: number,
      height: number,
    ) => { width: number; height: number; scale?: number };
  };

  function restoreElements<T extends ExcalidrawElement>(
    targetElements: readonly T[] | undefined | null,
    /** used for additional context (e.g. repairing arrow bindings) */
    existingElements: Readonly<ElementsMapOrArray> | null | undefined,
    opts?: {
      refreshDimensions?: boolean;
      repairBindings?: boolean;
      deleteInvisibleElements?: boolean;
    },
  ): CombineBrandsIfNeeded<T, OrderedExcalidrawElement>;

  function exportToSvg(
    opts: Omit<ExportOpts, "getDimensions"> & {
      elements: ExcalidrawElement[];
      appState?: AppState;
      files?: BinaryFiles | null;
      exportPadding?: number;
      exportingFrame: ExcalidrawFrameElement | null | undefined;
      renderEmbeddables?: boolean;
      skipInliningFonts?: boolean;
    },
  ): Promise<SVGSVGElement>;

  function sceneCoordsToViewportCoords(
    sceneCoords: { sceneX: number; sceneY: number },
    viewParams: {
      zoom: Zoom;
      offsetLeft: number;
      offsetTop: number;
      scrollX: number;
      scrollY: number;
    },
  ): { x: number; y: number };

  function viewportCoordsToSceneCoords(
    viewportCoords: { clientX: number; clientY: number },
    viewParams: {
      zoom: Zoom;
      offsetLeft: number;
      offsetTop: number;
      scrollX: number;
      scrollY: number;
    },
  ): { x: number; y: number };

  function intersectElementWithLine(
    element: ExcalidrawBindableElement,
    a: GlobalPoint,
    b: GlobalPoint,
    gap?: number,
  ): GlobalPoint[];

  function getCommonBoundingBox(
    elements: ExcalidrawElement[] | readonly NonDeleted<ExcalidrawElement>[],
  ): BoundingBox;

  function getContainerElement(
    element: ExcalidrawTextElement | null,
    elementsMap: ElementsMap,
  ): ExcalidrawTextContainer | null;

  function refreshTextDimensions(
    textElement: ExcalidrawTextElement,
    container: ExcalidrawTextContainer | null,
    elementsMap: ElementsMap,
    text: string,
  ): {
    text: string;
    x: number;
    y: number;
    width: number;
    height: number;
  };

  function getMaximumGroups(
    elements: ExcalidrawElement[],
    elementsMap: ElementsMap,
  ): ExcalidrawElement[][];

  function getFontMetrics(
    fontFamily: ExcalidrawTextElement["fontFamily"],
    fontSize?: number,
  ): {
    unitsPerEm: number;
    ascender: number;
    descender: number;
    lineHeight: number;
    baseline: number;
    fontString: string;
  };

  function measureText(
    text: string,
    font: FontString,
    lineHeight: number,
  ): { width: number; height: number };

  function getLineHeight(fontFamily: FontFamilyValues): number;
  function wrapText(text: string, font: FontString, maxWidth: number): string;

  function getFontString({
    fontSize,
    fontFamily,
  }: {
    fontSize: number;
    fontFamily: FontFamilyValues;
  }): FontString;

  function getFontFamilyString({ fontFamily }: { fontFamily: number }): string;

  function getBoundTextMaxWidth(container: ExcalidrawElement): number;

  function exportToBlob(
    opts: ExportOpts & {
      mimeType?: string;
      quality?: number;
      exportPadding?: number;
    },
  ): Promise<Blob>;

  function mutateElement<TElement extends Mutable<ExcalidrawElement>>(
    element: TElement,
    updates: ElementUpdate<TElement>,
    informMutation?: boolean,
  ): TElement;

  function getEmbedLink(link: string | null | undefined): EmbeddedLink;

  const mermaidToExcalidraw: typeof import("@zsviczian/excalidraw/types/excalidraw/components/TTDDialog/MermaidToExcalidrawLib").mermaidToExcalidraw;

  /*function mermaidToExcalidraw(
    mermaidDefinition: string,
    opts: MermaidConfig,
  ): Promise<{
    elements?: ExcalidrawElement[];
    files?: any;
    error?: string;
  } | undefined>;*/

  let getSceneVersion: typeof import("@zsviczian/excalidraw/types/excalidraw").getSceneVersion;
  let Excalidraw: typeof import("@zsviczian/excalidraw").Excalidraw;
  let MainMenu: typeof import("@zsviczian/excalidraw").MainMenu;
  let WelcomeScreen: typeof import("@zsviczian/excalidraw").WelcomeScreen;
  let TTDDialogTrigger: typeof import("@zsviczian/excalidraw").TTDDialogTrigger;
  let TTDDialog: typeof import("@zsviczian/excalidraw").TTDDialog;
  let DiagramToCodePlugin: typeof import("@zsviczian/excalidraw").DiagramToCodePlugin;

  function getDataURL(file: Blob | File): Promise<DataURL>;
  function destroyObsidianUtils(): void;
  function registerLocalFont(fontMetrics: FontMetadata, uri: string): void;
  function getFontFamilies(): string[];
  function registerFontsInCSS(): Promise<void>;
  function getCSSFontDefinition(fontFamily: number): Promise<string>;
  function getTextFromElements(
    elements: readonly ExcalidrawElement[],
    separator?: string,
  ): string;
  function safelyParseJSON(json: string): JsonObject | null;
  function loadSceneFonts(
    elements: NonDeletedExcalidrawElement[],
  ): Promise<void>;
  function loadMermaid(): Promise<MermaidToExcalidrawLibProps>;
  function syncInvalidIndices(
    elements: readonly ExcalidrawElement[],
  ): OrderedExcalidrawElement[];
  function syncMovedIndices(
    elements: readonly ExcalidrawElement[],
    movedElements: ElementsMap,
  ): OrderedExcalidrawElement[];
  function getDefaultColorPalette(): [string, string, string, string, string][];
}
