# -*- coding: utf-8 -*-
"""Helper for interacting with git."""

from __future__ import unicode_literals

import os

from l2tdevtools.review_helpers import cli


class GitHelper(cli.CLIHelper):
  """Git command helper."""

  def __init__(self, git_repo_url):
    """Initializes a git helper.

    Args:
      git_repo_url (str): git repo URL.
    """
    super(GitHelper, self).__init__()
    self._git_repo_url = git_repo_url
    self._remotes = []

  def _GetRemotes(self):
    """Retrieves the git repository remotes.

    Returns:
      list[str]: git repository remotes or None.
    """
    if not self._remotes:
      exit_code, output, _ = self.RunCommand('git remote -v')
      if exit_code == 0:
        self._remotes = list(filter(None, output.split('\n')))

    return self._remotes

  def AddPath(self, path):
    """Adds a specific path to be managed by git.

    Args:
      path (str): path.

    Returns:
      bool: True if the path was added.
    """
    command = 'git add -A {0:s}'.format(path)
    exit_code, _, _ = self.RunCommand(command)
    return exit_code == 0

  def CheckHasBranch(self, branch):
    """Checks if the git repo has a specific branch.

    Args:
      branch (str): name of the feature branch.

    Returns:
      bool: True if git repo has the specific branch.
    """
    exit_code, output, _ = self.RunCommand('git branch')
    if exit_code != 0:
      return False

    # Check for remote entries starting with upstream.
    for line in output.split('\n'):
      # Ignore the first 2 characters of the line.
      if line[2:] == branch:
        return True
    return False

  def CheckHasProjectOrigin(self):
    """Checks if the git repo has the project remote origin defined.

    Returns:
      bool: True if the git repo has the project origin defined.
    """
    origin_git_repo_url = self.GetRemoteOrigin()

    is_match = origin_git_repo_url == self._git_repo_url
    if not is_match:
      is_match = origin_git_repo_url == self._git_repo_url[:-4]

    return is_match

  def CheckHasProjectUpstream(self):
    """Checks if the git repo has the project remote upstream defined.

    Returns:
      bool: True if the git repo has the project remote upstream defined.
    """
    # Check for remote entries starting with upstream.
    for remote in self._GetRemotes():
      if remote.startswith('upstream\t{0:s}'.format(self._git_repo_url)):
        return True
    return False

  def CheckHasUncommittedChanges(self):
    """Checks if the git repo has uncommitted changes.

    Returns:
      bool: True if the git repo has uncommitted changes.
    """
    exit_code, output, _ = self.RunCommand('git status -s')
    if exit_code != 0:
      return False

    # Check if 'git status -s' yielded any output.
    for line in output.split('\n'):
      if line:
        return True
    return False

  def CheckSynchronizedWithUpstream(self):
    """Checks if the git repo is synchronized with upstream.

    Returns:
      bool: True if the git repo is synchronized with upstream.
    """
    # Fetch the entire upstream repo information not only that of
    # the master branch. Otherwise the information about the current
    # upstream HEAD is not updated.
    exit_code, _, _ = self.RunCommand('git fetch upstream')
    if exit_code != 0:
      return False

    # The result of "git log HEAD..upstream/master --oneline" should be empty
    # if the git repo is synchronized with upstream.
    exit_code, output, _ = self.RunCommand(
        'git log HEAD..upstream/master --oneline')
    return exit_code == 0 and not output

  def DropUncommittedChanges(self):
    """Drops the uncommitted changes."""
    self.RunCommand('git stash')
    self.RunCommand('git stash drop')

  def GetActiveBranch(self):
    """Retrieves the active branch.

    Returns:
      str: name of the active branch or None if not available.
    """
    exit_code, output, _ = self.RunCommand('git branch')
    if exit_code != 0:
      return False

    # Check for remote entries starting with upstream.
    for line in output.split('\n'):
      if line.startswith('* '):
        # Ignore the first 2 characters of the line.
        return line[2:]
    return None

  def GetChangedFiles(self, diffbase=None):
    """Retrieves the changed files.

    Args:
      diffbase (Optional[str]): git diffbase, for example "upstream/master".

    Returns:
      list[str]: names of the changed files.
    """
    if diffbase:
      command = 'git diff --name-only {0:s}'.format(diffbase)
    else:
      command = 'git ls-files'

    exit_code, output, _ = self.RunCommand(command)
    if exit_code != 0:
      return []

    return output.split('\n')

  def GetChangedPythonFiles(self, diffbase=None):
    """Retrieves the changed Python files.

    Note that several Python files are excluded:
    * Python files generated by the protobuf compiler (*_pb2.py)
    * Python files used as test data (test_data/*.py)
    * setup.py and review/lib/upload.py

    Args:
      diffbase (Optional[str]): git diffbase, for example "upstream/master".

    Returns:
      list[str]: names of the changed Python files.
    """
    upload_path = os.path.join('l2tdevtools', 'lib', 'upload.py')
    python_files = []