import "@home-assistant/webawesome/dist/components/divider/divider";
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item-base";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { fireEvent } from "../../../common/dom/fire_event";
import {
  PROTOCOL_INTEGRATIONS,
  protocolIntegrationPicked,
} from "../../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../../common/navigate";
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import "../../../components/item/ha-list-item-button";
import "../../../components/list/ha-list-base";
import { localizeConfigFlowTitle } from "../../../data/config_flow";
import type { DataEntryFlowProgress } from "../../../data/data_entry_flow";
import {
  domainToName,
  fetchIntegrationManifest,
} from "../../../data/integration";
import type { Brand, Integration } from "../../../data/integrations";
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
import "./ha-integration-list-item";
import { showYamlIntegrationDialog } from "./show-add-integration-dialog";

const standardToDomain = { zigbee: "zha", zwave: "zwave_js" } as const;

@customElement("ha-domain-integrations")
class HaDomainIntegrations extends LitElement {
  public hass!: HomeAssistant;

  @property() public domain!: string;

  @property({ attribute: false }) public integration?: Brand | Integration;

  @property({ attribute: false })
  public flowsInProgress?: DataEntryFlowProgress[];

  @property({ attribute: false })
  public navigateToResult = false;

  @property({ attribute: false })
  public showManageLink = false;

  protected render() {
    return html`<ha-list-base>
      ${
        this.flowsInProgress?.length
          ? html`<h3>
                ${this.hass.localize("ui.panel.config.integrations.discovered")}
              </h3>
              ${this.flowsInProgress.map(
                (flow) =>
                  html`<ha-list-item-button
                    .flow=${flow}
                    @click=${this._flowInProgressPicked}
                  >
                    <img
                      alt=""
                      slot="start"
                      loading="lazy"
                      src=${brandsUrl(
                        {
                          domain: flow.handler,
                          type: "icon",
                          darkOptimized: this.hass.themes?.darkMode,
                        },
                        this.hass.auth.data.hassUrl
                      )}
                      crossorigin="anonymous"
                      referrerpolicy="no-referrer"
                    />
                    <span slot="headline"
                      >${localizeConfigFlowTitle(this.hass.localize, flow)}</span
                    >
                    <span slot="supporting-text"
                      >${domainToName(this.hass.localize, flow.handler)}</span
                    >
                    <ha-icon-next slot="end"></ha-icon-next>
                  </ha-list-item-button>`
              )}
              <wa-divider></wa-divider>
              ${
                this.integration &&
                "integrations" in this.integration &&
                this.integration.integrations
                  ? html`<h3>
                      ${this.hass.localize(
                        "ui.panel.config.integrations.available_integrations"
                      )}
                    </h3>`
                  : nothing
              }`
          : nothing
      }
      ${
        this.integration?.iot_standards
          ? this.integration.iot_standards
              .filter((standard) =>
                (PROTOCOL_INTEGRATIONS as readonly string[]).includes(
                  standardToDomain[standard] || standard
                )
              )
              .map((standard) => {
                const domain: (typeof PROTOCOL_INTEGRATIONS)[number] =
                  standardToDomain[standard] || standard;
                return html`<ha-list-item-button
                  .domain=${domain}
                  @click=${this._standardPicked}
                >
                  <img
                    slot="start"
                    loading="lazy"
                    alt=""
                    src=${brandsUrl(
                      {
                        domain,
                        type: "icon",
                        darkOptimized: this.hass.themes?.darkMode,
                      },
                      this.hass.auth.data.hassUrl
                    )}
                    crossorigin="anonymous"
                    referrerpolicy="no-referrer"
                  />
                  <span slot="headline"
                    >${this.hass.localize(
                      `ui.panel.config.integrations.add_${domain}_device`
                    )}</span
                  >
                  <ha-icon-next slot="end"></ha-icon-next>
                </ha-list-item-button>`;
              })
          : ""
      }
      ${
        this.integration &&
        "integrations" in this.integration &&
        this.integration.integrations
          ? Object.entries(this.integration.integrations)
              .filter(([, val]) => val.integration_type !== "hardware")
              .sort((a, b) => {
                if (a[1].config_flow && !b[1].config_flow) {
                  return -1;
                }
                if (b[1].config_flow && !a[1].config_flow) {
                  return 0;
                }
                return caseInsensitiveStringCompare(
                  a[1].name || domainToName(this.hass.localize, a[0]),
                  b[1].name || domainToName(this.hass.localize, b[0]),
                  this.hass.locale.language
                );
              })
              .map(
                ([dom, val]) =>
                  html`<ha-integration-list-item
                    .domain=${dom}
                    .integration=${{
                      ...val,
                      domain: dom,
                      name: val.name || domainToName(this.hass.localize, dom),
                      is_built_in: val.is_built_in !== false,
                      cloud: val.iot_class?.startsWith("cloud_"),
                    }}
                    @click=${this._integrationPicked}
                  >
                  </ha-integration-list-item>`
              )
          : ""
      }
      ${
        (PROTOCOL_INTEGRATIONS as readonly string[]).includes(this.domain)
          ? html`<ha-list-item-button
              .domain=${this.domain}
              @click=${this._standardPicked}
            >
              <img
                slot="start"
                loading="lazy"
                alt=""
                src=${brandsUrl(
                  {
                    domain: this.domain,
                    type: "icon",
                    darkOptimized: this.hass.themes?.darkMode,
                  },
                  this.hass.auth.data.hassUrl
                )}
                crossorigin="anonymous"
                referrerpolicy="no-referrer"
              />
              <span slot="headline"
                >${this.hass.localize(
                  `ui.panel.config.integrations.add_${
                    this.domain as (typeof PROTOCOL_INTEGRATIONS)[number]
                  }_device`
                )}</span
              >
              <ha-icon-next slot="end"></ha-icon-next>
            </ha-list-item-button>`
          : ""
      }
      ${
        this.integration &&
        "config_flow" in this.integration &&
        this.integration.config_flow
          ? html`${
              this.flowsInProgress?.length
                ? html`<ha-list-item-button
                    .domain=${this.domain}
                    @click=${this._integrationPicked}
                    .integration=${{
                      ...this.integration,
                      domain: this.domain,
                      name:
                        this.integration.name ||
                        domainToName(this.hass.localize, this.domain),
                      is_built_in: this.integration.is_built_in !== false,
                      cloud: this.integration.iot_class?.startsWith("cloud_"),
                    }}
                  >
                    <span slot="headline">
                      ${this.hass.localize(
                        "ui.panel.config.integrations.new_flow",
                        {
                          integration:
                            this.integration.name ||
                            domainToName(this.hass.localize, this.domain),
                        }
                      )}
                    </span>
                    <ha-icon-next slot="end"></ha-icon-next>
                  </ha-list-item-button>`
                : html`<ha-integration-list-item
                    .domain=${this.domain}
                    .integration=${{
                      ...this.integration,
                      domain: this.domain,
                      name:
                        this.integration.name ||
                        domainToName(this.hass.localize, this.domain),
                      is_built_in: this.integration.is_built_in !== false,
                      cloud: this.integration.iot_class?.startsWith("cloud_"),
                    }}
                    @click=${this._integrationPicked}
                  >
                  </ha-integration-list-item>`
            }`
          : ""
      }
      ${
        this.showManageLink &&
        // Only show manage link if not already on the integrations dashboard
        !location.pathname.startsWith("/config/integrations")
          ? html`<ha-list-item-button @click=${this._manageDiscovered}>
              <span slot="headline"
                >${this.hass.localize(
                  "ui.panel.config.integrations.manage_discovered"
                )}</span
              >
              <span slot="supporting-text"
                >${this.hass.localize(
                  "ui.panel.config.integrations.manage_discovered_description"
                )}</span
              >
              <ha-icon-next slot="end"></ha-icon-next>
            </ha-list-item-button>`
          : nothing
      }
    </ha-list-base> `;
  }

