import * as uuid from 'uuid';
import Emitter from './Emitter';
import { IIPPConfig, IConfig } from './Config';
import { IStorageService } from './Storage';
import Promise from 'bluebird';
import * as request from 'xhr';
import Helpers, { IHelpers } from './Helpers';
import Utils, { IUtils } from './Utils';

interface IRequest {
  method: (method: string) => IRequest;
  url: (method: string) => IRequest;
  headers: (method: { [s: string]: string }) => IRequest;
  data: (method: any) => IRequest;
  params: (method: { [s: string]: string }) => IRequest;
  cache: (method: boolean) => IRequest;
}

export interface IApiError {
  method: string;
  data: any;
  code: number;
  statusCode: number;
  httpCode: number;
  error: any;
  httpText: any;
  message: string;
  [key: string]: any;
}

interface IApiErrorParams {
  message: string;
  [key: string]: any;
}

export interface IRequestResult<T> {
  success: boolean;
  data: T;
  httpCode: number;
  httpText: string;
}

export interface IApiTokens {
  access_token: string;
  refresh_token: string;
}

export interface IApiDataUserLogin {
  email: string;
  password: string;
  grant_type?: string;
  client_id?: string;
  client_secret?: string;
}

export interface IApiDataUserLogout {
  all?: string;
}

export interface IApiDataCreateFolder {
  data: IApiDataFolderData
}

export interface IApiDataFolderData {
  name: string;
  display_name?: string;
  description?: string;
  default_symphony_sid?: string;
  page_access_mode?: number
}

export interface IApiDataUpdateDomin {
  domainId: number;
  data: IApiDataFolderData;
}

export interface IApiDataGetPage {
  domainId: number;
  pageId: number;
  seq_no: number;
}

export interface IApiDataGetPageByName {
  domainId: string;
  pageId: string;
  seq_no: number;
}

export interface IApiDataGetPageByUuid {
  uuid: string;
  seq_no: number;
}

export interface IApiDataGetPageVerions {
  pageId: number;
  before: string;
  after: string;
  max: number;
  page: number;
}

export class ApiError extends Error implements IApiError  {
  public method: string = '';
  public data: any;
  public code: number = 0;
  public statusCode: number = 0;
  public httpCode: number =  0;
  public error: any = {};
  public httpText: any = '';
  constructor(params: IApiErrorParams) {
    super(params.message);
    Object.keys(params).forEach(param=>{
      (<IApiError>this)[param] = params[param];
    })
  }
}

class Request implements IRequest {
  public static uuid: string;
  public static config: IConfig;
  public static xipp: any = {
    uuid: '',
    client: '',
    clientVersion: '',
    hsts: true,
  };
  private _method: string;
  private _url: string;
  private _headers: { [s: string]: string } = {};
  private _data: any;
  private _params: any;
  private _cache: boolean = false;
  private _overrideLock: boolean = false;
  private _json: boolean = true;
  private _reponse_type: string = '';

  public static get(url: string): Request {
    return new Request('GET', url);
  }

  public static post(url: string): Request {
    return new Request('POST', url);
  }

  public static put(url: string): Request {
    return new Request('PUT', url);
  }

  public static del(url: string): Request {
    return new Request('DELETE', url);
  }

  constructor(method: string, url: string) {
    this._method = method;
    this._url = url;

    this._headers = {
      // "Strict-Transport-Security": "max-age=15768000;includeSubDomains",
      'Content-Type': 'application/json',
      'x-requested-with': 'XMLHttpRequest',
      'x-ipp-device-uuid': Request.config.uuid || Request.uuid,
      'x-ipp-client': Request.config.api_key,
      'x-ipp-client-version': Request.config.client_version || '1.0',
    };

    if (Request.config.hsts) {
      this._headers['Strict-Transport-Security'] = 'max-age=15768000;includeSubDomains';
    }
  }

