import { mdiOpenInNew } from "@mdi/js";
import { css, html, nothing, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { extractSearchParam } from "../../src/common/url/search-params";
import "../../src/components/animation/ha-fade-in";
import "../../src/components/ha-alert";
import "../../src/components/ha-button";
import "../../src/components/ha-spinner";
import "../../src/components/ha-svg-icon";
import "../../src/components/progress/ha-progress-bar";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
import "../../src/onboarding/onboarding-welcome-links";
import { onBoardingStyles } from "../../src/onboarding/styles";
import { haStyle } from "../../src/resources/styles";
import "./components/landing-page-logs";
import "./components/landing-page-network";
import {
  getSupervisorJobsInfo,
  getSupervisorNetworkInfo,
  pingSupervisor,
  type NetworkInfo,
} from "./data/supervisor";
import { LandingPageBaseElement } from "./landing-page-base-element";

export const ASSUME_CORE_START_SECONDS = 60;
const SCHEDULE_CORE_CHECK_SECONDS = 1;
const SCHEDULE_FETCH_NETWORK_INFO_SECONDS = 5;
const SCHEDULE_FETCH_JOBS_INFO_SECONDS = 2;

@customElement("ha-landing-page")
class HaLandingPage extends LandingPageBaseElement {
  @property({ attribute: false }) public translationFragment = "landing-page";

  @state() private _supervisorError = false;

  @state() private _networkInfo?: NetworkInfo;

  @state() private _coreStatusChecked = false;

  @state() private _networkInfoError = false;

  @state() private _coreCheckActive = false;

  @state() private _progress = -1;

  private _mobileApp =
    extractSearchParam("redirect_uri") === "homeassistant://auth-callback";

  render() {
    const networkIssue = this._networkInfo && !this._networkInfo.host_internet;

    if (!this.localize) {
      return html`
        <ha-fade-in>
          <ha-spinner size="large"></ha-spinner>
        </ha-fade-in>
      `;
    }

    return html`
      <ha-card>
        <div class="card-content">
          <h1>${this.localize("header")}</h1>
          ${
            !networkIssue && !this._supervisorError
              ? html`
                  <p>${this.localize("subheader")}</p>
                  <ha-progress-bar
                    .indeterminate=${this._progress <= 0}
                    .value=${this._progress > 0 ? this._progress : undefined}
                    .loading=${this._progress >= 0}
                    >${
                      this._progress > 0
                        ? `${Math.round(this._progress)}%`
                        : nothing
                    }</ha-progress-bar
                  >
                `
              : nothing
          }
          ${
            networkIssue || this._networkInfoError
              ? html`
                  <landing-page-network
                    .localize=${this.localize}
                    .networkInfo=${this._networkInfo}
                    .error=${this._networkInfoError}
                    @dns-set=${this._fetchSupervisorInfo}
                  ></landing-page-network>
                `
              : nothing
          }
          ${
            this._supervisorError
              ? html`
                  <ha-alert
                    alert-type="error"
                    .title=${this.localize("error_title")}
                  >
                    ${this.localize("error_description")}
                  </ha-alert>
                `
              : nothing
          }
          <landing-page-logs
            .localize=${this.localize}
            @landing-page-error=${this._showError}
          ></landing-page-logs>
        </div>
      </ha-card>
      <onboarding-welcome-links
        .localize=${this.localize}
        .mobileApp=${this._mobileApp}
      ></onboarding-welcome-links>
      <div class="footer">
        <ha-language-picker
          .value=${this.language}
          .label=${""}
          button-style
          native-name
          @value-changed=${this._languageChanged}
        ></ha-language-picker>
        <ha-button
          appearance="plain"
          variant="neutral"
          href="https://www.home-assistant.io/getting-started/onboarding/"
          target="_blank"
          rel="noreferrer noopener"
        >
          ${this.localize("ui.panel.page-onboarding.help")}
          <ha-svg-icon slot="end" .path=${mdiOpenInNew}></ha-svg-icon>
        </ha-button>
      </div>
    `;
  }

  protected firstUpdated(changedProps: PropertyValues<this>) {
    super.firstUpdated(changedProps);

    makeDialogManager(this);

    if (window.innerWidth > 450) {
      import("../../src/resources/particles");
    }
    import("../../src/components/ha-language-picker");

    this._fetchSupervisorInfo(true);
    this._fetchSupervisorJobsInfo();
  }

  private _scheduleFetchSupervisorInfo() {
    setTimeout(
      () => this._fetchSupervisorInfo(true),
      // on assumed core start check every second, otherwise every 5 seconds
      (this._coreCheckActive
        ? SCHEDULE_CORE_CHECK_SECONDS
        : SCHEDULE_FETCH_NETWORK_INFO_SECONDS) * 1000
    );
  }

  private _scheduleFetchSupervisorJobsInfo() {
    setTimeout(
      () => this._fetchSupervisorJobsInfo(),
      SCHEDULE_FETCH_JOBS_INFO_SECONDS * 1000
    );
  }

  private _scheduleTurnOffCoreCheck() {
    setTimeout(() => {
      this._coreCheckActive = false;
    }, ASSUME_CORE_START_SECONDS * 1000);
  }

  private async _fetchSupervisorInfo(schedule = false) {
    try {
      const response = await pingSupervisor();
      if (!response.ok) {
        throw new Error("ping-failed");
      }

      this._networkInfo = await getSupervisorNetworkInfo();
      this._networkInfoError = false;
      this._coreStatusChecked = false;
    } catch (err: any) {
      if (await this._checkCoreAvailability()) {
        // core is available, page reload in progress -> don't show an error
        return;
      }

      // assume supervisor update if ping fails -> don't show an error
      if (!this._coreCheckActive && err.message !== "ping-failed") {
        // eslint-disable-next-line no-console
        console.error("Failed to fetch supervisor info", err);
        this._networkInfoError = true;
      }
    }

    if (schedule) {
      this._scheduleFetchSupervisorInfo();
    }
  }

  private async _fetchSupervisorJobsInfo() {
    try {
      const jobsInfo = await getSupervisorJobsInfo();
      const coreInstallJob =
        jobsInfo.result === "ok"
          ? jobsInfo.data.jobs.find(
              (job) => job.name === "home_assistant_core_install"
            )
          : undefined;
      if (coreInstallJob) {
        this._progress = coreInstallJob.progress;
      } else {
        this._progress = -1;
      }
    } catch (err: any) {
      if (await this._checkCoreAvailability()) {
        // core is available, page reload in progress -> stop polling
        return;
      }

      if (!this._coreCheckActive) {
        this._progress = -1;
        // eslint-disable-next-line no-console
        console.error("Failed to fetch supervisor jobs info", err);
      }
    }

    this._scheduleFetchSupervisorJobsInfo();
  }

  private async _checkCoreAvailability(): Promise<boolean> {
    try {
      const response = await fetch("/manifest.json");
      if (!response.ok) {
        throw new Error("Failed to fetch manifest");
      }
      location.reload();
      return true;
    } catch (_err) {
      if (!this._coreStatusChecked) {
        // wait before showing errors, because we assume that core is starting
        this._coreStatusChecked = true;
        this._coreCheckActive = true;
        this._scheduleTurnOffCoreCheck();
      }
      return false;
    }
  }

  private _showError() {
    this._supervisorError = true;
  }

  private _languageChanged(ev: CustomEvent) {
    const language = ev.detail.value;
    if (language !== this.language && language) {
      this.language = language;
      try {
        window.localStorage.setItem(
          "selectedLanguage",
          JSON.stringify(language)
        );
      } catch (_err: any) {
        // Ignore
      }
    }
  }

  static styles = [
    haStyle,
    onBoardingStyles,
    css`
      .footer {
        padding-top: 8px;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      ha-card .card-content {
        display: flex;
        flex-direction: column;
        gap: var(--ha-space-4);
      }
      ha-alert p {
        text-align: unset;
      }
      .footer ha-svg-icon {
        --mdc-icon-size: var(--ha-space-5);
      }
      ha-language-picker {
        margin-inline-start: calc(-1 * var(--ha-space-4));
      }
      ha-button {
        margin-inline-end: calc(-1 * var(--ha-space-2));
      }
      ha-fade-in {
        min-height: calc(100vh - 64px - 88px);
        display: flex;
        justify-content: center;
        align-items: center;
      }
      ha-progress-bar {
        --ha-progress-bar-track-height: 20px;
      }
    `,
  ];
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-landing-page": HaLandingPage;
  }
}
