import { ExcalidrawEmbeddableElement } from "@zsviczian/excalidraw/types/element/src/types";
import { Mutable } from "@zsviczian/excalidraw/types/common/src/utility-types";
import { Modal, Notice, Setting, TFile } from "obsidian";
import { getEA } from "src/core";
import { ExcalidrawAutomate } from "src/shared/ExcalidrawAutomate";
import ExcalidrawView from "src/view/ExcalidrawView";
import { t } from "src/lang/helpers";
import ExcalidrawPlugin from "src/core/main";
import {
  getNewUniqueFilepath,
  getPathWithoutExtension,
  splitFolderAndFilename,
} from "src/utils/fileUtils";
import { addAppendUpdateCustomData, fragWithHTML } from "src/utils/utils";
import {
  getYouTubeStartAt,
  isValidYouTubeStart,
  isYouTube,
  updateYouTubeStartTime,
} from "src/utils/YoutTubeUtils";
import { EmbeddalbeMDFileCustomDataSettingsComponent } from "./EmbeddableMDFileCustomDataSettingsComponent";
import { isWinCTRLorMacCMD, labelCTRL } from "src/utils/modifierkeyHelper";
import { CaptureUpdateAction } from "src/constants/constants";

export type EmbeddableMDCustomProps = {
  useObsidianDefaults: boolean;
  backgroundMatchCanvas: boolean;
  backgroundMatchElement: boolean;
  backgroundColor: string;
  backgroundOpacity: number;
  borderMatchElement: boolean;
  borderColor: string;
  borderOpacity: number;
  filenameVisible: boolean;
  propertiesVisible?: boolean;
  lockedReadingMode?: boolean;
};

export class EmbeddableSettings extends Modal {
  private ea: ExcalidrawAutomate;
  private updatedFilepath: string = null;
  private zoomValue: number;
  private isYouTube: boolean;
  private youtubeStart: string = null;
  private isMDFile: boolean;
  private notExcalidrawIsInternal: boolean;
  private isLocalURI: boolean;
  private mdCustomData: EmbeddableMDCustomProps;
  private onKeyDown: (ev: KeyboardEvent) => void;
  private listenerHost: Document | null = null;

  constructor(
    private plugin: ExcalidrawPlugin,
    private view: ExcalidrawView,
    private file: TFile,
    private element: ExcalidrawEmbeddableElement,
  ) {
    super(plugin.app);
    this.ea = getEA(this.view);
    this.ea.copyViewElementsToEAforEditing([this.element]);
    this.zoomValue = element.scale[0];
    this.isYouTube = isYouTube(this.element.link);
    this.notExcalidrawIsInternal =
      this.file && !this.view.plugin.isExcalidrawFile(this.file);
    this.isMDFile = this.file && this.file.extension.toLowerCase() === "md"; // && !this.view.plugin.isExcalidrawFile(this.file);
    this.isLocalURI = this.element.link.startsWith("file://");
    if (isYouTube) {
      this.youtubeStart = getYouTubeStartAt(this.element.link);
    }

    this.mdCustomData = (element.customData?.mdProps ??
      view.plugin.settings
        .embeddableMarkdownDefaults) as EmbeddableMDCustomProps;
    if (!element.customData?.mdProps) {
      const bgCM = this.plugin.ea.getCM(element.backgroundColor);
      this.mdCustomData.backgroundColor = bgCM.stringHEX({ alpha: false });
      this.mdCustomData.backgroundOpacity = element.opacity;
      const borderCM = this.plugin.ea.getCM(element.strokeColor);
      this.mdCustomData.borderColor = borderCM.stringHEX({ alpha: false });
      this.mdCustomData.borderOpacity = element.opacity;
    }
  }

  onOpen(): void {
    this.containerEl.classList.add("excalidraw-release");
    //this.titleEl.setText(t("ES_TITLE"));
    void this.createForm();
  }

  onClose() {
    if (this.listenerHost && this.onKeyDown) {
      this.listenerHost.removeEventListener("keydown", this.onKeyDown);
      this.listenerHost = null;
    }
    this.plugin = null;
    this.view = null;
    this.file = null;
    this.element = null;
    this.ea.destroy();
    this.ea = null;
    this.mdCustomData = null;
  }

