import { consume } from "@lit/context";
import {
  mdiAppleKeyboardCommand,
  mdiArrowDown,
  mdiArrowUp,
  mdiCommentEditOutline,
  mdiCommentTextOutline,
  mdiDelete,
  mdiDotsVertical,
  mdiPlusCircleMultipleOutline,
  mdiRenameBox,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ensureArray } from "../../../../common/array/ensure-array";
import { fireEvent } from "../../../../common/dom/fire_event";
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { truncateWithEllipsis } from "../../../../common/string/truncate-with-ellipsis";
import "../../../../components/automation/ha-automation-row";
import type { HaAutomationRow } from "../../../../components/automation/ha-automation-row";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-svg-icon";
import type {
  Condition,
  OptionSidebarConfig,
} from "../../../../data/automation";
import { describeCondition } from "../../../../data/automation_i18n";
import { fullEntitiesContext } from "../../../../data/context";
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
import type { Action, Option } from "../../../../data/script";
import { showPromptDialog } from "../../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import "../action/ha-automation-action";
import type HaAutomationAction from "../action/ha-automation-action";
import "../condition/ha-automation-condition";
import type HaAutomationCondition from "../condition/ha-automation-condition";
import { showEditorToast } from "../editor-toast";
import {
  editorStyles,
  indentStyle,
  overflowStyles,
  rowStyles,
} from "../styles";

