import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  ElementRef,
  HostListener,
  OnDestroy,
  OnInit,
  ViewChild
} from '@angular/core';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
import { AccountService } from 'src/app/shared/server-services/account.service';
import {
  AccountAvatarRecord,
  MeRecord
} from 'src/app/shared/server-services/query-records/account-records';
import { ID } from 'src/app/shared/server-services/query-records/common-records';
import {
  RoomRecord,
  RoomWithData
} from 'src/app/shared/server-services/query-records/room-records';
import { getAllRoomQuery, getContactListIdsQuery } from 'src/app/shared/server-services/querys';
import { RoomService } from 'src/app/shared/server-services/room.service';
import { CordovaService } from 'src/app/shared/services/cordova.service';
import { ServerRestApiService } from 'src/app/shared/services/server-rest-api.service';
import { sideMenuActiveObserver } from 'src/app/shared/shared-observers';
import { WeightedSearch } from 'src/app/shared/weighted-search';
import { SearchMode, SearchService } from '../../../shared/server-services/search-service';
import { RouterHandler } from '../../../shared/services/router-handler.service';

@Component({
  selector: 'app-search-input',
  templateUrl: './search-input.component.html',
  styleUrls: ['./search-input.component.scss'],
})
export class SearchInputComponent implements OnInit, OnDestroy, AfterViewInit {
  public selectedRoom: RoomRecord;
  public searchStarted = false;
  public search: string = '';
  public searchMode = SearchMode;
  public selectedSearchMode: SearchMode[];

  public searchedContacts = [];
  public searchedRooms = [];
  public me: MeRecord;

  public isOnApp: boolean = false;
  public placeholder = this.translate.instant(marker('Search...'));

  // timeout on typing before search
  private timeout: any = null;

  @ViewChild('searchInput') searchInput: ElementRef;

  private onRouteChangeSearch = (res) => {
    this.search = res.fragment?.search;
  };

  private onRouteChangeResource = (res) => {
    if (
      (this.routerHandler.isOnPage('room') && res.params.type == 'room') ||
      this.routerHandler.isOnPage('drive') ||
      this.routerHandler.isOnPage('details')
    ) {
      let resourceId: string = res.params.id;
      let pinnedKey = res.fragment?.roomkey;
      if (pinnedKey) {
        this.roomService.registerPinnedKey(resourceId, pinnedKey);
      }
      this.roomService.getRoom(resourceId).then((roomRecord: RoomRecord) => {
        if (Object.keys(roomRecord.nanoSessions).length > 0) this.selectedRoom = roomRecord;
        else this.selectedRoom = null;
      });
    } else {
      this.selectedRoom = null;
    }
  };

  constructor(
    private eRef: ElementRef,
    private routerHandler: RouterHandler,
    private searchService: SearchService,
    private roomService: RoomService,
    private translate: TranslateService,
    private cordovaService: CordovaService,
    private accountService: AccountService,
    private serverRestApiService: ServerRestApiService,

    private cdRef: ChangeDetectorRef
  ) {
    routerHandler.subscribe('search', this.onRouteChangeSearch);
    routerHandler.subscribe('room', this.onRouteChangeResource);

    if (this.routerHandler.isOnPage('room') || this.routerHandler.isOnPage('search')) {
      this.onRouteChangeResource(this.routerHandler.getRoute());
    }

    this.search = this.routerHandler.getRoute().fragment?.search;

    this.isOnApp = this.cordovaService.isOnApp();
  }

  ngOnInit(): void {
    let route = this.routerHandler.getRoute();
    if (route.fragment['search'] && this.routerHandler.isOnPage('search')) {
      //if there is a search param, and we are on search page, start search
      this.doSearch(route.fragment['search']);
    }

    this.accountService.getMe().then((res) => {
      this.me = res;
    });
  }

  ngAfterViewInit(): void {}

  ngOnDestroy() {
    this.routerHandler.unsubscribe('search', this.onRouteChangeSearch);
    this.routerHandler.unsubscribe('room', this.onRouteChangeResource);
  }

  @HostListener('document:click', ['$event']) clickout(event) {
    if (this.searchStarted) {
      if (!this.eRef.nativeElement.contains(event.target)) {
        // clicked outside
        this.searchStarted = false;
      }
    }
  }

  @HostListener('window:keydown', ['$event']) onKeyDown(e: KeyboardEvent) {
    if (e.code == 'Escape') {
      // if (this.showSearchOptions && !this.codeMirror.hasFocus()) {
      //   this.codeMirror.focus();
      // } else if (this.codeMirror.hasFocus()) {
      //   this.codeMirror.display.input.blur();
      //   this.searchStarted = false;
      // }
    }
  }

