import { commitActionTypes } from '../constants';

export const dataStructure = () => ({
  id: '',
  // Key will contain a mixture of ID and path
  // it can also contain a prefix `pending-` for files opened in review mode
  key: '',
  type: '',
  projectId: '',
  branchId: '',
  name: '',
  url: '',
  path: '',
  tempFile: false,
  tree: [],
  loading: false,
  opened: false,
  active: false,
  changed: false,
  staged: false,
  lastCommitPath: '',
  lastCommitSha: '',
  lastCommit: {
    id: '',
    url: '',
    message: '',
    updatedAt: '',
    author: '',
  },
  blamePath: '',
  commitsPath: '',
  permalink: '',
  rawPath: '',
  binary: false,
  html: '',
  raw: '',
  content: '',
  parentTreeUrl: '',
  renderError: false,
  base64: false,
  editorRow: 1,
  editorColumn: 1,
  fileLanguage: '',
  eol: '',
  viewMode: 'editor',
  previewMode: null,
  size: 0,
  parentPath: null,
  lastOpenedAt: 0,
  mrChange: null,
  deleted: false,
  prevPath: '',
  movedPath: '',
  moved: false,
});

export const decorateData = entity => {
  const {
    id,
    projectId,
    branchId,
    type,
    url,
    name,
    path,
    renderError,
    content = '',
    tempFile = false,
    active = false,
    opened = false,
    changed = false,
    parentTreeUrl = '',
    base64 = false,
    binary = false,
    rawPath = '',
    previewMode,
    file_lock,
    html,
    parentPath = '',
  } = entity;

  return Object.assign(dataStructure(), {
    id,
    projectId,
    branchId,
    key: `${name}-${type}-${id}`,
    type,
    name,
    url,
    path,
    tempFile,
    opened,
    active,
    parentTreeUrl,
    changed,
    renderError,
    content,
    base64,
    binary,
    rawPath,
    previewMode,
    file_lock,
    html,
    parentPath,
  });
};

export const findEntry = (tree, type, name, prop = 'name') =>
  tree.find(f => f.type === type && f[prop] === name);

export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path);

export const setPageTitle = title => {
  document.title = title;
};

export const commitActionForFile = file => {
  if (file.prevPath) {
    return commitActionTypes.move;
  } else if (file.deleted) {
    return commitActionTypes.delete;
  } else if (file.tempFile) {
    return commitActionTypes.create;
  }

  return commitActionTypes.update;
};

export const getCommitFiles = stagedFiles =>
  stagedFiles.reduce((acc, file) => {
    if (file.moved) return acc;

    return acc.concat({
      ...file,
    });
  }, []);

export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
  branch,
  commit_message: state.commitMessage || getters.preBuiltCommitMessage,
  actions: getCommitFiles(rootState.stagedFiles).map(f => ({
    action: commitActionForFile(f),
    file_path: f.path,
    previous_path: f.prevPath === '' ? undefined : f.prevPath,
    content: f.content || undefined,
    encoding: f.base64 ? 'base64' : 'text',
    last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
  })),
  start_branch: newBranch ? rootState.currentBranchId : undefined,
});

export const createNewMergeRequestUrl = (projectUrl, source, target) =>
  `${projectUrl}/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}`;

const sortTreesByTypeAndName = (a, b) => {
  if (a.type === 'tree' && b.type === 'blob') {
    return -1;
  } else if (a.type === 'blob' && b.type === 'tree') {
    return 1;
  }
  if (a.name < b.name) return -1;
  if (a.name > b.name) return 1;
  return 0;
};

export const sortTree = sortedTree =>
  sortedTree
    .map(entity =>
      Object.assign(entity, {
        tree: entity.tree.length ? sortTree(entity.tree) : [],
      }),
    )
    .sort(sortTreesByTypeAndName);

export const filePathMatches = (filePath, path) => filePath.indexOf(`${path}/`) === 0;

export const getChangesCountForFiles = (files, path) =>
  files.filter(f => filePathMatches(f.path, path)).length;

export const mergeTrees = (fromTree, toTree) => {
  if (!fromTree || !fromTree.length) {
    return toTree;
  }

  const recurseTree = (n, t) => {
    if (!n) {
      return t;
    }
    const existingTreeNode = t.find(el => el.path === n.path);

    if (existingTreeNode && n.tree.length > 0) {
      existingTreeNode.opened = true;
      recurseTree(n.tree[0], existingTreeNode.tree);
    } else if (!existingTreeNode) {
      const sorted = sortTree(t.concat(n));
      t.splice(0, t.length + 1, ...sorted);
    }
    return t;
  };

  for (let i = 0, l = fromTree.length; i < l; i += 1) {
    recurseTree(fromTree[i], toTree);
  }

  return toTree;
};