@customElement("ha-automation-option-row")
export default class HaAutomationOptionRow extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) public option?: Option;

  @property({ attribute: false }) public defaultActions?: Action[];

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

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

  @property({ type: Number }) public index!: number;

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

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

  @property({ type: Boolean, attribute: "sidebar" })
  public optionsInSidebar = false;

  @property({ type: Boolean, attribute: "sort-selected" })
  public sortSelected = false;

  @state() private _expanded = false;

  @state() private _selected = false;

  @state() private _collapsed = true;

  @state()
  @consume({ context: fullEntitiesContext, subscribe: true })
  _entityReg: EntityRegistryEntry[] = [];

  @query("ha-automation-condition")
  private _conditionElement?: HaAutomationCondition;

  @query("ha-automation-action")
  private _actionElement?: HaAutomationAction;

  @query("ha-automation-row")
  private _automationRowElement?: HaAutomationRow;

  get selected() {
    return this._selected;
  }

  private _expandedChanged(ev) {
    if (ev.currentTarget.id !== "option") {
      return;
    }
    this._expanded = ev.detail.expanded;
  }

  private _getDescription() {
    const conditions = ensureArray<Condition | string>(this.option!.conditions);
    if (!conditions || conditions.length === 0) {
      return this.hass.localize(
        "ui.panel.config.automation.editor.actions.type.choose.no_conditions"
      );
    }
    let str = "";
    if (typeof conditions[0] === "string") {
      str += conditions[0];
    } else {
      str += describeCondition(conditions[0], this.hass, this._entityReg);
    }
    if (conditions.length > 1) {
      str += this.hass.localize(
        "ui.panel.config.automation.editor.actions.type.choose.option_description_additional",
        { numberOfAdditionalConditions: conditions.length - 1 }
      );
    }
    return str;
  }

  private _renderOverflowLabel(label: string, shortcut?: TemplateResult) {
    return html`
      <div class="overflow-label">
        ${label}
        ${
          this.optionsInSidebar && !this.narrow
            ? shortcut ||
              html`<span
                class="shortcut-placeholder ${isMac ? "mac" : ""}"
              ></span>`
            : nothing
        }
      </div>
    `;
  }
  private _renderRow() {
    const noteTooltipText = truncateWithEllipsis(
      this.option?.note?.trim() || "",
      250
    );

    return html`
      <h3 slot="header">
        ${
          this.option
            ? `${this.hass.localize(
                "ui.panel.config.automation.editor.actions.type.choose.option",
                { number: this.index + 1 }
              )}: ${this.option.alias || (this._expanded ? "" : this._getDescription())}`
            : this.hass.localize(
                "ui.panel.config.automation.editor.actions.type.choose.default"
              )
        }
        ${
          this.option?.note?.trim()
            ? html`
                <ha-svg-icon
                  id="note-icon"
                  tabindex="0"
                  .path=${mdiCommentTextOutline}
                  .label=${this.hass.localize(
                    "ui.panel.config.automation.editor.note.label"
                  )}
                  class="note-indicator"
                ></ha-svg-icon>
                <ha-tooltip for="note-icon"
                  ><p>${noteTooltipText}</p></ha-tooltip
                >
              `
            : nothing
        }
      </h3>

      <slot name="icons" slot="icons"></slot>

      ${
        this.option
          ? html`
              <ha-dropdown
                slot="icons"
                @click=${preventDefaultStopPropagation}
                @keydown=${stopPropagation}
                @wa-select=${this._handleDropdownSelect}
                placement="bottom-end"
              >
                <ha-icon-button
                  slot="trigger"
                  .label=${this.hass.localize("ui.common.menu")}
                  .path=${mdiDotsVertical}
                ></ha-icon-button>

                <ha-dropdown-item value="rename" .disabled=${this.disabled}>
                  <ha-svg-icon slot="icon" .path=${mdiRenameBox}></ha-svg-icon>
                  ${this._renderOverflowLabel(
                    this.hass.localize(
                      "ui.panel.config.automation.editor.triggers.rename"
                    )
                  )}
                </ha-dropdown-item>
                <ha-dropdown-item value="edit_note">
                  <ha-svg-icon
                    slot="icon"
                    .path=${mdiCommentEditOutline}
                  ></ha-svg-icon>
                  ${this._renderOverflowLabel(
                    this.hass.localize(
                      `ui.panel.config.automation.editor.note.${this.option?.note ? "edit" : "add"}`
                    )
                  )}
                </ha-dropdown-item>

                <ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
                  <ha-svg-icon
                    slot="icon"
                    .path=${mdiPlusCircleMultipleOutline}
                  ></ha-svg-icon>

                  ${this._renderOverflowLabel(
                    this.hass.localize(
                      "ui.panel.config.automation.editor.actions.duplicate"
                    )
                  )}
                </ha-dropdown-item>

                ${
                  !this.optionsInSidebar
                    ? html`
                        <ha-dropdown-item
                          value="move_up"
                          .disabled=${this.disabled || !!this.first}
                        >
                          ${this.hass.localize(
                            "ui.panel.config.automation.editor.move_up"
                          )}
                          <ha-svg-icon
                            slot="icon"
                            .path=${mdiArrowUp}
                          ></ha-svg-icon
                        ></ha-dropdown-item>
                        <ha-dropdown-item
                          value="move_down"
                          .disabled=${this.disabled || !!this.last}
                        >
                          ${this.hass.localize(
                            "ui.panel.config.automation.editor.move_down"
                          )}
                          <ha-svg-icon
                            slot="icon"
                            .path=${mdiArrowDown}
                          ></ha-svg-icon
                        ></ha-dropdown-item>
                      `
                    : nothing
                }

                <ha-dropdown-item
                  value="delete"
                  variant="danger"
                  .disabled=${this.disabled}
                >
                  <ha-svg-icon
                    class="warning"
                    slot="icon"
                    .path=${mdiDelete}
                  ></ha-svg-icon>
                  ${this._renderOverflowLabel(
                    this.hass.localize(
                      "ui.panel.config.automation.editor.actions.type.choose.remove_option"
                    ),
                    html`<span class="shortcut">
                      <span
                        >${
                          isMac
                            ? html`<ha-svg-icon
                                .path=${mdiAppleKeyboardCommand}
                              ></ha-svg-icon>`
                            : this.hass.localize(
                                "ui.panel.config.automation.editor.ctrl"
                              )
                        }</span
                      >
                      <span>+</span>
                      <span
                        >${this.hass.localize(
                          "ui.panel.config.automation.editor.del"
                        )}</span
                      >
                    </span>`
                  )}
                </ha-dropdown-item>
              </ha-dropdown>
            `
          : nothing
      }
      ${!this.optionsInSidebar ? this._renderContent() : nothing}
    `;
  }

  private _renderContent() {
    return html`<div
      class=${classMap({
        "card-content": true,
        card: !this.optionsInSidebar,
        indent: this.optionsInSidebar,
        selected: this._selected,
        hidden: this.optionsInSidebar && this._collapsed,
      })}
    >
      ${
        this.option
          ? html`
              <h4 class="top">
                ${this.hass.localize(
                  "ui.panel.config.automation.editor.actions.type.choose.conditions"
                )}:
              </h4>
              <ha-automation-condition
                .conditions=${ensureArray<string | Condition>(
                  this.option.conditions
                )}
                .disabled=${this.disabled}
                .hass=${this.hass}
                .narrow=${this.narrow}
                @value-changed=${this._conditionChanged}
                .optionsInSidebar=${this.optionsInSidebar}
              ></ha-automation-condition>
            `
          : nothing
      }
      <h4 class=${this.option ? "" : "top"}>
        ${this.hass.localize(
          "ui.panel.config.automation.editor.actions.type.choose.sequence"
        )}:
      </h4>
      <ha-automation-action
        .actions=${
          (this.option
            ? ensureArray(this.option.sequence) || []
            : this.defaultActions
              ? ensureArray(this.defaultActions) || []
              : []) as Action[]
        }
        .disabled=${this.disabled}
        .hass=${this.hass}
        .narrow=${this.narrow}
        @value-changed=${this._actionChanged}
        .optionsInSidebar=${this.optionsInSidebar}
      ></ha-automation-action>
    </div>`;
  }

  protected render() {
    if (!this.option && !this.defaultActions) return nothing;

    return html`
      <ha-card outlined class=${this._selected ? "selected" : ""}>
        ${
          this.optionsInSidebar
            ? html`<ha-automation-row
                left-chevron
                .collapsed=${this._collapsed}
                .selected=${this._selected}
                .sortSelected=${this.sortSelected}
                @click=${this._toggleSidebar}
                @toggle-collapsed=${this._toggleCollapse}
                @delete-row=${this._removeOption}
                >${this._renderRow()}</ha-automation-row
              >`
            : html`
                <ha-expansion-panel
                  left-chevron
                  @expanded-changed=${this._expandedChanged}
                  id="option"
                >
                  ${this._renderRow()}
                </ha-expansion-panel>
              `
        }
      </ha-card>

      ${this.optionsInSidebar ? this._renderContent() : nothing}
    `;
  }

  private _duplicateOption = () => {
    fireEvent(this, "duplicate");
  };

  private _moveUp() {
    fireEvent(this, "move-up");
  }

  private _moveDown() {
    fireEvent(this, "move-down");
  }

  private _handleDropdownSelect(ev: HaDropdownSelectEvent) {
    ev.stopPropagation();
    const action = ev.detail?.item?.value;

    if (!action) {
      return;
    }

    switch (action) {
      case "rename":
        this._renameOption();
        break;
      case "edit_note":
        this._editNoteOption();
        break;
      case "delete":
        this._removeOption();
        break;
      case "duplicate":
        this._duplicateOption();
        break;
      case "move_up":
        this._moveUp();
        break;
      case "move_down":
        this._moveDown();
        break;
    }
  }

  private _removeOption = () => {
    if (this.option) {
      fireEvent(this, "value-changed", {
        value: null,
      });
      if (this._selected) {
        fireEvent(this, "close-sidebar");
      }

      showEditorToast(this, {
        message: this.hass.localize("ui.common.successfully_deleted"),
        duration: 4000,
        action: {
          text: this.hass.localize("ui.common.undo"),
          action: () => {
            fireEvent(window, "undo-change");
          },
        },
      });
    }
  };

  private _renameOption = async () => {
    const alias = await showPromptDialog(this, {
      title: this.hass.localize(
        "ui.panel.config.automation.editor.actions.type.choose.change_alias"
      ),
      inputLabel: this.hass.localize(
        "ui.panel.config.automation.editor.actions.type.choose.alias"
      ),
      inputType: "string",
      placeholder: capitalizeFirstLetter(this._getDescription()),
      defaultValue: this.option!.alias,
      confirmText: this.hass.localize("ui.common.submit"),
    });
    if (alias !== null) {
      const value = { ...this.option };
      if (alias === "") {
        delete value.alias;
      } else {
        value.alias = alias;
      }
      fireEvent(this, "value-changed", {
        value,
      });
    }
  };

  private _editNoteOption = async (): Promise<void> => {
    if (!this.option) {
      return;
    }
    const note = await showPromptDialog(this, {
      title: this.hass.localize(
        `ui.panel.config.automation.editor.note.${this.option.note ? "edit" : "add"}`
      ),
      inputLabel: this.hass.localize(
        "ui.panel.config.automation.editor.note.label"
      ),
      inputType: "string",
      defaultValue: this.option.note,
      confirmText: this.hass.localize("ui.common.submit"),
      multiline: true,
    });
    if (note !== null) {
      const value: Option = { ...this.option };
      if (note === "") {
        delete value.note;
      } else {
        value.note = note;
      }
      fireEvent(this, "value-changed", {
        value,
      });

      if (this._selected) {
        this.openSidebar(value); // refresh sidebar
      }
    }
  };

  private _conditionChanged(ev: CustomEvent) {
    ev.stopPropagation();
    const conditions = ev.detail.value as Condition[];
    const value = { ...this.option, conditions: conditions };
    fireEvent(this, "value-changed", {
      value,
    });
  }

  private _actionChanged(ev: CustomEvent) {
    if (this.defaultActions) {
      return;
    }
    ev.stopPropagation();
    const actions = ev.detail.value as Action[];
    const value = { ...this.option, sequence: actions };
    fireEvent(this, "value-changed", {
      value,
    });
  }

  private _toggleSidebar(ev: Event) {
    ev?.stopPropagation();

    if (this._selected) {
      fireEvent(this, "request-close-sidebar");
      return;
    }
    this.openSidebar();
  }

  public openSidebar(option?: Option): void {
    const sidebarOption = option ?? this.option;
    fireEvent(this, "open-sidebar", {
      close: (focus?: boolean) => {
        this._selected = false;
        fireEvent(this, "close-sidebar");
        if (focus) {
          this.focus();
        }
      },
      rename: () => {
        this._renameOption();
      },
      editNote: this._editNoteOption,
      delete: this._removeOption,
      duplicate: this._duplicateOption,
      defaultOption: !!this.defaultActions,
      note: sidebarOption?.note,
    } satisfies OptionSidebarConfig);
    this._selected = true;
    this._collapsed = false;

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

  public expand() {
    if (this.optionsInSidebar) {
      this._collapsed = false;
      return;
    }

    this.updateComplete.then(() => {
      this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
    });
  }

  public collapse() {
    this._collapsed = true;
  }

  public expandAll() {
    this.expand();

    this._conditionElement?.expandAll();
    this._actionElement?.expandAll();
  }

  public collapseAll() {
    this.collapse();

    this._conditionElement?.collapseAll();
    this._actionElement?.collapseAll();
  }

  private _toggleCollapse() {
    this._collapsed = !this._collapsed;
  }

  public focus() {
    this._automationRowElement?.focus();
  }

  static get styles(): CSSResultGroup {
    return [
      rowStyles,
      editorStyles,
      overflowStyles,
      indentStyle,
      css`
        h4 {
          color: var(--ha-color-text-secondary);
        }
        h4 {
          margin-bottom: 8px;
        }
        h4.top {
          margin-top: 0;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-automation-option-row": HaAutomationOptionRow;
  }
}
