import {
  mdiCheckCircle,
  mdiCircle,
  mdiCloseCircle,
  mdiProgressClock,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeDeviceNameDisplay } from "../../../../../common/entity/compute_device_name";
import { sanitizeHttpUrl } from "../../../../../common/url/sanitize-http-url";
import { groupBy } from "../../../../../common/util/group-by";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-card";
import "../../../../../components/ha-generic-picker";
import type { PickerComboBoxItem } from "../../../../../components/ha-picker-combo-box";
import "../../../../../components/ha-select";
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
import "../../../../../components/ha-selector/ha-selector-boolean";
import "../../../../../components/ha-settings-row";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/input/ha-input";
import type {
  ZWaveJSNodeConfigParam,
  ZWaveJSNodeConfigParams,
  ZWaveJSSetConfigParamResult,
  ZwaveJSNodeConfigParameterUpdate,
  ZwaveJSNodeMetadata,
} from "../../../../../data/zwave_js";
import {
  fetchZwaveNodeCapabilities,
  fetchZwaveNodeConfigParameters,
  fetchZwaveNodeMetadata,
  invokeZWaveCCApi,
  setZwaveNodeConfigParameter,
  subscribeZwaveNodeConfigParameterUpdates,
} from "../../../../../data/zwave_js";
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
import "../../../../../layouts/hass-error-screen";
import "../../../../../layouts/hass-loading-screen";
import "../../../../../layouts/hass-subpage";
import { haStyle } from "../../../../../resources/styles";
import type {
  HomeAssistant,
  Route,
  ValueChangedEvent,
} from "../../../../../types";
import "../../../ha-config-section";
import "./zwave_js-custom-param";

const icons = {
  accepted: mdiCheckCircle,
  queued: mdiProgressClock,
  error: mdiCloseCircle,
};

