const Authentication = require('./authentication')
const lang = require('./lang')

function safeParseReponse(str, status, url) {
  if (status === 0) {
    return new Error(lang.api_down)
  }
  if (str.slice(0, 9) === '<!doctype') {
    if (status === 500) {
      return new Error('Server is temporarily down, try again later.')
    }
    return new Error('Expected JSON but got HTML (' + status + ': ' + url.split('?')[0] + ')')
  }
  if (!str) {
    return {}
  }
  try {
    return JSON.parse(str)
  } catch (err) {
    return new Error('Unexpected non-JSON response: ' + err.message)
  }
}

const api = {
  loading: false,
  sendRequest: function(options, isPagination) {
    api.loading = true
    let token = Authentication.getToken()
    let pagination = isPagination
  
    if (token) {
      options.headers = options.headers || {}
      options.headers['Authorization'] = 'Bearer ' + token
    }
  
    options.extract = function(xhr) {
      let out = safeParseReponse(xhr.responseText, xhr.status, this.url)

      if (out instanceof Error) {
        throw out
      }
      if (xhr.status >= 300) {
        if (out.message) {
          throw out
        }
        console.error('Got error ' + xhr.status + ' but no error message:', out)
        throw new Error('Unknown or empty response from server.')
      }
      if (pagination) {
        let headers = {}
  
        xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
          let splitted = item.split(': ')
          headers[splitted[0]] = splitted[1]
        })

        out = {
          headers: headers || {},
          data: out,
        }
      }
      return out
    }
  
    return m.request(options)
      .then(function(res) {
        api.loading = false
        window.requestAnimationFrame(m.redraw.bind(m))
        return res
      })
      .catch(function (error) {
        api.loading = false
        window.requestAnimationFrame(m.redraw.bind(m))
        if (error.status === 403) {
          Authentication.clearToken()
          m.route.set('/', { redirect: m.route.get() })
        }
        if (error.response && error.response.status) {
          return Promise.reject(error.response)
        }
        return Promise.reject(error)
      })
  },

  uploadBanner: function(bannerFile, token) {
    var data = new FormData()
    data.append('file', bannerFile)
    data.append('preview', JSON.stringify({
      "out": "base64",
      "format": "avif",
      "resize": {
        "width": 360,
        "height": 203,
        "fit": "cover",
        "withoutEnlargement": true,
        "kernel": "mitchell"
      },
      "avif": {
        "quality": 50,
        "effort": 9
      }
    }))
    data.append('medium', JSON.stringify({
      "format": "avif",
      "resize": {
        "width": 1280,
        "height": 720,
        "fit": "cover",
        "withoutEnlargement": true,
        "kernel": "mitchell"
      },
      "avif": {
        "quality": 75,
        "effort": 3
      }
    }))

    return api.sendRequest({
      method: 'POST',
      url: token.resize + '?token=' + token.token,
      body: data,
    })
    .then(banner => {
      api.sendRequest({
        method: 'DELETE',
        url: token.delete + banner.filename + '?token=' + token.token,
      }).catch(err => console.error(err))

      return {
        file: banner,
        size: bannerFile.size,
        medium: {
          filename: banner.medium.filename,
          path: banner.medium.path,
        },
        preview: {
          base64: banner.preview.base64,
        },
      }
    })
  },

  prettyFormatBytes: function(bytes) {
    if (bytes < 1024) {
      return `${bytes} B`
    }
    if (bytes < 1024 * 1024) {
      return `${(bytes / 1024).toFixed(1)} KB`
    }
    if (bytes < 1024 * 1024 * 1024) {
      return `${(bytes / 1024 / 1024).toFixed(1)} MB`
    }
  },

  uploadFileProgress: function(options, file, reporter) {
    api.loading = true

    return new Promise(function(res, rej) {
      let report = reporter || function() {}
      let formdata = new FormData()

      formdata.append('file', file)

      let request = new XMLHttpRequest()

      let finished = false
      let lastMarker = new Date()
      let lastMarkerLoaded = 0
      let lastMarkerSpeed = '...'

      request.abortRequest = function() {
        finished = true
        request.abort()
      }

      request.upload.addEventListener('progress', function (e) {
        let check = new Date()
        if (check - lastMarker >= 1000) {
          let loaded = e.loaded - lastMarkerLoaded
          lastMarkerSpeed = api.prettyFormatBytes(loaded / ((check - lastMarker) / 1000))
          lastMarker = check
          lastMarkerLoaded = e.loaded
        }
        report(request, Math.min(e.loaded / file.size * 100, 100), lastMarkerSpeed)
      })
      request.addEventListener('abort', function(e) {
        finished = true
        window.requestAnimationFrame(m.redraw.bind(m))
        rej()
      })

      request.addEventListener('readystatechange', function(e) {
        if (finished) return
        if (request.readyState !== 4) return

        finished = true
        let out = safeParseReponse(request.responseText, request.status, options.url)
        if (out instanceof Error || request.status >= 300) {
          return rej(out)
        }
        return res(out)
      })

      request.open(options.method || 'POST', options.url)
      request.send(formdata)

      report(request, 0)
    })
    .then(function(res) {
      api.loading = false
      window.requestAnimationFrame(m.redraw.bind(m))
      return res
    }, function(err) {
      api.loading = false
      window.requestAnimationFrame(m.redraw.bind(m))
      return Promise.reject(err)
    })
  },
}

module.exports = api
