import * as express from 'express'
import { CLIError } from '@oclif/errors'
import { ExtrinsicFailedError } from '../../runtime/api'
import { BagIdValidationError } from '../../helpers/bagTypes'

/**
 * Dedicated error for the web api requests.
 */
export class WebApiError extends CLIError {
  httpStatusCode: number

  constructor(err: string, httpStatusCode: number) {
    super(err)

    this.httpStatusCode = httpStatusCode
  }
}

/**
 * Dedicated server error for the web api requests.
 */
export class ServerError extends WebApiError {
  constructor(err: string) {
    super(err, 500)
  }
}

/**
 * Returns a directory for file uploading from the response.
 *
 * @remarks
 * This is a helper function. It parses the response object for a variable and
 * throws an error on failure.
 */
export function getUploadsDir(res: express.Response): string {
  if (res.locals.uploadsDir) {
    return res.locals.uploadsDir
  }

  throw new ServerError('No upload directory path loaded.')
}

/**
 * Returns a directory for temporary file uploading from the response.
 *
 * @remarks
 * This is a helper function. It parses the response object for a variable and
 * throws an error on failure.
 */
export function getTempFileUploadingDir(res: express.Response): string {
  if (res.locals.tempFileUploadingDir) {
    return res.locals.tempFileUploadingDir
  }

  throw new ServerError('No temporary uploading directory path loaded.')
}

/**
 * Returns worker ID from the response.
 *
 * @remarks
 * This is a helper function. It parses the response object for a variable and
 * throws an error on failure.
 */
export function getWorkerId(res: express.Response): number {
  if (res.locals.workerId || res.locals.workerId === 0) {
    return res.locals.workerId
  }

  throw new ServerError('No Joystream worker ID loaded.')
}

/**
 * Returns the QueryNode URL from the starting parameters.
 *
 * @remarks
 * This is a helper function. It parses the response object for a variable and
 * throws an error on failure.
 */
export function getQueryNodeUrl(res: express.Response): string {
  if (res.locals.queryNodeUrl) {
    return res.locals.queryNodeUrl
  }

  throw new ServerError('No Query Node URL loaded.')
}

/**
 * Returns a command config.
 *
 * @remarks
 * This is a helper function. It parses the response object for a variable and
 * throws an error on failure.
 */
export function getCommandConfig(res: express.Response): {
  version: string
  userAgent: string
} {
  if (res.locals.config) {
    return res.locals.config
  }

  throw new ServerError('Cannot load command config.')
}

/**
 * Handles errors and sends a response.
 *
 * @param res - Response instance
 * @param err - error
 * @param errorType - defines request type
 * @returns void promise.
 */
export function sendResponseWithError(res: express.Response, err: Error, errorType: string): void {
  const message = isNofileError(err) ? `File not found.` : err.toString()

  res.status(getHttpStatusCodeByError(err)).json({
    type: errorType,
    message,
  })
}

/**
 * Checks the error for 'no-file' error (ENOENT).
 *
 * @param err - error
 * @returns true when error code contains 'ENOENT'.
 */
function isNofileError(err: Error): boolean {
  return err.toString().includes('ENOENT')
}

/**
 * Get the status code by error.
 *
 * @param err - error
 * @returns HTTP status code
 */
export function getHttpStatusCodeByError(err: Error): number {
  if (isNofileError(err)) {
    return 404
  }

  if (err instanceof ExtrinsicFailedError) {
    return 400
  }

  if (err instanceof WebApiError) {
    return err.httpStatusCode
  }

  if (err instanceof CLIError) {
    return 400
  }

  if (err instanceof BagIdValidationError) {
    return 400
  }

  return 500
}