@customElement("zwave_js-node-config")
class ZWaveJSNodeConfig extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) public route!: Route;

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

  @property({ attribute: "is-wide", type: Boolean }) public isWide = false;

  @property({ attribute: false }) public configEntryId?: string;

  @property({ attribute: false }) public deviceId!: string;

  @state() private _nodeMetadata?: ZwaveJSNodeMetadata;

  @state() private _config?: ZWaveJSNodeConfigParams;

  @state() private _canResetAll = false;

  @state() private _results: Record<string, ZWaveJSSetConfigParamResult> = {};

  @state() private _error?: string;

  @state() private _resetDialogProgress = false;

  private _unsubConfigParamUpdates?: Promise<UnsubscribeFunc>;

  private _resultTimeouts: Record<string, number> = {};

  public connectedCallback(): void {
    super.connectedCallback();
    this._subscribeConfigParameterUpdates();
  }

  public disconnectedCallback(): void {
    super.disconnectedCallback();
    this._unsubscribeConfigParameterUpdates();
    this._clearAllResultTimeouts();
  }

  protected willUpdate(changedProps: PropertyValues<this>): void {
    super.willUpdate(changedProps);
    if (!changedProps.has("route") || !this.route) {
      return;
    }
    const deviceId = this.route.path.slice(1);
    if (deviceId !== this.deviceId) {
      this.deviceId = deviceId;
      this._config = undefined;
      this._clearAllResultTimeouts();
      this._results = {};
      this._error = undefined;
    }
  }

  protected updated(changedProps: PropertyValues<this>): void {
    if (changedProps.has("deviceId")) {
      this._fetchData();
      this._subscribeConfigParameterUpdates();
    } else if (!this._config) {
      this._fetchData();
    }
  }

  protected render(): TemplateResult {
    if (this._error) {
      return html`<hass-error-screen
        .hass=${this.hass}
        .error=${this.hass.localize(
          `ui.panel.config.zwave_js.node_config.error_${this._error}`
        )}
      ></hass-error-screen>`;
    }

    if (!this._config || !this._nodeMetadata) {
      return html`<hass-loading-screen></hass-loading-screen>`;
    }

    const device = this.hass.devices[this.deviceId];

    const deviceName = device
      ? computeDeviceNameDisplay(device, this.hass.localize, this.hass.states)
      : "";

    return html`
      <hass-subpage
        .hass=${this.hass}
        .narrow=${this.narrow}
        .header=${this.hass.localize(
          "ui.panel.config.zwave_js.node_config.header"
        )}
        back-path="/config/devices/device/${this.deviceId}"
      >
        <ha-config-section
          .narrow=${this.narrow}
          .isWide=${this.isWide}
          vertical
        >
          <div slot="header">
            ${this.hass.localize("ui.panel.config.zwave_js.node_config.header")}
          </div>

          <div slot="introduction">
            ${
              device
                ? html`
                    <div class="device-info">
                      <h2>${deviceName}</h2>
                      <p>${device.manufacturer} ${device.model}</p>
                    </div>
                  `
                : ``
            }
            ${this.hass.localize(
              "ui.panel.config.zwave_js.node_config.introduction"
            )}
            <p>
              <em>
                ${this.hass.localize(
                  "ui.panel.config.zwave_js.node_config.attribution",
                  {
                    device_database: html`<a
                      rel="noreferrer noopener"
                      href=${
                        sanitizeHttpUrl(
                          this._nodeMetadata?.device_database_url
                        ) || "https://devices.zwave-js.io"
                      }
                      target="_blank"
                      >${this.hass.localize(
                        "ui.panel.config.zwave_js.node_config.zwave_js_device_database"
                      )}</a
                    >`,
                  }
                )}
              </em>
            </p>
          </div>
          ${Object.entries(
            groupBy(Object.entries(this._config), ([_, item]) =>
              item.endpoint.toString()
            )
          ).map(
            ([endpoint, configParamEntries]) =>
              html`<div class="content">
                <h3>
                  ${this.hass.localize(
                    "ui.panel.config.zwave_js.node_config.endpoint",
                    { endpoint }
                  )}
                </h3>
                <ha-card>
                  ${configParamEntries
                    .sort(([_, paramA], [__, paramB]) =>
                      paramA.property !== paramB.property
                        ? paramA.property - paramB.property
                        : paramA.property_key! - paramB.property_key!
                    )
                    .map(
                      ([id, item]) =>
                        html` <ha-settings-row
                          class="config-item"
                          .configId=${id}
                          .narrow=${this.narrow}
                        >
                          ${this._generateConfigBox(id, item)}
                        </ha-settings-row>`
                    )}
                </ha-card>
              </div>`
          )}
          ${
            this._canResetAll
              ? html`<div class="reset">
                  <ha-progress-button
                    .disabled=${this._resetDialogProgress}
                    .progress=${this._resetDialogProgress}
                    @click=${this._openResetDialog}
                  >
                    ${this.hass.localize(
                      "ui.panel.config.zwave_js.node_config.reset_to_default.button_label"
                    )}
                  </ha-progress-button>
                </div>`
              : nothing
          }
          <h3>
            ${this.hass.localize(
              "ui.panel.config.zwave_js.node_config.custom_config"
            )}
          </h3>
          <span class="secondary">
            ${this.hass.localize(
              "ui.panel.config.zwave_js.node_config.custom_config_description"
            )}
          </span>
          <ha-card class="custom-config">
            <zwave_js-custom-param
              .hass=${this.hass}
              .deviceId=${this.deviceId}
              @new-value=${this._handleNewValue}
            ></zwave_js-custom-param>
          </ha-card>
        </ha-config-section>
      </hass-subpage>
    `;
  }

  private _generateConfigBox(
    id: string,
    item: ZWaveJSNodeConfigParam
  ): TemplateResult {
    const result = this._results[id];

    const isTypeBoolean =
      item.configuration_value_type === "boolean" ||
      this._isEnumeratedBool(item);

    const labelAndDescription = html`
      <span slot="prefix" class="prefix">
        ${this.hass.localize("ui.panel.config.zwave_js.node_config.parameter")}
        <br />
        <span>${item.property}</span>
        ${
          item.property_key !== null
            ? html`<br />
                ${this.hass.localize(
                  "ui.panel.config.zwave_js.node_config.bitmask"
                )}
                <br />
                <span>${item.property_key.toString(16)}</span>`
            : nothing
        }
      </span>
      <span slot="heading" class="heading" .title=${item.metadata.label}>
        ${item.metadata.label}
      </span>
      <span slot="description">
        ${item.metadata.description}
        ${
          item.metadata.description !== null && !item.metadata.writeable
            ? html`<br />`
            : nothing
        }
        ${
          !item.metadata.writeable
            ? html`<em>
                ${this.hass.localize(
                  "ui.panel.config.zwave_js.node_config.parameter_is_read_only"
                )}
              </em>`
            : nothing
        }
        ${
          result?.status
            ? html`<p
                class="result ${classMap({
                  [result.status]: true,
                })}"
              >
                <ha-svg-icon
                  .path=${icons[result.status] ? icons[result.status] : mdiCircle}
                  class="result-icon"
                  slot="item-icon"
                ></ha-svg-icon>
                ${this.hass.localize(
                  `ui.panel.config.zwave_js.node_config.set_param_${result.status}`
                )}
                ${
                  result.status === "error" && result.error
                    ? html` <br /><em>${result.error}</em> `
                    : nothing
                }
              </p>`
            : nothing
        }
      </span>
    `;

    const defaultLabel =
      item.metadata.writeable && item.metadata.default !== undefined
        ? `${this.hass.localize("ui.panel.config.zwave_js.node_config.default")}:
          ${
            isTypeBoolean
              ? this.hass.localize(
                  item.metadata.default === 1 ? "ui.common.yes" : "ui.common.no"
                )
              : item.metadata.states?.[item.metadata.default]
                ? item.configuration_value_type === "manual_entry"
                  ? `${item.metadata.default} - ${item.metadata.states[item.metadata.default]}`
                  : item.metadata.states[item.metadata.default]
                : item.metadata.default
          }`
        : "";

    // Numeric entries with a min value of 0 and max of 1 are considered boolean
    if (isTypeBoolean) {
      return html`
        ${labelAndDescription}
        <div class="switch">
          <ha-selector-boolean
            .property=${item.property}
            .endpoint=${item.endpoint}
            .propertyKey=${item.property_key}
            .value=${item.value === 1}
            .key=${id}
            @value-changed=${this._switchToggled}
            .disabled=${!item.metadata.writeable}
            .helper=${defaultLabel}
          ></ha-selector-boolean>
        </div>
      `;
    }
    if (item.configuration_value_type === "manual_entry") {
      if (
        item.metadata.states &&
        item.metadata.min != null &&
        item.metadata.max != null &&
        item.metadata.max - item.metadata.min <= 100
      ) {
        return html`
          ${labelAndDescription}
          <ha-generic-picker
            .hass=${this.hass}
            .value=${item.value?.toString()}
            allow-custom-value
            hide-clear-icon
            .getItems=${this._getManualEntryItems(item.metadata.states)}
            .disabled=${!item.metadata.writeable}
            .invalid=${result?.status === "error"}
            .placeholder=${item.metadata.unit}
            .helper=${`${this.hass.localize("ui.panel.config.zwave_js.node_config.between_min_max", { min: item.metadata.min, max: item.metadata.max })}${defaultLabel ? `, ${defaultLabel}` : ""}`}
            .valueRenderer=${this._enumeratedPickerValueRenderer(
              item.metadata.states
            )}
            @value-changed=${this._getComboBoxValueChangedCallback(id, item)}
          >
          </ha-generic-picker>
        `;
      }
      return html`${labelAndDescription}
        <ha-input
          type="number"
          .value=${item.value?.toString()}
          .min=${item.metadata.min}
          .max=${item.metadata.max}
          .property=${item.property}
          .endpoint=${item.endpoint}
          .propertyKey=${item.property_key}
          .key=${id}
          .disabled=${!item.metadata.writeable}
          @change=${this._numericInputChanged}
          .hint=${`${this.hass.localize("ui.panel.config.zwave_js.node_config.between_min_max", { min: item.metadata.min, max: item.metadata.max })}${defaultLabel ? `, ${defaultLabel}` : ""}`}
        >
          ${
            item.metadata.unit
              ? html`<span slot="end">${item.metadata.unit}</span>`
              : nothing
          }
        </ha-input>`;
    }

    if (
      item.configuration_value_type === "enumerated" &&
      Object.keys(item.metadata.states).length < 5
    ) {
      return html`
        ${labelAndDescription}
        <ha-select
          .disabled=${!item.metadata.writeable}
          .value=${item.value?.toString()}
          .key=${id}
          .property=${item.property}
          .endpoint=${item.endpoint}
          .propertyKey=${item.property_key}
          @selected=${this._dropdownSelected}
          .helper=${defaultLabel}
          .options=${Object.entries(item.metadata.states).map(
            ([key, entityState]) => ({
              value: key,
              label: entityState,
            })
          )}
        >
        </ha-select>
      `;
    }
    if (item.configuration_value_type === "enumerated") {
      return html`
        ${labelAndDescription}
        <ha-generic-picker
          .hass=${this.hass}
          .disabled=${!item.metadata.writeable}
          .value=${item.value?.toString()}
          .key=${id}
          hide-clear-icon
          @value-changed=${this._pickerValueChanged}
          .helper=${defaultLabel}
          .getItems=${this._getEnumeratedPickerItems(item.metadata.states!)}
          .valueRenderer=${this._enumeratedPickerValueRenderer(
            item.metadata.states!
          )}
          .property=${item.property}
          .endpoint=${item.endpoint}
          .propertyKey=${item.property_key}
        >
        </ha-generic-picker>
      `;
    }

    return html`${labelAndDescription}
      <p>${item.value}</p>`;
  }

  private _subscribeConfigParameterUpdates(): void {
    this._unsubscribeConfigParameterUpdates();
    if (!this.isConnected || !this.hass || !this.deviceId) {
      return;
    }
    this._unsubConfigParamUpdates = subscribeZwaveNodeConfigParameterUpdates(
      this.hass,
      this.deviceId,
      this._handleConfigParameterUpdate
    );
    this._unsubConfigParamUpdates.catch(() => {
      // The backend doesn't support the subscription; the page still works,
      // it just won't receive live updates
      this._unsubConfigParamUpdates = undefined;
    });
  }

  private _unsubscribeConfigParameterUpdates(): void {
    if (this._unsubConfigParamUpdates) {
      this._unsubConfigParamUpdates
        .then((unsub) => unsub())
        .catch(() => {
          // The subscription never succeeded, so there is nothing to clean up
        });
      this._unsubConfigParamUpdates = undefined;
    }
  }

  private _handleConfigParameterUpdate = (
    update: ZwaveJSNodeConfigParameterUpdate
  ): void => {
    const param = this._config?.[update.id];
    if (!param) {
      return;
    }
    const status = this._results[update.id]?.status;
    if (status === "queued") {
      // The device applied the queued change; the accepted result is cleared
      // after a short delay by _setResult so the success message shows.
      // The value was already set optimistically when the change was queued,
      // so this runs even if the reported value matches the stored one.
      this._setResult(update.id, "accepted");
    } else if (status === "error" && param.value !== update.value) {
      // The parameter changed, so the previous error no longer applies
      this._setResult(update.id, undefined);
    }
    if (param.value !== update.value) {
      param.value = update.value;
      this._config = { ...this._config };
    }
  };

  private _isEnumeratedBool(item: ZWaveJSNodeConfigParam): boolean {
    // Some Z-Wave config values use a states list with two options where index 0 = Disabled and 1 = Enabled
    // We want those to be considered boolean and show a toggle switch
    const disabledStates = ["disable", "disabled"];
    const enabledStates = ["enable", "enabled"];

    if (item.configuration_value_type !== "enumerated") {
      return false;
    }
    if (!("states" in item.metadata)) {
      return false;
    }
    if (Object.keys(item.metadata.states).length !== 2) {
      return false;
    }
    if (!(0 in item.metadata.states) || !(1 in item.metadata.states)) {
      return false;
    }
    if (
      disabledStates.includes(item.metadata.states[0].toLowerCase()) &&
      enabledStates.includes(item.metadata.states[1].toLowerCase())
    ) {
      return true;
    }
    return false;
  }

  private _handleNewValue() {
    setTimeout(() => {
      this._fetchData();
    }, 750);
  }

  private _switchToggled(ev) {
    this._setResult(ev.target.key, undefined);
    this._updateConfigParameter(ev.target, ev.detail.value ? 1 : 0);
  }

  private _dropdownSelected(ev: HaSelectSelectEvent) {
    this._handleEnumeratedPickerValueChanged(ev, ev.detail.value);
  }

  private _pickerValueChanged(ev) {
    this._handleEnumeratedPickerValueChanged(ev, ev.detail.value);
  }

  private _handleEnumeratedPickerValueChanged(ev, value: string) {
    if (ev.target === undefined || this._config![ev.target.key] === undefined) {
      return;
    }
    if (this._config![ev.target.key].value === Number(value)) {
      return;
    }
    this._setResult(ev.target.key, undefined);

    this._updateConfigParameter(ev.target, Number(value));
  }

  private _numericInputChanged(ev) {
    if (ev.target === undefined || this._config![ev.target.key] === undefined) {
      return;
    }
    // An empty input must not be coerced to 0 by Number()
    const value =
      ev.target.value === undefined || ev.target.value === ""
        ? NaN
        : Number(ev.target.value);
    if (this._config![ev.target.key].value === value) {
      return;
    }
    if (isNaN(value)) {
      this._setError(
        ev.target.key,
        this.hass.localize(
          "ui.panel.config.zwave_js.node_config.error_not_numeric"
        )
      );
      return;
    }
    if (
      (ev.target.min !== undefined && value < ev.target.min) ||
      (ev.target.max !== undefined && value > ev.target.max)
    ) {
      this._setError(
        ev.target.key,
        this.hass.localize(
          "ui.panel.config.zwave_js.node_config.error_not_in_range",
          { min: ev.target.min, max: ev.target.max }
        )
      );
      return;
    }
    this._setResult(ev.target.key, undefined);
    this._updateConfigParameter(ev.target, value);
  }

  private _getEnumeratedPickerItems = memoizeOne(
    (states: Record<string, string>) => {
      const items: PickerComboBoxItem[] = Object.entries(states).map(
        ([value, label]) => ({
          id: value,
          primary: label,
          sorting_label: `${label}_${value}`,
        })
      );
      return () => items;
    }
  );

  private _enumeratedPickerValueRenderer = memoizeOne(
    (states: Record<string, string>) => (value: string) =>
      html`<span slot="headline">${states[value] || value}</span>`
  );

  private _getManualEntryItems = memoizeOne(
    (states: Record<string, string>) => {
      const items: PickerComboBoxItem[] = Object.entries(states).map(
        ([value, label]) => ({
          id: value,
          primary: `${label}`,
          secondary: value,
          sorting_label: `${label}_${value}`,
        })
      );
      return () => items;
    }
  );

  private _getComboBoxValueChangedCallback(
    id: string,
    item: ZWaveJSNodeConfigParam
  ) {
    return (ev: ValueChangedEvent<number>) =>
      this._numericInputChanged({
        ...ev,
        target: {
          ...ev.target,
          key: id,
          min: item.metadata.min,
          max: item.metadata.max,
          value: ev.detail.value,
          property: item.property,
          endpoint: item.endpoint,
          propertyKey: item.property_key,
        },
      });
  }

  private async _updateConfigParameter(target, value) {
    try {
      const result = await setZwaveNodeConfigParameter(
        this.hass,
        this.deviceId,
        target.property,
        target.endpoint,
        value,
        target.propertyKey ? target.propertyKey : undefined
      );
      this._config![target.key].value = value;

      this._setResult(target.key, result.status);
    } catch (err: any) {
      this._setError(target.key, err.message);
    }
  }

  private _clearResultTimeout(key: string): void {
    if (key in this._resultTimeouts) {
      clearTimeout(this._resultTimeouts[key]);
      delete this._resultTimeouts[key];
    }
  }

  private _clearAllResultTimeouts(): void {
    Object.values(this._resultTimeouts).forEach((timeout) =>
      clearTimeout(timeout)
    );
    this._resultTimeouts = {};
  }

  private _setResult(key: string, value: string | undefined) {
    this._clearResultTimeout(key);
    if (value === undefined) {
      delete this._results[key];
      this.requestUpdate();
    } else {
      this._results = { ...this._results, [key]: { status: value } };
      if (value === "accepted") {
        // Show the success message briefly, then clear it
        this._resultTimeouts[key] = window.setTimeout(() => {
          this._setResult(key, undefined);
        }, 2000);
      }
    }
  }

  private _setError(key: string, message: string) {
    this._clearResultTimeout(key);
    const errorParam = { status: "error", error: message };
    this._results = { ...this._results, [key]: errorParam };
  }

  private async _fetchData() {
    if (!this.configEntryId) {
      return;
    }

    const device = this.hass.devices[this.deviceId];
    if (!device) {
      this._error = "device_not_found";
      return;
    }

    const [nodeMetadata, config, capabilities] = await Promise.all([
      fetchZwaveNodeMetadata(this.hass, device.id),
      fetchZwaveNodeConfigParameters(this.hass, device.id),
      fetchZwaveNodeCapabilities(this.hass, device.id),
    ]);
    if (device.id !== this.deviceId) {
      // The user navigated to another node while the data was loading
      return;
    }
    this._nodeMetadata = nodeMetadata;
    this._config = config;
    this._canResetAll =
      capabilities &&
      Object.values(capabilities).some((endpoint) =>
        endpoint.some(
          (capability) => capability.id === 0x70 && capability.version >= 4
        )
      );
  }

  private async _openResetDialog(event: Event) {
    const progressButton = event.currentTarget as HaProgressButton;

    await showConfirmationDialog(this, {
      destructive: true,
      title: this.hass.localize(
        "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.title"
      ),
      text: this.hass.localize(
        "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.text"
      ),
      confirmText: this.hass.localize(
        "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.reset"
      ),
      confirm: () => this._resetAllConfigParameters(progressButton),
    });
  }

  private async _resetAllConfigParameters(progressButton: HaProgressButton) {
    this._resetDialogProgress = true;
    fireEvent(this, "hass-notification", {
      message: this.hass.localize(
        "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.text_loading"
      ),
    });

    try {
      const device = this.hass.devices[this.deviceId];
      if (!device) {
        throw new Error("device_not_found");
      }
      await invokeZWaveCCApi(
        this.hass,
        device.id,
        0x70, // 0x70 is the command class for Configuration
        undefined,
        "resetAll",
        [],
        true
      );

      fireEvent(this, "hass-notification", {
        message: this.hass.localize(
          "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.text_success"
        ),
      });

      await this._fetchData();
      progressButton.actionSuccess();
    } catch (_err: any) {
      fireEvent(this, "hass-notification", {
        message: this.hass.localize(
          "ui.panel.config.zwave_js.node_config.reset_to_default.dialog.text_error"
        ),
      });
      progressButton.actionError();
    }
    this._resetDialogProgress = false;
  }

  static get styles(): CSSResultGroup {
    return [
      haStyle,
      css`
        .accepted {
          color: var(--success-color);
        }

        .queued {
          color: var(--warning-color);
        }

        .error {
          color: var(--error-color);
        }

        .secondary {
          color: var(--secondary-text-color);
        }

        .flex {
          display: flex;
        }

        .flex .config-label,
        .flex ha-select {
          flex: 1;
        }

        .content {
          margin-top: 24px;
        }

        .sectionHeader {
          position: relative;
          padding-right: 40px;
          padding-inline-start: initial;
          padding-inline-end: 40px;
        }

        ha-settings-row {
          --settings-row-prefix-display: contents;
          --settings-row-content-width: 100%;
          border-top: 1px solid var(--divider-color);
          padding: 4px 16px;
        }

        ha-settings-row:first-child {
          border-top: none;
        }

        .prefix {
          color: var(--secondary-text-color);
          text-align: center;
          text-transform: uppercase;
          font-size: 0.8em;
          padding-right: 24px;
          padding-inline-end: 24px;
          padding-inline-start: initial;
          line-height: var(--ha-line-height-normal);
        }

        .prefix span {
          font-size: 1.3em;
        }

        .heading {
          white-space: normal;
        }

        :host(:not([narrow])) ha-settings-row ha-input {
          text-align: right;
        }

        ha-card:last-child {
          margin-bottom: 24px;
        }

        .switch {
          text-align: right;
        }

        .custom-config {
          padding: 16px;
        }

        .reset {
          display: flex;
          justify-content: flex-end;
          margin-bottom: 24px;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "zwave_js-node-config": ZWaveJSNodeConfig;
  }
}
