import { attempt, isError, take } from 'lodash';
import uuid from 'uuid/v4';
import { EditorialWorkflowError } from 'netlify-cms-lib-util';
import { Cursor, CURSOR_COMPATIBILITY_SYMBOL } from 'netlify-cms-lib-util';
import AuthenticationPage from './AuthenticationPage';

window.repoFiles = window.repoFiles || {};
window.repoFilesUnpublished = window.repoFilesUnpublished || [];

function getFile(path) {
  const segments = path.split('/');
  let obj = window.repoFiles;
  while (obj && segments.length) {
    obj = obj[segments.shift()];
  }
  return obj || {};
}

const pageSize = 10;

const getCursor = (collection, extension, entries, index) => {
  const count = entries.length;
  const pageCount = Math.floor(count / pageSize);
  return Cursor.create({
    actions: [
      ...(index < pageCount ? ['next', 'last'] : []),
      ...(index > 0 ? ['prev', 'first'] : []),
    ],
    meta: { index, count, pageSize, pageCount },
    data: { collection, extension, index, pageCount },
  });
};

const getFolderEntries = (folder, extension) => {
  return Object.keys(window.repoFiles[folder] || {})
    .filter(path => path.endsWith(`.${extension}`))
    .map(path => ({
      file: { path: `${folder}/${path}` },
      data: window.repoFiles[folder][path].content,
    }))
    .reverse();
};

export default class TestRepo {
  constructor(config, options = {}) {
    this.config = config;
    this.assets = [];
    this.options = options;
  }

  authComponent() {
    return AuthenticationPage;
  }

  restoreUser(user) {
    return this.authenticate(user);
  }

  authenticate() {
    return Promise.resolve();
  }

  logout() {
    return null;
  }

  getToken() {
    return Promise.resolve('');
  }

  traverseCursor(cursor, action) {
    const { collection, extension, index, pageCount } = cursor.data.toObject();
    const newIndex = (() => {
      if (action === 'next') {
        return index + 1;
      }
      if (action === 'prev') {
        return index - 1;
      }
      if (action === 'first') {
        return 0;
      }
      if (action === 'last') {
        return pageCount;
      }
    })();
    // TODO: stop assuming cursors are for collections
    const allEntries = getFolderEntries(collection.get('folder'), extension);
    const entries = allEntries.slice(newIndex * pageSize, newIndex * pageSize + pageSize);
    const newCursor = getCursor(collection, extension, allEntries, newIndex);
    return Promise.resolve({ entries, cursor: newCursor });
  }

  entriesByFolder(collection, extension) {
    const folder = collection.get('folder');
    const entries = folder ? getFolderEntries(folder, extension) : [];
    const cursor = getCursor(collection, extension, entries, 0);
    const ret = take(entries, pageSize);
    ret[CURSOR_COMPATIBILITY_SYMBOL] = cursor;
    return Promise.resolve(ret);
  }

  entriesByFiles(collection) {
    const files = collection.get('files').map(collectionFile => ({
      path: collectionFile.get('file'),
      label: collectionFile.get('label'),
    }));
    return Promise.all(
      files.map(file => ({
        file,
        data: getFile(file.path).content,
      })),
    );
  }

  getEntry(collection, slug, path) {
    return Promise.resolve({
      file: { path },
      data: getFile(path).content,
    });
  }

  unpublishedEntries() {
    return Promise.resolve(window.repoFilesUnpublished);
  }

  unpublishedEntry(collection, slug) {
    const entry = window.repoFilesUnpublished.find(
      e => e.metaData.collection === collection.get('name') && e.slug === slug,
    );
    if (!entry) {
      return Promise.reject(
        new EditorialWorkflowError('content is not under editorial workflow', true),
      );
    }
    return Promise.resolve(entry);
  }

  deleteUnpublishedEntry(collection, slug) {
    const unpubStore = window.repoFilesUnpublished;
    const existingEntryIndex = unpubStore.findIndex(
      e => e.metaData.collection === collection && e.slug === slug,
    );
    unpubStore.splice(existingEntryIndex, 1);
    return Promise.resolve();
  }

  persistEntry({ path, raw, slug }, mediaFiles, options = {}) {
    if (options.useWorkflow) {
      const unpubStore = window.repoFilesUnpublished;
      const existingEntryIndex = unpubStore.findIndex(e => e.file.path === path);
      if (existingEntryIndex >= 0) {
        const unpubEntry = { ...unpubStore[existingEntryIndex], data: raw };
        unpubEntry.title = options.parsedData && options.parsedData.title;
        unpubEntry.description = options.parsedData && options.parsedData.description;
        unpubStore.splice(existingEntryIndex, 1, unpubEntry);
      } else {
        const unpubEntry = {
          data: raw,
          file: {
            path,
          },
          metaData: {
            collection: options.collectionName,
            status: this.options.initialWorkflowStatus,
            title: options.parsedData && options.parsedData.title,
            description: options.parsedData && options.parsedData.description,
          },
          slug,
        };
        unpubStore.push(unpubEntry);
      }
      return Promise.resolve();
    }

    const newEntry = options.newEntry || false;
    const folder = path.substring(0, path.lastIndexOf('/'));
    const fileName = path.substring(path.lastIndexOf('/') + 1);
    window.repoFiles[folder] = window.repoFiles[folder] || {};
    window.repoFiles[folder][fileName] = window.repoFiles[folder][fileName] || {};
    if (newEntry) {
      window.repoFiles[folder][fileName] = { content: raw };
    } else {
      window.repoFiles[folder][fileName].content = raw;
    }
    return Promise.resolve();
  }

  updateUnpublishedEntryStatus(collection, slug, newStatus) {
    const unpubStore = window.repoFilesUnpublished;
    const entryIndex = unpubStore.findIndex(
      e => e.metaData.collection === collection && e.slug === slug,
    );
    unpubStore[entryIndex].metaData.status = newStatus;
    return Promise.resolve();
  }

  publishUnpublishedEntry(collection, slug) {
    const unpubStore = window.repoFilesUnpublished;
    const unpubEntryIndex = unpubStore.findIndex(
      e => e.metaData.collection === collection && e.slug === slug,
    );
    const unpubEntry = unpubStore[unpubEntryIndex];
    const entry = { raw: unpubEntry.data, slug: unpubEntry.slug, path: unpubEntry.file.path };
    unpubStore.splice(unpubEntryIndex, 1);
    return this.persistEntry(entry);
  }
  getMedia() {
    return Promise.resolve(this.assets);
  }

  persistMedia({ fileObj }) {
    const { name, size } = fileObj;
    const objectUrl = attempt(window.URL.createObjectURL, fileObj);
    const url = isError(objectUrl) ? '' : objectUrl;
    const normalizedAsset = { id: uuid(), name, size, path: url, url };

    this.assets.push(normalizedAsset);
    return Promise.resolve(normalizedAsset);
  }

  deleteFile(path) {
    const assetIndex = this.assets.findIndex(asset => asset.path === path);
    if (assetIndex > -1) {
      this.assets.splice(assetIndex, 1);
    } else {
      const folder = path.substring(0, path.lastIndexOf('/'));
      const fileName = path.substring(path.lastIndexOf('/') + 1);
      delete window.repoFiles[folder][fileName];
    }

    return Promise.resolve();
  }
}