# coding=utf-8
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""To update specified repositories to default tip and provide a short list of latest checkins.
Only supports hg (Mercurial) for now.

Assumes that the repositories are located in ../../trees/*.
"""

from copy import deepcopy
import logging
import os
from pathlib import Path
import platform
import subprocess
import time

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

# Add your repository here.
REPOS = ["gecko-dev", "octo"] + ["mozilla-" + x for x in ["central"]]

if platform.system() == "Windows":
    # pylint: disable=invalid-name
    git_64bit_path = Path(os.getenv("PROGRAMFILES")) / "Git" / "bin" / "git.exe"
    git_32bit_path = Path(os.getenv("PROGRAMFILES(X86)")) / "Git" / "bin" / "git.exe"
    if git_64bit_path.is_file():
        GITBINARY = str(git_64bit_path)
    elif git_32bit_path.is_file():
        GITBINARY = str(git_32bit_path)
    else:
        raise OSError("Git binary not found")
else:
    GITBINARY = str("git")


def time_cmd(cmd, cwd=None, env=None, timeout=None):
    """Calculates and outputs the time a command takes.

    Args:
        cmd (list): Command to be run.
        cwd (str): Working directory command is to be executed in.
        env (dict): Working environment command is to be executed in.
        timeout (int): Timeout for the command.
    """
    if not env:
        env = deepcopy(os.environ)

    logger.info("\nRunning `%s` now..\n", " ".join(cmd))
    cmd_start = time.time()

    cmd = subprocess.run(cmd, check=False, cwd=cwd, env=env, timeout=timeout)

    cmd_end = time.time()
    logger.info("\n`%s` took %.3f seconds.\n", subprocess.list2cmdline(cmd.args), cmd_end - cmd_start)


def typeOfRepo(r):  # pylint: disable=invalid-name,missing-param-doc,missing-raises-doc,missing-return-doc
    # pylint: disable=missing-return-type-doc,missing-type-doc
    """Return the type of repository."""
    repo_types = []
    repo_types.append(".hg")
    repo_types.append(".git")
    for rtype in repo_types:
        if (r / rtype).is_dir():
            return rtype[1:]
    raise OSError(f"Type of repository located at {r} cannot be determined.")


def updateRepo(repo):  # pylint: disable=invalid-name,missing-param-doc,missing-raises-doc,missing-return-doc
    # pylint: disable=missing-return-type-doc,missing-type-doc
    """Update a repository. Return False if missing; return True if successful; raise an exception if updating fails."""
    repo.is_dir()
    repo_type = typeOfRepo(repo)

    if repo_type == "hg":
        hg_pull_cmd = ["hg", "--time", "pull", "-u"]
        logger.info("\nRunning `%s` now..\n", " ".join(hg_pull_cmd))
        out_hg_pull = subprocess.run(hg_pull_cmd, check=True, cwd=str(repo), stderr=subprocess.PIPE)
        logger.info('"%s" had the above output and took - %s',
                    subprocess.list2cmdline(out_hg_pull.args),
                    out_hg_pull.stderr.decode("utf-8", errors="replace").rstrip())

        hg_log_default_cmd = ["hg", "--time", "log", "-r", "default"]
        logger.info("\nRunning `%s` now..\n", " ".join(hg_log_default_cmd))
        out_hg_log_default = subprocess.run(hg_log_default_cmd, check=True, cwd=str(repo),
                                            stderr=subprocess.PIPE)
        logger.info('"%s" had the above output and took - %s',
                    subprocess.list2cmdline(out_hg_log_default.args),
                    out_hg_log_default.stderr.decode("utf-8", errors="replace").rstrip())
    elif repo_type == "git":
        # Ignore exit codes so the loop can continue retrying up to number of counts.
        gitenv = deepcopy(os.environ)
        if platform.system() == "Windows":
            gitenv["GIT_SSH_COMMAND"] = "~/../../mozilla-build/msys/bin/ssh.exe -F ~/.ssh/config"
        time_cmd([GITBINARY, "pull"], cwd=str(repo), env=gitenv)
    else:
        raise OSError(f"Unknown repository type: {repo_type}")

    return True


def updateRepos():  # pylint: disable=invalid-name
    """Update Mercurial and Git repositories located in ~ and ~/trees ."""
    home_dir = Path.home()
    trees = [
        home_dir,
        home_dir / "trees",
    ]
    for tree in trees:
        for name in sorted(os.listdir(str(tree))):
            name_path = Path(tree) / name
            if name_path.is_dir() and (name in REPOS or (name.startswith("funfuzz") and "-" in name)):
                logger.info("Updating %s ...", name)
                updateRepo(name_path)


def main():  # pylint: disable=missing-docstring
    logger.info(time.asctime())
    try:
        updateRepos()
    except OSError as ex:
        logger.info("WARNING: OSError hit:")
        logger.info(ex)
    logger.info(time.asctime())


if __name__ == "__main__":
    main()