  // @todo Bleh...
  public get METHOD(): string {
    return this._method;
  }
  public get URL(): string {
    return this._url;
  }
  public get HEADERS(): { [s: string]: string } {
    return this._headers;
  }
  public get DATA(): any {
    return this._data;
  }
  public get PARAMS(): { [s: string]: string } {
    return this._params;
  }
  public get CACHE(): boolean {
    return this._cache;
  }
  public get OVERRIDE_LOCK(): boolean {
    return this._overrideLock;
  }
  public get JSON(): boolean {
    return this._json;
  }
  public get RESPONSE_TYPE(): string {
    return this._reponse_type;
  }

  public method(method: string): Request {
    this._method = method;
    return this;
  }

  public url(url: string): Request {
    this._url = url;
    return this;
  }

  public headers(headers: { [s: string]: string | number }, overwrite: boolean = false): Request {
    this._headers = overwrite ? headers : (<any>Object).assign({}, this._headers, headers);
    return this;
  }

  public data(data: any, serialize: boolean = false): Request {
    if (serialize) {
      this._data = Object.keys(data)
        .map(function(k) {
          return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]);
        })
        .join('&');
    } else {
      this._data = data;
    }
    return this;
  }

  public params(params: { [s: string]: string | number }, overwrite: boolean = false): Request {
    this._params = overwrite ? params : (<any>Object).assign({}, this._params, params);
    return this;
  }

  public cache(cache: boolean): Request {
    // Allow cache only for GET requests
    if (cache && this._method === 'GET') {
      this._cache = cache;
    }

    return this;
  }

  public overrideLock(override: boolean = true): Request {
    this._overrideLock = override;
    return this;
  }

  public json(json: boolean = true): Request {
    this._json = json;
    return this;
  }

  public responseType(str: string = ''): Request {
    this._reponse_type = str;
    return this;
  }
}

export interface IApiService extends Emitter {
  EVENT_REQ: string;
  EVENT_401: string;
  tokens: IApiTokens;
  req: any;
  lockedTimeCheck: number;
  block: () => void;
  unblock: () => void;

  getSelfInfo: () => Promise<IRequestResult<any>>;
  refreshAccessTokens: (refreshToken: string) => Promise<IRequestResult<any>>;
  userLogin: (data: IApiDataUserLogin) => Promise<IRequestResult<any>>;
  userLogout: (data?: any) => Promise<IRequestResult<any>>;

  getIntegrationChannels: () => Promise<IRequestResult<any>>;
  getIntegrationChannel: (data: any) => Promise<IRequestResult<any>>;
  createIntegrationChannel: (data: any) => Promise<IRequestResult<any>>;
  saveIntegrationChannel: (data: any) => Promise<IRequestResult<any>>;
  deleteIntegrationChannel: (data: any) => Promise<IRequestResult<any>>;

  createFolder: (data: IApiDataCreateFolder) => Promise<IRequestResult<any>>;
  createDomain: (data: IApiDataCreateFolder) => Promise<IRequestResult<any>>;
  getDomains: () => Promise<IRequestResult<any>>;
  getDomain: (domainId: number) => Promise<IRequestResult<any>>;
  removeDomain: (domainId: number) => Promise<IRequestResult<any>>;
  updateDomain: (data: any) => Promise<IRequestResult<any>>;

  getDomainPages: (domainId: number) => Promise<IRequestResult<any>>;
  getDomainsAndPages: (client: string) => Promise<IRequestResult<any>>;
  getPage: (data: any) => Promise<IRequestResult<any>>;
  getPageById(data: any): Promise<IRequestResult<any>>;
  getPageByName: (data: any) => Promise<IRequestResult<any>>;
  getPageByUuid: (data: any) => Promise<IRequestResult<any>>;
  getPageAccess: (data: any) => Promise<IRequestResult<any>>;

  getPageWebhooks: (data: any) => Promise<IRequestResult<any>>;
  getPageWebhook: (data: any) => Promise<IRequestResult<any>>;
  createPageWebhook: (data: any) => Promise<IRequestResult<any>>;
  savePageWebhook: (data: any) => Promise<IRequestResult<any>>;
  deletePageWebhook: (data: any) => Promise<IRequestResult<any>>;