  private async _integrationPicked(ev: CustomEvent<RequestSelectedDetail>) {
    const domain = (ev.currentTarget as any).domain;

    if (
      ["cloud", "google_assistant", "alexa"].includes(domain) &&
      isComponentLoaded(this.hass.config, "cloud")
    ) {
      navigate("/config/cloud");
      return;
    }

    const integration = (ev.currentTarget as any).integration;

    if (integration.supported_by) {
      // @ts-ignore
      fireEvent(this, "supported-by", { integration });
      return;
    }

    if (integration.iot_standards) {
      // @ts-ignore
      fireEvent(this, "select-brand", {
        brand: integration.domain,
      });
      return;
    }

    if (
      (domain === this.domain &&
        (("integration_type" in this.integration! &&
          !this.integration.config_flow) ||
          (!("integration_type" in this.integration!) &&
            (!this.integration!.integrations ||
              !(domain in this.integration!.integrations))))) ||
      // config_flow being undefined means its false
      (!("integration_type" in this.integration!) &&
        !this.integration!.integrations?.[domain]?.config_flow)
    ) {
      const manifest = await fetchIntegrationManifest(this.hass, domain);
      showYamlIntegrationDialog(this, { manifest });
      return;
    }

    const root = this.getRootNode();
    showConfigFlowDialog(
      root instanceof ShadowRoot ? (root.host as HTMLElement) : this,
      {
        startFlowHandler: domain,
        navigateToResult: this.navigateToResult,
        manifest: await fetchIntegrationManifest(this.hass, domain),
      }
    );
    fireEvent(this, "close-dialog");
  }

  private async _flowInProgressPicked(ev: Event) {
    const flow: DataEntryFlowProgress = (ev.currentTarget as any).flow;
    const root = this.getRootNode();
    showConfigFlowDialog(
      root instanceof ShadowRoot ? (root.host as HTMLElement) : this,
      {
        continueFlowId: flow.flow_id,
        navigateToResult: this.navigateToResult,
        manifest: await fetchIntegrationManifest(this.hass, flow.handler),
      }
    );
    fireEvent(this, "close-dialog");
  }

  private _manageDiscovered() {
    fireEvent(this, "close-dialog");
    navigate("/config/integrations/dashboard?historyBack=1");
  }

  private _standardPicked(ev: CustomEvent<RequestSelectedDetail>) {
    const domain = (ev.currentTarget as any).domain;
    const root = this.getRootNode();
    fireEvent(this, "close-dialog");
    protocolIntegrationPicked(
      root instanceof ShadowRoot ? (root.host as HTMLElement) : this,
      this.hass,
      domain,
      { brand: this.domain }
    );
  }

  static styles = [
    haStyle,
    css`
      :host {
        display: block;
        --ha-row-item-padding-inline: var(--ha-space-6);
      }
      h3 {
        margin: var(--ha-space-2) var(--ha-space-6) 0;
        color: var(--secondary-text-color);
        font-size: var(--ha-font-size-m);
        font-weight: var(--ha-font-weight-medium);
      }
      h3:first-of-type {
        margin-top: 0;
      }
      img {
        width: 32px;
        height: 32px;
      }
      wa-divider {
        margin-top: var(--ha-space-2);
      }
      ha-icon-next {
        color: var(--ha-color-text-secondary);
      }
    `,
  ];
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-domain-integrations": HaDomainIntegrations;
  }
}
