import { mdiPlus } from "@mdi/js";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, queryAll } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-svg-icon";
import type { Fields } from "../../../data/script";
import type { HomeAssistant } from "../../../types";
import "./ha-script-field-row";
import type HaScriptFieldRow from "./ha-script-field-row";

@customElement("ha-script-fields")
export default class HaScriptFields extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ type: Boolean }) public disabled = false;

  @property({ attribute: false }) public fields!: Fields;

  @property({ attribute: false }) public highlightedFields?: Fields;

  @property({ type: Boolean }) public narrow = false;

  @queryAll("ha-script-field-row")
  private _fieldRowElements?: HaScriptFieldRow[];

  private _focusLastActionOnChange = false;

  protected render() {
    return html`
      ${
        this.fields
          ? html`<div class="fields">
              ${Object.entries(this.fields).map(
                ([key, field]) => html`
                  <ha-script-field-row
                    .key=${key}
                    .excludeKeys=${Object.keys(this.fields).filter(
                      (k) => k !== key
                    )}
                    .field=${field}
                    .disabled=${this.disabled}
                    @value-changed=${this._fieldChanged}
                    .hass=${this.hass}
                    .highlight=${this.highlightedFields?.[key] !== undefined}
                    .narrow=${this.narrow}
                  >
                  </ha-script-field-row>
                `
              )}
            </div> `
          : nothing
      }
      <ha-button @click=${this._addField} .disabled=${this.disabled}>
        <ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
        ${this.hass.localize("ui.panel.config.script.editor.field.add_field")}
      </ha-button>
    `;
  }

  protected updated(changedProps: PropertyValues<this>) {
    super.updated(changedProps);

    if (changedProps.has("fields") && this._focusLastActionOnChange) {
      this._focusLastActionOnChange = false;
      this.focusLastField();
    }
  }

  public focusLastField() {
    const row = this.shadowRoot!.querySelector<HaScriptFieldRow>(
      "ha-script-field-row:last-of-type"
    )!;
    row.updateComplete.then(() => {
      row.openSidebar();
      row.focus();

      if (this.narrow) {
        window.setTimeout(() => {
          row.scrollIntoView({
            block: "start",
            behavior: "smooth",
          });
        }, 180); // duration of transition of added padding for bottom sheet
      }
    });
  }

  private _addField() {
    const key = this._getUniqueKey(
      this.hass.localize("ui.panel.config.script.editor.field.field") ||
        "field",
      this.fields || {}
    );
    const fields = {
      ...(this.fields || {}),
      [key]: {
        selector: {
          text: null,
        },
      },
    };
    this._focusLastActionOnChange = true;
    fireEvent(this, "value-changed", { value: fields });
  }

  private _fieldChanged(ev: CustomEvent) {
    ev.stopPropagation();
    const key = (ev.target as any).key;
    let fields: Fields = {};
    if (ev.detail.value === null) {
      fields = { ...this.fields };
      delete fields[key];
    } else {
      const newValue = { ...ev.detail.value };
      const newKey = newValue.key;
      delete newValue.key;
      const keyChanged = key !== newKey;

      // If key is changed, recreate the object to maintain the same insertion order.
      if (keyChanged) {
        Object.entries(this.fields).forEach(([k, v]) => {
          if (k === key) {
            fields[newKey] = newValue;
          } else fields[k] = v;
        });
      } else {
        fields = { ...this.fields };
        fields[key] = newValue;
      }
    }
    fireEvent(this, "value-changed", { value: fields });
  }

  private _getUniqueKey(base: string, fields: Fields): string {
    let key = base;
    if (base in fields) {
      let i = 2;
      do {
        key = `${base}_${i}`;
        i++;
      } while (key in fields);
    }
    return key;
  }

  public expandAll() {
    this._fieldRowElements?.forEach((row) => {
      row.expandAll();
    });
  }

  public collapseAll() {
    this._fieldRowElements?.forEach((row) => {
      row.collapseAll();
    });
  }

  static styles = css`
    ha-script-field-row {
      display: block;
      margin-bottom: 16px;
      scroll-margin-top: 48px;
    }
    ha-svg-icon {
      height: 20px;
    }
  `;
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-script-fields": HaScriptFields;
  }
}