  createPage: (data: any) => Promise<IRequestResult<any>>;
  createPageNotification: (data: any) => Promise<IRequestResult<any>>;
  createPageContentNotification: (data: any) => Promise<IRequestResult<any>>;
  createPageNotificationByUuid: (data: any) => Promise<IRequestResult<any>>;
  createAnonymousPage: (data: any) => Promise<IRequestResult<any>>;
  queryPageContent: (data: any) => Promise<IRequestResult<any>>;
  savePageContent: (data: any) => Promise<IRequestResult<any>>;
  savePageContentDelta: (data: any) => Promise<IRequestResult<any>>;
  savePageSettings: (data: any) => Promise<IRequestResult<any>>;
  deletePage: (data: any) => Promise<IRequestResult<any>>;
  saveUserInfo: (data: any) => Promise<IRequestResult<any>>;
  getUserMetaData: (data: any) => Promise<IRequestResult<any>>;
  saveUserMetaData: (data: any) => Promise<IRequestResult<any>>;
  deleteUserMetaData: (data: any) => Promise<IRequestResult<any>>;
  changePassword: (data: any) => Promise<IRequestResult<any>>;
  changeEmail: (data: any) => Promise<IRequestResult<any>>;
  forgotPassword: (data: any) => Promise<IRequestResult<any>>;
  resetPassword: (data: any) => Promise<IRequestResult<any>>;
  inviteUsers: (data: any) => Promise<IRequestResult<any>>;
  acceptInvitation: (data: any) => Promise<IRequestResult<any>>;
  refuseInvitation: (data: any) => Promise<IRequestResult<any>>;
  domainInvitations: (data: any) => Promise<IRequestResult<any>>;
  userInvitations: () => Promise<IRequestResult<any>>;
  domainAccessLog: (data: any) => Promise<IRequestResult<any>>;
  domainUsers: (data: any) => Promise<IRequestResult<any>>;
  signupUser: (data: any) => Promise<IRequestResult<any>>;
  activateUser: (data: any) => Promise<IRequestResult<any>>;
  setDomainDefault: (data: any) => Promise<IRequestResult<any>>;
  resendInvite: (data: any) => Promise<IRequestResult<any>>;
  updateDomainAccess: (data: any) => Promise<IRequestResult<any>>;
  removeUsersFromDomain: (data: any) => Promise<IRequestResult<any>>;
  getInvitation: (data: any) => Promise<IRequestResult<any>>;
  cancelInvitations: (data: any) => Promise<IRequestResult<any>>;
  getDomainNotificationDestinations: (data: any) => Promise<IRequestResult<any>>;
  createDomainNotificationDestination: (data: any) => Promise<IRequestResult<any>>;
  updateDomainNotificationDestination: (data: any) => Promise<IRequestResult<any>>;
  removeDomainNotificationDestination: (data: any) => Promise<IRequestResult<any>>;
  getUserSymphonyStreams: (data: any) => Promise<IRequestResult<any>>;
  getDomainAccessGroups: (data: any) => Promise<IRequestResult<any>>;
  getDomainAccessGroup: (data: any) => Promise<IRequestResult<any>>;
  addDomainAccessGroup: (data: any) => Promise<IRequestResult<any>>;
  putDomainAgroupMembers: (data: any) => Promise<IRequestResult<any>>;
  putDomainAgroupPages: (data: any) => Promise<IRequestResult<any>>;
  updateDomainAgroup: (data: any) => Promise<IRequestResult<any>>;
  deleteDomainAGroup: (data: any) => Promise<IRequestResult<any>>;
  getDomainPageAccess: (data: any) => Promise<IRequestResult<any>>;
  getDomainCustomers: (data: any) => Promise<IRequestResult<any>>;
  getDomainUsage: (data: any) => Promise<IRequestResult<any>>;
  saveDomainPageAccess: (data: any) => Promise<IRequestResult<any>>;
  getTemplates: (data: any) => Promise<IRequestResult<any>>;
  saveCustomer: (data: any) => Promise<IRequestResult<any>>;
  updateCustomer: (data: any) => Promise<IRequestResult<any>>;
  removeCustomer: (data: any) => Promise<IRequestResult<any>>;
  getDocEmailRules: (data: any) => Promise<IRequestResult<any>>;
  createDocEmailRule: (data: any) => Promise<IRequestResult<any>>;
  updateDocEmailRule: (data: any) => Promise<IRequestResult<any>>;
  deleteDocEmailRule: (data: any) => Promise<IRequestResult<any>>;
  getOrganization: (data: any) => Promise<IRequestResult<any>>;
  getOrganizationMetaData: (data: any) => Promise<IRequestResult<any>>;
  saveOrganizationMetaData: (data: any) => Promise<IRequestResult<any>>;
  deleteOrganizationMetaData: (data: any) => Promise<IRequestResult<any>>;  
  getOrganizationDomains: (data: any) => Promise<IRequestResult<any>>;
  getOrganizationUsers: (data: any) => Promise<IRequestResult<any>>;
  getOrganizationUsage: (data: any) => Promise<IRequestResult<any>>;
  getApplicationPageList: (client: string) => Promise<IRequestResult<any>>;
  getSsoStatus: (email: string) => Promise<IRequestResult<any>>;
  downloadPage: (data: any) => Promise<IRequestResult<any>>;
}

