import store from '../../store';
import couchdbHelper from './helpers/couchdbHelper';
import Provider from './common/Provider';
import utils from '../utils';
import badgeSvc from '../badgeSvc';

let syncLastSeq;

export default new Provider({
  id: 'couchdbWorkspace',
  name: 'CouchDB',
  getToken() {
    return store.getters['workspace/syncToken'];
  },
  getWorkspaceParams({ dbUrl }) {
    return {
      providerId: this.id,
      dbUrl,
    };
  },
  getWorkspaceLocationUrl({ dbUrl }) {
    return dbUrl;
  },
  getSyncDataUrl(fileSyncData, { id }) {
    const { dbUrl } = this.getToken();
    return `${dbUrl}/${id}/data`;
  },
  getSyncDataDescription(fileSyncData, { id }) {
    return id;
  },
  async initWorkspace() {
    const dbUrl = (utils.queryParams.dbUrl || '').replace(/\/?$/, ''); // Remove trailing /
    const workspaceParams = this.getWorkspaceParams({ dbUrl });
    const workspaceId = utils.makeWorkspaceId(workspaceParams);

    // Create the token if it doesn't exist
    if (!store.getters['data/couchdbTokensBySub'][workspaceId]) {
      store.dispatch('data/addCouchdbToken', {
        sub: workspaceId,
        dbUrl,
      });
    }

    // Create the workspace if it doesn't exist
    if (!store.getters['workspace/workspacesById'][workspaceId]) {
      try {
        // Make sure the database exists and retrieve its name
        const db = await couchdbHelper.getDb(store.getters['data/couchdbTokensBySub'][workspaceId]);
        store.dispatch('workspace/patchWorkspacesById', {
          [workspaceId]: {
            id: workspaceId,
            name: db.db_name,
            providerId: this.id,
            dbUrl,
          },
        });
      } catch (e) {
        throw new Error(`${dbUrl} is not accessible. Make sure you have the proper permissions.`);
      }
    }

    badgeSvc.addBadge('addCouchdbWorkspace');
    return store.getters['workspace/workspacesById'][workspaceId];
  },
  async getChanges() {
    const syncToken = store.getters['workspace/syncToken'];
    const lastSeq = store.getters['data/localSettings'].syncLastSeq;
    const result = await couchdbHelper.getChanges(syncToken, lastSeq);
    const changes = result.changes.filter((change) => {
      if (!change.deleted && change.doc) {
        change.item = change.doc.item;
        if (!change.item || !change.item.id || !change.item.type) {
          return false;
        }
        // Build sync data
        change.syncData = {
          id: change.id,
          itemId: change.item.id,
          type: change.item.type,
          hash: change.item.hash,
          rev: change.doc._rev, // eslint-disable-line no-underscore-dangle
        };
      }
      change.syncDataId = change.id;
      return true;
    });
    syncLastSeq = result.lastSeq;
    return changes;
  },
  onChangesApplied() {
    store.dispatch('data/patchLocalSettings', {
      syncLastSeq,
    });
  },
  async saveWorkspaceItem({ item, syncData }) {
    const syncToken = store.getters['workspace/syncToken'];
    const { id, rev } = await couchdbHelper.uploadDocument({
      token: syncToken,
      item,
      documentId: syncData && syncData.id,
      rev: syncData && syncData.rev,
    });

    // Build sync data to save
    return {
      syncData: {
        id,
        itemId: item.id,
        type: item.type,
        hash: item.hash,
        rev,
      },
    };
  },
  removeWorkspaceItem({ syncData }) {
    const syncToken = store.getters['workspace/syncToken'];
    return couchdbHelper.removeDocument(syncToken, syncData.id, syncData.rev);
  },
  async downloadWorkspaceContent({ token, contentSyncData }) {
    const body = await couchdbHelper.retrieveDocumentWithAttachments(token, contentSyncData.id);
    const rev = body._rev; // eslint-disable-line no-underscore-dangle
    const content = Provider.parseContent(body.attachments.data, body.item.id);
    return {
      content,
      contentSyncData: {
        ...contentSyncData,
        hash: content.hash,
        rev,
      },
    };
  },
  async downloadWorkspaceData({ token, syncData }) {
    if (!syncData) {
      return {};
    }

    const body = await couchdbHelper.retrieveDocumentWithAttachments(token, syncData.id);
    const item = utils.addItemHash(JSON.parse(body.attachments.data));
    const rev = body._rev; // eslint-disable-line no-underscore-dangle
    return {
      item,
      syncData: {
        ...syncData,
        hash: item.hash,
        rev,
      },
    };
  },
  async uploadWorkspaceContent({ token, content, contentSyncData }) {
    const res = await couchdbHelper.uploadDocument({
      token,
      item: {
        id: content.id,
        type: content.type,
        hash: content.hash,
      },
      data: Provider.serializeContent(content),
      dataType: 'text/plain',
      documentId: contentSyncData && contentSyncData.id,
      rev: contentSyncData && contentSyncData.rev,
    });

    // Return new sync data
    return {
      contentSyncData: {
        id: res.id,
        itemId: content.id,
        type: content.type,
        hash: content.hash,
        rev: res.rev,
      },
    };
  },
  async uploadWorkspaceData({ token, item, syncData }) {
    const res = await couchdbHelper.uploadDocument({
      token,
      item: {
        id: item.id,
        type: item.type,
        hash: item.hash,
      },
      data: JSON.stringify(item),
      dataType: 'application/json',
      documentId: syncData && syncData.id,
      rev: syncData && syncData.rev,
    });

    // Return new sync data
    return {
      syncData: {
        id: res.id,
        itemId: item.id,
        type: item.type,
        hash: item.hash,
        rev: res.rev,
      },
    };
  },
  async listFileRevisions({ token, contentSyncDataId }) {
    const body = await couchdbHelper.retrieveDocumentWithRevisions(token, contentSyncDataId);
    const revisions = [];
    body._revs_info.forEach((revInfo, idx) => { // eslint-disable-line no-underscore-dangle
      if (revInfo.status === 'available') {
        revisions.push({
          id: revInfo.rev,
          sub: null,
          created: idx,
          loaded: false,
        });
      }
    });
    return revisions;
  },
  async loadFileRevision({ token, contentSyncDataId, revision }) {
    if (revision.loaded) {
      return false;
    }
    const body = await couchdbHelper.retrieveDocument(token, contentSyncDataId, revision.id);
    revision.sub = body.sub;
    revision.created = body.time;
    revision.loaded = true;
    return true;
  },
  async getFileRevisionContent({ token, contentSyncDataId, revisionId }) {
    const body = await couchdbHelper
      .retrieveDocumentWithAttachments(token, contentSyncDataId, revisionId);
    return Provider.parseContent(body.attachments.data, body.item.id);
  },
});