import Api from '~/api';
import { addNumericSuffix } from '~/ide/utils';
import {
  leftSidebarViews,
  packageJsonPath,
  DEFAULT_PERMISSIONS,
  PERMISSION_READ_MR,
  PERMISSION_CREATE_MR,
  PERMISSION_PUSH_CODE,
  PUSH_RULE_REJECT_UNSIGNED_COMMITS,
} from '../constants';
import {
  MSG_CANNOT_PUSH_CODE,
  MSG_CANNOT_PUSH_CODE_SHOULD_FORK,
  MSG_CANNOT_PUSH_CODE_GO_TO_FORK,
  MSG_CANNOT_PUSH_UNSIGNED,
  MSG_CANNOT_PUSH_UNSIGNED_SHORT,
  MSG_FORK,
  MSG_GO_TO_FORK,
} from '../messages';
import { getChangesCountForFiles, filePathMatches } from './utils';

const getCannotPushCodeViewModel = (state) => {
  const { ide_path: idePath, fork_path: forkPath } = state.links.forkInfo || {};

  if (idePath) {
    return {
      message: MSG_CANNOT_PUSH_CODE_GO_TO_FORK,
      action: {
        href: idePath,
        text: MSG_GO_TO_FORK,
      },
    };
  } else if (forkPath) {
    return {
      message: MSG_CANNOT_PUSH_CODE_SHOULD_FORK,
      action: {
        href: forkPath,
        isForm: true,
        text: MSG_FORK,
      },
    };
  }

  return {
    message: MSG_CANNOT_PUSH_CODE,
  };
};

export const activeFile = (state) => state.openFiles.find((file) => file.active) || null;

export const addedFiles = (state) => state.changedFiles.filter((f) => f.tempFile);

export const modifiedFiles = (state) => state.changedFiles.filter((f) => !f.tempFile);

export const projectsWithTrees = (state) =>
  Object.keys(state.projects).map((projectId) => {
    const project = state.projects[projectId];

    return {
      ...project,
      branches: Object.keys(project.branches).map((branchId) => {
        const branch = project.branches[branchId];

        return {
          ...branch,
          tree: state.trees[branch.treeId],
        };
      }),
    };
  });

export const currentMergeRequest = (state) => {
  if (
    state.projects[state.currentProjectId] &&
    state.projects[state.currentProjectId].mergeRequests
  ) {
    return state.projects[state.currentProjectId].mergeRequests[state.currentMergeRequestId];
  }
  return null;
};

export const findProject = (state) => (projectId) => state.projects[projectId];

export const currentProject = (state, getters) => getters.findProject(state.currentProjectId);

export const emptyRepo = (state) =>
  state.projects[state.currentProjectId] && state.projects[state.currentProjectId].empty_repo;

export const currentTree = (state) =>
  state.trees[`${state.currentProjectId}/${state.currentBranchId}`];

export const hasMergeRequest = (state) => Boolean(state.currentMergeRequestId);

export const allBlobs = (state) =>
  Object.keys(state.entries)
    .reduce((acc, key) => {
      const entry = state.entries[key];

      if (entry.type === 'blob') {
        acc.push(entry);
      }

      return acc;
    }, [])
    .sort((a, b) => b.lastOpenedAt - a.lastOpenedAt);

export const getChangedFile = (state) => (path) => state.changedFiles.find((f) => f.path === path);
export const getStagedFile = (state) => (path) => state.stagedFiles.find((f) => f.path === path);
export const getOpenFile = (state) => (path) => state.openFiles.find((f) => f.path === path);

export const lastOpenedFile = (state) =>
  [...state.changedFiles, ...state.stagedFiles].sort((a, b) => b.lastOpenedAt - a.lastOpenedAt)[0];

export const isEditModeActive = (state) => state.currentActivityView === leftSidebarViews.edit.name;
export const isCommitModeActive = (state) =>
  state.currentActivityView === leftSidebarViews.commit.name;
export const isReviewModeActive = (state) =>
  state.currentActivityView === leftSidebarViews.review.name;

export const someUncommittedChanges = (state) =>
  Boolean(state.changedFiles.length || state.stagedFiles.length);

export const getChangesInFolder = (state) => (path) => {
  const changedFilesCount = state.changedFiles.filter((f) => filePathMatches(f.path, path)).length;
  const stagedFilesCount = state.stagedFiles.filter(
    (f) => filePathMatches(f.path, path) && !getChangedFile(state)(f.path),
  ).length;

  return changedFilesCount + stagedFilesCount;
};

