import {
  ChangeDetectorRef,
  Component,
  HostListener,
  NgZone,
  OnDestroy,
  OnInit,
  Renderer2
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

import { MatDialog } from '@angular/material/dialog';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { DeviceDetectorService } from 'ngx-device-detector';
import { environment } from './../environments/environment';
import { ChangeUsernameDialogComponent } from './components/account-settings/change-username-dialog/change-username-dialog.component';
import { showWorkerGraph } from './components/advanced-settings/advanced-settings.component';
import { AccountService } from './shared/server-services/account.service';
import { AuthService } from './shared/server-services/auth.service';
import { MeRecord } from './shared/server-services/query-records/account-records';
import { TimedOfferRecord } from './shared/server-services/query-records/timed-offer-records';
import { getTimedOffersQuery } from './shared/server-services/querys';
import { SearchService } from './shared/server-services/search-service';
import {
  SubscriptionServiceEvent,
  SubscriptionServiceEventType
} from './shared/server-services/subscription-event';
import { SubscriptionService } from './shared/server-services/subscription.service';
import { DialogService } from './shared/services/dialog.service';
import { DownloadManagerService } from './shared/services/download-manager-service';
import { PushNotificationService } from './shared/services/push-notification.service';
import { RouterHandler } from './shared/services/router-handler.service';
import { ServerRestApiService } from './shared/services/server-rest-api.service';

import { appComponentAnimations } from 'src/app/shared/animations/animations';
import { AppStorage } from 'src/app/shared/app-storage';
import { CordovaService } from 'src/app/shared/services/cordova.service';
import { generateAvatar } from './shared/avatar-generator';
import { ResourceType } from './shared/server-services/query-records/sidebar-records';
import {
  WorkspaceSubscriptionDialogueMessageEventRecord,
  WorkspaceSubscriptionRoomMessageEventRecord
} from './shared/server-services/query-records/workspace-records';
import { RoomService } from './shared/server-services/room.service';
import { SidebarService } from './shared/server-services/sidebar.service';
import { AudioService, Sounds } from './shared/services/audio.service';
import { BlobService } from './shared/services/blob.service';
import { sideMenuActiveObserver, styleChangedObserver } from './shared/shared-observers';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: appComponentAnimations,
})
export class AppComponent implements OnInit, OnDestroy {
  public styleMode: string = 'dark';
  public showSidebar = false;
  public isMobileModule = false;
  public mobileShowSidebar = false;
  public initConnection = false;
  public newOffers = false;
  public hideOffers = false;
  public isCookieAccepted: boolean = false;
  public legalURL = environment.url_landing + '/legal';
  public dragging = false;
  public sideWidth: number = 280;
  public windowWidth: number;
  public isOnMobile: boolean;
  private supportedLanguages: string[] = ['hu', 'en']; //TODO collect these globally?

  /**
   * Returned unlistener from renderer2.listen
   */
  private mouseMovelistenerDestructor: Function;

  // development feature for debug
  public workerGraph: boolean = false;

  private hideSidebarHere = ['simplepay-back'];

  public onRouterChange = (res) => {
    if (this.initConnection) {
      this.handleAutoNavigation();
      this.showSidebar =
        !this.authService.isAnonym() &&
        !this.hideSidebarHere.some((hideHere) => res.url.startsWith(hideHere));
    }

    sideMenuActiveObserver.next(false);
  };

  private onWorkspaceLoaded() {
    if (!this.authService.isAnonym()) {
      this.checkAvatar();

      this.serverRestApiService
        .query({
          query: getTimedOffersQuery,
        })
        .then((offers: TimedOfferRecord[]) => {
          if (offers && offers.length > 0) {
            this.newOffers = true;
          }
        });
    } else {
      this.handleAutoNavigation();
    }
  }

  private onOfferEvent() {
    this.newOffers = true;
  }

  @HostListener('window:beforeunload')
  canDeactivate(): boolean {
    return !(
      this.downloadManagerService.isDownloadInProgress() ||
      this.downloadManagerService.isUploadInProgress()
    );
  }

  getAnimation(outlet) {
    if (!this.isOnMobile) return null;
    return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
  }

