import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume, type ContextType } from "@lit/context";
import {
  mdiApplicationImport,
  mdiArrowUpBoldCircleOutline,
  mdiCheckCircle,
  mdiChip,
  mdiCursorDefaultClickOutline,
  mdiDocker,
  mdiDotsVertical,
  mdiExclamationThick,
  mdiFlask,
  mdiHammer,
  mdiKey,
  mdiLinkLock,
  mdiNetwork,
  mdiNumeric1,
  mdiNumeric2,
  mdiNumeric3,
  mdiNumeric4,
  mdiNumeric5,
  mdiNumeric6,
  mdiNumeric7,
  mdiNumeric8,
  mdiPackageVariantRemove,
  mdiPlay,
  mdiPound,
  mdiRestart,
  mdiShield,
  mdiStop,
} from "@mdi/js";
import type { HassEntity } 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 { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import { consumeEntityState } from "../../../../../common/decorators/consume-context-entry";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeDomain } from "../../../../../common/entity/compute_domain";
import { navigate } from "../../../../../common/navigate";
import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter";
import type { LocalizeKeys } from "../../../../../common/translations/localize";
import { sanitizeHttpUrl } from "../../../../../common/url/sanitize-http-url";
import "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/chips/ha-assist-chip";
import "../../../../../components/chips/ha-chip-set";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-dropdown";
import "../../../../../components/ha-dropdown-item";
import "../../../../../components/ha-formfield";
import "../../../../../components/ha-markdown";
import "../../../../../components/ha-spinner";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-switch";
import type { HaSwitch } from "../../../../../components/ha-switch";
import "../../../../../components/item/ha-row-item";
import {
  apiContext,
  internationalizationContext,
  registriesContext,
} from "../../../../../data/context";
import type {
  AddonCapability,
  HassioAddonDetails,
  HassioAddonSetOptionParams,
  HassioAddonSetSecurityParams,
} from "../../../../../data/hassio/addon";
import {
  fetchHassioAddonChangelog,
  fetchHassioAddonInfo,
  installHassioAddon,
  rebuildLocalAddon,
  restartHassioAddon,
  setHassioAddonOption,
  setHassioAddonSecurity,
  startHassioAddon,
  stopHassioAddon,
  uninstallHassioAddon,
  validateHassioAddonOption,
} from "../../../../../data/hassio/addon";
import type { HassioStats } from "../../../../../data/hassio/common";
import {
  extractApiErrorMessage,
  fetchHassioStats,
} from "../../../../../data/hassio/common";
import type { StoreAddonDetails } from "../../../../../data/supervisor/store";
import {
  showAlertDialog,
  showConfirmationDialog,
} from "../../../../../dialogs/generic/show-dialog-box";
import { showMoreInfoDialog } from "../../../../../dialogs/more-info/show-ha-more-info-dialog";
import { MobileAwareMixin } from "../../../../../mixins/mobile-aware-mixin";
import { mdiHomeAssistant } from "../../../../../resources/home-assistant-logo-svg";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistantRegistries, Route } from "../../../../../types";
import { bytesToString } from "../../../../../util/bytes-to-string";
import { getAppDisplayName } from "../../common/app";
import "../../components/supervisor-apps-state";
import "../../components/supervisor-apps-tag";
import "../components/supervisor-app-metric";
import { extractChangelog } from "../util/supervisor-app";
import "./supervisor-app-system-managed";

const STAGE_ICON = {
  stable: mdiCheckCircle,
  experimental: mdiFlask,
  deprecated: mdiExclamationThick,
};

const RATING_ICON = {
  1: mdiNumeric1,
  2: mdiNumeric2,
  3: mdiNumeric3,
  4: mdiNumeric4,
  5: mdiNumeric5,
  6: mdiNumeric6,
  7: mdiNumeric7,
  8: mdiNumeric8,
};

const POLL_INTERVAL_SECONDS = 5;

@customElement("supervisor-app-info")
class SupervisorAppInfo extends MobileAwareMixin(LitElement) {
  @property({ type: Boolean }) public narrow = false;

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

  @property({ attribute: false }) public addon!:
    HassioAddonDetails | StoreAddonDetails;

  @property({ type: Boolean, attribute: "control-enabled" })
  public controlEnabled = false;

  @state()
  @consume({ context: internationalizationContext, subscribe: true })
  private i18n!: ContextType<typeof internationalizationContext>;

  @consume({ context: registriesContext, subscribe: true })
  private registries!: ContextType<typeof registriesContext>;

  @consume({ context: apiContext, subscribe: true })
  private api!: ContextType<typeof apiContext>;

  @state() private _metrics?: HassioStats;

  @state() private _error?: string;

  @state() private _addon?: HassioAddonDetails | StoreAddonDetails;

  @state() private _updateEntityId?: string;

  @state() private _uninstalling = false;

  @state()
  @consumeEntityState({
    entityIdPath: ["_updateEntityId"],
  })
  private _updateState?: HassEntity;

  private _pollInterval?: number;

