/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, css, html} from 'lit';
import {customElement, query, state} from 'lit/decorators.js';
import {assertIsDefined} from '../../../utils/common-util';

declare global {
  interface HTMLElementTagNameMap {
    'gr-page-nav': GrPageNav;
  }
}

@customElement('gr-page-nav')
export class GrPageNav extends LitElement {
  @query('nav') private nav?: HTMLElement;

  // private but used in test
  @state() headerHeight?: number;

  private readonly bodyScrollHandler: () => void;

  constructor() {
    super();
    this.bodyScrollHandler = () => this.handleBodyScroll();
  }

  override connectedCallback() {
    super.connectedCallback();
    window.addEventListener('scroll', this.bodyScrollHandler);
  }

  override disconnectedCallback() {
    window.removeEventListener('scroll', this.bodyScrollHandler);
    super.disconnectedCallback();
  }

  static override get styles() {
    return [
      sharedStyles,
      css`
        nav {
          background-color: var(--table-header-background-color);
          border: 1px solid var(--border-color);
          border-top: none;
          height: 100%;
          position: absolute;
          top: 0;
          width: 14em;
        }
        nav.pinned {
          position: fixed;
        }
        @media only screen and (max-width: 53em) {
          nav {
            display: none;
          }
        }
      `,
    ];
  }

  override render() {
    return html`
      <nav aria-label="Sidebar">
        <slot></slot>
      </nav>
    `;
  }

  // private but used in test
  handleBodyScroll() {
    assertIsDefined(this.nav, 'nav');
    if (this.headerHeight === undefined) {
      let top = this.getOffsetTop(this);
      // TODO(TS): Element doesn't have offsetParent,
      // while `offsetParent` are returning Element not HTMLElement
      for (
        let offsetParent = this.offsetParent as HTMLElement | undefined;
        offsetParent;
        offsetParent = this.getOffsetParent(offsetParent)
      ) {
        top += this.getOffsetTop(offsetParent);
      }
      this.headerHeight = top;
    }

    this.nav.classList.toggle(
      'pinned',
      this.getScrollY() >= (this.headerHeight || 0)
    );
  }

  /* Functions used for test purposes */
  private getOffsetParent(element?: HTMLElement) {
    if (!element || !('offsetParent' in element)) {
      return undefined;
    }
    return element.offsetParent as HTMLElement;
  }

  // private but used in test
  getOffsetTop(element: HTMLElement) {
    return element.offsetTop;
  }

  // private but used in test
  getScrollY() {
    return window.scrollY;
  }
}
