#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "tomlkit",
# ]
# ///

import re
import sys
from pathlib import Path

import tomlkit

# Official semver.org regex (named groups variant for Python)
SEMVER_RE = re.compile(
    r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)"
    r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))"
    r"?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
)

USAGE = """\
Usage: pyproject-version-sync [--check] <version-file>

  Reads <version-file> and sets the version field in the co-located pyproject.toml.

Options:
  --check     Verify version-file and pyproject.toml match; do not write
  -h, --help  show this help"""


def die(msg: str) -> None:
    print(f"error: {msg}", file=sys.stderr)
    sys.exit(1)


def read_version(version_file: Path) -> str:
    """Read, strip, and validate a semver from version_file."""
    if not version_file.is_file():
        die(f"file not found: {version_file}")
    version = version_file.read_text().strip()
    if not version:
        die(f"version file is empty: {version_file}")
    if not SEMVER_RE.match(version):
        die(f"invalid semver in {version_file}: {version}")
    return version


def load_pyproject(pyproject_path: Path) -> tuple[tomlkit.TOMLDocument, str]:
    """Load pyproject.toml and return (doc, current_version)."""
    if not pyproject_path.is_file():
        die(f"pyproject.toml not found: {pyproject_path}")
    doc = tomlkit.parse(pyproject_path.read_text())
    if "project" not in doc or "version" not in doc["project"]:
        die(f"[project] version key not found in {pyproject_path}")
    return doc, str(doc["project"]["version"])


def check_versions_match(expected: str, version_file: Path, pyproject_path: Path) -> None:
    """Die if pyproject.toml version does not match expected."""
    _, current_version = load_pyproject(pyproject_path)
    if expected != current_version:
        die(f"version mismatch: {version_file} has {expected!r}, {pyproject_path} has {current_version!r}")


def main() -> None:
    args = sys.argv[1:]

    if args and args[0] in ("-h", "--help"):
        print(USAGE)
        sys.exit(0)

    check_only = "--check" in args
    args = [a for a in args if a != "--check"]

    if len(args) != 1:
        print(USAGE, file=sys.stderr)
        sys.exit(2)

    version_file = Path(args[0])
    version = read_version(version_file)

    pyproject_path = version_file.parent / "pyproject.toml"
    doc, current_version = load_pyproject(pyproject_path)

    if check_only:
        check_versions_match(version, version_file, pyproject_path)
        print(f"ok: {pyproject_path} version matches {version!r}")
        return

    doc["project"]["version"] = version
    pyproject_path.write_text(tomlkit.dumps(doc))
    print(f"updated {pyproject_path} version → {version}")
    check_versions_match(version, version_file, pyproject_path)


if __name__ == "__main__":
    main()
