'use strict';

import MetaApiClient from '../metaapi.client';
import SubscriberSignalClient from './subscriberSignal.client';
import StrategySignalClient from './strategySignal.client';
import StopoutListenerManager from './streaming/stopoutListenerManager';
import UserLogListenerManager from './streaming/userLogListenerManager';
import ConfigurationClient from './configuration.client';
import DomainClient from '../domain.client';
import StopoutListener from './streaming/stopoutListener';
import UserLogListener from './streaming/userLogListener';
import {
  CopyFactoryStrategyStopout, CopyFactoryStrategyStopoutReason, CopyFactoryUserLogMessage
} from './trading.client.schemas';

export * from './trading.client.schemas';

/**
 * metaapi.cloud CopyFactory trading API (trade copying trading API) client (see
 * https://metaapi.cloud/docs/copyfactory/)
 */
export default class TradingClient extends MetaApiClient {
  
  private _configurationClient: ConfigurationClient;
  private _stopoutListenerManager: StopoutListenerManager;
  private _userLogListenerManager: UserLogListenerManager;

  /**
   * Constructs CopyFactory trading API client instance
   * @param {DomainClient} domainClient domain client
   * @param {ConfigurationClient} configurationClient configuration client
   */
  constructor(domainClient: DomainClient, configurationClient: ConfigurationClient) {
    super(domainClient);
    this._domainClient = domainClient;
    this._configurationClient = configurationClient;
    this._stopoutListenerManager = new StopoutListenerManager(domainClient);
    this._userLogListenerManager = new UserLogListenerManager(domainClient);
  }

