import Adapter from 'ember-data/adapter';
import run from 'ember-runloop';
import xhrPromise from 'web-client/utilities/xhr-promise';
import UploadScheduler from 'web-client/utilities/video-uploader/upload-scheduler';
import FileTransfer from 'web-client/utilities/video-uploader/file-transfer';
import RetryTimer from 'web-client/utilities/video-uploader/retry-timer';
import injectService from 'ember-service/inject';

const MEGABYTE = Math.pow(1024, 2);
const SECOND = 1000;

export default Adapter.extend({
  storage: injectService(),

  init() {
    this._super(...arguments);
    this.uploadScheduler = new UploadScheduler();
  },

  updateRecord(store, type, snapshot) {
    let record = snapshot.record;
    let token = snapshot.attr('token');
    let baseURL = snapshot.attr('url');

    let fileTransfer = new FileTransfer({
      file: snapshot.attr('file'),

      sendPart(part) {
        let uploadURL = `${baseURL}?part=${part.number}&upload_token=${token}`;
        let xhr = new XMLHttpRequest();

        xhr.open('PUT', uploadURL, true);

        xhr.upload.onprogress = (event) => {
          run(() => part.onProgress(event));
        };

        part.abort = () => xhr.abort();

        xhr.send(part.file);

        return xhrPromise(xhr);
      },

      onUploadStart() {
        record.onUploadStart();
      },

      onProgress(event) {
        record.onUploadProgress(event);
      },

      retryTimer: new RetryTimer({
        minInterval: 3 * SECOND,
        maxInterval: 20 * SECOND,
        backoff: SECOND
      }),

      partSize: 15 * MEGABYTE,

      maxInFlightRequests: this.get('storage.maxUploadRequests') || 2
    });

    // Give the record the ability to abort the file transfer
    record.xhrAbort = () => fileTransfer.abort();

    return this.uploadScheduler.push(fileTransfer).then(() => {
      return completeFileUpload(snapshot);
    }).then(() => {
      // These APIs don't return anything in the body. Return nothing here so
      // that model attrs are not updated by the serializer.
      return;
    }).finally(() => {
      // Remove the record's reference to the xhr to avoid a circular reference.
      record.xhrAbort = null;
      record.didComplete();
    });
  }
});

function completeFileUpload(snapshot) {
  let baseURL = snapshot.attr('url');
  let token = snapshot.attr('token');
  let completeURL = `${baseURL}/complete?upload_token=${token}`;
  let xhr = new XMLHttpRequest();

  xhr.open('POST', completeURL, true);
  xhr.send();

  return xhrPromise(xhr);
}
