import {
  mdiAndroid,
  mdiApple,
  mdiClockCheckOutline,
  mdiClockRemoveOutline,
  mdiDelete,
  mdiDotsVertical,
  mdiWeb,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-button";
import "../../components/ha-card";
import "../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../components/ha-dropdown";
import "../../components/ha-dropdown-item";
import "../../components/ha-icon-button";
import "../../components/item/ha-list-item-base";
import "../../components/list/ha-list-base";
import { deleteAllRefreshTokens } from "../../data/auth";
import type { RefreshToken } from "../../data/refresh_token";
import {
  showAlertDialog,
  showConfirmationDialog,
} from "../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";

// Client ID used by iOS app
const iOSclientId = "https://home-assistant.io/iOS";
// Client ID used by Android app
const androidClientId = "https://home-assistant.io/android";

const compareTokenLastUsedAt = (tokenA: RefreshToken, tokenB: RefreshToken) => {
  const timeA = tokenA.last_used_at ? new Date(tokenA.last_used_at) : 0;
  const timeB = tokenB.last_used_at ? new Date(tokenB.last_used_at) : 0;
  if (timeA < timeB) {
    return 1;
  }
  if (timeA > timeB) {
    return -1;
  }
  return 0;
};

@customElement("ha-refresh-tokens-card")
class HaRefreshTokens extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) public refreshTokens?: RefreshToken[];

  private _refreshTokens = memoizeOne(
    (refreshTokens: RefreshToken[]): RefreshToken[] =>
      refreshTokens
        .filter((token) => token.type === "normal")
        .sort(compareTokenLastUsedAt)
  );

  private _formatTokenName = (token: RefreshToken): string => {
    if (token.client_id === iOSclientId) {
      return this.hass.localize("ui.panel.profile.refresh_tokens.ios_app");
    }
    if (token.client_id === androidClientId) {
      return this.hass.localize("ui.panel.profile.refresh_tokens.android_app");
    }
    return token.client_name || token.client_id;
  };

  protected render(): TemplateResult {
    const refreshTokens = this.refreshTokens
      ? this._refreshTokens(this.refreshTokens)
      : [];
    return html`
      <ha-card
        .header=${this.hass.localize("ui.panel.profile.refresh_tokens.header")}
      >
        <div class="card-content">
          ${this.hass.localize("ui.panel.profile.refresh_tokens.description")}
          <ha-list-base>
            ${
              refreshTokens.length
                ? refreshTokens.map(
                    (token) => html`
                      <ha-list-item-base>
                        <ha-svg-icon
                          slot="start"
                          .path=${
                            token.client_id === iOSclientId
                              ? mdiApple
                              : token.client_id === androidClientId
                                ? mdiAndroid
                                : mdiWeb
                          }
                        ></ha-svg-icon>
                        <span slot="headline" class="primary">
                          ${this._formatTokenName(token)}
                        </span>
                        <div slot="supporting-text">
                          ${this.hass.localize(
                            "ui.panel.profile.refresh_tokens.created_at",
                            {
                              date: relativeTime(
                                new Date(token.created_at),
                                this.hass.locale
                              ),
                            }
                          )}
                        </div>
                        <div slot="supporting-text">
                          ${
                            token.is_current
                              ? html`
                                  <span class="current-session">
                                    <span class="dot"></span>
                                    ${this.hass.localize(
                                      "ui.panel.profile.refresh_tokens.current_session"
                                    )}
                                  </span>
                                `
                              : token.last_used_at
                                ? this.hass.localize(
                                    "ui.panel.profile.refresh_tokens.last_used",
                                    {
                                      date: relativeTime(
                                        new Date(token.last_used_at),
                                        this.hass.locale
                                      ),
                                      location: token.last_used_ip,
                                    }
                                  )
                                : this.hass.localize(
                                    "ui.panel.profile.refresh_tokens.not_used"
                                  )
                          }
                        </div>
                        <div slot="supporting-text">
                          ${
                            token.expire_at
                              ? this.hass.localize(
                                  "ui.panel.profile.refresh_tokens.expires_in",
                                  {
                                    date: relativeTime(
                                      new Date(token.expire_at),
                                      this.hass.locale
                                    ),
                                  }
                                )
                              : this.hass.localize(
                                  "ui.panel.profile.refresh_tokens.never_expires"
                                )
                          }
                        </div>
                        <ha-dropdown
                          slot="end"
                          @wa-select=${this._handleDropdownSelect}
                        >
                          <ha-icon-button
                            slot="trigger"
                            .label=${this.hass.localize("ui.common.menu")}
                            .path=${mdiDotsVertical}
                          ></ha-icon-button>
                          <ha-dropdown-item
                            .token=${token}
                            .action=${"toggle_expiration"}
                          >
                            <ha-svg-icon
                              slot="icon"
                              .path=${
                                token.expire_at
                                  ? mdiClockRemoveOutline
                                  : mdiClockCheckOutline
                              }
                            ></ha-svg-icon>
                            ${
                              token.expire_at
                                ? this.hass.localize(
                                    "ui.panel.profile.refresh_tokens.disable_token_expiration"
                                  )
                                : this.hass.localize(
                                    "ui.panel.profile.refresh_tokens.enable_token_expiration"
                                  )
                            }
                          </ha-dropdown-item>
                          <ha-dropdown-item
                            .token=${token}
                            .action=${"delete_token"}
                            variant="danger"
                            .disabled=${token.is_current}
                          >
                            <ha-svg-icon
                              slot="icon"
                              .path=${mdiDelete}
                            ></ha-svg-icon>
                            ${this.hass.localize("ui.common.delete")}
                          </ha-dropdown-item>
                        </ha-dropdown>
                      </ha-list-item-base>
                    `
                  )
                : nothing
            }
          </ha-list-base>
        </div>
        <div class="card-actions">
          <ha-button
            variant="danger"
            appearance="filled"
            size="s"
            @click=${this._deleteAllTokens}
          >
            ${this.hass.localize(
              "ui.panel.profile.refresh_tokens.delete_all_tokens"
            )}
          </ha-button>
        </div>
      </ha-card>
    `;
  }

  private _handleDropdownSelect(
    ev: HaDropdownSelectEvent<string> & {
      detail: { item: { action: string; token: RefreshToken } };
    }
  ) {
    if (ev.detail.item.action === "toggle_expiration") {
      this._toggleTokenExpiration(ev.detail.item.token);
    } else if (ev.detail.item.action === "delete_token") {
      this._deleteToken(ev.detail.item.token);
    }
  }

  private async _toggleTokenExpiration(token: RefreshToken): Promise<void> {
    const enable = !token.expire_at;
    if (!enable) {
      if (
        !(await showConfirmationDialog(this, {
          title: this.hass.localize(
            "ui.panel.profile.refresh_tokens.confirm_disable_token_expiration_title"
          ),
          text: this.hass.localize(
            "ui.panel.profile.refresh_tokens.confirm_disable_token_expiration_text",
            { name: this._formatTokenName(token) }
          ),
          confirmText: this.hass.localize("ui.common.disable"),
          destructive: true,
        }))
      ) {
        return;
      }
    }

    try {
      await this.hass.callWS({
        type: "auth/refresh_token_set_expiry",
        refresh_token_id: token.id,
        enable_expiry: enable,
      });
      fireEvent(this, "hass-refresh-tokens");
    } catch (err: unknown) {
      const message =
        typeof err === "object" && err !== null && "message" in err
          ? (err.message as string)
          : String(err);
      await showAlertDialog(this, {
        title: this.hass.localize(
          `ui.panel.profile.refresh_tokens.${enable ? "enable" : "disable"}_expiration_failed`
        ),
        text: message,
      });
    }
  }

  private async _deleteToken(token: RefreshToken): Promise<void> {
    if (
      !(await showConfirmationDialog(this, {
        title: this.hass.localize(
          "ui.panel.profile.refresh_tokens.confirm_delete_title"
        ),
        text: this.hass.localize(
          "ui.panel.profile.refresh_tokens.confirm_delete_text",
          { name: this._formatTokenName(token) }
        ),
        confirmText: this.hass.localize("ui.common.delete"),
        destructive: true,
      }))
    ) {
      return;
    }
    try {
      await this.hass.callWS({
        type: "auth/delete_refresh_token",
        refresh_token_id: token.id,
      });
      fireEvent(this, "hass-refresh-tokens");
    } catch (err: any) {
      await showAlertDialog(this, {
        title: this.hass.localize(
          "ui.panel.profile.refresh_tokens.delete_failed"
        ),
        text: err.message,
      });
    }
  }

  private async _deleteAllTokens(): Promise<void> {
    if (
      !(await showConfirmationDialog(this, {
        title: this.hass.localize(
          "ui.panel.profile.refresh_tokens.confirm_delete_all_title"
        ),
        text: this.hass.localize(
          "ui.panel.profile.refresh_tokens.confirm_delete_all"
        ),
        confirmText: this.hass.localize("ui.common.delete_all"),
        destructive: true,
      }))
    ) {
      return;
    }
    try {
      await deleteAllRefreshTokens(this.hass, "normal", false);
      fireEvent(this, "hass-refresh-tokens");
    } catch (err: any) {
      await showAlertDialog(this, {
        title: this.hass.localize(
          "ui.panel.profile.refresh_tokens.delete_failed"
        ),
        text: err.message,
      });
    }
  }

  static get styles(): CSSResultGroup {
    return [
      haStyle,
      css`
        ha-list-item-base {
          --ha-row-item-padding-inline: 0;
        }
        ha-icon-button {
          color: var(--primary-text-color);
        }
        ha-list-item-base .dot {
          display: inline-block;
          width: 8px;
          height: 8px;
          background-color: var(--success-color);
          border-radius: var(--ha-border-radius-circle);
          margin-right: 6px;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-refresh-tokens-card": HaRefreshTokens;
  }
}
