import Service from './service';
import EventsMixin from './mixin/events';
import prettyBytes from 'pretty-bytes';

class File {
  constructor(options) {
    this.id = ++File.guid;

    Object.assign(this, File.defaults, options);
  }
}

File.defaults = {
  state: 'complete',
  thumbnail: undefined,
  url: undefined,
  error: undefined,
  name: undefined,
  progress: 0,
  size: 0,
  type: '*',
  uploadFile: undefined,
};
File.guid = 0;

const parseType = type =>
  type.indexOf('/') > 0 ? type.slice(0, type.indexOf('/')) : type;

export default class UploadService extends EventsMixin(Service) {
  constructor(user, token) {
    super();
    this.user = user.id;
    this.maxFilesize = user.premium ? 250 : 100;
    this.token = token;

    this.files = [];
    this.refreshed = {};

    this.quota = 1000 * 1000 * 1000;
    this.used = 0;

    this.events = ['update', 'complete'];
  }

  getMaxFileSize() {
    return this.maxFilesize;
  }

  getUploadURL() {
    return '/upload?user=' + this.user;
  }

  getAuthHeaders() {
    return { 'w-utoken': this.token };
  }

  getFiles(type) {
    type = parseType(type);
    if (this.refreshed[type])
      return Promise.resolve(this.files.filter(f => f.type === type));

    return this._fetch(type)
      .then(files => this.files.push.apply(this.files, files))
      .then(this._broadcast.update)
      .then(_ => this.files.filter(f => f.type === type));
  }

  get(id) {
    return this.files.filter(file => file.id === id)[0];
  }

  upload(uploadFile) {
    this.files.push(
      new File({
        state: 'new',
        size: uploadFile.size,
        name: uploadFile.name,
        type: parseType(uploadFile.type),
        uploadFile,
      })
    );
    this.used += uploadFile.size;
    this._broadcast.update();
  }

  uploadComplete(uploadFile, instanceId) {
    if (uploadFile.status === 'error') return;
    const response = JSON.parse(uploadFile.xhr.response);

    const id = response.files.data[0].id;
    this.quota = response.quota.max;
    this.used = response.quota.used;

    this._update(uploadFile, f => {
      f.state = 'complete';
      f.id = id;
      f.path = response.files.data[0].path;
      f.url = response.hostUrl + f.path;
    });

    this._broadcast.complete([this.get(id), instanceId]);
  }

  accept(file, done) {
    // this.used was increased by adding file.size in the `upload` method
    if (this.used < this.quota) {
      return done();
    }
    done(
      `The file exceeds the quota by ${prettyBytes(this.used - this.quota)}.`
    );
  }

  remove(file) {
    this.files = this.files.filter(f => f.id !== file.id);
    this.used -= file.size;

    // if uploaded, also remove from the server
    if (file.state === 'complete') this._remove(file);

    this._broadcast.update();
  }

  setThumbnail(file, thumbnail) {
    this._update(file, f => f.thumbnail = thumbnail);
  }

  setError(file, error) {
    this.used -= file.size;
    this._update(file, f => {
      f.error = error;
      f.size = 0;
    });
  }

  setUploadProgress(file, percentage, size) {
    this._update(file, f => f.progress = percentage);
  }

  setUploadState(file, state) {
    this._update(file, f => f.state = state);
  }

  _update(file, transform) {
    this.files = this.files.map(f => {
      if (f.uploadFile === file) transform(f);
      return f;
    });
    this._broadcast.update();
  }

  _fetch(type) {
    return jQuery
      .ajax({
        url: `/upload/?user=${this.user}&type=${type}`,
        headers: this.getAuthHeaders(),
      })
      .then(response => {
        this.refreshed[type] = true;
        this.quota = response.quota.max;
        this.used = response.quota.used;

        return response.files.data.map(file => {
          const type = parseType(file.mime);
          const url = response.hostUrl + file.path;
          const thumbnail = type === 'image' ? url : undefined;

          return new File({
            id: file.id,
            type,
            url,
            thumbnail,
            path: file.path,
            name: file.name,
            size: file.size,
          });
        });
      });
  }

  _remove(file) {
    return jQuery
      .ajax({
        method: 'DELETE',
        url: `/upload/?user=${this.user}&filePath=${file.path}`,
        headers: this.getAuthHeaders(),
      })
      .then(response => {
        // TODO update quota after UP-3 is done
      })
      .then(this._broadcast.update)
      .catch(console.error.bind(console));
  }
}
