import re
import subprocess
import sys
from pathlib import Path
from typing import Tuple, Iterable, Union

from dev_tools.utility.path import ROOT

GIT_VERSION_RE = re.compile(r"^v([^-]+)(.*)\n$")


def get_build_version_by_git() -> Tuple[str, str]:
    git_describe = subprocess.check_output(
        ["git.exe" if sys.platform.startswith("win") else "git", "describe", "--tags"], cwd=str(ROOT)
    )
    m = GIT_VERSION_RE.match(git_describe.decode())
    assert m, str(git_describe)
    return m.group(1), m.group(2)


def status_paths_by_git() -> Iterable[str]:
    args = ["git.exe" if sys.platform.startswith("win") else "git", "status", "--porcelain=v1"]
    try:
        git_status = subprocess.check_output(args, cwd=str(ROOT))
    except subprocess.CalledProcessError:
        args[-1] = "--porcelain"  # try older git argument variant
        git_status = subprocess.check_output(args, cwd=str(ROOT))
    for line in git_status.decode("utf-8").replace("\r\n", "\n").split("\n"):
        _, _, line = line.strip().replace("  ", " ").partition(" ")
        if line:
            path0, _, path1 = line.partition(" -> ")
            yield Path(path0).as_posix()
            if path1:
                yield Path(path1).as_posix()


def path_has_uncommitted_changes_by_git(path_prefix: str) -> bool:
    path_prefix = Path(path_prefix).as_posix()
    for path in status_paths_by_git():
        if path.startswith(path_prefix):
            return True
    return False


def git_archive(output_path: Union[str, Path], git_name: str = "HEAD"):
    return subprocess.check_call(
        [
            "git.exe" if sys.platform.startswith("win") else "git",
            "archive",
            "--format=zip",
            "-9",
            "-o",
            str(output_path),
            git_name,
        ],
        cwd=str(ROOT),
    )
