import { isLogInRequired, needsRetry, isUrl } from './utils'
import { CsrfToken } from './types/CsrfToken'
import { UploadFile } from './types/UploadFile'
import { ErrorResponse } from './types'

const requestRetryLimit = 5

export class FileUploader {
  constructor(
    private appLoc: string,
    private serverUrl: string,
    private jobsPath: string,
    private setCsrfTokenWeb: any,
    private csrfToken: CsrfToken | null = null
  ) {
    if (serverUrl) isUrl(serverUrl)
  }

  private retryCount = 0

  public uploadFile(sasJob: string, files: UploadFile[], params: any) {
    return new Promise((resolve, reject) => {
      if (files?.length < 1)
        reject(new ErrorResponse('At least one file must be provided.'))
      if (!sasJob || sasJob === '')
        reject(new ErrorResponse('sasJob must be provided.'))

      let paramsString = ''

      for (let param in params) {
        if (params.hasOwnProperty(param)) {
          paramsString += `&${param}=${params[param]}`
        }
      }

      const program = this.appLoc
        ? this.appLoc.replace(/\/?$/, '/') + sasJob.replace(/^\//, '')
        : sasJob
      const uploadUrl = `${this.serverUrl}${this.jobsPath}/?${
        '_program=' + program
      }${paramsString}`

      const headers = {
        'cache-control': 'no-cache'
      }

      const formData = new FormData()

      for (let file of files) {
        formData.append('file', file.file, file.fileName)
      }

      if (this.csrfToken) formData.append('_csrf', this.csrfToken.value)

      fetch(uploadUrl, {
        method: 'POST',
        body: formData,
        referrerPolicy: 'same-origin',
        headers
      })
        .then(async (response) => {
          if (!response.ok) {
            if (response.status === 403) {
              const tokenHeader = response.headers.get('X-CSRF-HEADER')

              if (tokenHeader) {
                const token = response.headers.get(tokenHeader)
                this.csrfToken = {
                  headerName: tokenHeader,
                  value: token || ''
                }

                this.setCsrfTokenWeb(this.csrfToken)
              }
            }
          }

          return response.text()
        })
        .then((responseText) => {
          if (isLogInRequired(responseText))
            reject(new ErrorResponse('You must be logged in to upload a file.'))

          if (needsRetry(responseText)) {
            if (this.retryCount < requestRetryLimit) {
              this.retryCount++
              this.uploadFile(sasJob, files, params).then(
                (res: any) => resolve(res),
                (err: any) => reject(err)
              )
            } else {
              this.retryCount = 0
              reject(responseText)
            }
          } else {
            this.retryCount = 0

            try {
              resolve(JSON.parse(responseText))
            } catch (e) {
              reject(
                new ErrorResponse(
                  'Error while parsing json from upload response.',
                  e
                )
              )
            }
          }
        })
        .catch((err: any) => {
          reject(new ErrorResponse('Upload request failed.', err))
        })
    })
  }
}
