import { Component, ViewChild, ChangeDetectionStrategy, ElementRef, ChangeDetectorRef, AfterViewInit, inject } from '@angular/core';
import { CommonData, CommonService, ScreenItem } from '../services/common.service';
import { Title } from '@angular/platform-browser';
import { MessageService } from '../services/message.service';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { AutoCompleteComponent, KENDO_AUTOCOMPLETE } from '@progress/kendo-angular-dropdowns';
import { combineLatest, BehaviorSubject, Observable, of, merge } from 'rxjs';
import { catchError, map, tap, delay, switchMap } from 'rxjs/operators';
import { NotifyService } from '../services/notify.service';
import * as util from '../utils/util';
import { KENDO_TOOLTIP, TooltipDirective } from '@progress/kendo-angular-tooltip';
import { KENDO_MENU, MenuEvent, MenuSelectEvent } from '@progress/kendo-angular-menu';
import { AsyncPipe } from '@angular/common';
import { KENDO_SVGICON } from '@progress/kendo-angular-icons';
import { FormsModule } from '@angular/forms';
import { KENDO_DIALOG } from '@progress/kendo-angular-dialog';
import { GlobalFavoritesComponent } from '../global-favorites/global-favorites.component';
import { GlobalBarIconPipe } from '../pipes/global-bar-icon.pipe';
import { FastWindowComponent } from "../elements/fast-window.component";
import { FastTipDirective } from '../elements/fast-tip.directive';
import { AuthService } from '../services/auth.service';

