// Angular Core
import { EventEmitter, Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';

// RxJs
import { BehaviorSubject, Observable, defer, throwError } from 'rxjs';
import { catchError } from "rxjs/operators";

// Services
import { ApiService } from '../api/api.service';

// Interfaces
import { SessionDataInterface } from 'src/app/interfaces/session-data-interface';
import { LocalStorageService } from '../localstorage/local-storage.service';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class SessionService {

  checkTrxadePrimeOnly = new EventEmitter<object>();
  checkGetAuthSession = new EventEmitter<boolean>();

  constructor(private http: HttpClient, public api: ApiService, private localStorageService: LocalStorageService, private router: Router) { }

  /**
  * Private session variable storage model
  * @type Object
  */
  session!:SessionDataInterface;

  /*
   * determines if the ajax call has already run
   * @type Boolean
   */
  hasRun = false;

  /**
   * Push data to required components when session object is updated.
   */
  getSessionOnUpdate = new BehaviorSubject<SessionDataInterface>(this.session);

  /**
   * wrapper for returning promises in backend format (avoids refactor of multiple pages)
   * @type object
   */
  promObj = {
    data: {
      data: {},
      debug: {}
    }
  };

  adminSession: any = {
    success: false,
    data: {
        logged_in: false,
        sitename: ''
    },
    debug: {}
  }

  hasAdminSession: boolean = false;

  /**
   * return session object as a promise
   * @returns {object.promise|$q@call;defer.promise}
   */
  getSessionPromise () {
      return new Promise((resolve, reject) => {
        this.promObj.data.data = this.getSession();
        resolve(this.promObj);
      })
  };

  /**
   * Gets session data and returns a promise
   * @returns {promise}
   */
  get(){
    return new Promise((resolve) => {
      let me = this;
      if(!this.hasRun) {
        this.api.read('/Session')
          .pipe(
            catchError(() => {
              return throwError(() => new Error('ups sommething happend'));
            })
          )
          .subscribe({
            next: (res:any) => {
              let response = res;
              this.session = {
                success : response.success,
                data : response.data,
                debug : response.debug
              }
              this.getSessionOnUpdate.next(this.session);
              this.hasRun = true;
              resolve(res)
            },
            error: (err:HttpErrorResponse) => {
              this.api.errorCallback;
            },
            complete:() => { }
          });
      } else {
        resolve(this.getSessionPromise());
      }
    })
  }

  /*
   * Checks if user logged in if not throws 403
   * @param {type} response
   * @returns {undefined}
   */
  getAuth(){
    return new Promise((resolve) => {
      let me = this;
      if(!this.hasRun) {
          this.api.read('/Session')
          .pipe(
            catchError(() => {
              return throwError(() => new Error('ups sommething happend'));
            })
          )
          .subscribe({
            next: (res:any) => {
              let response = res;
              this.session = {
                success : response.success,
                data : response.data,
                debug : response.debug
              }
              if(!this.session.data.logged_in) {
                  var err403 = {
                      status: 403
                  };
                  this.api.errorCallback(err403);
              }
              this.getSessionOnUpdate.next(this.session);
              this.hasRun = true;
              resolve(res);
            },
            error: (err:ErrorEvent) => {
              this.api.errorCallback;
            },
            complete:() => { }
          });
      } else {
          resolve(this.getSessionPromise());
      }
    });
  };

  /**
   * Hanldes error with APi
   * @param {object} response
   * @returns {undefined}
   */
  errorCallBack(response: any) {
    this.api.errorCallback(response);
  };

  /**
   * gets current session object
   */
  getSession() {
    return this.session?.data;
  };

  /**
   * get current debug object
   */
  getDebug() {
    return this.session?.debug
  };

  getHideControl() {
    return this.session.data.hide_control;
  };

  /**
   * Check if user is a GPO member
   * @returns {.response.data.data.trxade_gpo_member|session.session.trxade_gpo_member}
   */
  isGpoMember() {
    return this.session.data.trxade_gpo_member;
  };

  /*
   * force to check session again
   */
  reCheckSession() {
    this.hasRun = false;
  };

  /**
   * Gets Admin session if admin session object not exists
   * @returns {object.promise|.$q@call;defer.promise|$q@call;defer.promise}
  */
  getAdminSession() {

    return new Promise((resolve) => {
      if(!this.hasAdminSession) {
        this.api.read('/Session/getAdminSession')
          .pipe(
            catchError(() => {
              return throwError(() => new Error('ups sommething happend'));
            })
          )
          .subscribe({
            next: (res:any) => {
              this.adminSession.data = res.data;
              if(res.data.debug) {
                this.adminSession.debug = res.data.debug;
              }
              this.hasAdminSession = true;
              resolve(res)
            },
            error: (err:ErrorEvent) => {
              this.api.errorCallback;
            },
            complete:() => { }
          });
      } else {
        resolve(this.getAdminSessionPromise())
      }

      
    })
  };

  /**
   * return admin session object as a promise
   * @returns {object.promise|$q@call;defer.promise}
   */
  getAdminSessionPromise(): Observable<any> {
    const promObj = {
      data: {
        data: this.getAdminSessionObj(),
        debug: this.getAdminSessionDebug(),
      },
    };
    return defer(() => Promise.resolve(promObj));
  };

  /**
   * gets current admin session object
   */
  getAdminSessionObj(){
      return this.adminSession.data;
  };

  /**
   * get current admin debug object
   */
  getAdminSessionDebug(){
      return this.adminSession.debug;
  };

  /**
   * Clear local storage and navigate to login page
   * if logout response is successful.
   * @returns void
   */
  logOut() {
    return this.api.read('/Session/logout');
  }
}
