/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */
/* global Dropzone */
import _ from 'underscore';
import './preview_markdown';

window.DropzoneInput = (function() {
  function DropzoneInput(form) {
    const divHover = '<div class="div-dropzone-hover"></div>';
    const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
    const $attachButton = form.find('.button-attach-file');
    const $attachingFileMessage = form.find('.attaching-file-message');
    const $cancelButton = form.find('.button-cancel-uploading-files');
    const $retryLink = form.find('.retry-uploading-link');
    const $uploadProgress = form.find('.uploading-progress');
    const $uploadingErrorContainer = form.find('.uploading-error-container');
    const $uploadingErrorMessage = form.find('.uploading-error-message');
    const $uploadingProgressContainer = form.find('.uploading-progress-container');
    const uploadsPath = window.uploads_path || null;
    const maxFileSize = gon.max_file_size || 10;
    const formTextarea = form.find('.js-gfm-input');
    let handlePaste;
    let pasteText;
    let addFileToForm;
    let updateAttachingMessage;
    let isImage;
    let getFilename;
    let uploadFile;

    formTextarea.wrap('<div class="div-dropzone"></div>');
    formTextarea.on('paste', (function(_this) {
      return function(event) {
        return handlePaste(event);
      };
    })(this));

    // Add dropzone area to the form.
    const $mdArea = formTextarea.closest('.md-area');
    form.setupMarkdownPreview();
    const $formDropzone = form.find('.div-dropzone');
    $formDropzone.parent().addClass('div-dropzone-wrapper');
    $formDropzone.append(divHover);
    $formDropzone.find('.div-dropzone-hover').append(iconPaperclip);

    if (!uploadsPath) return;

    const dropzone = $formDropzone.dropzone({
      url: uploadsPath,
      dictDefaultMessage: '',
      clickable: true,
      paramName: 'file',
      maxFilesize: maxFileSize,
      uploadMultiple: false,
      headers: {
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
      },
      previewContainer: false,
      processing: function() {
        return $('.div-dropzone-alert').alert('close');
      },
      dragover: function() {
        $mdArea.addClass('is-dropzone-hover');
        form.find('.div-dropzone-hover').css('opacity', 0.7);
      },
      dragleave: function() {
        $mdArea.removeClass('is-dropzone-hover');
        form.find('.div-dropzone-hover').css('opacity', 0);
      },
      drop: function() {
        $mdArea.removeClass('is-dropzone-hover');
        form.find('.div-dropzone-hover').css('opacity', 0);
        formTextarea.focus();
      },
      success: function(header, response) {
        const processingFileCount = this.getQueuedFiles().length + this.getUploadingFiles().length;
        const shouldPad = processingFileCount >= 1;

        pasteText(response.link.markdown, shouldPad);
        // Show 'Attach a file' link only when all files have been uploaded.
        if (!processingFileCount) $attachButton.removeClass('hide');
        addFileToForm(response.link.url);
      },
      error: function(file, errorMessage = 'Attaching the file failed.', xhr) {
        // If 'error' event is fired by dropzone, the second parameter is error message.
        // If the 'errorMessage' parameter is empty, the default error message is set.
        // If the 'error' event is fired by backend (xhr) error response, the third parameter is
        // xhr object (xhr.responseText is error message).
        // On error we hide the 'Attach' and 'Cancel' buttons
        // and show an error.

        // If there's xhr error message, let's show it instead of dropzone's one.
        const message = xhr ? xhr.responseText : errorMessage;

        $uploadingErrorContainer.removeClass('hide');
        $uploadingErrorMessage.html(message);
        $attachButton.addClass('hide');
        $cancelButton.addClass('hide');
      },
      totaluploadprogress: function(totalUploadProgress) {
        updateAttachingMessage(this.files, $attachingFileMessage);
        $uploadProgress.text(Math.round(totalUploadProgress) + '%');
      },
      sending: function(file) {
        // DOM elements already exist.
        // Instead of dynamically generating them,
        // we just either hide or show them.
        $attachButton.addClass('hide');
        $uploadingErrorContainer.addClass('hide');
        $uploadingProgressContainer.removeClass('hide');
        $cancelButton.removeClass('hide');
      },
      removedfile: function() {
        $attachButton.removeClass('hide');
        $cancelButton.addClass('hide');
        $uploadingProgressContainer.addClass('hide');
        $uploadingErrorContainer.addClass('hide');
      },
      queuecomplete: function() {
        $('.dz-preview').remove();
        $('.markdown-area').trigger('input');

        $uploadingProgressContainer.addClass('hide');
        $cancelButton.addClass('hide');
      }
    });

    const child = $(dropzone[0]).children('textarea');

    // removeAllFiles(true) stops uploading files (if any)
    // and remove them from dropzone files queue.
    $cancelButton.on('click', (e) => {
      const target = e.target.closest('form').querySelector('.div-dropzone');

      e.preventDefault();
      e.stopPropagation();
      Dropzone.forElement(target).removeAllFiles(true);
    });

    // If 'error' event is fired, we store a failed files,
    // clear dropzone files queue, change status of failed files to undefined,
    // and add that files to the dropzone files queue again.
    // addFile() adds file to dropzone files queue and upload it.
    $retryLink.on('click', (e) => {
      const dropzoneInstance = Dropzone.forElement(e.target.closest('form').querySelector('.div-dropzone'));
      const failedFiles = dropzoneInstance.files;

      e.preventDefault();

      // 'true' parameter of removeAllFiles() cancels uploading of files that are being uploaded at the moment.
      dropzoneInstance.removeAllFiles(true);

      failedFiles.map((failedFile, i) => {
        const file = failedFile;

        if (file.status === Dropzone.ERROR) {
          file.status = undefined;
          file.accepted = undefined;
        }

        return dropzoneInstance.addFile(file);
      });
    });

    handlePaste = function(event) {
      var filename, image, pasteEvent, text;
      pasteEvent = event.originalEvent;
      if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) {
        image = isImage(pasteEvent);
        if (image) {
          event.preventDefault();
          filename = getFilename(pasteEvent) || 'image.png';
          text = `{{${filename}}}`;
          pasteText(text);
          return uploadFile(image.getAsFile(), filename);
        }
      }
    };

    isImage = function(data) {
      var i, item;
      i = 0;
      while (i < data.clipboardData.items.length) {
        item = data.clipboardData.items[i];
        if (item.type.indexOf('image') !== -1) {
          return item;
        }
        i += 1;
      }
      return false;
    };

    pasteText = function(text, shouldPad) {
      var afterSelection, beforeSelection, caretEnd, caretStart, textEnd;
      var formattedText = text;
      if (shouldPad) formattedText += "\n\n";
      const textarea = child.get(0);
      caretStart = textarea.selectionStart;
      caretEnd = textarea.selectionEnd;
      textEnd = $(child).val().length;
      beforeSelection = $(child).val().substring(0, caretStart);
      afterSelection = $(child).val().substring(caretEnd, textEnd);
      $(child).val(beforeSelection + formattedText + afterSelection);
      textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
      textarea.style.height = `${textarea.scrollHeight}px`;
      formTextarea.get(0).dispatchEvent(new Event('input'));
      return formTextarea.trigger('input');
    };

    addFileToForm = function(path) {
      $(form).append('<input type="hidden" name="files[]" value="' + _.escape(path) + '">');
    };

    getFilename = function(e) {
      var value;
      if (window.clipboardData && window.clipboardData.getData) {
        value = window.clipboardData.getData('Text');
      } else if (e.clipboardData && e.clipboardData.getData) {
        value = e.clipboardData.getData('text/plain');
      }
      value = value.split("\r");
      return value[0];
    };

    const showSpinner = function(e) {
      return $uploadingProgressContainer.removeClass('hide');
    };

    const closeSpinner = function() {
      return $uploadingProgressContainer.addClass('hide');
    };

    const showError = function(message) {
      $uploadingErrorContainer.removeClass('hide');
      $uploadingErrorMessage.html(message);
    };

    const closeAlertMessage = function() {
      return form.find('.div-dropzone-alert').alert('close');
    };

    const insertToTextArea = function(filename, url) {
      return $(child).val(function(index, val) {
        return val.replace(`{{${filename}}}`, url);
      });
    };

    const appendToTextArea = function(url) {
      return $(child).val(function(index, val) {
        return val + url + "\n";
      });
    };

    uploadFile = function(item, filename) {
      var formData;
      formData = new FormData();
      formData.append('file', item, filename);
      return $.ajax({
        url: uploadsPath,
        type: 'POST',
        data: formData,
        dataType: 'json',
        processData: false,
        contentType: false,
        headers: {
          'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
        },
        beforeSend: function() {
          showSpinner();
          return closeAlertMessage();
        },
        success: function(e, textStatus, response) {
          return insertToTextArea(filename, response.responseJSON.link.markdown);
        },
        error: function(response) {
          return showError(response.responseJSON.message);
        },
        complete: function() {
          return closeSpinner();
        }
      });
    };

    updateAttachingMessage = (files, messageContainer) => {
      let attachingMessage;
      const filesCount = files.filter(function(file) {
        return file.status === 'uploading' ||
               file.status === 'queued';
      }).length;

      // Dinamycally change uploading files text depending on files number in
      // dropzone files queue.
      if (filesCount > 1) {
        attachingMessage = 'Attaching ' + filesCount + ' files -';
      } else {
        attachingMessage = 'Attaching a file -';
      }

      messageContainer.text(attachingMessage);
    };

    form.find('.markdown-selector').click(function(e) {
      e.preventDefault();
      $(this).closest('.gfm-form').find('.div-dropzone').click();
      formTextarea.focus();
    });
  }

  return DropzoneInput;
})();