text: import store from '../../store';
  import googleHelper from './helpers/googleHelper';
  import Provider from './common/Provider';
  import utils from '../utils';
  import workspaceSvc from '../workspaceSvc';
  
  let fileIdToOpen;
  let syncStartPageToken;
  
  export default new Provider({
    id: 'googleDriveWorkspace',
    name: 'Google Drive',
    getToken() {
      return store.getters['workspace/syncToken'];
    },
    getWorkspaceParams({ folderId }) {
      return {
        providerId: this.id,
        folderId,
      };
    },
    getWorkspaceLocationUrl({ folderId }) {
      return `https://docs.google.com/folder/d/${folderId}`;
    },
    getSyncDataUrl({ id }) {
      return `https://docs.google.com/file/d/${id}/edit`;
    },
    getSyncDataDescription({ id }) {
      return id;
    },
    async initWorkspace() {
      const makeWorkspaceId = folderId => folderId
        && utils.makeWorkspaceId(this.getWorkspaceParams({ folderId }));
  
      const getWorkspace = folderId =>
        store.getters['workspace/workspacesById'][makeWorkspaceId(folderId)];
  
      const initFolder = async (token, folder) => {
        const appProperties = {
          folderId: folder.id,
          dataFolderId: folder.appProperties.dataFolderId,
          trashFolderId: folder.appProperties.trashFolderId,
        };
  
        // Make sure data folder exists
        if (!appProperties.dataFolderId) {
          const dataFolder = await googleHelper.uploadFile({
            token,
            name: '.stackedit-data',
            parents: [folder.id],
            appProperties: { folderId: folder.id },
            mediaType: googleHelper.folderMimeType,
          });
          appProperties.dataFolderId = dataFolder.id;
        }
  
        // Make sure trash folder exists
        if (!appProperties.trashFolderId) {
          const trashFolder = await googleHelper.uploadFile({
            token,
            name: '.stackedit-trash',
            parents: [folder.id],
            appProperties: { folderId: folder.id },
            mediaType: googleHelper.folderMimeType,
          });
          appProperties.trashFolderId = trashFolder.id;
        }
  
        // Update workspace if some properties are missing
        if (appProperties.folderId !== folder.appProperties.folderId
          || appProperties.dataFolderId !== folder.appProperties.dataFolderId
          || appProperties.trashFolderId !== folder.appProperties.trashFolderId
        ) {
          await googleHelper.uploadFile({
            token,
            appProperties,
            mediaType: googleHelper.folderMimeType,
            fileId: folder.id,
          });
        }
  
        // Update workspace in the store
        const workspaceId = makeWorkspaceId(folder.id);
        store.dispatch('workspace/patchWorkspacesById', {
          [workspaceId]: {
            id: workspaceId,
            sub: token.sub,
            name: folder.name,
            providerId: this.id,
            folderId: folder.id,
            teamDriveId: folder.teamDriveId,
            dataFolderId: appProperties.dataFolderId,
            trashFolderId: appProperties.trashFolderId,
          },
        });
      };
  
      // Token sub is in the workspace or in the url if workspace is about to be created
      const { sub } = getWorkspace(utils.queryParams.folderId) || utils.queryParams;
      // See if we already have a token
      let token = store.getters['data/googleTokensBySub'][sub];
      // If no token has been found, popup an authorize window and get one
      if (!token || !token.isDrive || !token.driveFullAccess) {
        await store.dispatch('modal/open', 'workspaceGoogleRedirection');
        token = await googleHelper.addDriveAccount(true, utils.queryParams.sub);
      }
  
      let { folderId } = utils.queryParams;
      // If no folderId is provided, create one
      if (!folderId) {
        const folder = await googleHelper.uploadFile({
          token,
          name: 'StackEdit workspace',
          parents: [],
          mediaType: googleHelper.folderMimeType,
        });
        await initFolder(token, {
          ...folder,
          appProperties: {},
        });
        folderId = folder.id;
      }
  
      // Init workspace
      if (!getWorkspace(folderId)) {
        let folder;
        try {
          folder = await googleHelper.getFile(token, folderId);
        } catch (err) {
          throw new Error(`Folder ${folderId} is not accessible. Make sure you have the right permissions.`);
        }
        folder.appProperties = folder.appProperties || {};
        const folderIdProperty = folder.appProperties.folderId;
        if (folderIdProperty && folderIdProperty !== folderId) {
          throw new Error(`Folder ${folderId} is part of another workspace.`);
        }
        await initFolder(token, folder);
      }
  
      return getWorkspace(folderId);
    },
    async performAction() {
      const state = googleHelper.driveState || {};
      const token = this.getToken();
      switch (token && state.action) {
        case 'create': {
          const driveFolder = googleHelper.driveActionFolder;
          let syncData = store.getters['data/syncDataById'][driveFolder.id];
          if (!syncData && driveFolder.appProperties.id) {
            // Create folder if not already synced
            store.commit('folder/setItem', {
              id: driveFolder.appProperties.id,
              name: driveFolder.name,
            });
            const item = store.state.folder.itemsById[driveFolder.appProperties.id];
            syncData = {
              id: driveFolder.id,
              itemId: item.id,
              type: item.type,
              hash: item.hash,
            };
            store.dispatch('data/patchSyncDataById', {
              [syncData.id]: syncData,
            });
          }
          const file = await workspaceSvc.createFile({
            parentId: syncData && syncData.itemId,
          }, true);
          store.commit('file/setCurrentId', file.id);
          // File will be created on next workspace sync
          break;
        }
        case 'open': {
          // open first file only
          const firstFile = googleHelper.driveActionFiles[0];
          const syncData = store.getters['data/syncDataById'][firstFile.id];
          if (!syncData) {
            fileIdToOpen = firstFile.id;
          } else {
            store.commit('file/setCurrentId', syncData.itemId);
          }
          break;
        }
        default:
      }
    },
    async getChanges() {
      const workspace = store.getters['workspace/currentWorkspace'];
      const syncToken = store.getters['workspace/syncToken'];
      const lastStartPageToken = store.getters['data/localSettings'].syncStartPageToken;
      const { changes, startPageToken } = await googleHelper
        .getChanges(syncToken, lastStartPageToken, false, workspace.teamDriveId);
  
      syncStartPageToken = startPageToken;
      return changes;
    },
    prepareChanges(changes) {
      // Collect possible parent IDs
      const parentIds = {};
      Object.entries(store.getters['data/syncDataByItemId']).forEach(([id, syncData]) => {
        parentIds[syncData.id] = id;
      });
      changes.forEach((change) => {
        const { id } = (change.file || {}).appProperties || {};
        if (id) {
          parentIds[change.fileId] = id;
        }
      });
  
      // Collect changes
      const workspace = store.getters['workspace/currentWorkspace'];
      const result = [];
      changes.forEach((change) => {
        // Ignore changes on StackEdit own folders
        if (change.fileId === workspace.folderId
          || change.fileId === workspace.dataFolderId
          || change.fileId === workspace.trashFolderId
        ) {
          return;
        }
  
        let contentChange;
        if (change.file) {
          // Ignore changes in files that are not in the workspace
          const { appProperties } = change.file;
          if (!appProperties || appProperties.folderId !== workspace.folderId
          ) {
            return;
          }
  
          // If change is on a data item
          if (change.file.parents[0] === workspace.dataFolderId) {
            // Data item has a JSON filename
            try {
              change.item = JSON.parse(change.file.name);
            } catch (e) {
              return;
            }
          } else {
            // Change on a file or folder
            const type = change.file.mimeType === googleHelper.folderMimeType
              ? 'folder'
              : 'file';
            const item = {
              id: appProperties.id,
              type,
              name: change.file.name,
              parentId: null,
            };
  
            // Fill parentId
            if (change.file.parents.some(parentId => parentId === workspace.trashFolderId)) {
              item.parentId = 'trash';
            } else {
              change.file.parents.some((parentId) => {
                if (!parentIds[parentId]) {
                  return false;
                }
                item.parentId = parentIds[parentId];
                return true;
              });
            }
            change.item = utils.addItemHash(item);
  
            if (type === 'file') {
              // create a fake change as a file content change
              const id = `${appProperties.id}/content`;
              const syncDataId = `${change.fileId}/content`;
              contentChange = {
                item: {
                  id,
                  type: 'content',
                  // Need a truthy value to force saving sync data
                  hash: 1,
                },
                syncData: {
                  id: syncDataId,
                  itemId: id,
                  type: 'content',
                  // Need a truthy value to force downloading the content
                  hash: 1,
                },
                syncDataId,
              };
            }
          }
  
          // Build sync data
          change.syncData = {
            id: change.fileId,
            parentIds: change.file.parents,
            itemId: change.item.id,
            type: change.item.type,
            hash: change.item.hash,
          };
        } else {
          // Item was removed
          const syncData = store.getters['data/syncDataById'][change.fileId];
          if (syncData && syncData.type === 'file') {
            // create a fake change as a file content change
            contentChange = {
              syncDataId: `${change.fileId}/content`,
            };
          }
        }
  
        // Push change
        change.syncDataId = change.fileId;
        result.push(change);
        if (contentChange) {
          result.push(contentChange);
        }
      });
  
      return result;
    },
    onChangesApplied() {
      store.dispatch('data/patchLocalSettings', {
        syncStartPageToken,
      });
    },
    async saveWorkspaceItem({ item, syncData, ifNotTooLate }) {
      const workspace = store.getters['workspace/currentWorkspace'];
      const syncToken = store.getters['workspace/syncToken'];
      let file;
      if (item.type !== 'file' && item.type !== 'folder') {
        // For sync/publish locations, store item as filename
        file = await googleHelper.uploadFile({
          token: syncToken,
          name: JSON.stringify(item),
          parents: [workspace.dataFolderId],
          appProperties: {
            folderId: workspace.folderId,
          },
          fileId: syncData && syncData.id,
          oldParents: syncData && syncData.parentIds,
          ifNotTooLate,
        });
      } else {
        // For type `file` or `folder`
        const parentSyncData = store.getters['data/syncDataByItemId'][item.parentId];
        let parentId;
        if (item.parentId === 'trash') {
          parentId = workspace.trashFolderId;
        } else if (parentSyncData) {
          parentId = parentSyncData.id;
        } else {
          parentId = workspace.folderId;
        }
  
        file = await googleHelper.uploadFile({
          token: syncToken,
          name: item.name,
          parents: [parentId],
          appProperties: {
            id: item.id,
            folderId: workspace.folderId,
          },
          mediaType: item.type === 'folder' ? googleHelper.folderMimeType : undefined,
          fileId: syncData && syncData.id,
          oldParents: syncData && syncData.parentIds,
          ifNotTooLate,
        });
      }
  
      // Build sync data to save
      return {
        syncData: {
          id: file.id,
          parentIds: file.parents,
          itemId: item.id,
          type: item.type,
          hash: item.hash,
        },
      };
    },
    async removeWorkspaceItem({ syncData, ifNotTooLate }) {
      // Ignore content deletion
      if (syncData.type !== 'content') {
        const syncToken = store.getters['workspace/syncToken'];
        await googleHelper.removeFile(syncToken, syncData.id, ifNotTooLate);
      }
    },
    async downloadWorkspaceContent({ token, contentSyncData, fileSyncData }) {
      const data = await googleHelper.downloadFile(token, fileSyncData.id);
      const content = Provider.parseContent(data, contentSyncData.itemId);
  
      // Open the file requested by action if it wasn't synced yet
      if (fileIdToOpen && fileIdToOpen === fileSyncData.id) {
        fileIdToOpen = null;
        // Open the file once downloaded content has been stored
        setTimeout(() => {
          store.commit('file/setCurrentId', fileSyncData.itemId);
        }, 10);
      }
  
      return {
        content,
        contentSyncData: {
          ...contentSyncData,
          hash: content.hash,
        },
      };
    },
    async downloadWorkspaceData({ token, syncData }) {
      if (!syncData) {
        return {};
      }
  
      const content = await googleHelper.downloadFile(token, syncData.id);
      const item = JSON.parse(content);
      return {
        item,
        syncData: {
          ...syncData,
          hash: item.hash,
        },
      };
    },
    async uploadWorkspaceContent({
      token,
      content,
      file,
      fileSyncData,
      ifNotTooLate,
    }) {
      let gdriveFile;
      let newFileSyncData;
  
      if (fileSyncData) {
        // Only update file media
        gdriveFile = await googleHelper.uploadFile({
          token,
          media: Provider.serializeContent(content),
          fileId: fileSyncData.id,
          ifNotTooLate,
        });
      } else {
        // Create file with media
        const workspace = store.getters['workspace/currentWorkspace'];
        const parentSyncData = store.getters['data/syncDataByItemId'][file.parentId];
        gdriveFile = await googleHelper.uploadFile({
          token,
          name: file.name,
          parents: [parentSyncData ? parentSyncData.id : workspace.folderId],
          appProperties: {
            id: file.id,
            folderId: workspace.folderId,
          },
          media: Provider.serializeContent(content),
          ifNotTooLate,
        });
  
        // Create file sync data
        newFileSyncData = {
          id: gdriveFile.id,
          parentIds: gdriveFile.parents,
          itemId: file.id,
          type: file.type,
          hash: file.hash,
        };
      }
  
      // Return new sync data
      return {
        contentSyncData: {
          id: `${gdriveFile.id}/content`,
          itemId: content.id,
          type: content.type,
          hash: content.hash,
        },
        fileSyncData: newFileSyncData,
      };
    },
    async uploadWorkspaceData({
      token,
      item,
      syncData,
      ifNotTooLate,
    }) {
      const workspace = store.getters['workspace/currentWorkspace'];
      const file = await googleHelper.uploadFile({
        token,
        name: JSON.stringify({
          id: item.id,
          type: item.type,
          hash: item.hash,
        }),
        parents: [workspace.dataFolderId],
        appProperties: {
          folderId: workspace.folderId,
        },
        media: JSON.stringify(item),
        mediaType: 'application/json',
        fileId: syncData && syncData.id,
        oldParents: syncData && syncData.parentIds,
        ifNotTooLate,
      });
  
      // Return new sync data
      return {
        syncData: {
          id: file.id,
          parentIds: file.parents,
          itemId: item.id,
          type: item.type,
          hash: item.hash,
        },
      };
    },
    async listFileRevisions({ token, fileSyncData }) {
      const revisions = await googleHelper.getFileRevisions(token, fileSyncData.id);
      return revisions.map(revision => ({
        id: revision.id,
        sub: `go:${revision.lastModifyingUser.permissionId}`,
        created: new Date(revision.modifiedTime).getTime(),
      }));
    },
    async loadFileRevision() {
      // Revision are already loaded
      return false;
    },
    async getFileRevisionContent({
      token,
      contentId,
      fileSyncData,
      revisionId,
    }) {
      const content = await googleHelper.downloadFileRevision(token, fileSyncData.id, revisionId);
      return Provider.parseContent(content, contentId);
    },
  });
  