export type PresignedPostData = {
  url: string
  fields: { [key in string]: string }
}

enum AWS_ACL {
  PUBLIC_READ = 'public-read'
}

interface IAWSService {
  uploadPhotoToS3(url: string, file: File | Blob | undefined): Promise<any>
  uploadVideoToS3(
    presignedPostData: PresignedPostData,
    file: File | Blob | undefined
  ): Promise<any>
}

export default class AWSService implements IAWSService {
  static instance: AWSService

  uploadVideoToS3 = (
    data: PresignedPostData,
    file: File | Blob | undefined,
    callbackFn?: (value: number, xhr?: any) => void,
    onFinish?: () => void
  ): Promise<string> => {
    if (!file) throw new Error('File undefined')
    return new Promise((resolve, reject) => {
      const formData = new FormData()

      Object.keys(data.fields).forEach(key => {
        formData.append(key, data.fields[key])
      })

      // Actual file has to be appended last.
      formData.append('x-amz-acl', AWS_ACL.PUBLIC_READ)
      formData.append('Content-Type', file.type)
      formData.append('file', file)

      const xhr = new XMLHttpRequest()
      xhr.upload.addEventListener('progress', value =>
        callbackFn
          ? callbackFn(Math.ceil((value.loaded / value.total) * 100), xhr)
          : null
      )

      xhr.open('POST', data.url, true)
      xhr.send(formData)
      xhr.onload = function () {
        onFinish && onFinish()
        this.status === 204
          ? resolve(`${data.url}/${data.fields.key}`)
          : reject({
              message: this.responseText
            })
      }
    })
  }

  uploadPhotoToS3 = (
    url: string,
    file: File | Blob | undefined,
    callbackFn?: (value: number, xhr?: any) => void,
    onFinish?: () => void
  ): Promise<string> => {
    if (!file) throw new Error('File undefined')

    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest()
      xhr.upload.addEventListener('progress', value =>
        callbackFn
          ? callbackFn(Math.floor((value.loaded / value.total) * 100), xhr)
          : null
      )
      xhr.open('PUT', url, true)
      xhr.setRequestHeader('x-amz-acl', AWS_ACL.PUBLIC_READ)
      xhr.setRequestHeader('Content-Type', file.type)
      xhr.send(file)
      xhr.onload = function async() {
        this.status === 200
          ? (() => {
              resolve(url.split('?')[0])
              onFinish && onFinish()
            })()
          : reject({
              message: this.responseText
            })
      }
    })
  }

  static getInstance() {
    if (!AWSService.instance) {
      AWSService.instance = new AWSService()
    }
    return AWSService.instance
  }
}
