import { DirectUpload } from '@rails/activestorage';
import { ajax } from '@utils';
import ProgressBar from './progress-bar';
import FileUploadError, {
  errorFromDirectUploadMessage,
  ERROR_CODE_ATTACH
} from './file-upload-error';

/**
  Uploader class is a delegate for DirectUpload instance
  used to track lifecycle and progress of an upload.
  */
export default class Uploader {
  constructor(input, file, directUploadUrl, autoAttachUrl) {
    this.directUpload = new DirectUpload(file, directUploadUrl, this);
    this.progressBar = new ProgressBar(input, this.directUpload.id, file);
    this.autoAttachUrl = autoAttachUrl;
  }

  /**
    Upload (and optionally attach) the file.
    Returns the blob signed id on success.
    Throws a FileUploadError on failure.
    */
  async start() {
    this.progressBar.start();

    try {
      let blobSignedId = await this._upload();

      if (this.autoAttachUrl) {
        await this._attach(blobSignedId);
        // On response, the attachment HTML fragment will replace the progress bar.
      } else {
        this.progressBar.end();
        this.progressBar.destroy();
      }

      return blobSignedId;
    } catch (error) {
      this.progressBar.error(error.message);
      throw error;
    }
  }

  /**
    Upload the file using the DirectUpload instance, and return the blob signed_id.
    Throws a FileUploadError on failure.
    */
  async _upload() {
    return new Promise((resolve, reject) => {
      this.directUpload.create((errorMsg, attributes) => {
        if (errorMsg) {
          let error = errorFromDirectUploadMessage(errorMsg);
          reject(error);
        } else {
          resolve(attributes.signed_id);
        }
      });
    });
  }

  /**
    Attach the file by sending a POST request to the autoAttachUrl.
    Throws a FileUploadError on failure (containing the first validation
    error message, if any).
    */
  async _attach(blobSignedId) {
    const attachmentRequest = {
      url: this.autoAttachUrl,
      type: 'PUT',
      data: `blob_signed_id=${blobSignedId}`
    };

    try {
      await ajax(attachmentRequest);
    } catch (e) {
      let message = e.response && e.response.errors && e.response.errors[0];
      throw new FileUploadError(
        message || 'Error attaching file.',
        e.xhr.status,
        ERROR_CODE_ATTACH
      );
    }
  }

  uploadRequestDidProgress(event) {
    const progress = (event.loaded / event.total) * 100;
    if (progress) {
      this.progressBar.progress(progress);
    }
  }

  directUploadWillStoreFileWithXHR(xhr) {
    xhr.upload.addEventListener('progress', (event) =>
      this.uploadRequestDidProgress(event)
    );
  }
}