import $ from 'jquery';
import 'jquery-hoverintent';
import { BaseComponent } from '../../base/ts/models/DefaultComponent';
import { ProgressBar } from '../../base/ts/components/ProgressBar';
import { WindowService } from '../../base/ts/services/window.service';
import { Helper } from '../../base/ts/services/Helper';

import { takeWhile, map } from 'rxjs/operators';

interface JQueryExtended extends JQuery {
  hoverIntent?( params: any ): void;
  imagesLoaded?( params: any );
}

export class StickyHeader extends BaseComponent {

  private ProgressBar: ProgressBar;
  private $body: JQuery = $( 'body' );
  private $document: JQuery<Document> = $( document );
  private $mainMenu: JQuery = $( '.menu--primary' );
  private $secondaryMenu: JQuery = $( '.menu--secondary' );
  private $mainMenuItems: JQueryExtended = this.$mainMenu.find( 'li' );
  private $readingBar: JQuery = $( '.js-reading-bar' );
  private $stickyHeader: JQuery = $( '.js-site-header-sticky' );
  private $menuToggle: JQuery = $( '#menu-toggle' );
  private $searchTrigger: JQuery;
  private isStickyHeaderEnabled: boolean = this.$body.hasClass( 'u-site-header-sticky' );
  private isSingular: boolean = this.$body.hasClass( 'single' );
  private isMobileHeaderInitialised: boolean = false;
  private isDesktopHeaderInitialised: boolean = false;
  private areMobileBindingsDone: boolean = false;
  private stickyHeaderShown: boolean = false;
  private hideReadingBar: boolean = this.$readingBar !== null;
  private currentScrollPosition: number = 0;
  private initialMenuOffset: number = 0;
  private shouldUpdate: boolean;
  private overflowTimeout;
  private subscriptionActive: boolean = true;
  private preventOneSelector: string = 'a.prevent-one';

  constructor() {
    super();

    ( $( '.c-navbar .c-logo' ) as JQueryExtended ).imagesLoaded( () => {

      this.bindEvents();
      this.eventHandlers();
      this.appendSearchTrigger();
      this.updateOnResize();

      this.refresh(WindowService.getScrollY());

    });
  }

  public destroy() {
    this.subscriptionActive = false;
  }

  public bindEvents() {
    if ( this.$mainMenu.length === 1 ) {
      this.$document.on( 'click', '.js-sticky-menu-trigger', this.onClickStickyMenu.bind( this ) );
    }

    this.$menuToggle.on( 'change', this.onMenuToggleChange.bind(this));

    this.$mainMenuItems.hoverIntent( {
      out: (e) => this.toggleSubMenu(e, false),
      over: (e) => this.toggleSubMenu(e, true),
      timeout: 300
    } );

    WindowService
      .onScroll()
      .pipe( takeWhile( () => this.subscriptionActive ) )
      .pipe( map(() => WindowService.getScrollY()) )
      .subscribe( (scrollPosition) => {
        this.refresh( scrollPosition );
      } );

    WindowService
      .onResize()
      .pipe( takeWhile( () => this.subscriptionActive ) )
      .subscribe( () => {
        this.updateOnResize();
      } );

  }

  public eventHandlers() {
    if ( Helper.below( 'lap' ) && !this.areMobileBindingsDone ) {
      this.$document.on( 'click', this.preventOneSelector, this.onMobileMenuExpand.bind(this) );
      this.areMobileBindingsDone = true;
    }

    if ( Helper.above( 'lap' ) && this.areMobileBindingsDone ) {
      this.$document.off( 'click', this.preventOneSelector, this.onMobileMenuExpand.bind(this) );
      this.areMobileBindingsDone = false;
    }
  }

  private onMobileMenuExpand(e: Event): void {
    e.preventDefault();
    e.stopPropagation();

    const $button = $( e.currentTarget );
    const activeClass = 'active';
    const hoverClass = 'hover';

    if ( $button.hasClass( activeClass ) ) {
      window.location.href = $button.attr( 'href' );
      return;
    }

    $( this.preventOneSelector ).removeClass( activeClass );
    $button.addClass( activeClass );

    // When a parent menu item is activated,
    // close other menu items on the same level
    $button.parent().siblings().removeClass( hoverClass );

    // Open the sub menu of this parent item
    $button.parent().addClass( hoverClass );
  }

