import { mdiDotsVertical, mdiDownload, mdiRefresh, mdiText } from "@mdi/js";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-spinner";
import { getSignedPath } from "../../../data/auth";
import { getErrorLogDownloadUrl } from "../../../data/error_log";
import { domainToName } from "../../../data/integration";
import type { LoggedError } from "../../../data/system_log";
import {
  fetchSystemLog,
  getLoggedErrorIntegration,
  isCustomIntegrationError,
} from "../../../data/system_log";
import type { HomeAssistant } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
import { formatSystemLogTime } from "./util";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";

@customElement("system-log-card")
export class SystemLogCard extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property() public filter = "";

  @property() public header?: string;

  public loaded = false;

  @state() private _items?: LoggedError[];

  public async fetchData(): Promise<void> {
    this._items = undefined;
    this._items = await fetchSystemLog(this.hass!);
  }

  private _timestamp(item: LoggedError): string {
    return formatSystemLogTime(
      item.timestamp,
      this.hass.locale,
      this.hass.config
    );
  }

  private _multipleMessages(item: LoggedError): string {
    return this.hass.localize("ui.panel.config.logs.multiple_messages", {
      time: formatSystemLogTime(
        item.first_occurred,
        this.hass.locale,
        this.hass.config
      ),
      counter: item.count,
    });
  }

  private _getFilteredItems = memoizeOne(
    (localize: LocalizeFunc, items: LoggedError[], filter: string) =>
      items.filter((item: LoggedError) => {
        if (filter) {
          const integration = getLoggedErrorIntegration(item);
          return (
            item.message.some((message: string) =>
              message.toLowerCase().includes(filter)
            ) ||
            item.source[0].toLowerCase().includes(filter) ||
            item.name.toLowerCase().includes(filter) ||
            (integration &&
              domainToName(localize, integration)
                .toLowerCase()
                .includes(filter)) ||
            this._timestamp(item).toLowerCase().includes(filter) ||
            this._multipleMessages(item).toLowerCase().includes(filter)
          );
        }
        return item;
      })
  );

  protected render() {
    const filteredItems = this._items
      ? this._getFilteredItems(
          this.hass.localize,
          this._items,
          this.filter.toLowerCase()
        )
      : [];
    const integrations = filteredItems.length
      ? filteredItems.map((item) => getLoggedErrorIntegration(item))
      : [];
    return html`
      <div class="system-log-intro">
        <ha-card outlined>
          ${this._items === undefined
            ? html`
                <div class="loading-container">
                  <ha-spinner></ha-spinner>
                </div>
              `
            : html`
                <div class="header">
                  <h1 class="card-header">${this.header || "Logs"}</h1>
                  <div class="header-buttons">
                    <ha-icon-button
                      .path=${mdiDownload}
                      @click=${this._downloadLogs}
                      .label=${this.hass.localize(
                        "ui.panel.config.logs.download_logs"
                      )}
                    ></ha-icon-button>
                    <ha-icon-button
                      .path=${mdiRefresh}
                      @click=${this.fetchData}
                      .label=${this.hass.localize("ui.common.refresh")}
                    ></ha-icon-button>

                    <ha-dropdown @wa-select=${this._handleOverflowAction}>
                      <ha-icon-button
                        slot="trigger"
                        .path=${mdiDotsVertical}
                        .label=${this.hass.localize("ui.common.menu")}
                      ></ha-icon-button>
                      <ha-dropdown-item value="show-full-logs">
                        <ha-svg-icon slot="icon" .path=${mdiText}></ha-svg-icon>
                        ${this.hass.localize(
                          "ui.panel.config.logs.show_full_logs"
                        )}
                      </ha-dropdown-item>
                    </ha-dropdown>
                  </div>
                </div>
                ${this._items.length === 0
                  ? html`
                      <div class="card-content empty-content">
                        ${this.hass.localize("ui.panel.config.logs.no_issues")}
                      </div>
                    `
                  : filteredItems.length === 0 && this.filter
                    ? html`<div class="card-content">
                        ${this.hass.localize(
                          "ui.panel.config.logs.no_issues_search",
                          { term: this.filter }
                        )}
                      </div>`
                    : html`<ha-list
                        >${filteredItems.map(
                          (item, idx) => html`
                            <ha-list-item
                              @click=${this._openLog}
                              .logItem=${item}
                              twoline
                            >
                              ${item.message[0]}
                              <span slot="secondary" class="secondary">
                                ${this._timestamp(item)} –
                                ${html`(<span class=${item.level}
                                    >${this.hass.localize(
                                      `ui.panel.config.logs.level.${item.level}`
                                    )}</span
                                  >) `}
                                ${integrations[idx]
                                  ? `${domainToName(
                                      this.hass!.localize,
                                      integrations[idx]!
                                    )}${
                                      isCustomIntegrationError(item)
                                        ? ` (${this.hass.localize(
                                            "ui.panel.config.logs.custom_integration"
                                          )})`
                                        : ""
                                    }`
                                  : item.source[0]}
                                ${item.count > 1
                                  ? html` - ${this._multipleMessages(item)} `
                                  : nothing}
                              </span>
                            </ha-list-item>
                          `
                        )}</ha-list
                      >`}

                <div class="card-actions">
                  <ha-call-service-button domain="system_log" service="clear"
                    >${this.hass.localize(
                      "ui.panel.config.logs.clear"
                    )}</ha-call-service-button
                  >
                </div>
              `}
        </ha-card>
      </div>
    `;
  }

  protected firstUpdated(changedProps: PropertyValues<this>): void {
    super.firstUpdated(changedProps);
    this.fetchData();
    this.loaded = true;
    this.addEventListener("hass-service-called", (ev) =>
      this.serviceCalled(ev)
    );
  }

  protected serviceCalled(ev): void {
    // Check if this is for us
    if (ev.detail.success && ev.detail.domain === "system_log") {
      // Do the right thing depending on service
      if (ev.detail.service === "clear") {
        this._items = [];
      }
    }
  }

  private _handleOverflowAction(ev: HaDropdownSelectEvent) {
    if (ev.detail.item.value === "show-full-logs") {
      // @ts-ignore
      fireEvent(this, "switch-log-view");
    }
  }

  private async _downloadLogs() {
    const timeString = new Date().toISOString().replace(/:/g, "-");
    const downloadUrl = getErrorLogDownloadUrl(this.hass);
    const logFileName = `home-assistant_${timeString}.log`;
    const signedUrl = await getSignedPath(this.hass, downloadUrl);
    fileDownload(signedUrl.path, logFileName);
  }

  private _openLog(ev: Event): void {
    const item = (ev.currentTarget as any).logItem;
    showSystemLogDetailDialog(this, { item });
  }

  static styles = css`
    ha-card {
      padding-top: 8px;
    }

    :host {
      direction: var(--direction);
    }
    ha-list {
      direction: ltr;
    }

    .header {
      display: flex;
      justify-content: space-between;
      padding: 0 16px;
    }

    .header-buttons {
      display: flex;
      align-items: flex-start;
    }

    .card-header {
      color: var(--ha-card-header-color, var(--primary-text-color));
      font-family: var(--ha-card-header-font-family, inherit);
      font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
      letter-spacing: -0.012em;
      line-height: var(--ha-line-height-expanded);
      display: block;
      margin-block-start: 0px;
      font-weight: var(--ha-font-weight-normal);
    }

    .system-log-intro {
      margin: 16px;
    }

    .loading-container {
      height: 100px;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .error {
      color: var(--error-color);
    }

    .warning {
      color: var(--warning-color);
    }

    .card-content {
      border-top: 1px solid var(--divider-color);
      padding-top: 16px;
      padding-bottom: 16px;
    }

    .row-secondary {
      text-align: left;
    }
  `;
}

declare global {
  interface HTMLElementTagNameMap {
    "system-log-card": SystemLogCard;
  }
}