export class Api extends Emitter implements IApiService {
  public static $inject: string[] = [
    '$httpParamSerializerJQLike',
    '$q',
    'ippStorageService',
    'ippConfig',
    'ippUtilsService',
    'ippReqService',
  ];
  public get EVENT_REQ(): string {
    return 'req';
  }
  public get EVENT_401(): string {
    return '401';
  }
  public req: any;
  public tokens: IApiTokens = {
    access_token: '',
    refresh_token: '',
  };
  public lockedTimeCheck: number = 10000;
  // private _endPoint: string;
  private _locked: boolean = false;
  private _lockedTime!: Date;
  private request: any;
  private helpers: IHelpers;
  private utils: IUtils;
  private get END_POINT(): string {
    return this.config.api_url;
  }

  constructor(
    private config: IConfig,
    private storage: IStorageService,
  ) {
    super();
    this.request = request.default;
    // this.END_POINT = `${this.config.api_url}`;
    let _uuid: string = storage.persistent.get('ipp_uuid', '', true);
    if (!_uuid) {
      _uuid = uuid.v4();
      storage.persistent.save('ipp_uuid', _uuid, 365, true);
    }
    Request.config = config;
    Request.uuid = _uuid;
    this.helpers = new Helpers();
    this.utils = new Utils();
  }

  public block(): void {
    this._locked = true;
    this._lockedTime = new Date();
  }

  public unblock(): void {
    this._locked = false;
  }

