import JournalEntryPageTextSheet from "./journal-entry-page-text-sheet.mjs";
import TextEditor from "@client/applications/ux/text-editor.mjs";

/**
 * An abstract class that provides code-mirror-specific methods for editing journal page content.
 * @extends JournalEntryPageTextSheet
 */
export default class JournalEntryPageCodeMirrorSheet extends JournalEntryPageTextSheet {
  /** @override */
  static DEFAULT_OPTIONS = {
    window: {
      contentClasses: ["codemirror", "flexcol"]
    }
  };

  /** @inheritDoc */
  static VIEW_PARTS = {
    content: {
      root: true,
      template: "templates/journal/pages/text/view.hbs"
    }
  };

  /* -------------------------------------------- */
  /*  Properties                                  */
  /* -------------------------------------------- */

  /**
   * Store the dirty flag for this editor.
   * @type {boolean}
   */
  #isDirty = false;

  /* -------------------------------------------- */
  /*  Rendering                                   */
  /* -------------------------------------------- */

  /** @inheritDoc */
  async _prepareContentContext(context, options) {
    await super._prepareContentContext(context, options);
    if ( this.isView ) context.text.enriched = await TextEditor.implementation.enrichHTML(context.text.content, {
      relativeTo: this.page,
      secrets: this.page.isOwner
    });
  }

  /* -------------------------------------------- */

  /** @inheritDoc */
  _preSyncPartState(partId, newElement, priorElement, state) {
    super._preSyncPartState(partId, newElement, priorElement, state);
    if ( !this.isView && (partId === "content") ) {
      state.cursor = priorElement?.querySelector("code-mirror")?.cursor ?? null;
    }
  }

  /* -------------------------------------------- */

  /** @inheritDoc */
  _syncPartState(partId, newElement, priorElement, state) {
    super._syncPartState(partId, newElement, priorElement, state);
    if ( !this.isView && (partId === "content") && Number.isFinite(state.cursor) ) {
      newElement.querySelector("code-mirror").scrollTo({ top: state.cursor });
    }
  }

  /* -------------------------------------------- */
  /*  Event Listeners & Handlers                  */
  /* -------------------------------------------- */

  /** @inheritDoc */
  _attachFrameListeners() {
    super._attachFrameListeners();
    this.element.addEventListener("keypress", this.#onEdit.bind(this), { passive: true });
    this.element.addEventListener("paste", this.#onEdit.bind(this), { passive: true });
    this.element.addEventListener("drop", this._onDrop.bind(this));
  }

  /* -------------------------------------------- */

  /**
   * Handle dropping something onto the code-mirror editor.
   * @param {DragEvent} event  The triggering event.
   * @protected
   */
  _onDrop(event) {
    if ( !event.target.closest("code-mirror") ) return;
    event.preventDefault();
    const eventData = TextEditor.implementation.getDragEventData(event);
    return this._onDropContentLink(event, eventData);
  }

  /* -------------------------------------------- */

  /**
   * Handle dropping a content link onto the code-mirror editor.
   * @param {DragEvent} event   The originating drop event.
   * @param {object} eventData  The parsed event data.
   * @protected
   * @returns {Promise<void>}
   */
  async _onDropContentLink(event, eventData) {
    const link = await TextEditor.implementation.getContentLink(eventData, { relativeTo: this.page });
    if ( !link ) return;
    const editor = this.element.querySelector("code-mirror");
    const content = editor.value;
    const pos = editor.posAtCoords({ x: event.clientX, y: event.clientY });
    editor.value = content.substring(0, pos) + link + content.substring(pos);
    this.#isDirty = true;
  }

  /* -------------------------------------------- */

  /**
   * Handle an editing event.
   * @param {Event} event  The triggering event.
   */
  #onEdit(event) {
    // When pasting to a brand-new line of a CodeMirror editor, the event target seems to be an un-parented <br> tag.
    if ( event.target.closest("code-mirror") || (event.target.tagName === "BR") ) this.#isDirty = true;
  }

  /* -------------------------------------------- */
  /*  Form Submission                             */
  /* -------------------------------------------- */

  /** @override */
  _isEditorDirty() {
    return this.#isDirty;
  }
}