  /**
   * Resynchronizes the account. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/resynchronize/
   * @param {string} accountId account id
   * @param {Array<string>} [strategyIds] array of strategy ids to recynchronize. Default is to synchronize all
   * strategies
   * @param {Array<string>} [positionIds] array of position ids to resynchronize. Default is to synchronize all
   * positions
   * @return {Promise} promise which resolves when resynchronization is scheduled
   */
  async resynchronize(accountId: string, strategyIds?: Array<string>, positionIds?: Array<string>): Promise<any> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('resynchronize');
    }
    const opts = {
      url: `/users/current/subscribers/${accountId}/resynchronize`,
      method: 'POST',
      headers: {
        'auth-token': this._token
      },
      params: {
        strategyId: strategyIds,
        positionId: positionIds
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Generates an instance of signal client for a subscriber
   * @param {string} subscriberId subscriber account id
   */
  async getSubscriberSignalClient(subscriberId: string): Promise<SubscriberSignalClient> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getSubscriberSignalClient');
    }

    let accountData = await this._domainClient.getAccountInfo(subscriberId);
    const host = await this._domainClient.getSignalClientHost(accountData.regions);
    return new SubscriberSignalClient(accountData.id, host, this._domainClient);
  }

  /**
   * Generates an instance of signal client for a strategy
   * @param {string} strategyId strategy id
   */
  async getStrategySignalClient(strategyId: string): Promise<StrategySignalClient> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getStrategySignalClient');
    }

    const strategy = await this._configurationClient.getStrategy(strategyId);
    const accountData = await this._domainClient.getAccountInfo(strategy.accountId);
    const host = await this._domainClient.getSignalClientHost(accountData.regions);
    return new StrategySignalClient(accountData.id, strategyId, host, this._domainClient);
  }

  /**
   * Returns subscriber account stopouts. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/getStopOuts/
   * @param {string} subscriberId subscriber id
   * @return {Promise<Array<CopyFactoryStrategyStopout>>} promise which resolves with stopouts found
   */
  async getStopouts(subscriberId: string): Promise<Array<CopyFactoryStrategyStopout>> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getStopouts');
    }
    const opts = {
      url: `/users/current/subscribers/${subscriberId}/stopouts`,
      method: 'GET',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Resets subscription stopouts. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/resetSubscriptionStopOuts/
   * @param {string} subscriberId subscriber id
   * @param {string} strategyId strategy id
   * @param {CopyFactoryStrategyStopoutReason} reason stopout reason to reset
   * yearly-equity, monthly-equity, daily-equity
   * @return {Promise} promise which resolves when the stopouts are reset
   */
  resetSubscriptionStopouts(
    subscriberId: string, strategyId: string, reason: CopyFactoryStrategyStopoutReason
  ): Promise<any> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('resetSubscriptionStopouts');
    }
    const opts = {
      url: `/users/current/subscribers/${subscriberId}/subscription-strategies/` +
        `${strategyId}/stopouts/${reason}/reset`,
      method: 'POST',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Resets subscriber stopouts. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/resetSubscriberStopOuts/
   * @param {string} subscriberId subscriber id
   * @param {CopyFactoryStrategyStopoutReason} reason stopout reason to reset
   * yearly-equity, monthly-equity, daily-equity
   * @return {Promise} promise which resolves when the stopouts are reset
   */
  resetSubscriberStopouts(subscriberId: string, reason: CopyFactoryStrategyStopoutReason): Promise<any> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('resetSubscriberStopouts');
    }
    const opts = {
      url: `/users/current/subscribers/${subscriberId}/stopouts/${reason}/reset`,
      method: 'POST',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Returns copy trading user log for an account and time range. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/getUserLog/
   * @param {string} subscriberId subscriber id
   * @param {Date} [startTime] time to start loading data from
   * @param {Date} [endTime] time to stop loading data at
   * @param {string} [strategyId] strategy id filter
   * @param {string} [positionId] position id filter
   * @param {'DEBUG'|'INFO'|'WARN'|'ERROR'} [level] minimum severity level
   * @param {Number} [offset] pagination offset. Default is 0
   * @param {Number} [limit] pagination limit. Default is 1000
   * @return {Promise<Array<CopyFactoryUserLogMessage>>} promise which resolves with log records found
   */
  async getUserLog(
    subscriberId: string, startTime?: Date, endTime?: Date, strategyId?: string, positionId?: string,
    level?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', offset = 0, limit = 1000
  ): Promise<Array<CopyFactoryUserLogMessage>> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getUserLog');
    }
    const opts = {
      url: `/users/current/subscribers/${subscriberId}/user-log`,
      method: 'GET',
      params: {
        startTime,
        endTime,
        strategyId, 
        positionId, 
        level,
        offset,
        limit
      },
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    let result = await this._domainClient.requestCopyFactory(opts, true);
    if (result) {
      result.map(r => r.time = new Date(r.time));
    }
    return result;
  }

  /**
   * Returns event log for CopyFactory strategy, sorted in reverse chronological order. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/trading/getStrategyLog/ 
   * @param {string} strategyId strategy id to retrieve log for
   * @param {Date} [startTime] time to start loading data from
   * @param {Date} [endTime] time to stop loading data at
   * @param {string} [positionId] position id filter
   * @param {'DEBUG'|'INFO'|'WARN'|'ERROR'} [level] minimum severity level
   * @param {Number} [offset] pagination offset. Default is 0
   * @param {Number} [limit] pagination limit. Default is 1000
   * @return {Promise<Array<CopyFactoryUserLogMessage>>} promise which resolves with log records found
   */
  async getStrategyLog(
    strategyId: string, startTime?: Date, endTime?: Date, positionId?: string,
    level?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', offset = 0, limit = 1000
  ): Promise<Array<CopyFactoryUserLogMessage>> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getStrategyLog');
    }
    const opts = {
      url: `/users/current/strategies/${strategyId}/user-log`,
      method: 'GET',
      params: {
        startTime,
        endTime,
        positionId,
        level,
        offset,
        limit
      },
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    let result = await this._domainClient.requestCopyFactory(opts, true);
    if (result) {
      result.map(r => r.time = new Date(r.time));
    }
    return result;
  }

  /**
   * Adds a stopout listener and creates a job to make requests
   * @param {StopoutListener} listener stopout listener
   * @param {string} [accountId] account id
   * @param {string} [strategyId] strategy id
   * @param {Number} [sequenceNumber] sequence number
   * @return {string} listener id
   */
  addStopoutListener(
    listener: StopoutListener, accountId?: string, strategyId?: string, sequenceNumber?: number
  ): string {
    return this._stopoutListenerManager.addStopoutListener(listener, accountId, strategyId, sequenceNumber);
  }

  /**
   * Removes stopout listener and cancels the event stream
   * @param {string} listenerId stopout listener id
   */
  removeStopoutListener(listenerId: string): void {
    this._stopoutListenerManager.removeStopoutListener(listenerId);
  }

  /**
   * Adds a strategy log listener and creates a job to make requests
   * @param {UserLogListener} listener user log listener
   * @param {string} strategyId strategy id
   * @param {Date} [startTime] log search start time
   * @param {string} [positionId] position id filter
   * @param {'DEBUG'|'INFO'|'WARN'|'ERROR'} [level] minimum severity level
   * @param {Number} [limit] log pagination limit
   * @return {string} listener id
   */
  addStrategyLogListener(
    listener: UserLogListener, strategyId: string, startTime?: Date, positionId?: string,
    level?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', limit?: number
  ): string {
    return this._userLogListenerManager.addStrategyLogListener(
      listener, 
      strategyId, 
      startTime, 
      positionId, 
      level, 
      limit
    );
  }

  /**
   * Removes strategy log listener and cancels the event stream
   * @param {string} listenerId strategy log listener id
   */
  removeStrategyLogListener(listenerId: string): void {
    this._userLogListenerManager.removeStrategyLogListener(listenerId);
  }

  /**
   * Adds a subscriber log listener and creates a job to make requests
   * @param {UserLogListener} listener user log listener
   * @param {string} subscriberId subscriber id
   * @param {Date} [startTime] log search start time
   * @param {string} [strategyId] strategy id filter
   * @param {string} [positionId] position id filter
   * @param {'DEBUG'|'INFO'|'WARN'|'ERROR'} [level] minimum severity level
   * @param {Number} [limit] log pagination limit
   * @return {string} listener id
   */
  addSubscriberLogListener(
    listener: UserLogListener, subscriberId: string, startTime?: Date, strategyId?: string, positionId?: string,
    level?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', limit?: number
  ): string {
    return this._userLogListenerManager.addSubscriberLogListener(
      listener, 
      subscriberId, 
      startTime, 
      strategyId, 
      positionId, 
      level, 
      limit
    );
  }

  /**
   * Removes subscriber log listener and cancels the event stream
   * @param {string} listenerId subscriber log listener id
   */
  removeSubscriberLogListener(listenerId: string): void {
    this._userLogListenerManager.removeSubscriberLogListener(listenerId);
  }
}