  public getSelfInfo(): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/users/self/')
        .cache(false)
        .overrideLock(),
    );
  }

  public refreshAccessTokens(refreshToken: string): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/oauth/token/')
        .data(
          {
            grant_type: 'refresh_token',
            client_id: this.config.api_key,
            client_secret: this.config.api_secret,
            refresh_token: refreshToken,
          },
          true,
        )
        .headers({
          'Content-Type': 'application/x-www-form-urlencoded',
          // "x-ipp-client": this.config.api_key
        })
        .json(false)
        .overrideLock(),
    );
  }

  public userLogin(data: IApiDataUserLogin): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/oauth/token/')
        .data(
          {
            grant_type: 'password',
            client_id: this.config.api_key,
            client_secret: this.config.api_secret,
            username: data.email,
            password: data.password,
          },
          true,
        )
        .headers({
          'Content-Type': 'application/x-www-form-urlencoded',
        })
        .json(false),
    );
  }

  public userLoginByCode(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/oauth/token/')
        .params(
          (<any>Object).assign(
            {},
            {
              grant_type: 'authorization_code',
              client_id: this.config.api_key,
              client_secret: this.config.api_secret,
            },
            data,
          ),
        )
        .headers({
          'Content-Type': 'application/x-www-form-urlencoded',
        }),
    );
  }

  public userLogout(data: IApiDataUserLogout = {}): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/oauth/logout/').params({ all: data.all || '' }));
  }

  public getIntegrationChannels(): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/organizations/self/integration_channels/'));
  }

  public getIntegrationChannel(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/integration_channels/' + data.id + '/'));
  }

  public createIntegrationChannel(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/organizations/self/integration_channels/').data(data.data));
  }  

  public saveIntegrationChannel(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/integration_channels/' + data.id + '/').data(data.data));
  }

  public deleteIntegrationChannel(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/integration_channels/' + data.id + '/'));
  }

  public getDomains(): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/'));
  }

  public getDomain(domainId: number): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + domainId + '/'));
  }

  public createFolder(data: IApiDataCreateFolder): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/').data(data.data));
  }

  public createDomain(data: IApiDataCreateFolder): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/').data(data.data));
  }

  public updateDomain(data: IApiDataUpdateDomin): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/domains/' + data.domainId + '/').data(data.data));
  }

  public removeDomain(domainId: number): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + domainId + '/'));
  }

  public disableDomain(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + data.domainId + '/').data(data.data));
  }

  public getDomainPages(domainId: number): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + domainId + '/page_access/'));
  }

  public getDomainsAndPages(client: string): Promise<IRequestResult<any>> {
    if (!client) {
      client = '';
    }
    return this.send(Request.get(this.END_POINT + '/domain_page_access/').params({ client: client }));
  }

  public getPage(data: IApiDataGetPage): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/id/' + data.domainId + '/page_content/id/' + data.pageId + '/').params({
        client_seq_no: data.seq_no,
      }),
    );
  }

  public getPageByName(data: IApiDataGetPageByName): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/name/' + data.domainId + '/page_content/name/' + data.pageId + '/').params(
        { client_seq_no: data.seq_no },
      ),
    );
  }

  public getPageByUuid(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/internal/page_content/' + data.uuid + '/').params({
        client_seq_no: data.seq_no,
      }),
    );
  }

  public getPageVerions(data: IApiDataGetPageVerions): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/page/id/' + data.pageId + '/versions/').params({
        before: data.before,
        after: data.after,
        max: data.max,
        page: data.page,
      }),
    );
  }

  public getPageVerion(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/page/id/' + data.pageId + '/version/' + data.seqNo + '/'));
  }

  public restorePageVerion(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/page/id/' + data.pageId + '/version/' + data.seqNo + '/restore/'));
  }

  public getPageAccess(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/id/' + data.domainId + '/page_access/id/' + data.pageId + '/'),
    );
  }

  public getPageWebhooks(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/page/' + data.pageId + '/webhooks/'),
    );
  }

  public createPageWebhook(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/page/' + data.pageId + '/webhooks/').data(data.data),
    );
  }

  public getPageWebhook(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/webhook/' + data.id + '/'),
    );
  }

  public savePageWebhook(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/webhook/' + data.id + '/').data(data.data),
    );
  }

  public deletePageWebhook(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.del(this.END_POINT + '/webhook/' + data.id + '/'),
    );
  }

  public getPageById(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + data.domainId + '/pages/' + data.pageId + '/'));
  }

  public createPage(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/' + data.domainId + '/pages/').data(data.data));
  }

  public createPageNotification(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/page/' + data.pageId + '/notification/').data(data.data));
  }

  public createPageContentNotification(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/page/' + data.pageId + '/content_notification/').data(data.data));
  }

  public createPageNotificationByUuid(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/page/' + data.uuid + '/notification/').data(data.data));
  }

  public createAnonymousPage(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/anonymous/page/').data(data.data));
  }

  public queryPageContent(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/page/' + data.pageId + '/query/').data(
        data.data,
      ),
    );
  }
  public savePageContent(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/id/' + data.domainId + '/page_content/id/' + data.pageId + '/').data(
        data.data,
      ),
    );
  }

  public savePageContentDelta(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/id/' + data.domainId + '/page_content_delta/id/' + data.pageId + '/').data(
        data.data,
      ),
    );
  }

  public savePageSettings(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/' + data.domainId + '/pages/' + data.pageId + '/').data(data.data),
    );
  }

  public deletePage(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + data.domainId + '/pages/' + data.pageId + '/'));
  }

  public saveUserInfo(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/users/self/').data(data));
  }

  public getUserMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/users/' + data.userId + '/meta/').data(data.data));
  }

  public saveUserMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/users/' + data.userId + '/meta/').data(data.data));
  }

  public deleteUserMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/users/' + data.userId + '/meta/').data(data.data));
  }

  public changePassword(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/credentials/self/').data(data));
  }

  public changeEmail(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/credentials/self/').data(data));
  }

  public forgotPassword(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/password_reset/').data(data));
  }

  public resetPassword(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/password_reset/confirm/').data(data));
  }

  public inviteUsers(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/' + data.domainId + '/invitations/').data(data.data));
  }

  public acceptInvitation(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/users/invitation/confirm/').data(data));
  }

  public refuseInvitation(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/users/invitation/confirm/').data(data));
  }

  public domainInvitations(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/' + data.domainId + '/invitations/').params({ is_complete: 'False' }),
    );
  }

  public userInvitations(): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/users/self/invitations/').params({ is_complete: 'False' }));
  }

  public domainAccessLog(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domain_access/' + data.domainId + '/events/').params({
        page_size: data.limit,
      }),
    );
  }

  public domainUsers(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domain_access/' + data.domainId + '/users/'));
  }

  public signupUser(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/users/signup/').data(data));
  }

  public activateUser(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/users/signup/confirm/').data(data));
  }

  public setDomainDefault(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/domain_access/' + data.domainId + '/users/self/').data(data.data));
  }

  public resendInvite(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/' + data.domainId + '/invitations/' + data.inviteId + '/resend/'),
    );
  }

  public updateDomainAccess(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/domain_access/' + data.domainId + '/users/').data(data.data));
  }

  public removeUsersFromDomain(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domain_access/' + data.domainId + '/users/').data(data.data));
  }

  public getInvitation(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/users/invitations/' + data.token + '/'));
  }

  public cancelInvitations(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + data.domainId + '/invitations/').data(data.data));
  }

  public getDomainNotificationDestinations(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/notification/user/self/destinations/'));
  }

  public createDomainNotificationDestination(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/notification/user/self/destinations/').data(data.data));
  }

  public updateDomainNotificationDestination(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/notification/user_destination/' + data.id + '/').data(data.data));
  }

  public removeDomainNotificationDestination(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/notification/user_destination/' + data.id + '/'));
  }

  public getUserSymphonyStreams(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/notification/user/self/symphony_streams/').params(data && data.params ? data.params : {}));
  }  

  public getDomainAccessGroups(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + data.domainId + '/access_groups/'));
  }

  public getDomainAccessGroup(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/' + data.domainId + '/access_groups/' + data.groupId + '/'),
    );
  }

  public addDomainAccessGroup(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/' + data.domainId + '/access_groups/').data(data.data));
  }

  public putDomainAgroupMembers(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/domains/' + data.domainId + '/access_groups/' + data.agroupId + '/members/').data(
        data.data,
      ),
    );
  }

  public putDomainAgroupPages(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/domains/' + data.domainId + '/access_groups/' + data.agroupId + '/pages/').data(
        data.data,
      ),
    );
  }

  public updateDomainAgroup(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/' + data.domainId + '/access_groups/' + data.agroupId + '/').data(
        data.data,
      ),
    );
  }

  public deleteDomainAGroup(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.del(this.END_POINT + '/domains/' + data.domainId + '/access_groups/' + data.agroupId + '/'),
    );
  }

  public getDomainPageAccess(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domain_page_access/' + data.domainId + '/'));
  }

  public getDomainCustomers(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + data.domainId + '/customers/'));
  }

  public getDomainUsage(data: any = {}): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/domains/id/' + data.domainId + '/usage/').params({
        from_date: data.fromDate,
        to_date: data.toDate,
      }),
    );
  }

  public saveDomainPageAccess(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/domain_page_access/' + data.domainId + '/basic/').data(data.data));
  }

  public getTemplates(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/templates/'));
  }

  public saveCustomer(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/' + data.domainId + '/customers/').data(data.data));
  }

  public updateCustomer(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/' + data.domainId + '/customers/' + data.data.id + '/').data(data.data),
    );
  }

  public removeCustomer(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + data.domainId + '/customers/' + data.customerId + '/'));
  }

  public getDocEmailRules(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/domains/' + data.domainId + '/docsnames/'));
  }

  public createDocEmailRule(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.post(this.END_POINT + '/domains/' + data.domainId + '/docsnames/').data(data.data));
  }

  public updateDocEmailRule(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/domains/' + data.domainId + '/docsnames/' + data.docRuleId + '/').data(data.data),
    );
  }

  public deleteDocEmailRule(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/domains/' + data.domainId + '/docsnames/' + data.docRuleId + '/'));
  }

  private send(request: Request): Promise<IRequestResult<any>> {
    let p: Promise<any> = new Promise((resolve, reject) => {
      // Add auth header
      let token: string = '';
      if (this.storage) {
        token = this.storage.persistent.get('access_token');
      }
      if (!token && this.tokens && this.tokens.access_token) {
        token = this.tokens.access_token;
      }

      if (token) {
        request.headers({
          Authorization: `Bearer ${token}`,
        });
      }

      // check how long api has been locked
      if (this._locked && !request.OVERRIDE_LOCK) {
        let date = new Date().getTime();
        if (date > this._lockedTime.getTime() + this.lockedTimeCheck) {
          this.storage.persistent.remove('renew');
          this.unblock();
          // this.emit(this.EVENT_401);
        }      
      }

      // @todo Proper type...
      let provider: any = this._locked && !request.OVERRIDE_LOCK ? this.dummyRequest : this.request;

      // for now, disabled cache on all requests
      request.cache(false);

      // @todo Add micro time to get requests - !!STUPID IE!!
      /*if (request.METHOD === "GET" && ipp.config.isIE){
              request.params({ie: new Date().getTime()});
          }*/

      let data: any = {
        url: `${request.URL}?${this.helpers.serializeObject(request.PARAMS)}`,
        cache: request.CACHE,
        method: request.METHOD,
        // qs: request.PARAMS,
        data: request.DATA,
        headers: request.HEADERS,
        resolveWithFullResponse: true,
        json: request.JSON,
        responseType: request.RESPONSE_TYPE,
      };

      // dummy request
      if (this._locked && !request.OVERRIDE_LOCK) {
        provider(data).catch(reject);
        return;
      }

      let req: any = provider(
        data,
        (err: any, resp: any, body: any) => {
          if (err || resp.statusCode >= 300) {
            if (err) {
              reject(err);
            } else {
              if (!request.JSON) {
                try {
                  body = JSON.parse(body);
                } catch (e) {
                  body = {};
                }
              }
              let message = this.utils.parseApiError({ data: body }, 'Error');
              let error: IApiError = new ApiError({
                method: resp.method,
                data: body,
                code: resp.statusCode,
                statusCode: resp.statusCode,
                httpCode: resp.statusCode,
                error: message,
                httpText: message,
                message
              });
              // Emit 401
              if (error.code === 401 && !this._locked && error.data.error !== 'invalid_grant') {
                this.emit(this.EVENT_401);
              }
              // let errMsg: any = new Error(err.message);
              reject(error);
            }
          } else {
            if (!request.JSON) {
              try {
                resp.body = JSON.parse(resp.body);
              } catch (e) {
                resp.body = {};
              }
            }
            resolve(this.handleSuccess(resp));
          }
        },
      );
      this.req = req;
      this.emit(this.EVENT_REQ, req);
    });
    return p;
  }

  public getApplicationPageList(client: string = ''): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/application_page_list/').params({
        client: client,
      }),
    );
  }

  public getOrganization(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/'));
  }

  public getOrganizationMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/meta/').data(data.data));
  }

  public saveOrganizationMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.put(this.END_POINT + '/organizations/' + data.organizationId + '/meta/').data(data.data));
  }

  public deleteOrganizationMetaData(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.del(this.END_POINT + '/organizations/' + data.organizationId + '/meta/').data(data.data));
  }  

  public getOrganizationUsers(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/users/').params({
        query: data.query,
      }),
    );
  }

  public getOrganizationUsage(data: any = {}): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/organizations/self/usage/').params({
        from_date: data.fromDate,
        to_date: data.toDate,
      }),
    );
  }

  public getOrganizationUser(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/users/' + data.userId + '/'),
    );
  }

  public createOrganizationUser(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/organizations/' + data.organizationId + '/users/').data(data.data),
    );
  }

  public saveOrganizationUser(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/organizations/' + data.organizationId + '/users/' + data.userId + '/').data(
        data.data,
      ),
    );
  }

  public getOrganizationDomains(data: any): Promise<IRequestResult<any>> {
    return this.send(Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/domains/'));
  }

  public getOrganizationDomain(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/organizations/' + data.organizationId + '/domains/' + data.domainId + '/'),
    );
  }

  public createOrganizationDomain(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/organizations/' + data.organizationId + '/domains/').data(data.data),
    );
  }

  public saveOrganizationDomain(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/organizations/' + data.organizationId + '/domains/' + data.domainId + '/').data(
        data.data,
      ),
    );
  }

  public getSsoStatus(email: string): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/sso/status/').params({
        user: email,
      }),
    );
  }

  public downloadPage(data: any): Promise<IRequestResult<any>> {
    let url: string = `${this.END_POINT}/page/${data.pageId}`;
    if (data.type === 8) {
      url += `/query/`
    } else {
      url += `/download/`;
    }
    return this.send(
      Request.get(url)
        .params({
          type: 'excel',
          header_row: data.header === undefined ? 'true' : 'false',
          live: data.live === undefined ? 'true' : 'false',
          snapshot: data.snapshot === undefined ? 'true' : 'false',
        })
        .headers({
          // "Content-Disposition": "attachment; filename='test.xlxs'",
          // "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        })
        .responseType('arraybuffer'),
    );
  }

  public getTokens(): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/credentials/client_app_access/token/self/')
    );
  }

  public generateToken(): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/credentials/client_app_access/token/self/')
    );
  }

  public deleteToken(id: Number): Promise<IRequestResult<any>> {
    return this.send(
      Request.del(this.END_POINT + '/credentials/client_app_access/token/' + id)
    );
  }

  //Page Schema

  public getSchemas(): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/organizations/self/page_schemas')
    );
  }

  public getSingleSchema(id: Number): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + '/page_schemas/' + id)
    );
  }

  public createSchema(data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.post(this.END_POINT + '/organizations/self/page_schemas/').data(
        data
      )
    );
  }

  public updateSchema(id: Number, data: any): Promise<IRequestResult<any>> {
    return this.send(
      Request.put(this.END_POINT + '/page_schemas/' + id).data(
        data
      )
    );
  }

  public deleteSchema(id: Number): Promise<IRequestResult<any>> {
    return this.send(
      Request.del(this.END_POINT + '/page_schemas/' + id)
    );
  }

  // SDL get history
  public getConnectedHistory(id: Number): Promise<IRequestResult<any>> {
    return this.send(
      Request.get(this.END_POINT + `/connected_page/message_history/page_id/${id}/?max=10`)
    );
  }

  private dummyRequest = (data: any): Promise<any> => {
    console.log('Api is locked down, preventing call ' + data.url);

    let p: Promise<any> = new Promise((resolve, reject) => {
      reject(new ApiError({
        data: data,
        code: 666,
        statusCode: 666,
        message: 'Api is locked',
      }));
    });

    return p;
  };

  private handleSuccess = (response: any): IRequestResult<any> => {
    return {
      success: true,
      data: response.body,
      httpCode: parseInt(response.statusCode, 10),
      httpText: response.statusMessage,
    };
  };
  
}
