import argparse
import re

from typing import Literal

from packaging.version import parse


def make_version(
    major: int,
    minor: int | None,
    patch: int | None,
    pre: tuple[str, int] | None,
    version_template: str = "{major}.{minor}.{patch}",
    rc_template: str = "-{pre_label}{pre_number}",
) -> str:
    version = version_template.format(major=major, minor=minor, patch=patch)
    if pre is not None:
        version += rc_template.format(pre_label=pre[0], pre_number=pre[1])

    return version


def parse_full_version(version_string: str) -> tuple[str, str, str]:
    parts = re.split(r"(/v?)", version_string, maxsplit=1)
    if len(parts) == 1:
        prefix, separator, version_string = ("", "", parts[0])
        if version_string.startswith("v"):
            prefix = "v"
            version_string = version_string[1:]
    else:
        prefix, separator, version_string = parts
    parse(version_string)  # Validate
    return prefix, separator, version_string


def bump_version(
    version_string: str,
    part: Literal["major", "minor", "patch", "dev"],
    version_template: str = "{major}.{minor}.{patch}",
    rc_template="-{pre_label}{pre_number}",
    rc_bump_part: Literal["major", "minor", "patch"] = "minor",
) -> str:
    leading_v = "v" if version_string.startswith("v") else ""
    version = parse(version_string)

    major, minor, patch, *_ = version.release + (0,) * 2
    pre = version.pre
    if part == "major":
        major, minor, patch = major + 1, 0, 0
    elif part == "minor":
        major, minor, patch = major, minor + 1, 0
    elif part == "patch":
        major, minor, patch = major, minor or 0, patch + 1
    elif part == "dev":
        if version.is_prerelease:
            if pre is None:
                # Could be .devN
                pre_label, pre_num = "dev", version.dev
            else:
                pre_label, pre_num = pre
            pre_num += 1
        else:
            pre_label, pre_num = "rc", 0
            # Which part to bump?
            if rc_bump_part == "major":
                major, minor, patch = major + 1, 0, 0
            elif rc_bump_part == "minor":
                major, minor, patch = major, minor + 1, 0
            elif rc_bump_part == "patch":
                major, minor, patch = major, minor, patch + 1
            else:
                raise ValueError(f"Unknown rc_bump_part: {rc_bump_part}")

        pre = (pre_label, pre_num)
    else:
        raise ValueError(f"Unknown part to bump: {part}")

    new_version = make_version(major, minor, patch, pre, version_template, rc_template)

    return f"{leading_v}{new_version}"


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Bump version script")
    parser.add_argument(
        "version",
        type=str,
        help="The new version to set (e.g., '1.2.3'). Supports prefixes like 'app/v1.2.3'",
    )
    parser.add_argument(
        "part",
        type=str,
        choices=["major", "minor", "patch", "dev"],
        help="Which part of the version to bump",
    )

    args = parser.parse_args()
    print(bump_version(args.version, args.part))
