import { LitElement, html, css } from "lit";
import { customElement, property } from "lit/decorators.js";

@customElement("tag-input")
export default class TagInput extends LitElement {
  @property({ type: String }) placeholder = "";

  @property({
    type: Array,
    attribute: "value",
    converter: {
      // `value="a,b"` is a comma-separated list, not the JSON Lit's Array converter expects
      fromAttribute: (value) => (value ? value.split(",").filter((v) => v.trim() !== "") : []),
      toAttribute: (value) => (Array.isArray(value) ? value.join(",") : value),
    },
    hasChanged(newVal, oldVal) {
      if (newVal === oldVal) return false;
      if (!Array.isArray(newVal) || !Array.isArray(oldVal)) return true;
      if (newVal.length !== oldVal.length) return true;
      return newVal.some((v, i) => v !== oldVal[i]);
    },
  })
  value = [];

  // For backwards compatibility with static properties inspection in tests
  static get properties() {
    return Object.fromEntries(this.elementProperties || []);
  }

  render() {
    let tagItemTmpl = "";
    if (Array.isArray(this.value)) {
      tagItemTmpl = html`${this.value
        .filter((v) => v !== "" && v !== null && v !== undefined && String(v).trim() !== "")
        .map((v) => html`<span class="tag">${v}</span>`)}`;
    }
    return html`<div class="tags" @click="${(e) => this.focusEditor(e)}">
      ${tagItemTmpl}
      <input
        type="text"
        class="editor"
        @paste="${(e) => this.afterPaste(e)}"
        @keydown="${(e) => this.afterKeyDown(e)}"
        @blur="${(e) => this.onBlur(e)}"
        placeholder="${this.placeholder || ""}"
      />
    </div>`;
  }

  focusEditor(e) {
    if (e.target.classList.contains("tags")) {
      this.shadowRoot?.querySelector(".editor")?.focus();
    }
  }

  dispatchEvents() {
    this.dispatchEvent(
      new CustomEvent("change", { bubbles: true, composed: true, detail: { value: this.value } }),
    );
    this.dispatchEvent(
      new CustomEvent("input", { bubbles: true, composed: true, detail: { value: this.value } }),
    );
  }

  afterPaste(e) {
    const clipboardData = e.clipboardData || window.clipboardData;
    const pastedData = clipboardData.getData("Text");
    const pastedArray = pastedData ? pastedData.split(",").filter((v) => v.trim() !== "") : "";
    if (pastedArray) {
      if (Array.isArray(this.value)) {
        this.value = [...this.value, ...pastedArray];
      } else {
        this.value = pastedArray;
      }
      this.dispatchEvents();
    }
    e.preventDefault();
  }

  afterKeyDown(e) {
    if (e.key === "Enter" || e.keyCode === 13) {
      e.stopPropagation();
      e.preventDefault();
      const val = e.target.value ? e.target.value.trim() : "";
      if (val) {
        if (Array.isArray(this.value)) {
          this.value = [...this.value, val];
        } else {
          this.value = [val];
        }
        e.target.value = "";
        this.dispatchEvents();
      }
    } else if (e.key === "Backspace" || e.key === "Delete" || e.keyCode === 8 || e.keyCode === 46) {
      if (!e.target.value || e.target.value.length === 0) {
        if (Array.isArray(this.value) && this.value.length > 0) {
          e.preventDefault();
          this.value = this.value.slice(0, -1);
          this.dispatchEvents();
        }
      }
    }
  }

  onBlur(e) {
    const val = e.target.value ? e.target.value.trim() : "";
    if (val) {
      if (Array.isArray(this.value)) {
        this.value = [...this.value, val];
      } else {
        this.value = [val];
      }
      e.target.value = "";
      this.dispatchEvents();
    }
  }

  static get styles() {
    return [
      css`
        .tags {
          display: flex;
          flex-wrap: wrap;
          align-items: center;
          box-sizing: border-box;
          /* Same height as the text inputs in a parameter row (see param-list-styles.js) */
          min-height: calc(2.75 * var(--font-size-small) + 2px);
          outline: none;
          padding: 0;
          border-radius: var(--radius);
          border: 1px solid var(--input);
          cursor: text;
          overflow: hidden;
          background: var(--input-background);
          transition:
            border-color 0.2s ease,
            box-shadow 0.2s ease;

          /* Same focus ring as the text inputs */
          &:focus-within {
            border-color: var(--primary);
            box-shadow: var(--focus-shadow);
          }
        }
        .tag,
        .editor {
          padding: 3px;
          margin: 2px;
        }
        /* Chips: a muted fill with the form-field border, so each one stands apart on a white or dark field */
        .tag {
          border: 1px solid var(--input);
          background: var(--muted);
          color: var(--foreground);
          border-radius: var(--radius);
          word-break: break-all;
          font-size: var(--font-size-small);
        }
        .tag:hover ~ #cursor {
          display: block;
        }
        .editor {
          flex: 1;
          border: 1px solid transparent;
          color: var(--foreground);
          min-width: 60px;
          outline: none;
          line-height: inherit;
          font-family: inherit;
          background: transparent;
          font-size: calc(var(--font-size-small) + 1px);
        }
        .editor::placeholder {
          color: var(--muted-foreground);
          opacity: 1;
        }
      `,
    ];
  }
}
