import {EventEmitter, Injectable} from '@angular/core';
import {AppConfig} from '../app.config';
import {ApiV1LoginResponse} from '../models/api-v1-login-response';
import {PeopleModel} from '../models/people.model';
import {HttpClient} from '@angular/common/http';
import {Router} from '@angular/router';
import {NotificationService} from '@progress/kendo-angular-notification';
import {debounce} from 'ts-debounce';
import {UserExModel} from '../models/user-ex.model';
import {UserExtraModel} from '../models/user-extra.model';
import {WebSocketService} from '../websocket';
import {UserRoles} from '../models/user-ex.model';

@Injectable()
export class SessionService {
  public loggedIn = new ApiV1LoginResponse({});
  loginResult = new EventEmitter <ApiV1LoginResponse>();
  logoutEvent = new EventEmitter <ApiV1LoginResponse>();
  private machineId = localStorage.getItem('mid') || '';
  private socketId = ''; // only lives in memory
  debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes

  constructor(private config: AppConfig,              private http: HttpClient,
              private router: Router,              private ns: NotificationService,
              private ws: WebSocketService){
    //
              const self = this;
              this.AutoLogin();
              window.addEventListener('storage', event => {
                if (event.storageArea === localStorage && event.key === this.config.storageKey) {
                  // It's local storage
                  self.debouncedLocalStorageMonitor(event).then( r => {});
                }
              }, false);
  }

  private AutoLogin(): void {
    const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey));
    if (!sfm) {
      return;
    }
    this.login(sfm);
  }


  public login( resp: ApiV1LoginResponse): void{
    // console.log( 'login in session', this);
    this.loggedIn = new ApiV1LoginResponse(resp);

    if ( this.MachineId === '' || (resp.machineId !== '' && this.MachineId !== resp.machineId )) {
      this.MachineId = resp.machineId; // update machine id
    }

    if (this.loggedIn.session !== ''){
      this.ws.SessionId = this.loggedIn.session;
    }

    if ( resp.UserExtra !== undefined ) {
      this.loggedIn.UserExtra = new UserExtraModel(resp.UserExtra);
    }

    if (resp.UserEx) {
      this.loggedIn.UserEx = new UserExModel(resp.UserEx);
      // console.log(this.loggedIn, resp);
    }

    this.loginResult.emit(this.loggedIn);
  }

  private localStorageChange(event: StorageEvent): void {
    console.log('local storage change', event);
    if ( event.key === this.config.storageKey ){
      const newSigin: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey));
      if ( newSigin && newSigin.session && newSigin.User && newSigin.User.Id && this.loggedIn.User.Id) {
        if ( newSigin.machineId === this.loggedIn.machineId && newSigin.User.Id !== this.loggedIn.User.Id){
          this.logoutWithoutPersistingStorage(); // silently logout without touching any storage
        }
      }
    }
  }

  public saveSessionInfo(): void {
    localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn));
  }

  public isAdmin(): boolean {
    return this.loggedIn.role === 'admin';
  }

  public isBroker(): boolean {
    return this.loggedIn.role === 'broker';
  }

  public isUser(): boolean {
    return this.loggedIn.login === true;
  }

  public get CurrentUser(): PeopleModel {
    if (this.isLoggedIn() ) {
      return this.loggedIn.User;
    }else{
      return new PeopleModel({});
    }
  }

  public isCurrentUser(id: string): boolean {
    return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id;
  }

  public UpdatePeopleInfo(people: PeopleModel): void{
    this.loggedIn.User.Display = people.Display;
    this.loggedIn.User.First = people.First;
    this.loggedIn.User.Last = people.Last;
    this.saveSessionInfo();
  }

  logout(): void {
    if ( ! this.isLoggedIn() ){
      return;
    }

    this.http.post(`${this.config.apiUrl}logout`, '').subscribe(
      resp => {
        this.ToastMessage();
      },
      event => {
        this.ToastMessage('logout from server (with warnings)');
        this.reLogin();
      }, () => {
        this.reLogin();
      }
    );
  }

  public isLoggedIn(): boolean{
    if ( this.loggedIn.login !== undefined &&
      this.loggedIn.login &&
      this.machineId !== '' &&
      this.loggedIn.session !== '' ) {
      return true;
    }
    return false;
  }

  private reLogin(): void {
    this.loggedIn = new ApiV1LoginResponse({});
    localStorage.removeItem(this.config.storageKey);
    this.router.navigate(['/login']).then( () => {
      this.logoutEvent.emit(this.loggedIn);
    });
  }

  logoutWithoutPersistingStorage(): void {
    if ( !this.isLoggedIn() ){
      return;
    }
    console.log('logout without storate');
    this.loggedIn = new ApiV1LoginResponse({});
    this.logoutEvent.emit(this.loggedIn);
    this.router.navigate(['/login']).then(r => {
      this.ToastMessage();
    } );
  }

  logoutAndClearLocalStorage(): void {
    if ( !this.isLoggedIn() ){
      return;
    }
    console.log('logout ++++++++++++++ storate');
    localStorage.removeItem(this.config.storageKey);
    this.loggedIn = new ApiV1LoginResponse({});
    this.logoutEvent.emit(this.loggedIn);
    this.router.navigate(['/login']).then(r => {
      this.ToastMessage();
    } );
  }

  public ToastMessage(msg: string = 'Successfully logged out'): void {
    this.ns.show({
      content: msg,
      cssClass: 'button-notification',
      animation: { type: 'slide', duration: 400 },
      position: { horizontal: 'right', vertical: 'top' },
      type: { style: 'info', icon: true },
      hideAfter : 2000
    });
  }

  public get MachineId(): string {
    return this.machineId || '';
  }

  public set MachineId(mid) {
    if ( this.machineId === '' || this.machineId !== mid ){
      this.machineId = mid;
      localStorage.setItem('mid', mid);
    }
  }

  public get SessionId(): string{
    return this.loggedIn.session || '';
  }

  public set SessionId(sid: string){
    if ( this.loggedIn.session  === '' || this.loggedIn.session !== sid ){
      this.loggedIn.session  = sid;
      this.saveSessionInfo();
      this.ws.SessionId = sid;
    }
  }

  public get SocketId(): string{
    return this.socketId;
  }
  public set SocketId(socketId: string ) {
    this.socketId = socketId;
  }

  public canEditOtherUser(): boolean {
    return this.loggedIn.UserEx.Role === UserRoles.Admin ||
           this.loggedIn.UserEx.Role === UserRoles.Manager;
  }
}