  private onMenuToggleChange( e: Event ): void {
    const isMenuOpen = $( e.currentTarget ).prop( 'checked' );
    this.$body.toggleClass( 'nav--is-open', isMenuOpen );
    if ( !isMenuOpen ) {
      setTimeout( () => {
        // Close the open submenus in the mobile menu overlay
        this.$mainMenuItems.removeClass( 'hover' );
        this.$mainMenuItems.find('a').removeClass( 'active' );
      }, 300 );
    }
  }

  private toggleSubMenu(e: Event, toggle: boolean) {
    $( e.currentTarget ).toggleClass( 'hover', toggle );
  }

  private refresh( scrollPosition: number = 0 ) {
    this.shouldUpdate = this.$body.is( '.u-site-header-sticky' );

    if ( Helper.below( 'lap' ) ) {
      this.shouldUpdate = false;
    }

    this.updateOnScroll( scrollPosition );
  }

  private prepareDesktopMenuMarkup(): void {
    if ( this.isDesktopHeaderInitialised ) {
      return;
    }

    const htmlTop = parseInt( $( 'html' ).css( 'marginTop' ), 10 );

    this.$stickyHeader.css( 'top', htmlTop );

    // Figure out where is the offset of the Main Menu.
    // If there is no Main Menu set, show the reading bar
    // after passing the branding.
    if ( this.$mainMenu.length === 1 ) {
      this.initialMenuOffset = this.$mainMenu.offset().top - htmlTop;
    } else {
      const $branding = $( '.c-branding' );
      this.initialMenuOffset = $branding.offset().top + $branding.outerHeight();
    }

    // Fallback to the other, secondary menu (top left one).
    if ( this.$mainMenu.length === 0 ) {
      this.$mainMenu = $( '.menu--secondary' );
      this.$secondaryMenu = $();
    }

    // If there is a menu, either the "true" main one or the fallback one,
    // clone it and append it to the reading bar.
    if ( this.$mainMenu.length === 1 ) {
      this.$mainMenu = this.$mainMenu
        .clone( true, true )
        .appendTo( this.$stickyHeader.find( '.c-navigation-bar__middle' ) );
    }

    if ( this.$secondaryMenu.length === 1 ) {
      this.$secondaryMenu = this.$secondaryMenu
          .clone( true, true )
          .appendTo( this.$stickyHeader.find( '.c-navigation-bar__left' ) );
    }

    this.$stickyHeader
        .find( '.c-navbar' )
        .css( 'height', this.$stickyHeader.height() );

    // this.$readingBar = null;
    // this.$progressBar = null;

    this.prepareSingleHeader();

    this.refresh();

    this.isDesktopHeaderInitialised = true;
  }

  private prepareMobileMenuMarkup() {
    // If if has not been done yet, prepare the mark-up for the mobile navigation
    if ( !this.isMobileHeaderInitialised ) {

      // Append the branding
      const $branding = $( '.c-branding' );
      const $navbarZone = $( '.c-navbar__zone--right' );
      $branding.clone().addClass('c-branding--mobile').appendTo( '.c-navbar' );
      $branding.find( 'img' ).removeClass( 'is--loading' );

      // Create the mobile site header
      const $siteHeaderMobile = $( '<div class="site-header-mobile u-container-sides-spacing"></div>' )
        .appendTo( '.c-navbar' );

      // Append the social menu
      const $socialMenu = $( '.c-navbar__zone--left .jetpack-social-navigation' ).clone();
      const $searchTrigger = $socialMenu.find('.js-search-trigger').parent().clone();
      $navbarZone.append( $socialMenu );
      $navbarZone.find('.js-search-trigger' ).parent().remove();
      $siteHeaderMobile.append( $socialMenu.empty().append( $searchTrigger ) );

      // Handle sub menus:
      // Make sure there are no open menu items
      $( '.menu-item-has-children' ).removeClass( 'hover' );

      // Add a class so we know the items to handle
      $( '.menu-item-has-children > a' ).each( ( index, element ) => {
        $( element ).addClass( 'prevent-one' );
      } );

      // Replace the label text and make it visible
      $( '.c-navbar__label-text' ).html( $( '.js-menu-mobile-label' ).html() ).removeClass( 'screen-reader-text' );

      this.isMobileHeaderInitialised = true;
    }
  }

