import { mdiFileCodeOutline, mdiPackageVariant, mdiWeb } from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { PROTOCOL_INTEGRATIONS } from "../../../common/integrations/protocolIntegrationPicked";
import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/ha-button";
import "../../../components/ha-card";
import "../../../components/ha-ripple";
import "../../../components/ha-svg-icon";
import "../../../components/ha-tooltip";
import type { ConfigEntry } from "../../../data/config_entries";
import { ERROR_STATES } from "../../../data/config_entries";
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
import type {
  IntegrationLogInfo,
  IntegrationManifest,
} from "../../../data/integration";
import { LogSeverity } from "../../../data/integration";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { ConfigEntryExtended } from "./ha-config-integrations";
import "./ha-integration-header";

@customElement("ha-integration-card")
export class HaIntegrationCard extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property() public domain!: string;

  @property({ attribute: false }) public items!: ConfigEntryExtended[];

  @property({ attribute: false }) public manifest?: IntegrationManifest;

  @property({ attribute: false })
  public entityRegistryEntries!: EntityRegistryEntry[];

  @property({ attribute: false }) public logInfo?: IntegrationLogInfo;

  @property({ attribute: false }) public domainEntities: string[] = [];

  protected render(): TemplateResult {
    const entryState = this._getState(this.items);

    const debugLoggingEnabled =
      this.logInfo && this.logInfo.level === LogSeverity.DEBUG;

    return html`
      <ha-card
        outlined
        class=${classMap({
          "state-loaded": entryState === "loaded",
          "state-not-loaded": entryState === "not_loaded",
          "state-failed-unload": entryState === "failed_unload",
          "state-setup": entryState === "setup_in_progress",
          "state-error": ERROR_STATES.includes(entryState),
          "debug-logging": Boolean(debugLoggingEnabled),
        })}
      >
        <a
          href=${`/config/integrations/integration/${this.domain}`}
          class="ripple-anchor"
        >
          <ha-ripple></ha-ripple>
          <ha-integration-header
            .hass=${this.hass}
            .domain=${this.domain}
            .localizedDomainName=${this.items[0].localized_domain_name}
            .error=${
              ERROR_STATES.includes(entryState)
                ? this.hass.localize(
                    `ui.panel.config.integrations.config_entry.state.${entryState}`
                  )
                : undefined
            }
            .warning=${
              entryState !== "loaded" && !ERROR_STATES.includes(entryState)
                ? this.hass.localize(
                    `ui.panel.config.integrations.config_entry.state.${entryState}`
                  )
                : debugLoggingEnabled
                  ? this.hass.localize(
                      "ui.panel.config.integrations.config_entry.debug_logging_enabled"
                    )
                  : undefined
            }
            .manifest=${this.manifest}
          >
          </ha-integration-header>
        </a>

        ${this._renderSingleEntry()}
      </ha-card>
    `;
  }

  private _renderSingleEntry(): TemplateResult {
    const devices = this._getDevices(this.items, this.hass.devices);
    const entitiesCount = devices.length
      ? 0
      : this._getEntityCount(
          this.items,
          this.entityRegistryEntries,
          this.domainEntities
        );

    const services = !devices.some((device) => device.entry_type !== "service");

    return html`
      <div class="card-actions">
        ${
          devices.length > 0
            ? html`<ha-button
                appearance="plain"
                href=${
                  devices.length === 1 &&
                  // Always link to device page for protocol integrations to show Add Device button
                  // @ts-expect-error
                  !PROTOCOL_INTEGRATIONS.includes(this.domain)
                    ? `/config/devices/device/${devices[0].id}`
                    : `/config/devices/dashboard?historyBack=1&domain=${this.domain}`
                }
              >
                ${this.hass.localize(
                  `ui.panel.config.integrations.config_entry.${
                    services ? "services" : "devices"
                  }`,
                  { count: devices.length }
                )}
              </ha-button>`
            : entitiesCount > 0
              ? html`<ha-button
                  appearance="plain"
                  href=${`/config/entities?historyBack=1&domain=${this.domain}`}
                >
                  ${this.hass.localize(
                    `ui.panel.config.integrations.config_entry.entities`,
                    { count: entitiesCount }
                  )}
                </ha-button>`
              : this.items.find((itm) => itm.source !== "yaml")
                ? html`<ha-button
                    appearance="plain"
                    href=${`/config/integrations/integration/${this.domain}`}
                  >
                    ${this.hass.localize(
                      `ui.panel.config.integrations.config_entry.entries`,
                      {
                        count: this.items.filter((itm) => itm.source !== "yaml")
                          .length,
                      }
                    )}
                  </ha-button>`
                : html`<div class="spacer"></div>`
        }
        <div class="icons">
          ${
            this.manifest && !this.manifest.is_built_in
              ? html`<span
                  class="icon ${
                    this.manifest.overwrites_built_in ? "overwrites" : "custom"
                  }"
                >
                  <ha-svg-icon
                    id="icon-custom"
                    .path=${mdiPackageVariant}
                  ></ha-svg-icon>
                  <ha-tooltip
                    for="icon-custom"
                    .placement=${
                      computeRTL(
                        this.hass.language,
                        this.hass.translationMetadata.translations
                      )
                        ? "right"
                        : "left"
                    }
                  >
                    ${this.hass.localize(
                      this.manifest.overwrites_built_in
                        ? "ui.panel.config.integrations.config_entry.custom_overwrites_core"
                        : "ui.panel.config.integrations.config_entry.custom_integration"
                    )}
                  </ha-tooltip>
                </span>`
              : nothing
          }
          ${
            this.manifest && this.manifest.iot_class?.startsWith("cloud_")
              ? html`<div class="icon cloud">
                  <ha-svg-icon id="icon-cloud" .path=${mdiWeb}></ha-svg-icon>
                  <ha-tooltip
                    for="icon-cloud"
                    .placement=${
                      computeRTL(
                        this.hass.language,
                        this.hass.translationMetadata.translations
                      )
                        ? "right"
                        : "left"
                    }
                  >
                    ${this.hass.localize(
                      "ui.panel.config.integrations.config_entry.depends_on_cloud"
                    )}
                  </ha-tooltip>
                </div>`
              : nothing
          }
          ${
            this.manifest &&
            !this.manifest?.config_flow &&
            !this.items.every((itm) => itm.source === "system")
              ? html`<div class="icon yaml">
                  <ha-svg-icon
                    id="icon-yaml"
                    .path=${mdiFileCodeOutline}
                  ></ha-svg-icon>
                  <ha-tooltip
                    for="icon-yaml"
                    .placement=${
                      computeRTL(
                        this.hass.language,
                        this.hass.translationMetadata.translations
                      )
                        ? "right"
                        : "left"
                    }
                  >
                    ${this.hass.localize(
                      "ui.panel.config.integrations.config_entry.no_config_flow"
                    )}
                  </ha-tooltip>
                </div>`
              : nothing
          }
        </div>
      </div>
    `;
  }

  private _getState = memoizeOne(
    (configEntry: ConfigEntry[]): ConfigEntry["state"] => {
      if (configEntry.length === 1) {
        return configEntry[0].state;
      }
      let entryState: ConfigEntry["state"];
      for (const entry of configEntry) {
        if (ERROR_STATES.includes(entry.state)) {
          return entry.state;
        }
        entryState = entry.state;
      }
      return entryState!;
    }
  );

  private _getEntityCount = memoizeOne(
    (
      configEntry: ConfigEntry[],
      entityRegistryEntries: EntityRegistryEntry[],
      domainEntities: string[]
    ): number => {
      if (!entityRegistryEntries) {
        return domainEntities.length;
      }

      const entryIds = configEntry
        .map((entry) => entry.entry_id)
        .filter(Boolean);

      if (!entryIds.length) {
        return domainEntities.length;
      }

      const entityRegEntities = entityRegistryEntries.filter(
        (entity) =>
          entity.config_entry_id && entryIds.includes(entity.config_entry_id)
      );

      if (entityRegEntities.length === domainEntities.length) {
        return domainEntities.length;
      }

      const entityIds = new Set<string>(
        entityRegEntities.map((reg) => reg.entity_id)
      );

      for (const entity of domainEntities) {
        entityIds.add(entity);
      }

      return entityIds.size;
    }
  );

  private _getDevices = memoizeOne(
    (
      configEntry: ConfigEntry[],
      deviceRegistryEntries: HomeAssistant["devices"]
    ): DeviceRegistryEntry[] => {
      if (!deviceRegistryEntries) {
        return [];
      }
      const entryIds = configEntry.map((entry) => entry.entry_id);
      return Object.values(deviceRegistryEntries).filter((device) =>
        device.config_entries.some((entryId) => entryIds.includes(entryId))
      );
    }
  );

  static get styles(): CSSResultGroup {
    return [
      haStyle,
      css`
        ha-card {
          display: flex;
          flex-direction: column;
          justify-content: space-between;
          height: 100%;
          overflow: hidden;
          --state-color: var(--divider-color, #e0e0e0);
          --state-message-color: var(--state-color);
        }
        .ripple-anchor {
          flex-grow: 1;
          position: relative;
          outline: none;
        }
        .ripple-anchor:focus-visible:before {
          position: absolute;
          display: block;
          content: "";
          inset: 0;
          background-color: var(--secondary-text-color);
          opacity: 0.08;
        }
        ha-integration-header {
          height: 100%;
        }
        .card-actions {
          display: flex;
          align-items: center;
          justify-content: space-between;
        }
        .debug-logging {
          --state-color: var(--warning-color);
          --ha-card-border-color: var(--state-color);
          --text-on-state-color: var(--primary-text-color);
        }
        .state-error {
          --state-color: var(--error-color);
          --ha-card-border-color: var(--state-color);
          --text-on-state-color: var(--text-primary-color);
        }
        .state-failed-unload {
          --state-color: var(--warning-color);
          --ha-card-border-color: var(--state-color);
          --text-on-state-color: var(--primary-text-color);
        }
        .state-not-loaded {
          opacity: 0.8;
          --state-color: var(--warning-color);
          --ha-card-border-color: var(--state-color);
          --state-message-color: var(--primary-text-color);
        }
        .state-setup {
          opacity: 0.8;
          --state-message-color: var(--secondary-text-color);
        }
        :host(.highlight) ha-card {
          --state-color: var(--primary-color);
          --ha-card-border-color: var(--state-color);
          --text-on-state-color: var(--text-primary-color);
        }
        .content {
          flex: 1;
          --mdc-list-side-padding-right: 20px;
          --mdc-list-side-padding-left: 24px;
          --mdc-list-item-graphic-margin: 24px;
        }
        a {
          text-decoration: none;
          color: var(--primary-text-color);
        }
        .icons {
          display: flex;
        }
        .icon {
          color: var(--label-badge-grey);
          padding: 4px;
          margin-left: 8px;
          margin-inline-start: 8px;
          margin-inline-end: initial;
        }
        .icon.custom {
          color: var(--warning-color);
        }
        .icon.overwrites {
          color: var(--error-color);
        }
        .icon ha-svg-icon {
          width: 24px;
          height: 24px;
          display: block;
        }
        .spacer {
          height: 36px;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-integration-card": HaIntegrationCard;
  }
}
