import { Context } from '@nuxt/types/app'
import {DataManager, DataPersisterArgs, DataProviderArgs, UrlArgs} from '~/types/interfaces'
import Connection from '~/services/connection/connection'
import Hookable from '~/services/data/hookable'
import { HTTP_METHOD, QUERY_TYPE } from '~/types/enums'
import ApiError from '~/services/exception/apiError'

/**
 * Base class for data providers, persisters or deleters
 */
abstract class BaseDataManager extends Hookable implements DataManager {
  protected ctx!: Context
  protected defaultArguments: object = {
    type: QUERY_TYPE.MODEL,
    showProgress: true
  }

  /**
   * Initialise le contexte (la connection en particulier)
   * @param {Context} ctx
   */
  public initCtx (ctx: Context) {
    Connection.initConnector(ctx.$axios)
    this.ctx = ctx
  }

  /**
   * Exécute la requête et retourne la réponse désérialisée
   * @param {UrlArgs} _args
   */
  // eslint-disable-next-line require-await
  protected async _invoke (_args: UrlArgs): Promise<any> {
    throw new Error('Not implemented')
  }

  /**
   * Exécute la requête
   * @param {DataProviderArgs|DataPersisterArgs} args
   */
  public async invoke (args: DataProviderArgs|DataPersisterArgs): Promise<any> {
    const queryArguments = { ...this.defaultArguments, ...args }
    BaseDataManager.startLoading(queryArguments)

    await this.triggerHooks(queryArguments)

    try {
      return await this._invoke(queryArguments)
    } catch (error) {
      throw new ApiError(error.response.status, error.response.data.detail)
    }
  }

  /**
   * Signale le début du chargement à nuxt (si showProgress est true)
   * @param {UrlArgs} args
   */
  private static startLoading (args: UrlArgs) {
    if (args.showProgress) {
      const $nuxt = window.$nuxt
      $nuxt.$loading.start()
    }
  }

  /**
   * Send the request trough the Connection
   * @param {string} url
   * @param {HTTP_METHOD} method
   * @param {UrlArgs} args
   */
  protected static request (url: string, method: HTTP_METHOD, args: UrlArgs): Promise<any> {
    return Connection.invoke(method, url, args)
  }
}

export default BaseDataManager