interface menuItem {
  text: string;
  path: string;
  type: 'item' | 'category' | 'link' | 'entry';
  items: menuItem[];
}
@Component({
  selector: 'global-bar',
  templateUrl: './global-bar.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styles: `
    .homeImage:hover {
      filter: drop-shadow(0 0 4px var(--color-highlight)) drop-shadow(0 0 4px var(--color-highlight)) drop-shadow(0 0 4px var(--color-highlight));
    }

    .homeImage:hover:active {
      filter: drop-shadow(0 0 4px var(--color-highlight)) drop-shadow(0 0 4px var(--color-highlight)) drop-shadow(0 0 4px var(--color-highlight)) blur(1px);
    }
  `,
  imports: [AsyncPipe, KENDO_SVGICON, KENDO_AUTOCOMPLETE, KENDO_MENU, KENDO_TOOLTIP, KENDO_DIALOG, RouterLink, FormsModule, GlobalFavoritesComponent, GlobalBarIconPipe, FastWindowComponent, FastTipDirective]
})
export class GlobalBarComponent implements AfterViewInit {
  @ViewChild("autoComplete") autoComplete: AutoCompleteComponent;
  @ViewChild("copyInfoElem") copyInfoElem: ElementRef;
  @ViewChild("copyErrorElem") copyErrorElem: ElementRef;
  @ViewChild("screenTip") screenTip: TooltipDirective;
  @ViewChild("searchTip") searchTip: TooltipDirective;
  @ViewChild("searchAnchor") searchAnchorElem: ElementRef;

  commonService = inject(CommonService);
  messageService = inject(MessageService);
  titleService = inject(Title);
  notify = inject(NotifyService);
  activatedRoute = inject(ActivatedRoute);
  router = inject(Router);
  ref = inject(ChangeDetectorRef);
  private readonly authService = inject(AuthService);

  readonly minFilterLength = 3;

  icons = util.icons;

  appName$: Observable<string>
  title$: Observable<string>
  companyHeaderStr$: Observable<string>
  isDashModule$: Observable<boolean>
  homeUrl$: Observable<string>

  areFavoritesOpen$: Observable<boolean>
  isToplessSubject = new BehaviorSubject<boolean>(false)
  isTopless$: Observable<boolean>
  errorText$: Observable<string>
  infoText$: Observable<string>
  filterAction$ = new BehaviorSubject<string>(null)

  filterSelected$ = new BehaviorSubject<string>(null)
  modulesMenus$: Observable<{
    text: string;
    items: menuItem[];
    path: string;
    type: string;
  }[]>
  userInfo$: Observable<CommonData>
  screenItems$: Observable<ScreenItem[]>
  selectedScreenItem$: Observable<[CommonData, string]>
  searchText: string = null;

  constructor() {
    this.appName$ = this.commonService.serverSettings$.pipe(map(data => data.appName));
    this.companyHeaderStr$ = this.commonService.serverSettings$.pipe(map(data => data.companyHeaderStr));
    this.isDashModule$ = this.commonService.serverSettings$.pipe(map(data => data.moduleName.toLowerCase().includes('dash')));
    this.homeUrl$ = this.commonService.commonData$.pipe(map(data => data.homeUrl));

    this.title$ = this.appName$.pipe(
      tap((title) => util.trySetTitle(this.titleService, title, false))
    )

    this.areFavoritesOpen$ = this.commonService.areFavoritesOpenResult$;
    this.isTopless$ = this.isToplessSubject.asObservable();
    this.errorText$ = this.messageService.errorMessage$;
    this.infoText$ = this.messageService.infoMessage$;

    this.modulesMenus$ = merge(of(null), this.commonService.commonData$).pipe(
      map(data => {
        const entryMenu: menuItem = { text: '', items: null, path: null, type: 'entry' };

        //if data is null, then commonData$ has not yet emitted, but we still want to render the entry menu immediately
        if (data === null)
          return [entryMenu];

        const menuItems: menuItem[] = data.moduleMenus.map(topMenu => {
          const childItems: menuItem[] = topMenu.children.map(childMenu => {
            const childItem: menuItem = { text: childMenu.name, path: childMenu.path, type: 'item', items: null };
            return childItem;
          });
          const topItem: menuItem = { text: topMenu.name, path: childItems.length === 1 ? childItems[0].path : null, type: 'category', items: childItems };
          return topItem;
        });

        entryMenu.items = menuItems;
        return [entryMenu];
      })
    )

    this.userInfo$ = this.commonService.commonData$.pipe(
      tap(data => {
        const userInfo = data.userInfo;
        localStorage.setItem('displayName', userInfo.displayName);
      })
    )

    this.screenItems$ = this.filterAction$.pipe(
      tap((filterText: string) => {
        if (!filterText || (filterText.length < this.minFilterLength)) {
          if (this.autoComplete?.isOpen)
            this.autoComplete.toggle(false);
        }
      }),
      switchMap((filterText: string) => this.commonService.commonData$.pipe(
        delay(100),
        map(data => {
          let screenItems: ScreenItem[] = data.screenItems.filter(x => x.isVisible && x.userHasPermission) ?? [];
          if (screenItems && filterText && filterText.length >= this.minFilterLength) {
            screenItems = screenItems.filter(s => {
              const searchterms = filterText.split(/\s+/);
              //loops through searchterms and returns true only if the list item has every term
              const ismatch: boolean = searchterms.reduce(function (result, term) {
                const nameAndKeywords = `${s.name};${s.keywords};`
                return result && nameAndKeywords.toUpperCase().includes(term.toUpperCase());;
              }, true);
              return ismatch;
            })
            return screenItems;
          }
        })
      )),
      catchError(err => util.handleError(err, this.messageService))
    )

    this.selectedScreenItem$ = combineLatest([this.commonService.commonData$, this.filterSelected$]).pipe(
      tap(([data, selectedItemText]) => {
        const screenItems = data.screenItems.filter(x => x.isVisible && x.userHasPermission);
        if (selectedItemText && selectedItemText.length >= this.minFilterLength) {
          const screenItem = screenItems.find((s) => s.name === selectedItemText);
          if (screenItem)
            this.externalNavigate(screenItem.path);
          setTimeout(() => {
            this.searchText = null;
            this.ref.detectChanges();
          }, 100);
        }
      }),
      catchError(err => util.handleError(err, this.messageService))
    )
  }

  ngAfterViewInit(): void {
    this.activatedRoute.queryParams.subscribe(x => {
      const isTopless = x['topless'] ? true : false;
      this.isToplessSubject.next(isTopless);
    });
  }

  public onMenuSelect({ item }: MenuSelectEvent): void {
    if (item.path) {
      this.externalNavigate(item.path);
    }
  }

  public menuOpened(event: MenuEvent): void {
    if (event.index === '0' && this.screenTip) {
      this.screenTip.showOn = 'none';
      this.screenTip.hide();
    }
  }

  public menuClosed(event: MenuEvent): void {
    if (event.index === '0' && this.screenTip)
      this.screenTip.showOn = 'hover';
  }

  signOut(): void {
    this.authService.logout();
  }

  closeError(): void {
    //throwing a null error closes the error dialog
    this.messageService.throw(null);
  }

  closeInfo(): void {
    //raising a null info closes the info dialog
    this.messageService.info(null);
  }

  async copyInfo() {
    this.copyInfoElem.nativeElement.select();
    const selection = window.getSelection()?.toString();
    if (selection)
      await navigator.clipboard.writeText(selection);
    this.notify.show('copied', 'success', false, 'top');
  }

  async copyError() {
    this.copyErrorElem.nativeElement.select();
    const selection = window.getSelection()?.toString();
    if (selection)
      await navigator.clipboard.writeText(selection);
    this.notify.show('copied', 'success', false, 'top');
  }

  valueChange(value: string): void {
    if (value && value.length >= this.minFilterLength) {
      this.filterSelected$.next(value);
    }
  }

  filterChange(filter: string): void {
    this.filterAction$.next(filter);
    if (filter.length > 0 && filter.length < this.minFilterLength)
      this.searchTip.show(this.searchAnchorElem);
    else
      this.searchTip.hide();
  }

  externalNavigate(path: string) {
    this.router.navigate(['/externalRedirect', { externalUrl: path }], {
      skipLocationChange: true,
    });
  }

  setMouseOver(isMouseOver: boolean, force: boolean = false) {
    this.commonService.setMouseOver(isMouseOver, force);
  }

  toggleFavorites() {
    this.commonService.toggleFavorites();
  }

}
