# cli.py - Command line interface for automation
#
# Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
# Copyright 2020 Matt Harbison <mharbison72@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

# no-check-code because Python 3 native.

import argparse
import os
import pathlib
import shutil
import subprocess
import sys

from . import (
    util as thgutil,
    venv,
    venv_py2,
    wix,
)

HERE = pathlib.Path(os.path.abspath(os.path.dirname(__file__)))
SOURCE_DIR = HERE.parent.parent.parent
SOURCE_DIRS = thgutil.SourceDirs(SOURCE_DIR)


def build_venv(python='', hg='', qt=None, output='', force=False, clean=False):
    if not os.path.isabs(python):
        raise Exception("--python arg must be an absolute path")

    if not os.path.isabs(hg):
        raise Exception("--hg arg must be an absolute path")

    if not os.path.isabs(hg):
        raise Exception("--hg arg must be an absolute path")

    if output and not os.path.isabs(output):
        raise Exception("--output arg must be an absolute path")

    build_dir = SOURCE_DIR / "build"
    python_exe = pathlib.Path(python)

    py_info = thgutil.python_exe_info(python_exe)

    if not output:
        version = str(py_info['version']).strip()
        arch = 'x64' if py_info['arch'] == '64bit' else 'x86'
        output = build_dir / ('venv-thg-py%s-%s' % (version, arch))
    else:
        output = pathlib.Path(output)

    if output.exists():
        if force:
            shutil.rmtree(output)
        else:
            raise Exception(
                "the output path already exists. (Use --force to overwrite)"
            )

    if py_info.get('py3'):
        venv.build_py3(SOURCE_DIR, pathlib.Path(hg), python_exe, output)
    elif qt is None:
        raise Exception(
            "--python arg must be Python 3.7 or later; or use venv_py2 instead"
        )
    else:
        venv_py2.build_py2(
            SOURCE_DIR,
            pathlib.Path(hg),
            pathlib.Path(qt),
            build_dir,
            python_exe,
            output,
            clean,
        )


def build_wix(
    name=None,
    python=None,
    version=None,
    sign_sn=None,
    sign_cert=None,
    sign_password=None,
    sign_timestamp_url=None,
    extra_packages_script=None,
    extra_wxs=None,
    extra_features=None,
):
    if python is None:
        python = sys.executable
    elif not os.path.isabs(python):
        raise Exception("--python arg must be an absolute path")

    kwargs = {
        "source_dirs": SOURCE_DIRS,
        "version": version,
    }

    fn = wix.build_installer_py2exe
    kwargs["python_exe"] = pathlib.Path(python)

    if extra_packages_script:
        kwargs["extra_packages_script"] = extra_packages_script
    if extra_wxs:
        kwargs["extra_wxs"] = dict(
            thing.split("=") for thing in extra_wxs.split(",")
        )
    if extra_features:
        kwargs["extra_features"] = extra_features.split(",")

    if sign_sn or sign_cert:
        kwargs["signing_info"] = {
            "name": name,
            "subject_name": sign_sn,
            "cert_path": sign_cert,
            "cert_password": sign_password,
            "timestamp_url": sign_timestamp_url,
        }

    fn(**kwargs)


def stage_dependencies(
    hg_version=None,
    evolve_version=None,
    shellext_version=None,
    clean=False,
):
    """Stage the other required repositories locally."""

    if clean:
        SOURCE_DIRS.clean()

    repos = [
        (
            "WinBuild",
            "https://foss.heptapod.net/mercurial/tortoisehg/thg-winbuild",
            SOURCE_DIRS.winbuild,
            "default",
        ),
        (
            "Mercurial",
            "https://www.mercurial-scm.org/repo/hg",
            SOURCE_DIRS.hg,
            hg_version,
        ),
        (
            "Evolve",
            "https://www.mercurial-scm.org/repo/evolve",
            SOURCE_DIRS.evolve,
            evolve_version,
        ),
        (
            "ShellExt",
            "https://foss.heptapod.net/mercurial/tortoisehg/thg-shellext",
            SOURCE_DIRS.shellext,
            shellext_version,
        )
    ]

    for name, url, repo_dir, version in repos:
        if repo_dir.exists():
            print("Pulling %s" % name)
            subprocess.run(
                ["hg", "pull"],
                cwd=str(repo_dir),
                check=True,
            )
        else:
            print("Cloning %s" % name)
            subprocess.run(
                ["hg", "clone", "--noupdate", url, str(repo_dir)],
                check=True,
            )

        if version:
            print("Updating %s to %s" % (name, version))
            subprocess.run(
                ["hg", "update", version],
                cwd=str(repo_dir),
                check=True,
            )

        print()