  public onSearch(event, search) {
    if (event.key == 'Enter') {
      this.doSearch(search);
    } else {
      this.startTimedSearch(event);
    }
  }

  private startTimedSearch = (event) => {
    this.searchStarted = true;
    // timeout for typing before search
    clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      if (event.keyCode != 13) {
        this.executeOnChange();
      }
    }, 500);
  };

  public doSearch(
    search: string,
    searchMode: SearchMode = SearchMode.GLOBAL,
    event?: [{ id: string; type: 'room' | 'private' }]
  ): void {
    this.searchStarted = false;
    let fragment = { search: search };

    switch (searchMode) {
      case SearchMode.GLOBAL: {
        this.searchService.search(search);
        break;
      }
      case SearchMode.ROOM: {
        this.searchService.search(search, [this.selectedRoom.id]);
        break;
      }
      case SearchMode.CUSTOM: {
        break;
      }
    }

    const currEvent = event?.shift();
    if (currEvent?.id && currEvent?.type) {
      this.routerHandler.navigate([['room', currEvent.id, 'chat', currEvent.type].join('/')]);
    } else {
      this.routerHandler.navigate(['/search'], {
        fragment: this.routerHandler.fragmentToRaw(fragment),
      });
    }

    this.selectedSearchMode = [];

    sideMenuActiveObserver.next(false);
  }

  // private setUpCodeMirror(): void {
  //   this.codeMirror = CodeMirror(this.searchInput.nativeElement, {
  //     value: this.search ?? '',
  //     mode: 'claraMarkdownSearch',
  //     inputStyle: 'contenteditable',
  //     scrollbarStyle: null,
  //     cursorHeight: 0.85,
  //     placeholder: this.translate.instant(marker('Search...')),
  //     autofocus: true,
  //     tabindex: 0,
  //     extraKeys: {
  //       Enter: () => {
  //         this.doSearch(this.search);
  //       },
  //       'Shift-Enter': () => {
  //         this.doSearch(this.search);
  //       },
  //       Tab: () => {
  //         this.selectMode.focus();
  //       },
  //       Down: () => {
  //         this.selectMode.focus();
  //       },
  //     },
  //   });

  //   this.codeMirror.on('change', (ev) => {
  //     let newString: string = this.codeMirror.getValue();

  //     if (newString.indexOf('\n') > -1) {
  //       //strip newline when copy pasting
  //       newString = newString.replace('\n', ' ');
  //       this.codeMirror.setValue(newString);
  //     }
  //     this.search = newString;
  //     this.searchStarted = true;
  //     // timeout for typing before search
  //     clearTimeout(this.timeout);
  //     let $this = this;
  //     this.timeout = setTimeout(function () {
  //       if (ev.keyCode != 13) {
  //         $this.executeOnChange();
  //       }
  //     }, 500);
  //   });
  //   this.codeMirror.on('focus', () => {
  //     this.searchStarted = true;
  //   });
  // }

  private executeOnChange = () => {
    this.roomService.preloadRooms().then((roomsWithData) => {
      Promise.all([this.getFilteredContacts(this.search), this.getFilteredRooms(this.search)]).then(
        ([contacts, room]) => {
          // max number of results or pagination here (in the future)
          // console.log('contacts, room', contacts, room);
          this.cdRef.detectChanges();
        }
      );
    });
  };

  private getFilteredContacts = (searchString) => {
    return this.serverRestApiService
      .query({
        query: getContactListIdsQuery,
      })
      .then((contactIds: ID[]) => {
        return this.accountService.getAccounts(contactIds).then((accounts) => {
          this.searchedContacts = WeightedSearch.search<AccountAvatarRecord>(
            searchString,
            ['avatarName', 'id'],
            Object.values(accounts),
            1
          );
          return this.searchedContacts.sort((a, b) =>
            a.avatarName > b.avatarName ? 1 : a.avatarName < b.avatarName ? -1 : 0
          );
        });
      });
  };

  private getFilteredRooms = (searchString) => {
    return this.serverRestApiService
      .query({
        query: getAllRoomQuery,
      })
      .then((roomRecords: RoomWithData[]) => {
        const filteredRooms = roomRecords.filter(
          (room) => !room?.data?.decryptionError && room?.data?.name
        );

        this.searchedRooms = WeightedSearch.search<any>(
          searchString,
          ['name', 'id'],
          filteredRooms.map((room) => ({
            id: room.id,
            ownerAccountId: room.ownerAccountId,
            isNanoConnected: Object.keys(room.nanoSessions).length === 1,
            name: room?.data?.name,
            blob: room?.data?.avatar,
          }))
        );
        return this.searchedRooms;
      });
  };

  get showSearchOptions(): boolean {
    return this.searchStarted && this.search?.length > 0; //&& this.selectedRoom != null;
  }
}
