import AbstractFormInputElement from "./form-element.mjs";
import FormulaEditor from "../apps/formula-editor.mjs";

/**
 * @import {FormInputConfig} from "@common/data/_types.mjs";
 */

/**
 * A form input element custom tailored to formula expressions.
 * @extends {AbstractFormInputElement}
 * @fires {Event} edit  Fired when the formula editor has been requested.
 *
 * @example Using the custom element in markup
 * ```html
 * <formula-input context="default">8 + @prof + @abilities.cha.mod</formula-input>
 * ```
 *
 * @example Creating the element programmatically
 * ```js
 * HTMLFormulaInputElement.create({ value: "8 + @prof + @abilities.cha.mod" });
 * ```
 */
export default class HTMLFormulaInputElement extends AbstractFormInputElement {
  /** @override */
  static tagName = "formula-input";

  /**
   * A button to open the formula editor interface.
   * @type {HTMLButtonElement}
   */
  button;

  /**
   * The formula input.
   * @type {HTMLInputElement}
   */
  input;

  /**
   * A reference to the formula editor application instance spawned by this element.
   * @type {FormulaEditor}
   */
  editor;

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

  /**
   * An identifier to distinguish contexts a formula might be in, which may control which auto-complete suggestions are
   * available, or other behavior. Context configuration is available in CONFIG.formulaEditor.
   * @type {string}
   */
  get context() {
    return this.getAttribute("context");
  }

  set context(value) {
    this.setAttribute("context", value);
  }

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

  /** @override */
  _activateListeners() {
    this.input.addEventListener("input", () => {
      this._setValue(this.input.value);
      this._refresh();
    });
    this.input.addEventListener("change", this.#onChangeInput.bind(this));
    this.button.addEventListener("click", this.#onClickButton.bind(this));
  }

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

  /**
   * Handle changes to one of the inputs of the formula input element.
   * @param {InputEvent} event     The originating input change event
   */
  #onChangeInput(event) {
    event.stopPropagation();
    this.value = event.currentTarget.value;
  }

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

  /** @override */
  _buildElements() {
    this._value ??= this.getAttribute("value") || this.textContent || "";
    this.removeAttribute("value");
    this.input = this._primaryInput = this.ownerDocument.createElement("input");
    Object.assign(this.input, { placeholder: this.getAttribute("placeholder") ?? "", type: "text" });
    this.button = this.ownerDocument.createElement("button");
    Object.assign(this.button, { className: "fa-solid fa-lambda fa-fw icon", type: "button" });
    return [this.input, this.button];
  }

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

  /** @override */
  _refresh() {
    this.input.value = this._value;
  }

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

  /** @override */
  _toggleDisabled(disabled) {
    this.input.disabled = disabled;
    this.button.disabled = disabled;
  }

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

  /**
   * Handle spawning the formula editor interface.
   * @param {PointerEvent} event  The triggering event.
   */
  #onClickButton(event) {
    event.preventDefault();
    const edit = new Event("edit", { bubbles: true, cancelable: true });
    this.dispatchEvent(edit);
    if ( edit.defaultPrevented ) return;
    this.editor = new FormulaEditor({ context: this.context, formula: this._value ?? "" });
    this.editor.addEventListener("close", () => {
      this.value = this.editor.formula;
      this.editor = undefined;
    }, { once: true });
    this.editor.render({ force: true, window: { windowId: this.ownerDocument.defaultView.id } });
  }

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

  /**
   * @typedef FormulaInputConfig
   * @property {string} [context="default"]  The formula editor context.
   */

  /**
   * Create an HTMLFormulaInputElement using provided configuration data.
   * @param {FormInputConfig<string> & FormulaInputConfig} config  The configuration.
   * @returns {HTMLFormulaInputElement}
   */
  static create(config) {
    const input = new this();
    const { context="default", name, value } = config;
    Object.assign(input, { context, name });
    input.setAttribute("value", value);
    foundry.applications.fields.setInputAttributes(input, config);
    return input;
  }
}