  constructor(
    private renderer: Renderer2,
    private translate: TranslateService,
    private dialog: MatDialog,
    private authService: AuthService,
    private accountService: AccountService,
    private routerHandler: RouterHandler,
    private serverRestApiService: ServerRestApiService,
    private subscriptionService: SubscriptionService,
    private searchService: SearchService,
    private cdRef: ChangeDetectorRef,
    private downloadManagerService: DownloadManagerService,
    private dialogService: DialogService,
    private deviceDetector: DeviceDetectorService,
    private roomService: RoomService,
    private cordovaService: CordovaService,
    private blobService: BlobService,
    private pushNotiService: PushNotificationService, // call for init
    private sidebarService: SidebarService,
    private zone: NgZone,
    private audioService: AudioService
  ) {
    if (this.cordovaService.isCordova()) {
      window['help'] = {
        app: this,
        rest: this.serverRestApiService,
        cache: this.serverRestApiService['cache'],
      };
    }
    //console.log('blob', this.blobService)

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

    this.isCookieAccepted = AppStorage.getItem('accept-cookies-v2') !== null;

    // load user from localstorage or generate anonym user
    this.subscriptionService.subscribeWorkspaceLoaded(this.onWorkspaceLoaded.bind(this));

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ACCOUNT_OFFER_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onOfferEvent.bind(this)
    );

    /*this.authService.onLogin(() => {
      console.log('on login');
      this.stateInit();
    });*/

    this.authService.onLogout(() => {
      console.log('on logout');
      this.stateInit();
    });

    this.serverRestApiService.subscribeOnWSAuthClosed(() => {
      this.routerHandler.navigate(['/login']);
    });

    // default css mode light/dark/etc
    this.styleMode = AppStorage.getItem('style_mode');
    if (this.styleMode === null) {
      this.styleMode = 'dark';
      AppStorage.setItem('style_mode', this.styleMode);
    }
    styleChangedObserver.next(this.styleMode); // cordova will handle it initially

    styleChangedObserver.subscribe((style) => {
      document.body.classList.remove('theme-' + this.styleMode);
      this.styleMode = style;
      AppStorage.setItem('style_mode', this.styleMode);
      document.body.classList.add('theme-' + this.styleMode);
    });

    // default languages
    let lang = AppStorage.getItem('lang');
    if (lang === null) {
      lang = navigator.language.split('-')[0];
      if (!this.supportedLanguages.some((supportedLang) => supportedLang == lang)) lang = 'en';

      AppStorage.setItem('lang', lang);
    } else if (!this.supportedLanguages.some((supportedLang) => supportedLang == lang)) {
      //fix if bad language is cached
      lang = 'en';
      AppStorage.setItem('lang', lang);
    }

    // this language will be used as a fallback when a translation isn't found in the current language
    this.translate.setDefaultLang('en');

    // the lang to use, if the lang isn't available, it will use the current loader to get them
    this.translate.use(lang);

    sideMenuActiveObserver.subscribe((shouldOpen: boolean | null) => {
      if (this.showSidebar) {
        if (shouldOpen != null) this.mobileShowSidebar = shouldOpen;
        else this.mobileShowSidebar = !this.mobileShowSidebar;

        this.cdRef.detectChanges();
      }
    });

    // do not allow to load outcome source into the view and reload the page
    window.addEventListener(
      'drop',
      function (e) {
        e.preventDefault();
      },
      false
    );
    window.addEventListener(
      'dragover',
      function (e) {
        e.preventDefault();
      },
      false
    );

    showWorkerGraph.subscribe((show) => {
      this.workerGraph = show;
      console.log('show graph', show);
    });

    if (this.deviceDetector.browser == 'Safari') {
      if (!AppStorage.getItem('safari-notice-read')) {
        AppStorage.setItem('safari-notice-read', '1');
        this.dialogService.openAlertDialog(
          marker('Safari experimental support'),
          marker(
            'Safari support is currently in experimental phase. If you experience any issues we recommend using Chrome or a chromium-based browser for the time being.'
          )
        );
      }
    } else if (
      this.deviceDetector.browser != 'Chrome' &&
      this.deviceDetector.browser != 'Opera' &&
      this.deviceDetector.browser != 'MS-Edge' &&
      this.deviceDetector.browser != 'MS-Edge-Chromium' &&
      !this.cordovaService.isOnApp()
    ) {
      if (!AppStorage.getItem('chrome-notice-read')) {
        AppStorage.setItem('chrome-notice-read', '1');
        this.dialogService.openAlertDialog(
          marker('Browser support notice'),
          marker(
            'Because of the technologies used in our webapp, we highly recommend using Chrome or a chromium based web browser.'
          )
        );
      }
    }

    this.setSidebarWidthFromStorage();