def get_parser():
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers()

    sp = subparsers.add_parser("venv", help="Build py3 virtualenv")
    sp.add_argument("--python", required=True, help="path to python.exe to use")
    sp.add_argument("--hg", required=True, help="path to hg repo to use")
    sp.add_argument("--output", help="path to the new virtualenv")
    sp.add_argument(
        "--force",
        help="remove the output path before creating",
        action="store_true",
    )
    sp.set_defaults(func=build_venv)

    sp = subparsers.add_parser("venv_py2", help="Build py2 virtualenv")
    sp.add_argument("--python", required=True, help="path to python.exe to use")
    sp.add_argument("--hg", required=True, help="path to hg repo to use")
    sp.add_argument("--qt", required=True, help="path to Qt bin path to use")
    sp.add_argument("--output", help="path to the new virtualenv")
    sp.add_argument(
        "--force",
        help="remove the virtualenv if it exists",
        action="store_true",
    )
    sp.add_argument(
        "--clean",
        help="remove previous build output and cached downloads",
        action="store_true",
    )
    sp.set_defaults(func=build_venv)

    sp = subparsers.add_parser(
        "wix", help="Build Windows installer with WiX Toolset"
    )
    sp.add_argument("--name", help="Application name", default="TortoiseHg")
    sp.add_argument("--python", help="Path to Python executable to use")
    sp.add_argument(
        "--sign-sn",
        help="Subject name (or fragment thereof) of certificate "
        "to use for signing",
    )
    sp.add_argument(
        "--sign-cert", help="Path to certificate to use for signing"
    )
    sp.add_argument("--sign-password", help="Password for signing certificate")
    sp.add_argument(
        "--sign-timestamp-url",
        help="URL of timestamp server to use for signing",
    )
    sp.add_argument("--version", help="Version string to use")
    sp.add_argument(
        "--extra-packages-script",
        help=(
            "Script to execute to include extra packages in py2exe binary."
        ),
    )
    sp.add_argument(
        "--extra-wxs", help="CSV of path_to_wxs_file=working_dir_for_wxs_file"
    )
    sp.add_argument(
        "--extra-features",
        help=(
            "CSV of extra feature names to include "
            "in the installer from the extra wxs files"
        ),
    )

    sp.set_defaults(func=build_wix)

    sp = subparsers.add_parser(
        "stage", help="Stage the projects bundled into the installer"
    )
    sp.add_argument(
        "--hg-version",
        help=("The version of Mercurial to bundle. (default: 'stable')"),
        default="stable",
    )
    sp.add_argument(
        "--evolve-version",
        help=("The version of evolve to bundle. (default: 'stable')"),
        default="stable",
    )
    sp.add_argument(
        "--shellext-version",
        help=(
            "The version of the shell extension to bundle. (default: 'default')"
        ),
        default="default",
    )
    sp.add_argument(
        "--clean",
        help="Reclone the dependencies",
        action="store_true",
    )

    sp.set_defaults(func=stage_dependencies)

    return parser


def main():
    parser = get_parser()
    args = parser.parse_args()

    if not hasattr(args, "func"):
        parser.print_help()
        return

    kwargs = dict(vars(args))
    del kwargs["func"]

    args.func(**kwargs)
