// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

import FauxtonAPI from '../../../core/api';
import { deleteRequest } from '../../../core/ajax';
import ActionTypes from './actiontypes';

var currentUploadHttpRequest;

const dispatchInitDocEditor = (params) => {
  FauxtonAPI.reduxDispatch(initDocEditor(params));
};

const initDocEditor = (params) => (dispatch) => {
  const doc = params.doc;

  // ensure a clean slate
  dispatch({ type: ActionTypes.RESET_DOC });

  doc.fetch().then(function () {
    dispatch({
      type: ActionTypes.DOC_LOADED,
      options: {
        doc: doc
      }
    });

    if (params.onLoaded) {
      params.onLoaded();
    }
  }, function (xhr) {
    if (xhr.status === 404) {
      errorNotification(`The ${doc.id} document does not exist.`);
    }

    FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', params.database.id, ''));
  });
};

const saveDoc = (doc, isValidDoc, onSave, navigateToUrl) => dispatch => {
  if (isValidDoc) {
    dispatch({ type: ActionTypes.SAVING_DOCUMENT });

    doc.save().then(function () {
      onSave(doc.prettyJSON());
      dispatch({ type: ActionTypes.SAVING_DOCUMENT_COMPLETED });
      FauxtonAPI.addNotification({
        msg: 'Document saved successfully.',
        type: 'success',
        clear: true
      });
      if (navigateToUrl) {
        FauxtonAPI.navigate(navigateToUrl, {trigger: true});
      } else {
        FauxtonAPI.navigate('#/' + FauxtonAPI.urls('allDocs', 'app',  FauxtonAPI.url.encode(doc.database.id)), {trigger: true});
      }
    }).fail(function (xhr) {
      dispatch({ type: ActionTypes.SAVING_DOCUMENT_COMPLETED });
      FauxtonAPI.addNotification({
        msg: 'Save failed: ' + JSON.parse(xhr.responseText).reason,
        type: 'error',
        fade: false,
        clear: true
      });
    });

  } else if (doc.validationError && doc.validationError === 'Cannot change a documents id.') {
    errorNotification('You cannot edit the _id of an existing document. Try this: Click \'Clone Document\', then change the _id on the clone before saving.');
    delete doc.validationError;
  } else {
    errorNotification('Please fix the JSON errors and try saving again.');
  }
};

const showDeleteDocModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.SHOW_DELETE_DOC_CONFIRMATION_MODAL });
};

const hideDeleteDocModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.HIDE_DELETE_DOC_CONFIRMATION_MODAL });
};

const deleteDoc = (doc) => {
  const databaseName = doc.database.safeID();
  const query = '?rev=' + doc.get('_rev');
  const url = FauxtonAPI.urls('document', 'server', databaseName, doc.safeID(), query);
  deleteRequest(url).then(res => {
    if (res.error) {
      throw new Error(res.reason || res.error);
    }
    FauxtonAPI.addNotification({
      msg: 'Your document has been successfully deleted.',
      type: 'success',
      clear: true
    });
    FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', databaseName, ''));
  }).catch(err => {
    FauxtonAPI.addNotification({
      msg: 'Failed to delete your document. Reason: ' + err.message,
      type: 'error',
      clear: true
    });
  });
};

const showCloneDocModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.SHOW_CLONE_DOC_MODAL });
};

const hideCloneDocModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.HIDE_CLONE_DOC_MODAL });
};

const cloneDoc = (database, doc, newId) => {
  hideCloneDocModal();

  doc.copy(newId).then(() => {
    const url = FauxtonAPI.urls('document', 'app', database.safeID(), encodeURIComponent(newId));
    FauxtonAPI.navigate(url, { trigger: true });

    FauxtonAPI.addNotification({
      msg: 'Document has been duplicated.'
    });

  }, (error) => {
    const errorMsg = `Could not duplicate document, reason: ${error.responseText}.`;
    FauxtonAPI.addNotification({
      msg: errorMsg,
      type: 'error'
    });
  });
};

const showUploadModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.SHOW_UPLOAD_MODAL });
};

const toggleWordWrap = () => (dispatch) => {
  dispatch({ type: ActionTypes.TOGGLE_WRAP_LINE_OPTION });
};

const hideUploadModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.HIDE_UPLOAD_MODAL });
};

const uploadAttachment = (params) => (dispatch) => {
  if (params.files.length === 0) {
    dispatch({
      type: ActionTypes.FILE_UPLOAD_ERROR,
      options: {
        error: 'Please select a file to be uploaded.'
      }
    });
    return;
  }
  dispatch({ type: ActionTypes.START_FILE_UPLOAD });

  const query = '?rev=' + params.rev;
  const db = params.doc.getDatabase().safeID();
  const docId = params.doc.safeID();
  const file = params.files[0];
  const url = FauxtonAPI.urls('document', 'attachment', db, docId, encodeURIComponent(file.name), query);

  const onProgress = (evt) => {
    if (evt.lengthComputable) {
      const percentComplete = evt.loaded / evt.total * 100;
      dispatch({
        type: ActionTypes.SET_FILE_UPLOAD_PERCENTAGE,
        options: {
          percent: percentComplete
        }
      });
    }
  };
  const onSuccess = (doc) => {
    // re-initialize the document editor. Only announce it's been updated when
    dispatch(initDocEditor({
      doc: doc,
      onLoaded: () => {
        dispatch({ type: ActionTypes.FILE_UPLOAD_SUCCESS });
        FauxtonAPI.addNotification({
          msg: 'Document saved successfully.',
          type: 'success',
          clear: true
        });
      }
    }));
  };
  const onError = (msg) => {
    dispatch({
      type: ActionTypes.FILE_UPLOAD_ERROR,
      options: {
        error: msg
      }
    });
  };
  const httpRequest = new XMLHttpRequest();
  currentUploadHttpRequest = httpRequest;
  httpRequest.withCredentials = true;
  if (httpRequest.upload) {
    httpRequest.upload.onprogress = onProgress;
  }
  httpRequest.onloadend = () => {
    currentUploadHttpRequest = undefined;
  };
  httpRequest.onerror = () => {
    onError('Error uploading file');
  };
  httpRequest.onload = () => {
    if (httpRequest.status >= 200 && httpRequest.status < 300) {
      onSuccess(params.doc);
    } else {
      let errorMsg = 'Error uploading file. ';
      try {
        if (httpRequest.responseText) {
          const json = JSON.parse(httpRequest.responseText);
          if (json.error) {
            errorMsg += 'Reason: ' + (json.reason || json.error);
          }
        }
      } catch (err) {
        //ignore parsing error
      }
      onError(errorMsg);
    }
  };
  httpRequest.open('PUT', url);
  httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  httpRequest.setRequestHeader('Content-Type', file.type || `application/octet-stream`);
  httpRequest.setRequestHeader('Accept', 'application/json');
  httpRequest.send(file);
};

const cancelUpload = () => {
  if (currentUploadHttpRequest) {
    currentUploadHttpRequest.abort();
  }
};

const resetUploadModal = () => (dispatch) => {
  dispatch({ type: ActionTypes.RESET_UPLOAD_MODAL });
};


// helpers

function errorNotification (msg) {
  FauxtonAPI.addNotification({
    msg: msg,
    type: 'error',
    clear: true
  });
}

export default {
  dispatchInitDocEditor,
  initDocEditor,
  saveDoc,
  toggleWordWrap,

  // clone doc
  showCloneDocModal,
  hideCloneDocModal,
  cloneDoc,

  // delete doc
  showDeleteDocModal,
  hideDeleteDocModal,
  deleteDoc,

  // upload modal
  showUploadModal,
  hideUploadModal,
  uploadAttachment,
  cancelUpload,
  resetUploadModal
};