  protected mobileSizeQuery =
    "all and (max-width: 1120px), all and (max-height: 500px)";

  private get _currentAddon(): HassioAddonDetails | StoreAddonDetails {
    return this._addon || this.addon;
  }

  public connectedCallback() {
    super.connectedCallback();
    this._startPolling();
  }

  protected willUpdate(changedProps: PropertyValues<this>) {
    super.willUpdate(changedProps);
    // Registries load asynchronously and change over time; resolve the update
    // entity reactively instead of only once on connect.
    this._updateEntityId = this._findUpdateEntityId(
      this.registries.devices,
      this.registries.entities,
      (this._currentAddon as HassioAddonDetails).slug
    );
  }

  public disconnectedCallback() {
    super.disconnectedCallback();
    this._stopPolling();
  }

  private _renderInfoCard() {
    const systemManaged = this._isSystemManaged(this._currentAddon);

    return html` <ha-card outlined>
      <div class="card-content">
        <div class="addon-header">
          <div class="title">
            ${
              this._currentAddon.logo
                ? html`
                    <img
                      class="logo"
                      alt=""
                      src="/api/hassio/addons/${this._currentAddon.slug}/logo"
                    />
                  `
                : nothing
            }
            ${
              !this.narrow
                ? getAppDisplayName(
                    this._currentAddon.name,
                    this._currentAddon.stage
                  )
                : nothing
            }
            <div class="description">
              ${
                this._currentAddon.version
                  ? html`
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.current_version",
                        { version: this._currentAddon.version }
                      )}
                      ${
                        this._currentAddon.changelog
                          ? html`<div
                              class="changelog"
                              @click=${this._openChangelog}
                            >
                              (<span class="changelog-link"
                                >${this.i18n.localize(
                                  "ui.panel.config.apps.dashboard.changelog"
                                )}</span
                              >)
                            </div>`
                          : nothing
                      }
                    `
                  : html`${this._currentAddon.version_latest}
                    ${
                      this._currentAddon.changelog
                        ? html`<span
                            class="changelog-link"
                            @click=${this._openChangelog}
                            >${this.i18n.localize(
                              "ui.panel.config.apps.dashboard.changelog"
                            )}</span
                          >`
                        : nothing
                    }`
              }
            </div>
          </div>
          ${
            this._currentAddon.update_available ||
            this._updateState?.attributes?.in_progress
              ? html`<supervisor-apps-tag
                  variant="brand"
                  .iconPath=${mdiArrowUpBoldCircleOutline}
                  .label=${this.i18n.localize(
                    `ui.panel.config.apps.state.${this._updateState?.attributes?.in_progress ? "updating" : "update_available"}`
                  )}
                ></supervisor-apps-tag>`
              : nothing
          }
          ${
            this._currentAddon.version
              ? html`<supervisor-apps-state
                  .state=${this._currentAddon.state}
                ></supervisor-apps-state>`
              : nothing
          }
        </div>

        <ha-chip-set class="capabilities">
          ${
            this._currentAddon.stage !== "stable"
              ? html`
                  <ha-assist-chip
                    filled
                    class=${classMap({
                      yellow: this._currentAddon.stage === "experimental",
                      red: this._currentAddon.stage === "deprecated",
                    })}
                    @click=${this._showMoreInfo}
                    id="stage"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        `ui.panel.config.apps.dashboard.capability.stages.${this._currentAddon.stage}`
                      )
                    )}
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${STAGE_ICON[this._currentAddon.stage]}
                    >
                    </ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }

          <ha-assist-chip
            filled
            class=${classMap({
              green: Number(this._currentAddon.rating) >= 6,
              yellow: [3, 4, 5].includes(Number(this._currentAddon.rating)),
              red: Number(this._currentAddon.rating) <= 2,
            })}
            @click=${this._showMoreInfo}
            id="rating"
            .label=${capitalizeFirstLetter(
              this.i18n.localize(
                "ui.panel.config.apps.dashboard.capability.label.rating"
              )
            )}
          >
            <ha-svg-icon
              slot="icon"
              .path=${RATING_ICON[this._currentAddon.rating]}
            >
            </ha-svg-icon>
          </ha-assist-chip>
          ${
            this._currentAddon.host_network
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="host_network"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.host"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.full_access
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="full_access"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.hardware"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.homeassistant_api
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="homeassistant_api"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.core"
                      )
                    )}
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${mdiHomeAssistant}
                    ></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._computeHassioApi
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="hassio_api"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        `ui.panel.config.apps.dashboard.capability.role.${this._currentAddon.hassio_role}`
                      ) || this._currentAddon.hassio_role
                    )}
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${mdiHomeAssistant}
                    ></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.docker_api
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="docker_api"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.docker"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.host_pid
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="host_pid"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.host_pid"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.apparmor !== "default"
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    class=${this._computeApparmorClassName}
                    id="apparmor"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.apparmor"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.auth_api
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="auth_api"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.auth"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.ingress
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="ingress"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.ingress"
                      )
                    )}
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${mdiCursorDefaultClickOutline}
                    ></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            this._currentAddon.signed
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showMoreInfo}
                    id="signed"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.capability.label.signed"
                      )
                    )}
                  >
                    <ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
          ${
            systemManaged
              ? html`
                  <ha-assist-chip
                    filled
                    @click=${this._showSystemManagedInfo}
                    id="system_managed"
                    .label=${capitalizeFirstLetter(
                      this.i18n.localize(
                        "ui.panel.config.apps.dashboard.system_managed.badge"
                      )
                    )}
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${mdiHomeAssistant}
                    ></ha-svg-icon>
                  </ha-assist-chip>
                `
              : nothing
          }
        </ha-chip-set>

        <div class="description">
          ${
            this._error
              ? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
              : nothing
          }
          ${this._currentAddon.description}.<br />
          ${this.i18n.localize(
            "ui.panel.config.apps.dashboard.visit_app_page",
            {
              name: html`<a
                href=${ifDefined(sanitizeHttpUrl(this._currentAddon.url))}
                target="_blank"
                rel="noreferrer"
                >${getAppDisplayName(
                  this._currentAddon.name,
                  this._currentAddon.stage
                )}</a
              >`,
            }
          )}
        </div>
      </div>
      ${
        (this._currentAddon.update_available && this._updateEntityId) ||
        this._computeShowWebUI ||
        this._computeShowIngressUI ||
        !this._currentAddon.version
          ? html`
              <div class="card-actions">
                ${
                  this._currentAddon.update_available && this._updateEntityId
                    ? html`
                        <ha-button
                          appearance="filled"
                          @click=${this._openUpdate}
                        >
                          <ha-svg-icon
                            slot="start"
                            .path=${mdiArrowUpBoldCircleOutline}
                          ></ha-svg-icon>
                          ${this.i18n.localize("ui.common.update")}
                        </ha-button>
                      `
                    : nothing
                }
                ${
                  this._computeShowWebUI || this._computeShowIngressUI
                    ? html`
                        <ha-button
                          href=${ifDefined(
                            !this._computeShowIngressUI
                              ? this._pathWebui!
                              : nothing
                          )}
                          target=${ifDefined(
                            !this._computeShowIngressUI ? "_blank" : nothing
                          )}
                          rel=${ifDefined(
                            !this._computeShowIngressUI ? "noopener" : nothing
                          )}
                          @click=${
                            !this._computeShowWebUI
                              ? this._openIngress
                              : undefined
                          }
                        >
                          ${this.i18n.localize(
                            "ui.panel.config.apps.dashboard.open_web_ui"
                          )}
                        </ha-button>
                      `
                    : nothing
                }
                ${
                  !this._currentAddon.version
                    ? html`
                        <ha-progress-button
                          .disabled=${!this._currentAddon.available}
                          @click=${this._installClicked}
                          .iconPath=${mdiApplicationImport}
                        >
                          ${this.i18n.localize(
                            "ui.panel.config.apps.dashboard.install"
                          )}
                        </ha-progress-button>
                      `
                    : nothing
                }
              </div>
            `
          : nothing
      }
    </ha-card>`;
  }

  private _renderDescriptionCard() {
    if (!this.addon.long_description) {
      return nothing;
    }

    return html`
      <ha-card class="long-description" outlined>
        <div class="card-content">
          <ha-markdown
            .content=${this.addon.long_description}
            lazy-images
          ></ha-markdown>
        </div>
      </ha-card>
    `;
  }

  private _renderControlsCard() {
    const systemManaged = this._isSystemManaged(this._currentAddon);
    const metrics = [
      {
        description: this.i18n.localize(
          "ui.panel.config.apps.dashboard.cpu_usage"
        ),
        value: this._metrics?.cpu_percent,
      },
      {
        description: this.i18n.localize(
          "ui.panel.config.apps.dashboard.ram_usage"
        ),
        value: this._metrics?.memory_percent,
        tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
          this._metrics?.memory_limit
        )}`,
      },
    ];

    return html`
      <ha-card class="controls" outlined>
        <div class="title">
          <span>
            ${this.i18n.localize("ui.panel.config.apps.dashboard.controls")}
          </span>
          ${
            this._currentAddon.version
              ? html`<ha-dropdown placement="bottom-end">
                  <ha-icon-button
                    slot="trigger"
                    .label=${this.i18n.localize("ui.common.menu")}
                    .path=${mdiDotsVertical}
                  ></ha-icon-button>

                  <ha-dropdown-item
                    variant="danger"
                    appearance="plain"
                    @click=${this._uninstallClicked}
                    .disabled=${
                      (systemManaged && !this.controlEnabled) ||
                      this._uninstalling
                    }
                  >
                    <ha-svg-icon
                      slot="icon"
                      .path=${mdiPackageVariantRemove}
                    ></ha-svg-icon>
                    ${this.i18n.localize(
                      "ui.panel.config.apps.dashboard.uninstall"
                    )}
                  </ha-dropdown-item>
                  ${
                    this._currentAddon.build
                      ? html`
                          <ha-dropdown-item
                            variant="danger"
                            @click=${this._rebuildClicked}
                          >
                            <ha-svg-icon
                              slot="icon"
                              .path=${mdiHammer}
                            ></ha-svg-icon>
                            ${this.i18n.localize(
                              "ui.panel.config.apps.dashboard.rebuild"
                            )}
                          </ha-dropdown-item>
                        `
                      : nothing
                  }
                </ha-dropdown>`
              : nothing
          }
        </div>
        <div class="content">
          ${
            systemManaged
              ? html`
                  <supervisor-app-system-managed
                    .narrow=${this.narrow}
                    .hideButton=${this.controlEnabled}
                  ></supervisor-app-system-managed>
                `
              : nothing
          }
          ${
            this._uninstalling
              ? html`
                  <ha-alert
                    alert-type="error"
                    .title=${this.i18n.localize(
                      "ui.panel.config.apps.dashboard.uninstalling"
                    )}
                  >
                    <ha-spinner
                      size="small"
                      slot="icon"
                      indeterminate
                    ></ha-spinner>
                  </ha-alert>
                `
              : nothing
          }
          <div class="actions">
            ${
              this._computeIsRunning
                ? html`<ha-progress-button
                      variant="danger"
                      @click=${this._stopClicked}
                      .disabled=${
                        (systemManaged && !this.controlEnabled) ||
                        this._uninstalling
                      }
                      .iconPath=${mdiStop}
                    >
                      ${this.i18n.localize("ui.panel.config.apps.dashboard.stop")}
                    </ha-progress-button>
                    <ha-progress-button
                      variant="danger"
                      appearance="filled"
                      @click=${this._restartClicked}
                      .disabled=${
                        (systemManaged && !this.controlEnabled) ||
                        this._uninstalling
                      }
                      .iconPath=${mdiRestart}
                    >
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.restart"
                      )}
                    </ha-progress-button> `
                : html`<ha-progress-button
                    @click=${this._startClicked}
                    .progress=${
                      (this._currentAddon as HassioAddonDetails).state ===
                      "startup"
                    }
                    .iconPath=${mdiPlay}
                  >
                    ${this.i18n.localize("ui.panel.config.apps.dashboard.start")}
                  </ha-progress-button>`
            }
          </div>
          ${
            this._currentAddon.version
              ? html`
                  <wa-divider></wa-divider>
                  <ha-row-item>
                    <span slot="headline">
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.option.boot.title"
                      )}
                    </span>
                    <span slot="supporting-text">
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.option.boot.description"
                      )}
                    </span>
                    <ha-switch
                      slot="end"
                      .disabled=${
                        (systemManaged && !this.controlEnabled) ||
                        this._uninstalling
                      }
                      @change=${this._startOnBootToggled}
                      .checked=${this._currentAddon.boot === "auto"}
                      haptic
                    ></ha-switch>
                  </ha-row-item>

                  ${
                    this._currentAddon.startup !== "once"
                      ? html`
                          <ha-row-item>
                            <span slot="headline">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.watchdog.title"
                              )}
                            </span>
                            <span slot="supporting-text">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.watchdog.description"
                              )}
                            </span>
                            <ha-switch
                              slot="end"
                              .disabled=${
                                (systemManaged && !this.controlEnabled) ||
                                this._uninstalling
                              }
                              @change=${this._watchdogToggled}
                              .checked=${this._currentAddon.watchdog || false}
                              haptic
                            ></ha-switch>
                          </ha-row-item>
                        `
                      : nothing
                  }
                  <ha-row-item>
                    <span slot="headline">
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.option.auto_update.title"
                      )}
                    </span>
                    <span slot="supporting-text">
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.option.auto_update.description"
                      )}
                    </span>
                    <ha-switch
                      slot="end"
                      .disabled=${
                        (systemManaged && !this.controlEnabled) ||
                        this._uninstalling
                      }
                      @change=${this._autoUpdateToggled}
                      .checked=${this._currentAddon.auto_update}
                      haptic
                    ></ha-switch>
                  </ha-row-item>
                  ${
                    !this._computeCannotIngressSidebar &&
                    this._currentAddon.ingress
                      ? html`
                          <ha-row-item>
                            <span slot="headline">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.ingress_panel.title"
                              )}
                            </span>
                            <span slot="supporting-text">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.ingress_panel.description"
                              )}
                            </span>
                            <ha-switch
                              slot="end"
                              .disabled=${
                                (systemManaged && !this.controlEnabled) ||
                                this._uninstalling
                              }
                              @change=${this._panelToggled}
                              .checked=${this._currentAddon.ingress_panel}
                              haptic
                            ></ha-switch>
                          </ha-row-item>
                        `
                      : nothing
                  }
                  ${
                    this._computeUsesProtectedOptions
                      ? html`
                          <ha-row-item>
                            <span slot="headline">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.protected.title"
                              )}
                            </span>
                            <span slot="supporting-text">
                              ${this.i18n.localize(
                                "ui.panel.config.apps.dashboard.option.protected.description"
                              )}
                            </span>
                            <ha-switch
                              slot="end"
                              .disabled=${
                                (systemManaged && !this.controlEnabled) ||
                                this._uninstalling
                              }
                              @change=${this._protectionToggled}
                              .checked=${this._currentAddon.protected}
                              haptic
                            ></ha-switch>
                          </ha-row-item>
                        `
                      : nothing
                  }
                `
              : nothing
          }
          ${
            this._currentAddon.version && this._currentAddon.state === "started"
              ? html`<wa-divider></wa-divider>
                  <ha-row-item>
                    <span slot="supporting-text">
                      ${this.i18n.localize(
                        "ui.panel.config.apps.dashboard.hostname"
                      )}
                    </span>
                    <code slot="headline">
                      ${this._currentAddon.hostname}
                    </code>
                  </ha-row-item>
                  ${metrics.map(
                    (metric) => html`
                      <supervisor-app-metric
                        .description=${metric.description}
                        .value=${metric.value ?? 0}
                        .tooltip=${metric.tooltip}
                      ></supervisor-app-metric>
                    `
                  )}`
              : nothing
          }
        </div>
      </ha-card>
    `;
  }

  protected render(): TemplateResult {
    return html`
      ${
        "protected" in this._currentAddon && !this._currentAddon.protected
          ? html`
              <ha-alert
                alert-type="error"
                .title=${this.i18n.localize(
                  "ui.panel.config.apps.dashboard.protection_mode.title"
                )}
              >
                ${this.i18n.localize(
                  "ui.panel.config.apps.dashboard.protection_mode.content"
                )}
                <ha-button
                  variant="danger"
                  slot="action"
                  @click=${this._protectionToggled}
                >
                  ${this.i18n.localize(
                    "ui.panel.config.apps.dashboard.protection_mode.enable"
                  )}
                </ha-button>
              </ha-alert>
            `
          : nothing
      }
      <div
        class="app ${
          this._isMobileSize || !this._currentAddon.version ? "column" : ""
        }"
      >
        ${
          this._isMobileSize || !this._currentAddon.version
            ? html`
                ${this._renderInfoCard()}
                ${
                  this._currentAddon.version
                    ? this._renderControlsCard()
                    : nothing
                }
                ${this._renderDescriptionCard()}
              `
            : html`<div class="info-column">
                  ${this._renderInfoCard()} ${this._renderDescriptionCard()}
                </div>
                <div class="control-column">${this._renderControlsCard()}</div>`
        }
      </div>
    `;
  }

  protected updated(changedProps: PropertyValues<this>) {
    super.updated(changedProps);
    if (changedProps.has("addon")) {
      this._loadMetrics();
    }
  }

  private _startPolling() {
    if (this._pollInterval) {
      return;
    }
    this._pollInterval = window.setInterval(() => {
      this._refreshAddonInfo();
    }, POLL_INTERVAL_SECONDS * 1000);
  }

  private _stopPolling() {
    if (this._pollInterval) {
      clearInterval(this._pollInterval);
      this._pollInterval = undefined;
    }
  }

  private async _refreshAddonInfo(): Promise<void> {
    const addon = this._currentAddon;
    if (!addon?.slug) {
      return;
    }
    try {
      this._addon = await fetchHassioAddonInfo(this.api.callWS, addon.slug);
    } catch (err: any) {
      // eslint-disable-next-line no-console
      console.error("Failed to fetch addon info", err);
    }
  }

  private async _loadMetrics(): Promise<void> {
    const addon = this._currentAddon;
    if ("state" in addon && addon.state === "started") {
      this._metrics = await fetchHassioStats(
        this.api.callWS,
        `addons/${addon.slug}`
      );
    }
  }

  private get _computeHassioApi(): boolean {
    const addon = this._currentAddon;
    return (
      addon.hassio_api &&
      (addon.hassio_role === "manager" || addon.hassio_role === "admin")
    );
  }

  private get _computeApparmorClassName(): string {
    const addon = this._currentAddon;
    if (addon.apparmor === "profile") {
      return "green";
    }
    if (addon.apparmor === "disable") {
      return "red";
    }
    return "";
  }

  private _showMoreInfo(ev): void {
    const id = ev.currentTarget.id as AddonCapability;
    showAlertDialog(this, {
      title: this.i18n.localize(
        `ui.panel.config.apps.dashboard.capability.${id}.title` as LocalizeKeys
      ),
      text: this.i18n.localize(
        `ui.panel.config.apps.dashboard.capability.${id}.description`
      ),
    });
  }

  private _showSystemManagedInfo() {
    showAlertDialog(this, {
      title: this.i18n.localize(
        "ui.panel.config.apps.dashboard.system_managed.title"
      ),
      text: this.i18n.localize(
        "ui.panel.config.apps.dashboard.system_managed.description"
      ),
    });
  }

  private get _computeIsRunning(): boolean {
    const addon = this._currentAddon as HassioAddonDetails;
    return addon?.state === "started";
  }

  private get _pathWebui(): string | null {
    const addon = this._currentAddon as HassioAddonDetails;
    return (
      sanitizeHttpUrl(
        addon.webui!.replace("[HOST]", document.location.hostname)
      ) ?? null
    );
  }

  private get _computeShowWebUI(): boolean | "" | null {
    const addon = this._currentAddon as HassioAddonDetails;
    return !addon.ingress && addon.webui && this._computeIsRunning;
  }

  private _openIngress(): void {
    navigate(`/app/${this.addon.slug}`);
  }

  private get _computeShowIngressUI(): boolean {
    const addon = this._currentAddon as HassioAddonDetails;
    return addon.ingress && this._computeIsRunning;
  }

  private get _computeCannotIngressSidebar(): boolean {
    const addon = this._currentAddon as HassioAddonDetails;
    return !addon.ingress;
  }

  private get _computeUsesProtectedOptions(): boolean {
    const addon = this._currentAddon as HassioAddonDetails;
    return addon.docker_api || addon.full_access || addon.host_pid;
  }

  private async _startOnBootToggled(): Promise<void> {
    this._error = undefined;
    const addon = this._currentAddon as HassioAddonDetails;
    const data: HassioAddonSetOptionParams = {
      boot: addon.boot === "auto" ? "manual" : "auto",
    };
    try {
      await setHassioAddonOption(this.api.callWS, addon.slug, data);
      const eventdata = {
        success: true,
        response: undefined,
        path: "option",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      this._error = this.i18n.localize(
        "ui.panel.config.apps.dashboard.failed_to_save",
        {
          error: extractApiErrorMessage(err),
        }
      );
    }
  }

  private async _watchdogToggled(): Promise<void> {
    this._error = undefined;
    const addon = this._currentAddon as HassioAddonDetails;
    const data: HassioAddonSetOptionParams = {
      watchdog: !addon.watchdog,
    };
    try {
      await setHassioAddonOption(this.api.callWS, addon.slug, data);
      const eventdata = {
        success: true,
        response: undefined,
        path: "option",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      this._error = this.i18n.localize(
        "ui.panel.config.apps.dashboard.failed_to_save",
        {
          error: extractApiErrorMessage(err),
        }
      );
    }
  }

  private async _autoUpdateToggled(): Promise<void> {
    this._error = undefined;
    const addon = this._currentAddon as HassioAddonDetails;
    const data: HassioAddonSetOptionParams = {
      auto_update: !addon.auto_update,
    };
    try {
      await setHassioAddonOption(this.api.callWS, addon.slug, data);
      const eventdata = {
        success: true,
        response: undefined,
        path: "option",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      this._error = this.i18n.localize(
        "ui.panel.config.apps.dashboard.failed_to_save",
        {
          error: extractApiErrorMessage(err),
        }
      );
    }
  }

  private async _protectionToggled(): Promise<void> {
    this._error = undefined;
    const addon = this._currentAddon as HassioAddonDetails;
    const data: HassioAddonSetSecurityParams = {
      protected: !addon.protected,
    };
    try {
      await setHassioAddonSecurity(this.api.callWS, addon.slug, data);
      const eventdata = {
        success: true,
        response: undefined,
        path: "security",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      this._error = this.i18n.localize(
        "ui.panel.config.apps.dashboard.failed_to_save",
        {
          error: extractApiErrorMessage(err),
        }
      );
    }
  }

  private async _panelToggled(): Promise<void> {
    this._error = undefined;
    const addon = this._currentAddon as HassioAddonDetails;
    const data: HassioAddonSetOptionParams = {
      ingress_panel: !addon.ingress_panel,
    };
    try {
      await setHassioAddonOption(this.api.callWS, addon.slug, data);
      const eventdata = {
        success: true,
        response: undefined,
        path: "option",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      this._error = this.i18n.localize(
        "ui.panel.config.apps.dashboard.failed_to_save",
        {
          error: extractApiErrorMessage(err),
        }
      );
    }
  }

  private async _openChangelog(): Promise<void> {
    try {
      const addon = this._currentAddon as HassioAddonDetails;
      const content = await fetchHassioAddonChangelog(this.api, addon.slug);

      showAlertDialog(this, {
        title: this.i18n.localize("ui.panel.config.apps.dashboard.changelog"),
        text: html`<ha-markdown
          .content=${extractChangelog(addon, content)}
        ></ha-markdown>`,
      });
    } catch (err: any) {
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.get_changelog"
        ),
        text: extractApiErrorMessage(err),
      });
    }
  }

  private async _installClicked(ev: CustomEvent): Promise<void> {
    const button = ev.currentTarget as any;
    button.progress = true;

    const addon = this._currentAddon as HassioAddonDetails;

    try {
      await installHassioAddon(this.api.callWS, addon.slug);
      const eventdata = {
        success: true,
        response: undefined,
        path: "install",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      showConfirmationDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.install"
        ),
        text: extractApiErrorMessage(err),
        confirmText: this.i18n.localize("ui.common.ok"),
        dismissText: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.view_supervisor_logs"
        ),
        cancel: () => navigate("/config/logs?provider=supervisor"),
      });
    }
    button.progress = false;
    this._refreshAddonInfo();
  }

  private async _stopClicked(ev: CustomEvent): Promise<void> {
    const addon = this._currentAddon as HassioAddonDetails;
    if (this._isSystemManaged(addon) && !this.controlEnabled) {
      return;
    }

    const button = ev.currentTarget as any;
    button.progress = true;

    try {
      await stopHassioAddon(this.api.callWS, addon.slug);
      const eventdata = {
        success: true,
        response: undefined,
        path: "stop",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.stop"
        ),
        text: extractApiErrorMessage(err),
      });
    }
    button.progress = false;
    this._refreshAddonInfo();
  }

  private async _restartClicked(ev: CustomEvent): Promise<void> {
    const addon = this._currentAddon as HassioAddonDetails;
    if (this._isSystemManaged(addon) && !this.controlEnabled) {
      return;
    }

    const button = ev.currentTarget as any;
    button.progress = true;

    try {
      await restartHassioAddon(this.api.callWS, addon.slug);
      const eventdata = {
        success: true,
        response: undefined,
        path: "restart",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.restart"
        ),
        text: extractApiErrorMessage(err),
      });
    }
    button.progress = false;
    this._refreshAddonInfo();
  }

  private async _rebuildClicked(ev: CustomEvent): Promise<void> {
    const button = ev.currentTarget as any;
    button.progress = true;

    const addon = this._currentAddon as HassioAddonDetails;

    try {
      await rebuildLocalAddon(this.api.callWS, addon.slug);
    } catch (err: any) {
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.rebuild"
        ),
        text: extractApiErrorMessage(err),
      });
    }
    button.progress = false;
    this._refreshAddonInfo();
  }

  private async _startClicked(ev: CustomEvent): Promise<void> {
    const button = ev.currentTarget as any;
    button.progress = true;
    const addon = this._currentAddon as HassioAddonDetails;
    try {
      const validate = await validateHassioAddonOption(
        this.api.callWS,
        addon.slug
      );
      if (!validate.valid) {
        await showConfirmationDialog(this, {
          title: this.i18n.localize(
            "ui.panel.config.apps.dashboard.action_error.start_invalid_config"
          ),
          text: validate.message.split(" Got ")[0],
          confirm: () => this._openConfiguration(),
          confirmText: this.i18n.localize(
            "ui.panel.config.apps.dashboard.action_error.go_to_config"
          ),
          dismissText: this.i18n.localize("ui.common.cancel"),
        });
        button.actionError();
        button.progress = false;
        return;
      }
    } catch (err: any) {
      button.actionError();
      button.progress = false;
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.validate_config"
        ),
        text: extractApiErrorMessage(err),
      });
      return;
    }

    try {
      await startHassioAddon(this.api.callWS, addon.slug);
      this._addon = await fetchHassioAddonInfo(this.api.callWS, addon.slug);
      const eventdata = {
        success: true,
        response: undefined,
        path: "start",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      button.actionError();
      button.progress = false;
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.start"
        ),
        text: extractApiErrorMessage(err),
      });
      return;
    }
    button.actionSuccess();
    button.progress = false;
    this._refreshAddonInfo();
  }

  private _openConfiguration(): void {
    const addon = this._currentAddon as HassioAddonDetails;
    navigate(`/config/app/${addon.slug}/config`);
  }

  private async _uninstallClicked(): Promise<void> {
    const addon = this._currentAddon as HassioAddonDetails;
    if (this._isSystemManaged(addon) && !this.controlEnabled) {
      return;
    }

    let removeData = false;
    const _removeDataToggled = (e: Event) => {
      removeData = (e.target as HaSwitch).checked;
    };

    const confirmed = await showConfirmationDialog(this, {
      title: this.i18n.localize(
        "ui.panel.config.apps.dashboard.uninstall_dialog.title",
        {
          name: getAppDisplayName(addon.name, addon.stage),
        }
      ),
      text: html`
        <ha-formfield
          .label=${html`<p>
            ${this.i18n.localize(
              "ui.panel.config.apps.dashboard.uninstall_dialog.remove_data"
            )}
          </p>`}
        >
          <ha-switch
            @change=${_removeDataToggled}
            .checked=${removeData}
            haptic
          ></ha-switch>
        </ha-formfield>
      `,
      confirmText: this.i18n.localize(
        "ui.panel.config.apps.dashboard.uninstall_dialog.uninstall"
      ),
      dismissText: this.i18n.localize("ui.common.cancel"),
      destructive: true,
    });

    if (!confirmed) {
      return;
    }

    this._uninstalling = true;
    this._error = undefined;
    try {
      await uninstallHassioAddon(this.api.callWS, addon.slug, removeData);
      const eventdata = {
        success: true,
        response: undefined,
        path: "uninstall",
      };
      fireEvent(this, "hass-api-called", eventdata);
    } catch (err: any) {
      showAlertDialog(this, {
        title: this.i18n.localize(
          "ui.panel.config.apps.dashboard.action_error.uninstall"
        ),
        text: extractApiErrorMessage(err),
      });
    } finally {
      this._uninstalling = false;
      this._refreshAddonInfo();
    }
  }

  private _isSystemManaged = memoizeOne(
    (addon: HassioAddonDetails | StoreAddonDetails) =>
      "system_managed" in addon && addon.system_managed
  );

  private _findUpdateEntityId = memoizeOne(
    (
      devices: HomeAssistantRegistries["devices"],
      entities: HomeAssistantRegistries["entities"],
      slug: string
    ): string | undefined => {
      const device = Object.values(devices).find((d) =>
        d.identifiers.some(([domain, id]) => domain === "hassio" && id === slug)
      );
      if (!device) {
        return undefined;
      }
      return Object.values(entities).find(
        (e) =>
          e.device_id === device.id && computeDomain(e.entity_id) === "update"
      )?.entity_id;
    }
  );

  private _openUpdate() {
    showMoreInfoDialog(this, { entityId: this._updateEntityId! });
  }

  static get styles(): CSSResultGroup {
    return [
      haStyle,
      css`
        :host {
          display: block;
        }

        .app {
          display: flex;
          gap: var(--ha-space-4);
          max-width: 1200px;
        }
        .app.column {
          flex-direction: column;
          gap: var(--ha-space-2);
        }
        @media (max-width: 1120px) {
          .app {
            flex-direction: column;
            gap: var(--ha-space-2);
          }
        }

        .info-column {
          display: flex;
          flex-direction: column;
          gap: var(--ha-space-4);
          flex: 2;
          min-width: 0;
        }

        .control-column {
          flex: 1;
          min-width: min(480px, 100%);
        }

        .controls .title {
          display: flex;
        }

        .controls .title span {
          flex: 1;
          font-size: var(--ha-font-size-l);
          font-weight: var(--ha-font-weight-medium);
          padding: var(--ha-space-3) var(--ha-space-4) 0;
          color: var(--ha-color-text-secondary);
        }

        .controls ha-row-item::part(supporting-text) {
          white-space: normal;
        }

        .controls .content {
          display: flex;
          flex-direction: column;
        }

        .controls .content .actions {
          display: flex;
          justify-content: flex-start;
          padding-inline: var(--ha-space-4);
          gap: var(--ha-space-3);
        }

        wa-divider {
          margin-inline: var(--ha-space-4);
        }

        ha-card {
          display: block;
        }
        .addon-header {
          display: flex;
          font-size: var(--ha-font-size-2xl);
          color: var(--ha-card-header-color, var(--primary-text-color));
          align-items: center;
          gap: var(--ha-space-2);
          flex-wrap: wrap;
          margin-bottom: var(--ha-space-4);
        }

        .addon-header .title {
          flex: 1;
          margin-inline-end: var(--ha-space-4);
        }

        .addon-header .title .description {
          font-size: var(--ha-font-size-s);
        }

        .addon-header supervisor-apps-state,
        .addon-header supervisor-apps-tag {
          align-self: start;
          display: inline-flex;
          height: 24px;
          align-items: center;
        }

        .errors {
          color: var(--error-color);
          margin-bottom: var(--ha-space-4);
        }
        .description a {
          color: var(--primary-color);
        }

        img.logo {
          max-width: 100%;
          max-height: 40px;
          display: block;
          margin-bottom: var(--ha-space-2);
        }
        ha-assist-chip {
          --md-sys-color-primary: var(--text-primary-color);
          --md-sys-color-on-surface: var(--text-primary-color);
          --ha-assist-chip-filled-container-color: var(--primary-color);
        }

        ha-assist-chip.red {
          --ha-assist-chip-filled-container-color: var(
            --label-badge-red,
            #df4c1e
          );
        }
        ha-assist-chip.blue {
          --ha-assist-chip-filled-container-color: var(
            --label-badge-blue,
            #039be5
          );
        }
        ha-assist-chip.green {
          --ha-assist-chip-filled-container-color: var(
            --label-badge-green,
            #0da035
          );
        }
        ha-assist-chip.yellow {
          --ha-assist-chip-filled-container-color: var(
            --label-badge-yellow,
            #f4b400
          );
        }
        .capabilities {
          margin-bottom: var(--ha-space-4);
        }
        .card-actions {
          justify-content: flex-end;
          display: flex;
          gap: var(--ha-space-2);
        }
        .changelog {
          display: contents;
        }
        .changelog-link {
          color: var(--primary-color);
          text-decoration: underline;
          cursor: pointer;
        }
        ha-markdown {
          padding: var(--ha-space-4);
          --markdown-image-background-color: transparent;
          --markdown-image-border-radius: 0;
          --markdown-image-min-height: auto;
          --markdown-image-text-indent: 0;
          --markdown-image-transition: none;
        }

        ha-alert {
          display: block;
          margin-bottom: var(--ha-space-4);
        }

        ha-alert ha-button {
          --mdc-theme-primary: var(--primary-text-color);
        }

        a {
          text-decoration: none;
        }
      `,
    ];
  }
}
declare global {
  interface HTMLElementTagNameMap {
    "supervisor-app-info": SupervisorAppInfo;
  }
}
