"""Build and inspect explicit Python dependency environments through PM."""
from __future__ import annotations

import argparse
from collections.abc import Sequence
from pathlib import Path
import sys


def main(argv: Sequence[str] | None = None) -> int:
    import pm

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--source", type=Path)
    parser.add_argument("--out", type=Path)
    parser.add_argument("--python", type=Path)
    parser.add_argument("--cache", type=Path)
    parser.add_argument("--extra", dest="extras", action="append", default=[])
    parser.add_argument("--group", dest="groups", action="append", default=[])
    parser.add_argument("--all-extras", action="store_true")
    parser.add_argument("--no-install-project", action="store_true")
    parser.add_argument("--resolve", action="store_true", help="resolve the source lock before building")
    operation = parser.add_mutually_exclusive_group()
    operation.add_argument("--lock-only", action="store_true")
    operation.add_argument("--check-lock", action="store_true")
    operation.add_argument("--export-requirements", type=Path)
    operation.add_argument("--prune-cache", action="store_true")
    operation.add_argument("--exact-lock", action="store_true",
                           help="prune to the project lock: entries the uv.lock cannot resolve are deleted")
    operation.add_argument("--manager-runtime", action="store_true")
    parser.add_argument("--lock-source", type=Path, default=None,
                        help="repo whose uv.lock selects the kept entries for --exact-lock (default: cwd)")
    parser.add_argument("--upgrade", action="store_true")
    parser.add_argument("--ci", action="store_true")
    parser.add_argument("--sealed", action="store_true")
    parser.add_argument("--offline", action="store_true")
    parser.add_argument("--explicit", action="store_true", help="CLI builds are always explicit requests")
    parser.add_argument("--wheelhouse", type=Path)
    parser.add_argument("--requirements", type=Path)
    parser.add_argument("--requirement", action="append", default=[])
    args = parser.parse_args(argv)
    requirements = list(args.requirement)
    if args.requirements is not None:
        requirements.extend(line.strip() for line in args.requirements.read_text(encoding="utf-8-sig").splitlines()
                            if line.strip() and not line.lstrip().startswith("#"))
    if args.exact_lock:
        if args.ci or args.prune_cache:
            parser.error("--exact-lock replaces --ci/--prune-cache: downloaded wheels the lock keeps must survive")
        if args.lock_source is None:
            parser.error("--exact-lock requires --lock-source")
        if args.cache is None:
            parser.error("--exact-lock requires --cache")
    elif args.prune_cache:
        if args.cache is None:
            parser.error("--prune-cache requires --cache")
    elif not requirements and args.source is None:
        parser.error("--source is required for project operations")
    build = not (args.lock_only or args.check_lock or args.export_requirements
                 or args.prune_cache or args.exact_lock)
    if build and args.out is None:
        parser.error("--out is required when building an environment")
    if args.manager_runtime and args.python is None:
        parser.error("--manager-runtime requires the target --python")
    try:
        if args.exact_lock:
            from pm.uv_cache_prune import prune_uv_cache_to_lock

            pruned = prune_uv_cache_to_lock(args.cache, args.lock_source)
            print(f"pruned {pruned} cache entries outside the lock")
            return 0
        if args.prune_cache:
            pm.prune_cache(args.cache, ci=args.ci)
            return 0
        if args.check_lock:
            pm.check_project_lock(args.source, python=args.python, cache=args.cache,
                                  offline=args.offline, explicit=True)
            return 0
        if args.export_requirements:
            pm.export_requirements(args.source, args.export_requirements, extras=args.extras,
                                   python=args.python, cache=args.cache, explicit=True)
            return 0
        if args.lock_only:
            pm.lock_project(args.source, upgrade=args.upgrade, python=args.python,
                            cache=args.cache, offline=args.offline, explicit=True)
            return 0
        if args.manager_runtime:
            executable = pm.stage_manager_runtime(python=args.python, destination=args.out,
                                                  project=args.source, cache=args.cache,
                                                  offline=args.offline, wheelhouse=args.wheelhouse)
        elif requirements:
            executable = pm.build_requirements_environment(
                requirements, out=args.out, python=args.python, cache=args.cache,
                wheelhouse=args.wheelhouse, offline=args.offline, sealed=args.sealed, explicit=True,
            )
        else:
            executable = pm.build_environment(
                source=args.source, out=args.out, python=args.python, cache=args.cache,
                extras=args.extras, groups=args.groups, all_extras=args.all_extras,
                no_install_project=args.no_install_project, frozen=not args.resolve,
                sealed=args.sealed, offline=args.offline, explicit=True,
            )
    except (pm.InstallError, OSError, ValueError) as exc:
        print(f"python environment: {exc}", file=sys.stderr)
        return 1
    print(executable)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
