var algo = "AES-CBC";
import Dropzone from "dropzone";
import { initializeButtons } from "./downloader.js";

/**
 *
 * define a dropzone for chill usage
 *
 * An event is launched when dropzone is initialize, allowing to customize events
 * on dropzone :
 *
 * ```
 * window.addEventListener("chill_dropzone_initialized", (e) => {
 *    // do something with dropzone:
 *    e.detail.dropzone.on("success", (e) => {
 *      // see https://www.dropzonejs.com/#event-success
 *    });
 * });
 * ```
 *
 */

// load css
//require('dropzone/dist/basic.css');
require("dropzone/dist/dropzone.css");
require("./index.scss");
//

// disable dropzone autodiscover
Dropzone.autoDiscover = false;

var keyDefinition = {
  name: algo,
  length: 256,
};

var searchForZones = function (root) {
  var zones = root.querySelectorAll("div[data-stored-object]");
  for (let i = 0; i < zones.length; i++) {
    initialize(zones[i]);
  }
};

var getUploadUrl = function (zoneData, files) {
  var generateTempUrlPost = zoneData.zone.querySelector(
      "input[data-async-file-upload]",
    ).dataset.generateTempUrlPost,
    oReq = new XMLHttpRequest();
  // arg, dropzone, you cannot handle async upload...
  oReq.open("GET", generateTempUrlPost, false);
  oReq.send();

  if (oReq.readyState !== XMLHttpRequest.DONE) {
    throw new Error("Error while fetching url to upload");
  }

  zoneData.params = JSON.parse(oReq.responseText);

  return zoneData.params.url;
};

var encryptFile = function (originalFile, zoneData, done) {
  var iv = crypto.getRandomValues(new Uint8Array(16)),
    reader = new FileReader(),
    jsKey,
    rawKey;

  zoneData.originalType = originalFile.type;

  reader.onload = (e) => {
    window.crypto.subtle
      .generateKey(keyDefinition, true, ["encrypt", "decrypt"])
      .then((key) => {
        jsKey = key;

        // we register the key somwhere
        return window.crypto.subtle.exportKey("jwk", key);
      })
      .then((exportedKey) => {
        rawKey = exportedKey;

        // we start encryption
        return window.crypto.subtle.encrypt(
          { name: algo, iv: iv },
          jsKey,
          e.target.result,
        );
      })
      .then((encrypted) => {
        zoneData.crypto = {
          jsKey: jsKey,
          rawKey: rawKey,
          iv: iv,
        };

        done(new File([encrypted], zoneData.suffix));
      });
  };

  reader.readAsArrayBuffer(originalFile);
};

var addBelowButton = (btn, zone, zoneData) => {
  let belowZone = zone.querySelector(".chill-dropzone__below-zone");

  if (belowZone === null) {
    belowZone = document.createElement("div");
    belowZone.classList.add("chill-dropzone__below-zone");
    zone.appendChild(belowZone);
  }

  belowZone.appendChild(btn);
};

var createZone = (zone, zoneData) => {
  var created = document.createElement("div"),
    initMessage = document.createElement("div"),
    initContent = zone.dataset.labelInitMessage,
    dropzoneI;

  created.classList.add("dropzone");
  initMessage.classList.add("dz-message");
  initMessage.appendChild(document.createTextNode(initContent));
  console.log(Dropzone);
  dropzoneI = new Dropzone(created, {
    url: function (files) {
      return getUploadUrl(zoneData, files);
    },
    dictDefaultMessage: zone.dataset.dictDefaultMessage,
    dictFileTooBig: zone.dataset.dictFileTooBig,
    dictRemoveFile: zone.dataset.dictRemoveFile,
    dictMaxFilesExceeded: zone.dataset.dictMaxFilesExceeded,
    dictCancelUpload: zone.dataset.dictCancelUpload,
    dictCancelUploadConfirm: zone.dataset.dictCancelUploadConfirm,
    dictUploadCanceled: zone.dataset.dictUploadCanceled,
    maxFiles: 1,
    addRemoveLinks: true,
    transformFile: function (file, done) {
      encryptFile(file, zoneData, done);
    },
    renameFile: function (file) {
      return zoneData.suffix;
    },
  });

  dropzoneI.on("sending", function (file, xhr, formData) {
    formData.append("redirect", zoneData.params.redirect);
    formData.append("max_file_size", zoneData.params.max_file_size);
    formData.append("max_file_count", zoneData.params.max_file_count);
    formData.append("expires", zoneData.params.expires);
    formData.append("signature", zoneData.params.signature);
  });

  dropzoneI.on("success", function (file, response) {
    zoneData.currentFile = file;
    storeDataInForm(zone, zoneData);
  });

  dropzoneI.on("addedfile", function (file) {
    if (zoneData.hasOwnProperty("currentFile")) {
      dropzoneI.removeFile(zoneData.currentFile);
    }
  });

  dropzoneI.on("removedfile", function (file) {
    removeDataInForm(zone, zoneData);
  });

  zone.insertBefore(created, zone.firstChild);

  let event = new CustomEvent("chill_dropzone_initialized", {
    detail: {
      dropzone: dropzoneI,
      zoneData: zoneData,
    },
  });
  window.dispatchEvent(event);
};

var initialize = function (zone) {
  var allowRemove = zone.dataset.allowRemove,
    zoneData = {
      zone: zone,
      suffix: createFilename(),
      allowRemove: allowRemove,
      old: null,
    };
  if (hasDataInForm(zone, zoneData)) {
    insertRemoveButton(zone, zoneData);
    insertDownloadButton(zone, zoneData);
  } else {
    createZone(zone, zoneData);
  }
};

