import {NuxtAxiosInstance} from '@nuxtjs/axios'
import {AxiosRequestConfig} from 'axios'
import {AnyJson, DataPersisterArgs, DataProviderArgs, UrlArgs} from '~/types/interfaces'
import {HTTP_METHOD, QUERY_TYPE} from '~/types/enums'
import TypesTesting from "~/services/utils/typesTesting";

/**
 * @category Services/connection
 * @class Connection
 *
 * Classe Wrapper du connecteur de requête (Axios dans notre cas)
 */
class Connection {
  static connector: NuxtAxiosInstance;

  /**
   * Initialisation du connecteur (Axios dans notre cas)
   * @param {NuxtAxiosInstance} connector
   */
  static initConnector (connector: NuxtAxiosInstance) {
    Connection.connector = connector
  }

  /**
   * Main méthode qui appellera les méthodes privées correspondantes (getItem, getCollection, put, post, delete)
   * @param {HTTP_METHOD} method Mode de requêtage (GET, PUT, DELETE...)
   * @param {string} url
   * @param {DataProviderArgs|DataPersisterArgs} args
   * @return {Promise<any>}
   */
  public static invoke (method: HTTP_METHOD, url: string, args: UrlArgs): Promise<any> {
    switch (method) {
      case HTTP_METHOD.GET:
        if (args.id) {
          return Connection.getItem(url, args.id, args.showProgress, args.params)
        } else {
          return Connection.getCollection(url, args.type, args.showProgress, args.params)
        }

      case HTTP_METHOD.PUT:
      case HTTP_METHOD.POST:
        if (!TypesTesting.isDataPersisterArgs(args)) {
          throw new Error('*args* is not a dataPersisterArgs')
        }
        if (!args.data) {
          throw new Error('*args* has no data')
        }
        return method === HTTP_METHOD.PUT ? Connection.put(url, args.id, args.data, args.showProgress, args.params) :
                                            Connection.post(url, args.data, args.showProgress, args.params)

      case HTTP_METHOD.DELETE:
        return Connection.deleteItem(url, args.id, args.showProgress, args.params)
    }

    throw new Error('Unknown connection method was invoked')
  }

  /**
   * GET Item : préparation de la config pour la récupération d'un item
   * @param {string} url
   * @param {number} id
   * @param {boolean} showProgress
   * @param {AnyJson} params
   * @return {Promise<any>}
   */
  public static getItem (url: string, id: number, showProgress: boolean = true, params: AnyJson = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      url: `${url}/${id}`,
      method: HTTP_METHOD.GET,
      progress: showProgress,
      params: params
    }
    return Connection.request(config)
  }

  /**
   * Get collection : préparation de la config pour la récupération d'une collection d'items
   * @param {string} url
   * @param {boolean} progress
   * @param {QUERY_TYPE} type
   * @param {AnyJson} params
   * @return {Promise<any>}
   */
  public static getCollection (url: string, type: QUERY_TYPE, progress: boolean = true, params: AnyJson = {}): Promise<any> {
    let config: AxiosRequestConfig = {
      url: `${url}`,
      method: HTTP_METHOD.GET,
      progress,
      params: params
    }
    if(type === QUERY_TYPE.IMAGE)
      config = {...config, responseType: 'blob'}

    return Connection.request(config)
  }

  /**
   * Post : préparation de la config pour la création d'un item
   * @param {string} url
   * @param {AnyJson} data
   * @param {boolean} progress
   * @param {AnyJson} params
   * @return {Promise<any>}
   */
  public static post (url: string, data: AnyJson, progress: boolean = true, params: AnyJson = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      url: `${url}`,
      method: HTTP_METHOD.POST,
      data,
      progress,
      params: params
    }
    return Connection.request(config)
  }

  /**
   * Put : préparation de la config pour la mise à jour d'un item
   * @param {string} url
   * @param {number} id
   * @param {AnyJson} data
   * @param {boolean} progress
   * @param {AnyJson} params
   * @return {Promise<any>}
   */
  public static put (url: string, id: number, data: AnyJson, progress: boolean = true, params: AnyJson = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      url: `${url}/${id}`,
      method: HTTP_METHOD.PUT,
      data,
      progress,
      params: params
    }
    return Connection.request(config)
  }

  /**
   * DELETE Item : préparation de la config pour la suppression d'un item
   * @param {string} url
   * @param {number} id
   * @param {boolean} progress
   * @param {AnyJson} params
   * @return {Promise<any>}
   */
  public static deleteItem (url: string, id: number, progress: boolean = true, params: AnyJson = {}): Promise<any> {
    const config: AxiosRequestConfig = {
      url: `${url}/${id}`,
      method: HTTP_METHOD.DELETE,
      progress,
      params: params
    }
    return Connection.request(config)
  }

  /**
   * Exécute la requete
   * @param {AxiosRequestConfig} config
   * @return {Promise<any>}
   */
  public static async request (config: AxiosRequestConfig): Promise<any> {
    return await Connection.connector.$request(config)
  }
}

export default Connection
