'use strict';

import DomainClient from '../domain.client';
import MetaApiClient from '../metaapi.client';
import randomstring from 'randomstring';
import {
  ClassicPaginationList, CopyFactoryCloseInstructions, CopyFactoryPortfolioStrategy, CopyFactoryPortfolioStrategyUpdate,
  CopyFactoryStrategy, CopyFactoryStrategyUpdate, CopyFactorySubscriber, CopyFactorySubscriberUpdate, Webhook,
  GetPortfolioStrategiesOptions, GetStrategiesOptions, GetSubscribersOptions, GetWebhooksOptions, StrategyId,
  NewWebhook, WebhookIdAndUrl, WebhookUpdate
} from './configuration.client.schemas';

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

/**
 * metaapi.cloud CopyFactory configuration API (trade copying configuration API) client (see
 * https://metaapi.cloud/docs/copyfactory/)
 */
export default class ConfigurationClient extends MetaApiClient {

  /**
   * Constructs CopyFactory configuration API client instance
   * @param {DomainClient} domainClient domain client
   */
  constructor(domainClient: DomainClient) {
    super(domainClient);
    this._domainClient = domainClient;
  }

  /**
   * Strategy id
   * @typedef {Object} StrategyId
   * @property {String} id strategy id
   */

  /**
   * Retrieves new unused strategy id. Method is accessible only with API access token. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/generateStrategyId/
   * @return {Promise<StrategyId>} promise resolving with strategy id generated
   */
  generateStrategyId(): Promise<StrategyId> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('generateStrategyId');
    }
    const opts = {
      url: '/users/current/configuration/unused-strategy-id',
      method: 'GET',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Generates random account id
   * @return {String} account id
   */
  generateAccountId(): string {
    return randomstring.generate(64);
  }

  /**
   * Retrieves CopyFactory copy trading strategies with pagination in infinite scroll style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getStrategies/
   * @param {GetStrategiesOptions} [options] options
   * @return {Promise<Array<CopyFactoryStrategy>>} promise resolving with CopyFactory strategies found
   */
  async getStrategiesWithInfiniteScrollPagination(options?: GetStrategiesOptions): Promise<Array<CopyFactoryStrategy>> {
    return this._getStrategies('1', options);
  }

  /**
   * Retrieves CopyFactory copy trading strategies with pagination in classic style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getStrategies/
   * @param {GetStrategiesOptions} [options] options
   * @return {Promise<ClassicPaginationList<CopyFactoryStrategy>>} promise resolving with CopyFactory strategies found
   */
  async getStrategiesWithClassicPagination(options?: GetStrategiesOptions):
    Promise<ClassicPaginationList<CopyFactoryStrategy>> {
    return this._getStrategies('2', options);
  }

  private async _getStrategies(apiVersion, options) {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getStrategies');
    }
    return this._domainClient.requestCopyFactory({
      url: '/users/current/configuration/strategies',
      method: 'GET',
      params: options,
      headers: {
        'auth-token': this._token,
        'api-version': apiVersion
      },
      json: true
    }, true);
  }

  /**
   * Retrieves CopyFactory copy trading strategy by id. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getStrategy/
   * @param {string} strategyId trading strategy id
   * @return {Promise<CopyFactoryStrategy>} promise resolving with CopyFactory strategy found
   */
  getStrategy(strategyId: string): Promise<CopyFactoryStrategy> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getStrategy');
    }
    const opts = {
      url: `/users/current/configuration/strategies/${strategyId}`,
      method: 'GET',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Updates a CopyFactory strategy or creates it if it does not exist. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/updateStrategy/
   * @param {String} strategyId copy trading strategy id
   * @param {CopyFactoryStrategyUpdate} strategy trading strategy update
   * @return {Promise} promise resolving when strategy is updated
   */
  updateStrategy(strategyId: string, strategy: CopyFactoryStrategyUpdate): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('updateStrategy');
    }
    const opts = {
      url: `/users/current/configuration/strategies/${strategyId}`,
      method: 'PUT',
      headers: {
        'auth-token': this._token
      },
      data: strategy,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Deletes a CopyFactory strategy. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/removeStrategy/
   * @param {String} strategyId copy trading strategy id
   * @param {CopyFactoryCloseInstructions} [closeInstructions] strategy close instructions
   * @return {Promise} promise resolving when strategy is removed
   */
  removeStrategy(strategyId: string, closeInstructions?: CopyFactoryCloseInstructions): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('removeStrategy');
    }
    const opts = {
      url: `/users/current/configuration/strategies/${strategyId}`,
      method: 'DELETE',
      headers: {
        'auth-token': this._token
      },
      data: closeInstructions,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Retrieves CopyFactory copy portfolio strategies with pagination in infinite scroll style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getPortfolioStrategies/
   * @param {GetPortfolioStrategiesOptions} [options] options
   * @return {Promise<Array<CopyFactoryPortfolioStrategy>>} promise resolving with CopyFactory portfolio strategies
   */
  async getPortfolioStrategiesWithInfiniteScrollPagination(options?: GetPortfolioStrategiesOptions):
    Promise<Array<CopyFactoryPortfolioStrategy>> {
    return this._getPortfolioStrategies('1', options);
  }

  /**
   * Retrieves CopyFactory copy portfolio strategies with pagination in classic style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getPortfolioStrategies/
   * @param {GetPortfolioStrategiesOptions} [options] options
   * @return {Promise<ClassicPaginationList<CopyFactoryStrategy>>} promise resolving with CopyFactory strategies found
   */
  async getPortfolioStrategiesWithClassicPagination(options?: GetPortfolioStrategiesOptions):
    Promise<ClassicPaginationList<CopyFactoryStrategy>> {
    return this._getPortfolioStrategies('2', options);
  }

  private async _getPortfolioStrategies(apiVersion, options) {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getPortfolioStrategies');
    }
    return this._domainClient.requestCopyFactory({
      url: '/users/current/configuration/portfolio-strategies',
      method: 'GET',
      params: options,
      headers: {
        'auth-token': this._token,
        'api-version': apiVersion
      },
      json: true
    }, true);
  }

  /**
   * Retrieves CopyFactory copy portfolio strategy by id. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getPortfolioStrategy/
   * @param {string} portfolioId portfolio strategy id
   * @return {Promise<CopyFactoryPortfolioStrategy>} promise resolving with CopyFactory portfolio strategy found
   */
  getPortfolioStrategy(portfolioId: string): Promise<CopyFactoryPortfolioStrategy> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getPortfolioStrategy');
    }
    const opts = {
      url: `/users/current/configuration/portfolio-strategies/${portfolioId}`,
      method: 'GET',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Updates a CopyFactory portfolio strategy or creates it if it does not exist. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/updatePortfolioStrategy/
   * @param {String} portfolioId copy trading portfolio strategy id
   * @param {CopyFactoryPortfolioStrategyUpdate} portfolio portfolio strategy update
   * @return {Promise} promise resolving when portfolio strategy is updated
   */
  updatePortfolioStrategy(portfolioId: string, portfolio: CopyFactoryPortfolioStrategyUpdate): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('updatePortfolioStrategy');
    }
    const opts = {
      url: `/users/current/configuration/portfolio-strategies/${portfolioId}`,
      method: 'PUT',
      headers: {
        'auth-token': this._token
      },
      data: portfolio,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Deletes a CopyFactory portfolio strategy. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/removePortfolioStrategy/
   * @param {String} portfolioId portfolio strategy id
   * @param {CopyFactoryCloseInstructions} [closeInstructions] strategy close instructions
   * @return {Promise} promise resolving when portfolio strategy is removed
   */
  removePortfolioStrategy(portfolioId: string, closeInstructions?: CopyFactoryCloseInstructions): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('removePortfolioStrategy');
    }
    const opts = {
      url: `/users/current/configuration/portfolio-strategies/${portfolioId}`,
      method: 'DELETE',
      headers: {
        'auth-token': this._token
      },
      data: closeInstructions,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Deletes a CopyFactory portfolio strategy member. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/removePortfolioStrategyMember/
   * @param {String} portfolioId portfolio strategy id
   * @param {String} strategyId id of the strategy to delete member for
   * @param {CopyFactoryCloseInstructions} [closeInstructions] strategy close instructions
   * @return {Promise} promise resolving when portfolio strategy is removed
   */
  removePortfolioStrategyMember(
    portfolioId: string, strategyId: string, closeInstructions?: CopyFactoryCloseInstructions
  ): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('removePortfolioStrategyMember');
    }
    const opts = {
      url: `/users/current/configuration/portfolio-strategies/${portfolioId}/members/${strategyId}`,
      method: 'DELETE',
      headers: {
        'auth-token': this._token
      },
      data: closeInstructions,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Returns CopyFactory subscribers the user has configured with pagination in infinite scroll style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/history/getSubscribers/
   * @param {GetSubscribersOptions} [options] options
   * @return {Promise<Array<CopyFactorySubscriber>>} promise resolving with subscribers found
   */
  async getSubscribersWithInfiniteScrollPagination(
    options?: GetSubscribersOptions
  ): Promise<Array<CopyFactorySubscriber>> {
    return this._getSubscribers('1', options);
  }

  /**
   * Returns CopyFactory subscribers the user has configured with pagination in classic style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/history/getSubscribers/
   * @param {GetSubscribersOptions} [options] options
   * @return {Promise<ClassicPaginationList<CopyFactorySubscriber>>} promise resolving with subscribers found
   */
  async getSubscribersWithClassicPagination(options?: GetSubscribersOptions):
    Promise<ClassicPaginationList<CopyFactorySubscriber>> {
    return this._getSubscribers('2', options);
  }

  private async _getSubscribers(apiVersion, options) {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getSubscribers');
    }
    return this._domainClient.requestCopyFactory({
      url: '/users/current/configuration/subscribers',
      method: 'GET',
      params: options,
      headers: {
        'auth-token': this._token,
        'api-version': apiVersion
      },
      json: true
    }, true);
  }

  /**
   * Returns CopyFactory subscriber by id. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getSubscriber/
   * @param {String} subscriberId subscriber id
   * @returns {Promise<CopyFactorySubscriber>} promise resolving with subscriber found
   */
  getSubscriber(subscriberId: string): Promise<CopyFactorySubscriber> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getSubscriber');
    }
    const opts = {
      url: `/users/current/configuration/subscribers/${subscriberId}`,
      method: 'GET',
      headers: {
        'auth-token': this._token
      },
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Updates CopyFactory subscriber configuration or creates it if it does not exist. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/updateSubscriber/
   * @param {String} subscriberId subscriber id
   * @param {CopyFactorySubscriberUpdate} subscriber subscriber update
   * @returns {Promise} promise resolving when subscriber is updated
   */
  updateSubscriber(subscriberId: string, subscriber: CopyFactorySubscriberUpdate): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('updateSubscriber');
    }
    const opts = {
      url: `/users/current/configuration/subscribers/${subscriberId}`,
      method: 'PUT',
      headers: {
        'auth-token': this._token
      },
      data: subscriber,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Deletes subscriber configuration. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/removeSubscriber/
   * @param {String} subscriberId subscriber id
   * @param {CopyFactoryCloseInstructions} [closeInstructions] subscriber close instructions
   * @returns {Promise} promise resolving when subscriber is removed
   */
  removeSubscriber(subscriberId: string, closeInstructions?: CopyFactoryCloseInstructions): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('removeSubscriber');
    }
    const opts = {
      url: `/users/current/configuration/subscribers/${subscriberId}`,
      method: 'DELETE',
      headers: {
        'auth-token': this._token
      },
      data: closeInstructions,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Deletes a subscription of subscriber to a strategy. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/removeSubscription/
   * @param {String} subscriberId subscriber id
   * @param {String} strategyId strategy id
   * @param {CopyFactoryCloseInstructions} [closeInstructions] subscriber close instructions
   * @returns {Promise} promise resolving when subscriber is removed
   */
  removeSubscription(
    subscriberId: string, strategyId: string, closeInstructions?: CopyFactoryCloseInstructions
  ): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('removeSubscription');
    }
    const opts = {
      url: `/users/current/configuration/subscribers/${subscriberId}/subscriptions/${strategyId}`,
      method: 'DELETE',
      headers: {
        'auth-token': this._token
      },
      data: closeInstructions,
      json: true
    };
    return this._domainClient.requestCopyFactory(opts);
  }

  /**
   * Retrieves CopyFactory user webhooks list with pagination in infinite scroll style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getWebhooks/
   * @param strategyId strategy ID
   * @param options additional options
   * @return promise resolving with webhooks found
   */
  async getWebhooksWithInfiniteScrollPagination(strategyId: string, options?: GetWebhooksOptions): Promise<Webhook[]> {
    let result: Webhook[] = await this._getWebhooks(strategyId, 'infiniteScroll', options);
    result.forEach(item => item.createdAt = new Date(item.createdAt));
    return result;
  }

  /**
   * Retrieves CopyFactory user webhooks list with pagination in classic style. See
   * https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/getWebhooks/
   * @param strategyId strategy ID
   * @param options additional options
   * @return promise resolving with webhooks found
   */
  async getWebhooksWithClassicPagination(
    strategyId: string, options?: GetWebhooksOptions
  ): Promise<ClassicPaginationList<Webhook>> {
    let result: ClassicPaginationList<Webhook> = await this._getWebhooks(strategyId, 'classic', options);
    result.items.forEach(item => item.createdAt = new Date(item.createdAt));
    return result;
  }

  private _getWebhooks(
    strategyId: string, paginationStyle: 'infiniteScroll' | 'classic', options?: GetWebhooksOptions
  ) {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('getWebhooks');
    }
    return this._domainClient.requestCopyFactory({
      url: `/users/current/configuration/strategies/${strategyId}/webhooks`,
      method: 'GET',
      params: {
        ...options,
        paginationStyle
      },
      headers: {'auth-token': this._token},
      json: true
    }, true);
  }

  /**
   * Creates a new webhook. The webhook can be used for an external app (e.g. TradingView) to submit trading signals to
   * CopyFactory. See https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/createWebhook/
   * @param strategyId strategy ID
   * @param webhook webhook
   * @returns promise resolving with created webhook ID and URL
   */
  createWebhook(strategyId: string, webhook: NewWebhook): Promise<WebhookIdAndUrl> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('createWebhook');
    }
    return this._domainClient.requestCopyFactory({
      url: `/users/current/configuration/strategies/${strategyId}/webhooks`,
      method: 'POST',
      headers: {'auth-token': this._token},
      data: webhook,
      json: true
    });
  }

  /**
   * Updates a webhook. See https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/updateWebhook/
   * @param strategyId webhook strategy ID
   * @param webhookId webhook ID
   * @param update webhook update
   * @returns promise resolving when updated
   */
  updateWebhook(strategyId: string, webhookId: string, update: WebhookUpdate): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('updateWebhook');
    }
    return this._domainClient.requestCopyFactory({
      url: `/users/current/configuration/strategies/${strategyId}/webhooks/${webhookId}`,
      method: 'PATCH',
      headers: {'auth-token': this._token},
      data: update,
      json: true
    });
  }

  /**
   * Deletes a webhook. See https://metaapi.cloud/docs/copyfactory/restApi/api/configuration/deleteWebhook/
   * @param strategyId webhook strategy ID
   * @param webhookId webhook ID
   * @returns promise resolving when deleted
   */
  deleteWebhook(strategyId: string, webhookId: string): Promise<void> {
    if (this._isNotJwtToken()) {
      return this._handleNoAccessError('deleteWebhook');
    }
    return this._domainClient.requestCopyFactory({
      url: `/users/current/configuration/strategies/${strategyId}/webhooks/${webhookId}`,
      method: 'DELETE',
      headers: {'auth-token': this._token},
      json: true
    });
  }
}
