import { mdiOpenInNew } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-footer";
import "../../../components/animation/ha-fade-in";
import "../../../components/ha-generic-picker";
import "../../../components/ha-markdown";
import type { PickerComboBoxItem } from "../../../components/ha-picker-combo-box";
import "../../../components/ha-spinner";
import "../../../components/input/ha-input";
import type {
  ApplicationCredential,
  ApplicationCredentialsConfig,
} from "../../../data/application_credential";
import {
  createApplicationCredential,
  fetchApplicationCredentialsConfig,
} from "../../../data/application_credential";
import type { IntegrationManifest } from "../../../data/integration";
import { domainToName } from "../../../data/integration";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import type { AddApplicationCredentialDialogParams } from "./show-dialog-add-application-credential";

interface CredentialFormState {
  domain: string;
  name: string;
  clientId: string;
  clientSecret: string;
}

interface Domain {
  id: string;
  name: string;
}

@customElement("dialog-add-application-credential")
export class DialogAddApplicationCredential extends DirtyStateProviderMixin<CredentialFormState>()(
  LitElement
) {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @state() private _loading = false;

  // Error message when can't talk to server etc
  @state() private _error?: string;

  @state() private _params?: AddApplicationCredentialDialogParams;

  @state() private _domain?: string;

  @state() private _manifest?: IntegrationManifest | null;

  @state() private _name?: string;

  @state() private _description?: string;

  @state() private _clientId?: string;

  @state() private _clientSecret?: string;

  @state() private _domains?: Domain[];

  @state() private _config?: ApplicationCredentialsConfig;

  @state() private _open = false;

  @state() private _invalid = false;

  public showDialog(params: AddApplicationCredentialDialogParams) {
    this._params = params;
    this._domain = params.selectedDomain;
    this._manifest = params.manifest;
    this._invalid = false;
    this._name = "";
    this._description = "";
    this._clientId = "";
    this._clientSecret = "";
    this._error = undefined;
    this._loading = false;
    this._open = true;
    this._initDirtyTracking({ type: "shallow" }, this._currentState());
    this._fetchConfig();
  }

  private async _fetchConfig() {
    this._config = await fetchApplicationCredentialsConfig(this.hass);
    this._domains = Object.keys(this._config.integrations).map((domain) => ({
      id: domain,
      name: domainToName(this.hass.localize, domain),
    }));
    await this.hass.loadBackendTranslation("application_credentials");
    this._updateDescription();
  }

  protected render() {
    if (!this._params) {
      return nothing;
    }
    const selectedDomainName = this._params.selectedDomain
      ? domainToName(this.hass.localize, this._domain!)
      : "";
    const documentationLink = this._manifest?.is_built_in
      ? documentationUrl(this.hass, `/integrations/${this._domain}`)
      : this._manifest?.documentation;
    return html`
      <ha-dialog
        .open=${this._open}
        @closed=${this._abortDialog}
        .preventScrimClose=${this.isDirtyState}
        .headerTitle=${this.hass.localize(
          "ui.panel.config.application_credentials.editor.caption"
        )}
      >
        ${
          !this._config
            ? html`<ha-fade-in .delay=${500}>
                <ha-spinner size="large"></ha-spinner>
              </ha-fade-in>`
            : html`<div>
                  ${
                    this._error
                      ? html`<ha-alert alert-type="error"
                          >${this._error}</ha-alert
                        > `
                      : nothing
                  }
                  ${
                    this._params.selectedDomain && !this._description
                      ? html`<p>
                          ${this.hass.localize(
                            "ui.panel.config.application_credentials.editor.missing_credentials",
                            {
                              integration: selectedDomainName,
                            }
                          )}
                          ${
                            documentationLink
                              ? html`<a
                                  href=${documentationLink}
                                  target="_blank"
                                  rel="noreferrer"
                                >
                                  ${this.hass.localize(
                                    "ui.panel.config.application_credentials.editor.missing_credentials_domain_link",
                                    {
                                      integration: selectedDomainName,
                                    }
                                  )}
                                  <ha-svg-icon
                                    .path=${mdiOpenInNew}
                                  ></ha-svg-icon>
                                </a>`
                              : nothing
                          }
                        </p>`
                      : nothing
                  }
                  ${
                    !this._params.selectedDomain || !this._description
                      ? html`<p>
                          ${this.hass.localize(
                            "ui.panel.config.application_credentials.editor.description"
                          )}
                          <a
                            href=${documentationUrl(
                              this.hass!,
                              "/integrations/application_credentials"
                            )}
                            target="_blank"
                            rel="noreferrer"
                          >
                            ${this.hass!.localize(
                              "ui.panel.config.application_credentials.editor.view_documentation"
                            )}
                            <ha-svg-icon .path=${mdiOpenInNew}></ha-svg-icon>
                          </a>
                        </p>`
                      : nothing
                  }
                  ${
                    this._params.selectedDomain
                      ? nothing
                      : html`<ha-generic-picker
                          name="domain"
                          .hass=${this.hass}
                          .label=${this.hass.localize(
                            "ui.panel.config.application_credentials.editor.domain"
                          )}
                          .value=${this._domain}
                          .invalid=${this._invalid && !this._domain}
                          .getItems=${this._getDomainItems}
                          required
                          .disabled=${!this._domains}
                          .valueRenderer=${this._domainRenderer}
                          @value-changed=${this._handleDomainPicked}
                          .errorMessage=${this.hass.localize(
                            "ui.common.error_required"
                          )}
                        ></ha-generic-picker>`
                  }
                  ${
                    this._description
                      ? html`<ha-markdown
                          breaks
                          .content=${this._description}
                        ></ha-markdown>`
                      : nothing
                  }
                  <ha-input
                    class="name"
                    name="name"
                    .label=${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.name"
                    )}
                    .value=${this._name}
                    .invalid=${this._invalid && !this._name}
                    required
                    @input=${this._handleValueChanged}
                    .validationMessage=${this.hass.localize(
                      "ui.common.error_required"
                    )}
                    dialogInitialFocus
                  ></ha-input>
                  <ha-input
                    class="clientId"
                    name="clientId"
                    .label=${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.client_id"
                    )}
                    .value=${this._clientId}
                    .invalid=${this._invalid && !this._clientId}
                    required
                    @input=${this._handleValueChanged}
                    .validationMessage=${this.hass.localize(
                      "ui.common.error_required"
                    )}
                    dialogInitialFocus
                    .hint=${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.client_id_helper"
                    )}
                  ></ha-input>
                  <ha-input
                    type="password"
                    password-toggle
                    .label=${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.client_secret"
                    )}
                    name="clientSecret"
                    .value=${this._clientSecret}
                    .invalid=${this._invalid && !this._clientSecret}
                    required
                    @input=${this._handleValueChanged}
                    .validationMessage=${this.hass.localize(
                      "ui.common.error_required"
                    )}
                    .hint=${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.client_secret_helper"
                    )}
                  ></ha-input>
                </div>

                <ha-dialog-footer slot="footer">
                  <ha-button
                    appearance="plain"
                    slot="secondaryAction"
                    @click=${this._closeDialog}
                    .disabled=${this._loading}
                  >
                    ${this.hass.localize("ui.common.cancel")}
                  </ha-button>
                  <ha-button
                    slot="primaryAction"
                    @click=${this._addApplicationCredential}
                    .loading=${this._loading}
                  >
                    ${this.hass.localize(
                      "ui.panel.config.application_credentials.editor.add"
                    )}
                  </ha-button>
                </ha-dialog-footer>`
        }
      </ha-dialog>
    `;
  }

  private _closeDialog() {
    this._open = false;
  }

  public closeDialog() {
    this._params = undefined;
    this._domains = undefined;
    fireEvent(this, "dialog-closed", { dialog: this.localName });
  }

  private _handleDomainPicked(ev: CustomEvent) {
    ev.stopPropagation();
    this._domain = ev.detail.value;
    this._updateDescription();
    this._updateDirtyState(this._currentState());
  }

  private async _updateDescription() {
    if (!this._domain) {
      return;
    }

    await this.hass.loadBackendTranslation(
      "application_credentials",
      this._domain
    );
    const info = this._config!.integrations[this._domain];
    this._description = this.hass.localize(
      `component.${this._domain}.application_credentials.description`,
      info.description_placeholders
    );
  }

  private _handleValueChanged(ev: CustomEvent) {
    this._error = undefined;
    const name = (ev.target as any).name;
    const value = (ev.target as any).value;
    this[`_${name}`] = value;
    this._updateDirtyState(this._currentState());
  }

  private _currentState(): CredentialFormState {
    return {
      domain: this._domain || "",
      name: this._name || "",
      clientId: this._clientId || "",
      clientSecret: this._clientSecret || "",
    };
  }

  private _abortDialog() {
    if (this._params && this._params.dialogAbortedCallback) {
      this._params.dialogAbortedCallback();
    }
    this.closeDialog();
  }

  private async _addApplicationCredential(ev) {
    ev.preventDefault();
    if (
      !this._domain ||
      !this._name ||
      !this._clientId ||
      !this._clientSecret
    ) {
      this._invalid = true;
      return;
    }
    this._invalid = false;

    this._loading = true;
    this._error = "";

    let applicationCredential: ApplicationCredential;
    try {
      applicationCredential = await createApplicationCredential(
        this.hass,
        this._domain,
        this._clientId,
        this._clientSecret,
        this._name
      );
    } catch (err: any) {
      this._loading = false;
      this._error = err.message;
      return;
    }
    this._params!.applicationCredentialAddedCallback(applicationCredential);
    this.closeDialog();
  }

  private _getDomainItems = (): PickerComboBoxItem[] =>
    this._domains?.map((domain) => ({
      id: domain.id,
      primary: domain.name,
      sorting_label: domain.name,
    })) ?? [];

  private _domainRenderer = (domainId: string) => {
    const domain = this._domains?.find((d) => d.id === domainId);
    return html`<span slot="headline"
      >${domain ? domain.name : domainId}</span
    >`;
  };

  static get styles(): CSSResultGroup {
    return [
      haStyleDialog,
      css`
        ha-dialog {
          --mdc-dialog-max-width: 500px;
          --dialog-z-index: 10;
        }
        .row {
          display: flex;
          padding: var(--ha-space-2) 0;
        }
        a {
          text-decoration: none;
        }
        a ha-svg-icon {
          --mdc-icon-size: 16px;
        }
        ha-markdown {
          margin-top: var(--ha-space-4);
          margin-bottom: var(--ha-space-4);
        }
        ha-fade-in {
          display: flex;
          width: 100%;
          justify-content: center;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "dialog-add-application-credential": DialogAddApplicationCredential;
  }
}