export const getUnstagedFilesCountForPath = (state) => (path) =>
  getChangesCountForFiles(state.changedFiles, path);

export const getStagedFilesCountForPath = (state) => (path) =>
  getChangesCountForFiles(state.stagedFiles, path);

export const lastCommit = (state, getters) => {
  const branch = getters.currentProject && getters.currentBranch;

  return branch ? branch.commit : null;
};

export const findBranch = (state, getters) => (projectId, branchId) => {
  const project = getters.findProject(projectId);

  return project && project.branches[branchId];
};

export const currentBranch = (state, getters) =>
  getters.findBranch(state.currentProjectId, state.currentBranchId);

export const branchName = (_state, getters) => getters.currentBranch && getters.currentBranch.name;

export const packageJson = (state) => state.entries[packageJsonPath];

export const isOnDefaultBranch = (_state, getters) =>
  getters.currentProject && getters.currentProject.default_branch === getters.branchName;

export const canPushToBranch = (_state, getters) => {
  return Boolean(getters.currentBranch ? getters.currentBranch.can_push : getters.canPushCode);
};

export const isFileDeletedAndReadded = (state, getters) => (path) => {
  const stagedFile = getters.getStagedFile(path);
  const file = state.entries[path];
  return Boolean(stagedFile && stagedFile.deleted && file.tempFile);
};

// checks if any diff exists in the staged or unstaged changes for this path
export const getDiffInfo = (state, getters) => (path) => {
  const stagedFile = getters.getStagedFile(path);
  const file = state.entries[path];
  const renamed = file.prevPath ? file.path !== file.prevPath : false;
  const deletedAndReadded = getters.isFileDeletedAndReadded(path);
  const deleted = deletedAndReadded ? false : file.deleted;
  const tempFile = deletedAndReadded ? false : file.tempFile;
  const changed = file.content !== (deletedAndReadded ? stagedFile.raw : file.raw);

  return {
    exists: changed || renamed || deleted || tempFile,
    changed,
    renamed,
    deleted,
    tempFile,
  };
};

export const findProjectPermissions = (state, getters) => (projectId) =>
  getters.findProject(projectId)?.userPermissions || DEFAULT_PERMISSIONS;

export const findPushRules = (state, getters) => (projectId) =>
  getters.findProject(projectId)?.pushRules || {};

export const canReadMergeRequests = (state, getters) =>
  Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_READ_MR]);

export const canCreateMergeRequests = (state, getters) =>
  Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_CREATE_MR]);

/**
 * Returns an object with `isAllowed` and `message` based on why the user cant push code
 */
export const canPushCodeStatus = (state, getters) => {
  const canPushCode = getters.findProjectPermissions(state.currentProjectId)[PERMISSION_PUSH_CODE];
  const rejectUnsignedCommits = getters.findPushRules(state.currentProjectId)[
    PUSH_RULE_REJECT_UNSIGNED_COMMITS
  ];

  if (window.gon?.features?.rejectUnsignedCommitsByGitlab && rejectUnsignedCommits) {
    return {
      isAllowed: false,
      message: MSG_CANNOT_PUSH_UNSIGNED,
      messageShort: MSG_CANNOT_PUSH_UNSIGNED_SHORT,
    };
  }
  if (!canPushCode) {
    return {
      isAllowed: false,
      messageShort: MSG_CANNOT_PUSH_CODE,
      ...getCannotPushCodeViewModel(state),
    };
  }

  return {
    isAllowed: true,
    message: '',
    messageShort: '',
  };
};

export const canPushCode = (state, getters) => getters.canPushCodeStatus.isAllowed;

export const entryExists = (state) => (path) =>
  Boolean(state.entries[path] && !state.entries[path].deleted);

export const getAvailableFileName = (state, getters) => (path) => {
  let newPath = path;

  while (getters.entryExists(newPath)) {
    newPath = addNumericSuffix(newPath);
  }

  return newPath;
};

export const getUrlForPath = (state) => (path) =>
  `/project/${state.currentProjectId}/tree/${state.currentBranchId}/-/${path}/`;

export const getJsonSchemaForPath = (state, getters) => (path) => {
  const [namespace, ...project] = state.currentProjectId.split('/');
  return {
    uri:
      // eslint-disable-next-line no-restricted-globals
      location.origin +
      Api.buildUrl(Api.projectFileSchemaPath)
        .replace(':namespace_path', namespace)
        .replace(':project_path', project.join('/'))
        .replace(':ref', getters.currentBranch?.commit.id || state.currentBranchId)
        .replace(':filename', path),
    fileMatch: [`*${path}`],
  };
};

export * from './getters/alert';