  async createForm() {
    this.contentEl.createEl("h1", { text: t("ES_TITLE") });

    if (this.file) {
      new Setting(this.contentEl).setName(t("ES_RENAME")).addText((text) =>
        text
          .setValue(getPathWithoutExtension(this.file))
          .onChange(async (value) => {
            this.updatedFilepath = value;
          }),
      );
    }

    const zoomValue = (): DocumentFragment => {
      return fragWithHTML(
        `${t("ES_ZOOM_100_RELATIVE_DESC")}<br>Current zoom is <b>${Math.round(this.zoomValue * 100)}%</b>`,
      );
    };

    const zoomSetting = new Setting(this.contentEl)
      .setName(t("ES_ZOOM"))
      .setDesc(zoomValue())
      .addButton((button) =>
        button.setButtonText(t("ES_ZOOM_100")).onClick(() => {
          const api = this.view.excalidrawAPI;
          this.zoomValue = 1 / api.getAppState().zoom.value;
          zoomSetting.setDesc(zoomValue());
        }),
      )
      .addSlider((slider) =>
        slider
          .setLimits(10, 400, 5)
          .setValue(this.zoomValue * 100)
          .onChange((value) => {
            this.zoomValue = value / 100;
            zoomSetting.setDesc(zoomValue());
          }),
      );

    if (this.isYouTube) {
      new Setting(this.contentEl)
        .setName(t("ES_YOUTUBE_START"))
        .setDesc(t("ES_YOUTUBE_START_DESC"))
        .addText((text) =>
          text.setValue(this.youtubeStart).onChange(async (value) => {
            this.youtubeStart = value;
          }),
        );
    }

    if (this.isMDFile || this.notExcalidrawIsInternal) {
      this.contentEl.createEl("h3", { text: t("ES_EMBEDDABLE_SETTINGS") });
      const isFullFile = this.element ? !this.element.link.includes("#") : true;
      new EmbeddalbeMDFileCustomDataSettingsComponent(
        this.contentEl,
        this.mdCustomData,
        undefined,
        this.isMDFile,
        isFullFile,
        this.app,
      ).render();
    }

    new Setting(this.contentEl)
      .addButton((button) =>
        button
          .setButtonText(t("PROMPT_BUTTON_CANCEL"))
          .setTooltip("ESC")
          .onClick(() => this.close()),
      )
      .addButton((button) =>
        button
          .setButtonText(t("PROMPT_BUTTON_OK"))
          .setTooltip(labelCTRL() + "Enter")
          .setCta()
          .onClick(() => this.applySettings()),
      );

    const onKeyDown = (ev: KeyboardEvent) => {
      if (isWinCTRLorMacCMD(ev) && ev.key === "Enter") {
        void this.applySettings();
      }
    };

    this.onKeyDown = onKeyDown;
    const ownerDoc = this.containerEl.ownerDocument;
    if (ownerDoc) {
      this.listenerHost = ownerDoc;
      ownerDoc.addEventListener("keydown", onKeyDown);
    }
  }

  private async applySettings() {
    let dirty = false;
    const el = this.ea.getElement(
      this.element.id,
    ) as Mutable<ExcalidrawEmbeddableElement>;
    if (this.updatedFilepath) {
      const newPathWithExt = `${this.updatedFilepath}.${this.file.extension}`;
      if (newPathWithExt !== this.file.path) {
        const fnparts = splitFolderAndFilename(newPathWithExt);
        const newPath = getNewUniqueFilepath(
          this.app.vault,
          fnparts.filename,
          fnparts.folderpath,
        );
        if (this.app.vault.getAbstractFileByPath(newPath)) {
          new Notice(
            `File rename failed. A file with this name already exists.\n${
              newPath
            }`,
            10000,
          );
        } else {
          try {
            await this.app.fileManager.renameFile(this.file, newPath);
            el.link = this.element.link.replace(
              /(\[\[)([^#\]]*)([^\]]*]])/,
              `$1${this.plugin.app.metadataCache.fileToLinktext(
                this.file,
                this.view.file.path,
                true,
              )}$3`,
            );
            dirty = true;
          } catch (e) {
            new Notice(`File rename failed. ${e}`, 10000);
          }
        }
      }
    }
    if (
      this.isYouTube &&
      this.youtubeStart !== getYouTubeStartAt(this.element.link)
    ) {
      dirty = true;
      if (this.youtubeStart === "" || isValidYouTubeStart(this.youtubeStart)) {
        el.link = updateYouTubeStartTime(el.link, this.youtubeStart);
      } else {
        new Notice(t("ES_YOUTUBE_START_INVALID"));
      }
    }
    if (
      this.isMDFile &&
      (this.mdCustomData.backgroundColor !==
        this.element.customData?.backgroundColor ||
        this.mdCustomData.borderColor !==
          this.element.customData?.borderColor ||
        this.mdCustomData.backgroundOpacity !==
          this.element.customData?.backgroundOpacity ||
        this.mdCustomData.borderOpacity !==
          this.element.customData?.borderOpacity ||
        this.mdCustomData.propertiesVisible !==
          (this.element.customData?.mdProps as EmbeddableMDCustomProps)
            ?.propertiesVisible ||
        this.mdCustomData.filenameVisible !==
          this.element.customData?.filenameVisible)
    ) {
      addAppendUpdateCustomData(el, { mdProps: this.mdCustomData });
      dirty = true;
    }

    if (this.zoomValue !== this.element.scale[0]) {
      dirty = true;

      el.scale = [this.zoomValue, this.zoomValue];
    }
    if (dirty) {
      void (async () => {
        await this.ea.addElementsToView();
        this.ea.viewUpdateScene({
          appState: {},
          captureUpdate: CaptureUpdateAction.NEVER,
        });
        this.close(); //close should only run once update scene is done
      })();
    } else {
      this.close();
    }
  }
}