  private prepareSingleHeader(): void {
    if ( !this.isSingular || !this.isStickyHeaderEnabled ) {
      return;
    }

    $( '.c-reading-bar__wrapper-social' ).find( '.share-end' ).remove();

    const entryHeader = $( '.entry-header, .entry-summary' );
    const entryContent = $( '.single-main' ).find( '.entry-content' );
    const entryHeaderHeight = entryHeader.outerHeight() || 0;
    const entryContentHeight = entryContent.outerHeight() || 0;
    let articleHeight = entryHeaderHeight + entryContentHeight;

    if ( this.$body.hasClass( 'entry-image--landscape' ) ) {
      articleHeight = articleHeight + $( '.entry-thumbnail' ).outerHeight();
    }

    this.ProgressBar = new ProgressBar({
      canShow: this.isSingular,
      max: entryHeader.offset().top + articleHeight - this.initialMenuOffset,
      offset: this.initialMenuOffset
    });

    if ( this.$mainMenu.length !== 1 ) {
      $( '.js-sticky-menu-trigger' ).remove();
    }

  }

  private updateOnScroll( scrollPosition: number = 0 ) {

    if ( !this.shouldUpdate ) {
      return;
    }

    const showSticky = scrollPosition > this.initialMenuOffset;
    const hideReadingBar = scrollPosition < this.currentScrollPosition && this.$mainMenu.length === 1;

    if ( this.$readingBar !== null && hideReadingBar !== this.hideReadingBar ) {
      clearTimeout( this.overflowTimeout );

      if ( !hideReadingBar ) {
        if ( this.$readingBar.length ) {
          this.$stickyHeader.css( 'overflow', 'hidden' );
        }
      } else {
        this.overflowTimeout = setTimeout( () => {
          this.$stickyHeader.css( 'overflow', '' );
        }, 350 );
      }

      this.$stickyHeader.toggleClass( 'reading-bar--hide', hideReadingBar );
      this.hideReadingBar = hideReadingBar;
    }

    if ( this.ProgressBar && null !== this.$readingBar ) {
      this.$readingBar.toggleClass( 'show-next-title', this.ProgressBar.isCloseToEnd() );
    }

    if ( showSticky !== this.stickyHeaderShown ) {
      this.$stickyHeader.toggleClass( 'site-header-sticky--show', showSticky );
      this.stickyHeaderShown = showSticky;
    }

    this.currentScrollPosition = scrollPosition;
  }

  private updateOnResize() {
    this.eventHandlers();

    if ( Helper.below( 'lap' ) ) {
      this.prepareMobileMenuMarkup();
    } else {
      this.prepareDesktopMenuMarkup();
    }
  }

  private onClickStickyMenu(): void {
    this.$stickyHeader.addClass( 'reading-bar--hide' );
    setTimeout( () => {
      this.$stickyHeader.css( 'overflow', '' );
    }, 350 );
  }

  private appendSearchTrigger() {
    const $headerSocialNavigation = $( '.c-navbar__zone--left .jetpack-social-navigation' );

    this.$searchTrigger = $( '.js-search-trigger' ).removeClass( 'u-hidden' );

    // Append the search trigger either to the social navigation
    if ( $headerSocialNavigation.length === 1 ) {
      this.$searchTrigger
          .clone()
          .wrap( '<li class="menu-item"></li>' )
          .parent()
          .appendTo( $headerSocialNavigation.find( '.menu' ) );
    } else {
      // Or directly to zone left if there is no social navigation
      this.$searchTrigger.clone().appendTo( '.c-navbar__zone--left' );
    }

    this.$searchTrigger.clone()
        .appendTo( '.c-navigation-bar__right' )
        .find( '.screen-reader-text' ).removeClass( 'screen-reader-text' );

    this.$searchTrigger.remove();
  }
}