var createFilename = () => {
  var text = "";
  var possible =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (let i = 0; i < 7; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return text;
};

var storeDataInForm = (zone, zoneData) => {
  var inputKey = zone.querySelector("input[data-stored-object-key]"),
    inputIv = zone.querySelector("input[data-stored-object-iv]"),
    inputObject = zone.querySelector("input[data-async-file-upload]"),
    inputType = zone.querySelector("input[data-async-file-type]");
  inputKey.value = JSON.stringify(zoneData.crypto.rawKey);
  inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv));
  inputType.value = zoneData.originalType;
  inputObject.value = zoneData.params.prefix + zoneData.suffix;

  insertDownloadButton(zone);
};

const restoreDataInForm = (zone, zoneData) => {
  var inputKey = zone.querySelector("input[data-stored-object-key]"),
    inputIv = zone.querySelector("input[data-stored-object-iv]"),
    inputObject = zone.querySelector("input[data-async-file-upload]"),
    inputType = zone.querySelector("input[data-async-file-type]");
  if (zoneData.old === null) {
    console.log("should not have restored data");
    return;
  }

  inputKey.value = zoneData.old.key;
  inputIv.value = zoneData.old.iv;
  inputType.value = zoneData.old.type;
  inputObject.value = zoneData.old.obj;

  insertDownloadButton(zone);
};

const hasDataInForm = (zone, zoneData) => {
  var inputObject = zone.querySelector("input[data-async-file-upload]");
  return inputObject.value.length > 0;
};

var removeDataInForm = (zone, zoneData) => {
  var inputKey = zone.querySelector("input[data-stored-object-key]"),
    inputIv = zone.querySelector("input[data-stored-object-iv]"),
    inputObject = zone.querySelector("input[data-async-file-upload]"),
    inputType = zone.querySelector("input[data-async-file-type]");
  // store data for future usage
  zoneData.old = {
    key: inputKey.value,
    iv: inputIv.value,
    obj: inputObject.value,
    type: inputType.value,
  };
  // set blank values
  inputKey.value = "";
  inputIv.value = "";
  inputType.value = "";
  inputObject.value = "";

  insertDownloadButton(zone);
};

var insertRemoveButton = (zone, zoneData) => {
  var removeButton = document.createElement("a"),
    cancelButton = document.createElement("a"),
    labelRemove = zone.dataset.dictRemove,
    labelCancel = "Restaurer";
  removeButton.classList.add("btn", "btn-delete");
  removeButton.textContent = labelRemove;

  cancelButton.classList.add("btn", "btn-cancel");
  cancelButton.textContent = labelCancel;

  removeButton.addEventListener("click", (e) => {
    e.preventDefault();
    if (zoneData.allowRemove === "true") {
      removeDataInForm(zone, zoneData);
      cancelButton.addEventListener("click", (e) => {
        e.preventDefault();

        restoreDataInForm(zone, zoneData);

        cancelButton.remove();
        zone.querySelector(".dropzone").remove();

        initialize(zone);
      });
    }
    addBelowButton(cancelButton, zone, zoneData);
    //zone.appendChild(cancelButton);
    removeButton.remove();
    createZone(zone, zoneData);
  });

  addBelowButton(removeButton, zone, zoneData);
  // zone.appendChild(removeButton);
};

const removeDownloadButton = (zone, zoneData) => {
  var existingButtons = zone.querySelectorAll("a[data-download-button]");
  // remove existing
  existingButtons.forEach(function (b) {
    b.remove();
  });
};

var insertDownloadButton = (zone, zoneData) => {
  var existingButtons = zone.querySelectorAll("a[data-download-button]"),
    newButton = document.createElement("a"),
    inputKey = zone.querySelector("input[data-stored-object-key]"),
    inputIv = zone.querySelector("input[data-stored-object-iv]"),
    inputObject = zone.querySelector("input[data-async-file-upload]"),
    inputType = zone.querySelector("input[data-async-file-type]"),
    labelPreparing = zone.dataset.labelPreparing,
    labelQuietButton = zone.dataset.labelQuietButton,
    labelReady = zone.dataset.labelReady,
    tempUrlGenerator = zone.dataset.tempUrlGenerator,
    tempUrlGeneratorParams = new URLSearchParams();
  // remove existing
  existingButtons.forEach(function (b) {
    b.remove();
  });

  if (inputObject.value === "") {
    return;
  }

  tempUrlGeneratorParams.append("object_name", inputObject.value);

  newButton.dataset.downloadButton = true;
  newButton.dataset.key = inputKey.value;
  newButton.dataset.iv = inputIv.value;
  newButton.dataset.mimeType = inputType.value;
  newButton.dataset.labelPreparing = labelPreparing;
  newButton.dataset.labelReady = labelReady;
  newButton.dataset.tempUrlGetGenerator =
    tempUrlGenerator + "?" + tempUrlGeneratorParams.toString();
  newButton.classList.add("btn", "btn-download", "dz-bt-below-dropzone");
  newButton.textContent = labelQuietButton;

  addBelowButton(newButton, zone, zoneData);
  //zone.appendChild(newButton);
  initializeButtons(zone);
};

window.addEventListener("load", function (e) {
  searchForZones(document);
});

window.addEventListener("collection-add-entry", function (e) {
  searchForZones(e.detail.entry);
});

export { searchForZones };
