import { mdiContentCopy } from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/ha-alert";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-dialog";
import type { IntegrationManifest } from "../../../data/integration";
import {
  domainToName,
  fetchIntegrationManifest,
  integrationIssuesUrl,
} from "../../../data/integration";
import {
  getLoggedErrorIntegration,
  isCustomIntegrationError,
} from "../../../data/system_log";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
import { formatSystemLogTime } from "./util";

/** Compares the host, so a URL that merely contains ours does not pass. */
const isOfficialDocumentationUrl = (url: string): boolean => {
  try {
    return new URL(url).hostname === "www.home-assistant.io";
  } catch (_err) {
    return false;
  }
};

@customElement("dialog-system-log-detail")
class DialogSystemLogDetail extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @state() private _params?: SystemLogDetailDialogParams;

  @state() private _manifest?: IntegrationManifest;

  @state() private _open = false;

  @query(".contents") private _contents?: HTMLElement;

  public async showDialog(params: SystemLogDetailDialogParams): Promise<void> {
    this._params = params;
    this._manifest = undefined;
    this._open = true;
    await this.updateComplete;
  }

  public closeDialog() {
    this._open = false;
  }

  private _dialogClosed() {
    this._params = undefined;
    fireEvent(this, "dialog-closed", { dialog: this.localName });
  }

  protected updated(changedProps: PropertyValues) {
    super.updated(changedProps);
    if (!changedProps.has("_params") || !this._params) {
      return;
    }
    const integration = getLoggedErrorIntegration(this._params.item);
    if (integration) {
      this._fetchManifest(integration);
    }
  }

  protected render() {
    if (!this._params) {
      return nothing;
    }
    const item = this._params.item;

    const integration = getLoggedErrorIntegration(item);

    const showDocumentation =
      this._manifest &&
      (this._manifest.is_built_in ||
        // Custom components with our official docs should not link to our docs
        (!!this._manifest.documentation &&
          !isOfficialDocumentationUrl(this._manifest.documentation)));

    const documentationLink = this._manifest?.is_built_in
      ? documentationUrl(this.hass, `/integrations/${this._manifest.domain}`)
      : this._manifest?.documentation;

    const title = this.hass.localize("ui.panel.config.logs.details", {
      level: html`<span class=${item.level}
        >${this.hass.localize(`ui.panel.config.logs.level.${item.level}`)}</span
      >`,
    });

    return html`
      <ha-dialog
        .open=${this._open}
        width="large"
        @closed=${this._dialogClosed}
      >
        <span slot="headerTitle">${title}</span>
        <ha-icon-button
          id="copy"
          @click=${this._copyLog}
          slot="headerActionItems"
          .label=${this.hass.localize("ui.panel.config.logs.copy")}
          .path=${mdiContentCopy}
        ></ha-icon-button>
        ${
          this.isCustomIntegration
            ? html`<ha-alert alert-type="warning">
                ${this.hass.localize(
                  "ui.panel.config.logs.error_from_custom_integration"
                )}
              </ha-alert>`
            : ""
        }
        <div class="contents" tabindex="-1" autofocus>
          <p>
            ${this.hass.localize("ui.panel.config.logs.detail.logger")}:
            ${item.name}<br />
            ${this.hass.localize("ui.panel.config.logs.detail.source")}:
            ${item.source.join(":")}
            ${
              integration
                ? html`
                    <br />
                    ${this.hass.localize(
                      "ui.panel.config.logs.detail.integration"
                    )}:
                    ${domainToName(this.hass.localize, integration)}
                    ${
                      !this._manifest ||
                      // Can happen with custom integrations
                      !showDocumentation ||
                      !documentationLink
                        ? ""
                        : html`
                            (<a
                              href=${documentationLink}
                              target="_blank"
                              rel="noreferrer"
                              >${this.hass.localize(
                                "ui.panel.config.logs.detail.documentation"
                              )}</a
                            >${
                              this._manifest.is_built_in ||
                              this._manifest.issue_tracker
                                ? html`,
                                    <a
                                      href=${integrationIssuesUrl(
                                        integration,
                                        this._manifest
                                      )}
                                      target="_blank"
                                      rel="noreferrer"
                                      >${this.hass.localize(
                                        "ui.panel.config.logs.detail.issues"
                                      )}</a
                                    >`
                                : ""
                            })
                          `
                    }
                  `
                : ""
            }
            <br />
            ${
              item.count > 0
                ? html`
                    ${this.hass.localize(
                      "ui.panel.config.logs.detail.first_occurred"
                    )}:
                    ${formatSystemLogTime(
                      item.first_occurred,
                      this.hass!.locale,
                      this.hass!.config
                    )}
                    (${this.hass.localize(
                      "ui.panel.config.logs.detail.number_of_occurrences",
                      {
                        count: item.count,
                      }
                    )}) <br />
                  `
                : ""
            }
            ${this.hass.localize("ui.panel.config.logs.detail.last_logged")}:
            ${formatSystemLogTime(
              item.timestamp,
              this.hass!.locale,
              this.hass!.config
            )}
          </p>
          ${
            item.message.length > 1
              ? html`
                  <ul>
                    ${item.message.map((msg) => html` <li>${msg}</li> `)}
                  </ul>
                `
              : item.message[0]
          }
          ${item.exception ? html` <pre>${item.exception}</pre> ` : nothing}
        </div>
      </ha-dialog>
    `;
  }

  private get isCustomIntegration(): boolean {
    return this._manifest
      ? !this._manifest.is_built_in
      : isCustomIntegrationError(this._params!.item);
  }

  private async _fetchManifest(integration: string) {
    try {
      this._manifest = await fetchIntegrationManifest(this.hass, integration);
    } catch (_err: any) {
      // Ignore if loading manifest fails. Probably bad JSON in manifest
    }
  }

  private async _copyLog(): Promise<void> {
    const copyElement = this._contents!;

    let text = copyElement.innerText;

    if (this.isCustomIntegration) {
      text =
        this.hass.localize(
          "ui.panel.config.logs.error_from_custom_integration"
        ) +
        "\n\n" +
        text;
    }

    await copyToClipboard(text);
    showToast(this, {
      message: this.hass.localize("ui.common.copied_clipboard"),
    });
  }

  static get styles(): CSSResultGroup {
    return [
      haStyleDialog,
      css`
        ha-dialog {
          --dialog-content-padding: 0px;
        }

        a {
          color: var(--primary-color);
        }
        p {
          margin-top: 0;
        }
        pre {
          margin-bottom: 0;
          font-family: var(--ha-font-family-code);
        }
        ha-alert {
          display: block;
          margin: -4px 0;
        }
        .contents {
          padding: 16px;
          outline: none;
          direction: ltr;
        }
        .error {
          color: var(--error-color);
        }
        .warning {
          color: var(--warning-color);
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "dialog-system-log-detail": DialogSystemLogDetail;
  }
}
