const axios = require('axios').default
const { readProfile } = require('../profile')
const { createWriteStream } = require('fs')
const wsstream = require('websocket-stream')

/**
 * Client Base class
 */
class Client {
  constructor (argv) {
    this.argv = Object.assign({}, argv)
    this.project = argv.project || argv.projectId
    this.instance = argv.instance || argv.instanceId
    this._profile = readProfile()
    this._version = require('../../package.json').version
  }

  /**
   * @protected
   * @returns default headers
   */
  _headers = () => ({
    Authorization: `Bearer ${this._profile.token}`,
    'User-Agent': `corellium-cli v${this._version}`
  })

  /**
   * Retrieve file stream
   * @protected
   * @param {string} path
   * @param {PathLike} outputFile
   * @returns {Promise<any>}
   */
  _fetchStream = async (path, outputFile) => {
    const response = await axios({
      method: 'GET',
      url: `${this._profile.endpoint}/api/${path}`,
      headers: this._headers(),
      responseType: 'stream'
    })

    const writeStream = createWriteStream(outputFile)
    response.data.pipe(writeStream)

    return new Promise((resolve, reject) => {
      writeStream.on('finish', resolve)
      writeStream.on('error', reject)
    })
  }

  /**
   * Get Websocket stream
   * @protected
   * @param {string} token
   * @returns {wsstream.WebSocketDuplex}
   */
  _fetchWSS = (token) => {
    /** @type {string} */
    const endpoint = this._profile.endpoint
    const match = endpoint.match(/^https?:\/\/([^/]+)$/)
    if (!match) {
      throw new Error('Unable to extract hostname from endpoint')
    }
    const host = match[1]
    const url = `wss://${host}/api/v1/agent/${token}`
    return wsstream(url, ['binary'])
  }

  /**
   * Perform HTTP transaction
   * @protected
   * @param {string} method
   * @param {string} path
   * @param {object} data
   * @returns {Promise<any>}
   */
  _fetch = async (method, path, data) => {
    const headers = this._headers()
    let response
    switch (method) {
      case 'GET':
        response = await axios.get(`${this._profile.endpoint}/api/${path}`, { headers })
        break
      case 'POST':
        response = await axios.post(`${this._profile.endpoint}/api/${path}`, data, { headers })
        break
      case 'DELETE':
        response = await axios.delete(`${this._profile.endpoint}/api/${path}`, { headers })
        break
      case 'HEAD':
        response = await axios.head(`${this._profile.endpoint}/api/${path}`, { headers })
        break
    }
    if (response.status >= 400) { throw new Error(response.statusText) }
    return response.data || {}
  }
}

module.exports = Client
