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 = {
  lastModified: undefined,
  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.token = token;

    this.refreshed = {};

    this.files = [];

    // max file size in MB
    this.maxFilesize = user.premium ? 250 : 25; // MB limit / file for premium / free plan

    // max quota in bytes
    this.quota = (user.premium ? 10000 : 250) * 1000 * 1000; // total quota for premium / free plan
    
    // used quota in bytes
    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 => {
        // console.log('fetched files:', files)
        return 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) {
    // console.log("on upload file:", uploadFile);

    // create a new File instance from the uploadFile 
    let file = new File({
      state: 'new',
      size: uploadFile.size,
      name: uploadFile.name,
      type: parseType(uploadFile.type),
      lastModified: Date.now(),
      uploadFile,
    });
    console.log("on upload file:", file);

    // push it to the files array
    this.files.push(file);

    // increase used quota
    this.used += uploadFile.size;

    // broadcast the update of the files array
    this._broadcast.update();
  }

  uploadComplete(uploadFile, instanceId) {
    // console.log("uploadComplete file:", uploadFile);

    if (uploadFile.status === 'error') return;

    // read metadata from the file that's on the server
    const response = JSON.parse(uploadFile.xhr.response);
    // console.log("uploadComplete server response:", response);
    
    // read file from the response
    let serverFile = response.files.data[0];
    // console.log("uploadComplete file on the server:", serverFile);

    // read quota from response
    // this.quota = response.quota.max;
    this.used = response.quota.used;

    // update the file in the files array with the data from the server
    this._update(uploadFile, f => {
      f.state = 'complete';
      f.id = serverFile.id;
      f.path = serverFile.path;
      f.url = response.hostUrl + f.path;
      f.lastModified = serverFile.lastModified;
    });

    // broadcast the completion of the upload based on the fileId and instanceId
    this._broadcast.complete([this.get(serverFile.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) {
    // apply the transform function to the file in the array
    this.files = this.files.map(f => {
      if (f.uploadFile === file) transform(f);
      return f;
    });

    // broadcast the update of the files array
    this._broadcast.update();
  }

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

        // read quota from response
        // this.quota = response.quota.max;
        this.used = response.quota.used;

        return response.files.data.map(file => {

          // console.log("fetched file:", file);

          const type = parseType(file.mime);
          const url = response.hostUrl + file.path;
          const thumbnail = type === 'image' ? url : undefined;

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

  _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));
  }
}
