import {
  addHasRemoveClass,
  BaseElement,
} from "@material/mwc-base/base-element";
import { supportsPassiveEventListener } from "@material/mwc-base/utils";
import { MDCTopAppBarAdapter } from "@material/top-app-bar/adapter";
import { strings } from "@material/top-app-bar/constants";
import MDCFixedTopAppBarFoundation from "@material/top-app-bar/fixed/foundation";
import { html, css, nothing } from "lit";
import { property, query, customElement } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styles } from "@material/mwc-top-app-bar/mwc-top-app-bar.css";
import { haStyleScrollbar } from "../resources/styles";

export const passiveEventOptionsIfSupported = supportsPassiveEventListener
  ? { passive: true }
  : undefined;

@customElement("ha-two-pane-top-app-bar-fixed")
export class TopAppBarBaseBase extends BaseElement {
  protected override mdcFoundation!: MDCFixedTopAppBarFoundation;

  protected override mdcFoundationClass = MDCFixedTopAppBarFoundation;

  @query(".mdc-top-app-bar") protected mdcRoot!: HTMLElement;

  // _actionItemsSlot should have type HTMLSlotElement, but when TypeScript's
  // emitDecoratorMetadata is enabled, the HTMLSlotElement constructor will
  // be emitted into the runtime, which will cause an "HTMLSlotElement is
  // undefined" error in browsers that don't define it (e.g. IE11).
  @query('slot[name="actionItems"]') protected _actionItemsSlot!: HTMLElement;

  protected _scrollTarget!: HTMLElement | Window;

  @property({ type: Boolean }) centerTitle = false;

  @property({ type: Boolean, reflect: true }) prominent = false;

  @property({ type: Boolean, reflect: true }) dense = false;

  @property({ type: Boolean }) pane = false;

  @property({ type: Boolean }) footer = false;

  @query(".content") private _contentElement!: HTMLElement;

  @query(".pane .ha-scrollbar") private _paneElement?: HTMLElement;

  @property({ type: Object })
  get scrollTarget() {
    return this._scrollTarget || window;
  }

  set scrollTarget(value) {
    this.unregisterListeners();
    const old = this.scrollTarget;
    this._scrollTarget = value;
    this.updateRootPosition();
    this.requestUpdate("scrollTarget", old);
    this.registerListeners();
  }

  protected updateRootPosition() {
    if (this.mdcRoot) {
      const windowScroller = this.scrollTarget === window;
      // we add support for top-app-bar's tied to an element scroller.
      this.mdcRoot.style.position = windowScroller ? "" : "absolute";
    }
  }

  protected barClasses() {
    return {
      "mdc-top-app-bar--dense": this.dense,
      "mdc-top-app-bar--prominent": this.prominent,
      "center-title": this.centerTitle,
      "mdc-top-app-bar--fixed": true,
      "mdc-top-app-bar--pane": this.pane,
    };
  }

  protected contentClasses() {
    return {
      "mdc-top-app-bar--fixed-adjust": !this.dense && !this.prominent,
      "mdc-top-app-bar--dense-fixed-adjust": this.dense && !this.prominent,
      "mdc-top-app-bar--prominent-fixed-adjust": !this.dense && this.prominent,
      "mdc-top-app-bar--dense-prominent-fixed-adjust":
        this.dense && this.prominent,
      "mdc-top-app-bar--pane": this.pane,
    };
  }

  protected override render() {
    const title = html`<span class="mdc-top-app-bar__title"
      ><slot name="title"></slot
    ></span>`;
    return html`
      <header class="mdc-top-app-bar ${classMap(this.barClasses())}">
        <div class="mdc-top-app-bar__row">
          ${this.pane
            ? html`<section
                class="mdc-top-app-bar__section mdc-top-app-bar__section--align-start"
                id="title"
              >
                <slot
                  name="navigationIcon"
                  @click=${this.handleNavigationClick}
                ></slot>
                ${title}
              </section>`
            : nothing}
          <section class="mdc-top-app-bar__section" id="navigation">
            ${this.pane
              ? nothing
              : html`<slot
                    name="navigationIcon"
                    @click=${this.handleNavigationClick}
                  ></slot
                  >${title}`}
          </section>
          <section
            class="mdc-top-app-bar__section mdc-top-app-bar__section--align-end"
            id="actions"
            role="toolbar"
          >
            <slot name="actionItems"></slot>
          </section>
        </div>
      </header>
      <div class=${classMap(this.contentClasses())}>
        ${this.pane
          ? html`<div class="pane">
              <div class="shadow-container"></div>
              <div class="ha-scrollbar">
                <slot name="pane"></slot>
              </div>
              ${this.footer
                ? html`<div class="footer">
                    <slot name="pane-footer"></slot>
                  </div>`
                : nothing}
            </div>`
          : nothing}
        <div class="main">
          ${this.pane ? html`<div class="shadow-container"></div>` : nothing}
          <div class="content">
            <slot></slot>
          </div>
        </div>
      </div>
    `;
  }

  protected updated(changedProperties) {
    super.updated(changedProperties);
    if (
      changedProperties.has("pane") &&
      changedProperties.get("pane") !== undefined
    ) {
      this.unregisterListeners();
      this.registerListeners();
    }
  }