    if (this.cordovaService.isCordova()) {
      this.cordovaService.subscribeNotificationTap(() => {
        this.zone.run(() => {
          // come from the background, so need to wait to load the ws
          let event = () => {
            this.navigateToLastMessageEvent();
            // after this, unsubscribe so we wont renavigate on every connection repair
            this.subscriptionService.unsubscribeWorkspaceLoaded(event);
          };
          this.subscriptionService.subscribeWorkspaceLoaded(event);
        });
      });

      this.cordovaService.notificationPullStart();
    }

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.playSound
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.playSound
    );
  }

  private playSound = (
    msg:
      | WorkspaceSubscriptionRoomMessageEventRecord
      | WorkspaceSubscriptionDialogueMessageEventRecord
  ) => {
    if (!this.cordovaService.isOnApp() || this.cordovaService.isAppInForeground()) {
      this.accountService.getMe().then((me: MeRecord) => {
        if (msg.posterId != me.id) {
          this.audioService.playSound(Sounds.NOTIFICATION);
        }
      });
    }
  };

  private navigateToLastMessageEvent(): Promise<any> {
    return this.sidebarService.getSidebarNews().then((news) => {
      let lastNews = news.resources[0];
      if (lastNews.type == ResourceType.ROOM) {
        return this.routerHandler.navigate(['room', lastNews.id, 'chat', 'room']);
      } else if (lastNews.type == ResourceType.DIALOGUE) {
        return this.routerHandler.navigate(['room', lastNews.id, 'chat', 'private']);
      } else {
        console.error('unnokwn resource type', lastNews);
        return;
      }
    });
  }

  public acceptCookies() {
    this.isCookieAccepted = true;
    AppStorage.setItem('accept-cookies-v2', '1');
  }

  private stateInit() {
    console.log('state init OK');
    this.initConnection = true;
    let route = this.routerHandler.getRoute();
    if (route) {
      var url = this.routerHandler.getRoute().url;
      this.showSidebar =
        !this.authService.isAnonym() &&
        !this.hideSidebarHere.some((hideHere) => url.startsWith(hideHere));
    }
  }

  private handleAutoNavigation() {
    this.stateInit();
  }

  /*
   * Check if avatarname is filled out. If not, prompt user to enter name.
   */
  checkAvatar() {
    this.accountService.getMe().then((me: MeRecord) => {
      if (!me.avatarImageKey) {
        generateAvatar(me.id + Math.random()).then((avatarBlob) => {
          avatarBlob.arrayBuffer().then((buffer) => {
            this.accountService
              .setAvatar(buffer)
              .then((res) => {
                me.avatarImageKey = res;
              })
              .catch((err) => console.error('set avatar', err));
          });
        });
      }
      if (me.avatarName) {
        this.handleAutoNavigation();
      } else {
        var ref = this.dialog.open(ChangeUsernameDialogComponent, {
          data: { firstSetup: true },
          disableClose: true,
        });

        ref.afterClosed().subscribe((userName) => {
          this.accountService
            .changeAvatarName(userName)
            .then((res) => {
              me.avatarName = userName;
              this.handleAutoNavigation();
            })
            .catch((err) => console.error(err));
        });
      }
    });
  }

  ngOnInit() {
    this.routerHandler.subscribeAll(this.onRouterChange);
    document.body.classList.add('theme-' + this.styleMode);
    this.windowWidth = window.innerWidth;
  }

  @HostListener('window:resize', ['$event']) windowResize(e): void {
    this.windowWidth = e.target.innerWidth;
  }

  get isMobileResponsiveView(): boolean {
    if (this.deviceDetector.isMobile()) return true;

    return this.windowWidth < 761;
  }

  ngOnDestroy() {
    this.mouseMovelistenerDestructor();

    this.routerHandler.unsubscribeAll(this.onRouterChange);

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ACCOUNT_OFFER_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onOfferEvent.bind(this)
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.playSound
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.playSound
    );
  }

  private setSidebarWidthFromStorage(): void {
    var storedWidth = AppStorage.getItem('sideBarWidth');
    if (storedWidth) this.sideWidth = parseInt(storedWidth);
  }

  public closeOffers(event) {
    this.hideOffers = true;
    event.stopPropagation();
  }

  public draggerPointerDown() {
    if (!this.mouseMovelistenerDestructor) {
      this.mouseMovelistenerDestructor = this.renderer.listen('document', 'mousemove', (e) => {
        if (this.dragging) {
          this.sideWidth = e.clientX;
        }
      });
    }

    this.dragging = true;
  }
  @HostListener('document:pointerup', ['$event']) pointerUp(e: MouseEvent) {
    if (this.dragging) {
      this.dragging = false;
      AppStorage.setItem('sideBarWidth', e.clientX.toString());
      this.mouseMovelistenerDestructor();
      this.mouseMovelistenerDestructor = null;
    }
  }
}
