#!/usr/bin/env python3

import argparse
import os
import subprocess
import sys
from pathlib import Path


def run(cmd, check=True):
    return subprocess.run(cmd, check=check)


def output(cmd):
    proc = subprocess.run(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False)
    return proc.stdout


def detect_vcs():
    probes = (
        ("git", ["git", "rev-parse", "--is-inside-work-tree"]),
        ("hg", ["hg", "root"]),
        ("svn", ["svn", "info"]),
        ("cvs", ["cvs", "status", "-l"]),
    )
    for name, cmd in probes:
        proc = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)
        if proc.returncode == 0:
            return name
    raise SystemExit("Unable to find a supported repository type")


def git_root():
    root = output(["git", "rev-parse", "--show-toplevel"]).strip()
    return Path(root) if root else Path.cwd()


def package_atom(path):
    root = git_root()
    rel = Path.cwd().resolve().relative_to(root.resolve())
    parts = rel.parts
    if len(parts) >= 2:
        return f"{parts[-2]}/{parts[-1]}"
    return ""


def ebuilds_under(path):
    return sorted(Path(path).glob("*.ebuild"))


def update_manifest():
    ebuilds = ebuilds_under(Path.cwd())
    if not ebuilds:
        return
    latest = sorted(ebuilds)[-1]
    run(["ebuild", str(latest), "manifest"])


def changed(vcs):
    if vcs == "git":
        return bool(output(["git", "status", "--porcelain"]))
    if vcs == "hg":
        return bool(output(["hg", "status"]))
    if vcs == "svn":
        return bool(output(["svn", "status"]))
    if vcs == "cvs":
        return bool(output(["cvs", "-n", "-q", "up"]))
    return False


def show_status(vcs):
    if vcs == "git":
        run(["git", "status", "--short"])
    elif vcs == "hg":
        run(["hg", "status"])
    elif vcs == "svn":
        run(["svn", "status"])
    elif vcs == "cvs":
        run(["cvs", "-n", "-q", "up"])


def show_diff(vcs):
    if vcs == "git":
        run(["git", "--no-pager", "diff", "HEAD"], check=False)
    elif vcs == "hg":
        run(["hg", "diff"], check=False)
    elif vcs == "svn":
        run(["svn", "diff"], check=False)
    elif vcs == "cvs":
        run(["cvs", "-n", "-q", "diff", "-u", "-p"], check=False)


def commit(vcs, message, noask):
    if not noask:
        answer = input("Commit changes? [yes/no] ")
        if answer.lower() not in ("y", "yes"):
            raise SystemExit("Aborting.")

    if vcs == "git":
        os.execvp("git", ["git", "commit", "-m", message])
    if vcs == "hg":
        os.execvp("hg", ["hg", "commit", "-m", message])
    if vcs == "svn":
        os.execvp("svn", ["svn", "commit", "-m", message])
    if vcs == "cvs":
        os.execvp("cvs", ["cvs", "commit", "-m", message])


def main(argv):
    parser = argparse.ArgumentParser(prog="go-commit")
    parser.add_argument("-d", "--noupdate", action="store_true", help="accepted for compatibility")
    parser.add_argument("--diff", action="store_true")
    parser.add_argument("-m", "--noformat", action="store_true")
    parser.add_argument("-q", "--quiet", action="store_true")
    parser.add_argument("-t", "--trivial", action="store_true", help="accepted for compatibility")
    parser.add_argument("-v", "--verbose", action="store_true")
    parser.add_argument("-y", "--noask", action="store_true")
    parser.add_argument("-V", "--version", action="version", version="go-utils 0.5.0")
    parser.add_argument("message", nargs="+")
    args = parser.parse_args(argv[1:])

    vcs = detect_vcs()
    update_manifest()
    if not changed(vcs):
        raise SystemExit("No changes found to commit.")

    atom = package_atom(Path.cwd())
    message = " ".join(args.message)
    if atom and not args.noformat:
        message = f"{atom}: {message}"

    if args.diff:
        show_diff(vcs)
    else:
        show_status(vcs)
    print()
    print("Commit message:")
    print(message)

    commit(vcs, message, args.noask)
    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