  protected createAdapter(): MDCTopAppBarAdapter {
    return {
      ...addHasRemoveClass(this.mdcRoot),
      setStyle: (prprty: string, value: string) =>
        this.mdcRoot.style.setProperty(prprty, value),
      getTopAppBarHeight: () => this.mdcRoot.clientHeight,
      notifyNavigationIconClicked: () => {
        this.dispatchEvent(
          new Event(strings.NAVIGATION_EVENT, {
            bubbles: true,
            cancelable: true,
          })
        );
      },
      getViewportScrollY: () =>
        this.scrollTarget instanceof Window
          ? this.scrollTarget.pageYOffset
          : this.scrollTarget.scrollTop,
      getTotalActionItems: () =>
        (this._actionItemsSlot as HTMLSlotElement).assignedNodes({
          flatten: true,
        }).length,
    };
  }

  protected handleTargetScroll = () => {
    this.mdcFoundation.handleTargetScroll();
  };

  protected handlePaneScroll = (ev) => {
    if (ev.target.scrollTop > 0) {
      ev.target.parentElement.classList.add("scrolled");
    } else {
      ev.target.parentElement.classList.remove("scrolled");
    }
  };

  protected handleNavigationClick = () => {
    this.mdcFoundation.handleNavigationClick();
  };

  protected registerListeners() {
    if (this.pane) {
      this._paneElement!.addEventListener(
        "scroll",
        this.handlePaneScroll,
        passiveEventOptionsIfSupported
      );
      this._contentElement.addEventListener(
        "scroll",
        this.handlePaneScroll,
        passiveEventOptionsIfSupported
      );
      return;
    }
    this.scrollTarget.addEventListener(
      "scroll",
      this.handleTargetScroll,
      passiveEventOptionsIfSupported
    );
  }

  protected unregisterListeners() {
    this._paneElement?.removeEventListener("scroll", this.handlePaneScroll);
    this._contentElement.removeEventListener("scroll", this.handlePaneScroll);
    this.scrollTarget.removeEventListener("scroll", this.handleTargetScroll);
  }

  protected override firstUpdated() {
    super.firstUpdated();
    this.updateRootPosition();
    this.registerListeners();
  }

  override disconnectedCallback() {
    super.disconnectedCallback();
    this.unregisterListeners();
  }

  static override styles = [
    styles,
    haStyleScrollbar,
    css`
      .mdc-top-app-bar__row {
        height: var(--header-height);
        border-bottom: var(--app-header-border-bottom);
      }
      .mdc-top-app-bar--fixed-adjust {
        padding-top: var(--header-height);
      }
      .shadow-container {
        position: absolute;
        top: calc(-1 * var(--header-height));
        width: 100%;
        height: var(--header-height);
        z-index: 1;
        transition: box-shadow 200ms linear;
      }
      .scrolled .shadow-container {
        box-shadow: var(
          --mdc-top-app-bar-fixed-box-shadow,
          0px 2px 4px -1px rgba(0, 0, 0, 0.2),
          0px 4px 5px 0px rgba(0, 0, 0, 0.14),
          0px 1px 10px 0px rgba(0, 0, 0, 0.12)
        );
      }
      .mdc-top-app-bar {
        --mdc-typography-headline6-font-weight: 400;
        color: var(--app-header-text-color, var(--mdc-theme-on-primary, #fff));
        background-color: var(
          --app-header-background-color,
          var(--mdc-theme-primary)
        );
      }
      .mdc-top-app-bar--pane.mdc-top-app-bar--fixed-scrolled {
        box-shadow: none;
      }
      #title {
        border-right: 1px solid rgba(255, 255, 255, 0.12);
        border-inline-end: 1px solid rgba(255, 255, 255, 0.12);
        border-inline-start: initial;
        box-sizing: border-box;
        flex: 0 0 var(--sidepane-width, 250px);
        width: var(--sidepane-width, 250px);
      }
      div.mdc-top-app-bar--pane {
        display: flex;
        height: calc(100vh - var(--header-height));
      }
      .pane {
        border-right: 1px solid var(--divider-color);
        border-inline-end: 1px solid var(--divider-color);
        border-inline-start: initial;
        box-sizing: border-box;
        display: flex;
        flex: 0 0 var(--sidepane-width, 250px);
        width: var(--sidepane-width, 250px);
        flex-direction: column;
        position: relative;
      }
      .pane .ha-scrollbar {
        flex: 1;
      }
      .pane .footer {
        border-top: 1px solid var(--divider-color);
        padding-bottom: 8px;
      }
      .main {
        min-height: 100%;
      }
      .mdc-top-app-bar--pane .main {
        position: relative;
        flex: 1;
        height: 100%;
      }
      .mdc-top-app-bar--pane .content {
        height: 100%;
        overflow: auto;
      }
      .mdc-top-app-bar__title {
        padding-inline-start: 20px;
        padding-inline-end: initial;
      }
    `,
  ];
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-two-pane-top-app-bar-fixed": TopAppBarBaseBase;
  }
}
