import { HttpService } from '../../http-service.js';
import JSZip from 'jszip';

import endpoints from '../../proxy/data/endpoints.json';
import config from '../../../config.json';
import ErrorHandlerService from '../../error-handler-service.js';
const importExperimentURL = `${config.api.proxy.url}${endpoints.proxy.storage.importExperiment.url}`;
const scanStorageURL = `${config.api.proxy.url}${endpoints.proxy.storage.scanStorage.url}`;

/**
 * The Import Experiment Service performs the requests (Extract),
 * processes data such as zip or folder (Transform), 
 * and passes them to the Import Experiment Component (Load).
 * Errors are handled by communicating witht he Error Handler Service.
 */

let _instance = null;
const SINGLETON_ENFORCER = Symbol();

/**
 * Non-default options (content type) for the POST request
 */
const options = {
  mode: 'cors', // no-cors, *cors, same-origin
  cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
  credentials: 'same-origin', // include, *same-origin, omit
  headers: {
    'Content-Type': 'application/octet-stream',
    Referer: 'http://localhost:9000/'
  },
  // redirect: manual, *follow, error
  redirect: 'follow',
  // referrerPolicy: no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin,
  // strict-origin, strict-origin-when-cross-origin, unsafe-url
  referrerPolicy: 'no-referrer',
  //body: JSON.stringify(data) // body data type must match "Content-Type" header
  method: 'POST'
};

export default class ImportExperimentService extends HttpService {
  constructor(enforcer) {
    super();
    if (enforcer !== SINGLETON_ENFORCER) {
      throw new Error('Use ' + this.constructor.name + '.instance');
    }
  }

  static get instance() {
    if (_instance == null) {
      _instance = new ImportExperimentService(SINGLETON_ENFORCER);
    }

    return _instance;
  }

  getImportZipResponses(responses) {
    let importZipResponses = {
      zipBaseFolderName: [],
      destFolderName: []
    };
    importZipResponses.numberOfZips = responses.length;
    responses.forEach(async response =>{
      response = await response.json();
      importZipResponses['zipBaseFolderName'].push(response['zipBaseFolderName']);
      importZipResponses['destFolderName'].push(response['destFolderName']);
    });
    return importZipResponses;
  }

  async getScanStorageResponse(response) {
    response = await response.json();
    let scanStorageResponse = {};
    ['deletedFolders', 'addedFolders'].forEach(name => {
      scanStorageResponse[`${name}Number`] = response[name].length;
      scanStorageResponse[name] = response[name].join(', ');
    });
    return scanStorageResponse;
  }

  async scanStorage() {
    return this.httpRequestPOST(scanStorageURL)
      .then(response => this.getScanStorageResponse(response))
      .catch(error => ErrorHandlerService.instance.networkError(error));
  }

  async zipExperimentFolder(event) {
    let zip = new JSZip();
    let files = event.target.files;
    if (files.length === 0){
      return; // The folder upload was aborted by user
    }
    let promises = [];
    Array.from(files).forEach(file => {
      promises.push(
        new Promise((resolve, reject) => {
          let reader = new FileReader();
          reader.onerror = error => {
            reader.abort();
            return reject(error);
          };
          reader.onload = f =>
            resolve([file.webkitRelativePath, f.target.result]);
          if (
            file.type.startsWith('image') ||
            file.type === 'application/zip' ||
            file.webkitRelativePath.split('.').pop() === 'h5'
          ) {
            reader.readAsArrayBuffer(file);
          }
          else {
            reader.readAsText(file);
          }
        })
          .then(([filepath, filecontent]) =>
            Promise.resolve(
              zip.file(filepath, filecontent, { createFolders: true })
            )
          )
          .catch(error => {
            ErrorHandlerService.instance.dataError(error);
            return Promise.reject(error);
          })
      );
    });

    return Promise.all(promises)
      .then(() => zip.generateAsync({ type: 'blob' }))
      .catch(error => {
        ErrorHandlerService.instance.dataError(error);
        return Promise.reject(error);
      });
  }

  async importExperimentFolder(event) {
    return this.zipExperimentFolder(event).then(async zipContent => {
      return this.httpRequestPOST(importExperimentURL, zipContent, options)
        .then(response => response.json())
        .catch(error => ErrorHandlerService.instance.networkError(error)
        );
    });
  }

  readZippedExperimentExperiment(event) {
    let files = event.target.files;
    let zipFiles = [];
    Array.from(files).forEach(file => {
      zipFiles.push(file);
    });
    let promises = zipFiles.map(zipFile => {
      return new Promise(resolve => {
        let reader = new FileReader();
        reader.onload = f => resolve(f.target.result);
        reader.readAsArrayBuffer(zipFile);
      });
    });
    return Promise.all(promises);
  }

  importZippedExperiment(event) {
    let promises = this.readZippedExperimentExperiment(event)
      .then(zipContents =>
        zipContents.map(zipContent =>
          this.httpRequestPOST(importExperimentURL, zipContent, options)
            .catch(error => {
              ErrorHandlerService.instance.networkError(error);
              return Promise.reject(error);
            })
        )
      )
      .then(responses =>
        Promise
          .all(responses)
          .then(responses => this.getImportZipResponses(responses))
      );
    return promises;
  }

}