import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { slugify } from "../../../../common/string/slugify";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-form/ha-form";
import "../../../../components/ha-dialog";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type {
  LovelaceDashboard,
  LovelaceDashboardCreateParams,
  LovelaceDashboardMutableParams,
} from "../../../../data/lovelace/dashboard";
import { DirtyStateProviderMixin } from "../../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail";
import { pickAvailableDashboardUrlPath } from "./pick-available-dashboard-url-path";

@customElement("dialog-lovelace-dashboard-detail")
export class DialogLovelaceDashboardDetail extends DirtyStateProviderMixin<
  Partial<LovelaceDashboard>
>()(LitElement) {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @state() private _params?: LovelaceDashboardDetailsDialogParams;

  @state() private _open = false;

  @state() private _urlPathChanged = false;

  @state() private _data?: Partial<LovelaceDashboard>;

  @state() private _error?: Record<string, string>;

  @state() private _submitting = false;

  public showDialog(params: LovelaceDashboardDetailsDialogParams): void {
    this._params = params;
    this._error = undefined;
    this._urlPathChanged = false;
    this._open = true;
    if (this._params.dashboard) {
      this._data = this._params.dashboard;
      this._initDirtyTracking({ type: "deep" }, this._data);
    } else {
      const suggestions = this._params.suggestions;
      this._data = {
        show_in_sidebar: true,
        icon: suggestions?.icon,
        title: suggestions?.title ?? "",
        require_admin: false,
        mode: "storage",
      };
      // New dashboards have no saved baseline, so track against an emptyobject to mark them dirty from the outset (keeps Create enabled).
      this._initDirtyTracking({ type: "deep" }, {});
      if (suggestions?.title) {
        this._fillUrlPath(suggestions.title);
      }
      this._updateDirtyState(this._data!);
    }
  }

  public closeDialog(): void {
    this._open = false;
  }

  private _dialogClosed(): void {
    this._params = undefined;
    this._data = undefined;
    fireEvent(this, "dialog-closed", { dialog: this.localName });
  }

  protected render() {
    if (!this._params || !this._data) {
      return nothing;
    }

    const yamlMode = this._params.dashboard?.mode === "yaml";

    const titleInvalid = !this._data.title || !this._data.title.trim();

    const cancelButton = html`
      <ha-button
        appearance="plain"
        slot="secondaryAction"
        @click=${this.closeDialog}
      >
        ${this.hass.localize("ui.common.cancel")}
      </ha-button>
    `;

    return html`
      <ha-dialog
        .open=${this._open}
        header-title=${this._params.urlPath
          ? this._data.title ||
            this.hass.localize(
              "ui.panel.config.lovelace.dashboards.detail.edit_dashboard"
            )
          : this.hass.localize(
              "ui.panel.config.lovelace.dashboards.detail.new_dashboard"
            )}
        .preventScrimClose=${this.isDirtyState}
        @closed=${this._dialogClosed}
      >
        <div>
          ${yamlMode
            ? this.hass.localize(
                "ui.panel.config.lovelace.dashboards.cant_edit_yaml"
              )
            : html`
                <ha-form
                  autofocus
                  .schema=${this._schema(
                    this._params,
                    this._data?.require_admin
                  )}
                  .data=${this._data}
                  .hass=${this.hass}
                  .error=${this._error}
                  .computeLabel=${this._computeLabel}
                  .computeHelper=${this._computeHelper}
                  @value-changed=${this._valueChanged}
                ></ha-form>
              `}
        </div>
        <ha-dialog-footer slot="footer">
          ${this._params.urlPath
            ? html`
                ${this._params.dashboard?.mode === "storage"
                  ? html`
                      <ha-button
                        slot="secondaryAction"
                        variant="danger"
                        appearance="plain"
                        @click=${this._deleteDashboard}
                        .disabled=${this._submitting}
                      >
                        ${this.hass.localize(
                          "ui.panel.config.lovelace.dashboards.detail.delete"
                        )}
                      </ha-button>
                    `
                  : cancelButton}
              `
            : cancelButton}
          <ha-button
            slot="primaryAction"
            @click=${this._updateDashboard}
            .disabled=${!yamlMode &&
            ((this._error && "url_path" in this._error) ||
              titleInvalid ||
              this._submitting ||
              !this.isDirtyState)}
            ?autofocus=${yamlMode}
          >
            ${this._params.urlPath
              ? this._params.dashboard?.mode === "storage"
                ? this.hass.localize(
                    "ui.panel.config.lovelace.dashboards.detail.update"
                  )
                : this.hass.localize("ui.common.close")
              : this.hass.localize(
                  "ui.panel.config.lovelace.dashboards.detail.create"
                )}
          </ha-button>
        </ha-dialog-footer>
      </ha-dialog>
    `;
  }

  private _schema = memoizeOne(
    (params: LovelaceDashboardDetailsDialogParams, requireAdmin?: boolean) =>
      [
        {
          name: "title",
          required: true,
          selector: {
            text: {},
          },
        },
        {
          name: "icon",
          required: false,
          selector: {
            icon: {},
          },
        },
        ...(!params.dashboard
          ? ([
              {
                name: "url_path",
                required: true,
                selector: { text: {} },
              },
            ] as const)
          : []),
        {
          name: "require_admin",
          required: true,
          disabled: params.isDefault && !requireAdmin,
          selector: {
            boolean: {},
          },
        },
        {
          name: "show_in_sidebar",
          required: true,
          selector: {
            boolean: {},
          },
        },
      ] as const
  );

  private _computeLabel = (
    entry: SchemaUnion<ReturnType<typeof this._schema>>
  ): string =>
    this.hass.localize(
      `ui.panel.config.lovelace.dashboards.detail.${
        entry.name === "show_in_sidebar"
          ? "show_sidebar"
          : entry.name === "url_path"
            ? "url"
            : entry.name
      }`
    );

  private _computeHelper = (
    entry: SchemaUnion<ReturnType<typeof this._schema>>
  ): string =>
    entry.name === "require_admin" && entry.disabled
      ? this.hass.localize(
          "ui.panel.config.lovelace.dashboards.panel_detail.require_admin_helper"
        )
      : "";

  private _valueChanged(ev: CustomEvent) {
    this._error = undefined;
    const value = ev.detail.value;
    if (value.url_path !== this._data?.url_path) {
      this._urlPathChanged = true;
      if (
        !value.url_path ||
        value.url_path === "lovelace" ||
        !/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(value.url_path)
      ) {
        this._error = {
          url_path: this.hass.localize(
            "ui.panel.config.lovelace.dashboards.detail.url_error_msg"
          ),
        };
      }
    }
    if (value.title !== this._data?.title) {
      this._data = value;
      this._fillUrlPath(value.title);
    } else {
      this._data = value;
    }
    this._updateDirtyState(this._data!);
  }

  private _fillUrlPath(title: string) {
    if (this._urlPathChanged || !title) {
      return;
    }

    const slugifyTitle = slugify(title, "-");
    const baseSlug = slugifyTitle.includes("-")
      ? slugifyTitle
      : `dashboard-${slugifyTitle}`;
    const taken = this._params?.takenUrlPaths;
    this._data = {
      ...this._data,
      url_path:
        taken !== undefined
          ? pickAvailableDashboardUrlPath(baseSlug, taken)
          : baseSlug,
    };
    this._updateDirtyState(this._data!);
  }

  private async _updateDashboard() {
    if (this._params?.urlPath && this._params.dashboard?.mode === "yaml") {
      this.closeDialog();
      return;
    }
    this._submitting = true;
    try {
      if (this._params!.dashboard) {
        const values: Partial<LovelaceDashboardMutableParams> = {
          require_admin: this._data!.require_admin,
          show_in_sidebar: this._data!.show_in_sidebar,
          icon: this._data!.icon || undefined,
          title: this._data!.title,
        };
        await this._params!.updateDashboard(values);
      } else if (this._params!.createDashboard) {
        await this._params!.createDashboard(
          this._data as LovelaceDashboardCreateParams
        );
      }
      this._markDirtyStateClean();
      this.closeDialog();
    } catch (err: any) {
      let localizedErrorMessage: string | undefined;
      if (err?.translation_domain && err?.translation_key) {
        const localize = await this.hass.loadBackendTranslation(
          "exceptions",
          err.translation_domain
        );
        localizedErrorMessage = localize(
          `component.${err.translation_domain}.exceptions.${err.translation_key}.message`,
          err.translation_placeholders
        );
      }
      this._error = {
        base: localizedErrorMessage || err?.message || "Unknown error",
      };
    } finally {
      this._submitting = false;
    }
  }

  private async _deleteDashboard() {
    this._submitting = true;
    try {
      if (await this._params!.removeDashboard()) {
        this.closeDialog();
      }
    } finally {
      this._submitting = false;
    }
  }

  static get styles(): CSSResultGroup {
    return [haStyleDialog, css``];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "dialog-lovelace-dashboard-detail": DialogLovelaceDashboardDetail;
  }
}
