#!/usr/bin/env python3
"""
build-release -- produce the sanitized, signed Donjon Platform customer artifact.

    python bin/build-release.py [--out dist] [--last-version X.Y.Z]
                                [--ed25519 PATH] [--mldsa PATH]
                                [--throwaway]   # CI/dry-run: ephemeral keys

Version comes from the single source of truth (pyproject.toml). The build:
  1. validates version is clean semver and advances --last-version (kills the
     7.5.1-predates-7.5.0 inversion),
  2. strips runtime state, dev/test trees, vendored binaries, and rewrites the
     founder's real lab IPs/secrets to RFC5737 documentation addresses,
  3. asserts the artifact contains NO real-lab IP or secret (fail-closed),
  4. dual-signs the zip with Ed25519 + ML-DSA-65 and writes a manifest.

PRODUCTION SIGNING KEYS (reused -- generating new keys orphans paid licenses):
    --ed25519  donjonsec-infrastructure/keys/donjon-private-classical.pem
    --mldsa    donjonsec-infrastructure/keys/donjon-private-pqc.bin
Defaults probe those locations + the CT100 mirror. Use --throwaway in CI to
verify the pipeline without touching real keys.

This script performs NO network/deploy action. It only writes to --out.
"""
from __future__ import annotations

import argparse
import importlib.util
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent


def _load_release_lib():
    spec = importlib.util.spec_from_file_location(
        "release_lib", REPO_ROOT / "bin" / "release_lib.py"
    )
    mod = importlib.util.module_from_spec(spec)
    sys.modules["release_lib"] = mod
    spec.loader.exec_module(mod)
    return mod


# Default key locations: repo-local gitignored mirror, the infrastructure repo,
# and the CT100 canonical mirror (Linux path). First existing wins.
_ED25519_CANDIDATES = (
    REPO_ROOT / "keys" / "donjon-private-classical.pem",
    REPO_ROOT.parent / "donjonsec-infrastructure" / "keys" / "donjon-private-classical.pem",
    Path("/opt/donjonsec-keys/donjon-private-classical.pem"),
)
_MLDSA_CANDIDATES = (
    REPO_ROOT / "keys" / "donjon-private-pqc.bin",
    REPO_ROOT.parent / "donjonsec-infrastructure" / "keys" / "donjon-private-pqc.bin",
    Path("/opt/donjonsec-keys/donjon-private-pqc.bin"),
)


def _first_existing(candidates):
    for c in candidates:
        if c.exists():
            return c
    return None


def main(argv=None) -> int:
    rel = _load_release_lib()
    ap = argparse.ArgumentParser(description="Build the sanitized, signed release artifact")
    ap.add_argument("--out", default=str(REPO_ROOT / "dist"))
    ap.add_argument("--last-version", default=None,
                    help="last published semver; build aborts if version does not advance it")
    ap.add_argument("--ed25519", default=None, help="Ed25519 PKCS8 PEM private key")
    ap.add_argument("--mldsa", default=None, help="ML-DSA-65 raw secret key (.bin)")
    ap.add_argument("--throwaway", action="store_true",
                    help="CI/dry-run: generate ephemeral keys instead of using real ones")
    args = ap.parse_args(argv)

    out_dir = Path(args.out)
    version = rel.read_version(REPO_ROOT)
    print(f"[build] version (single source / pyproject.toml): {version}")

    if args.throwaway:
        print("[build] THROWAWAY keys -- artifact NOT publishable (CI verification only)")
        ed_priv, _ = rel.generate_throwaway_ed25519(out_dir)
        mldsa_sk, _ = rel.generate_throwaway_mldsa(out_dir)
    else:
        ed_path = Path(args.ed25519) if args.ed25519 else _first_existing(_ED25519_CANDIDATES)
        mldsa_path = Path(args.mldsa) if args.mldsa else _first_existing(_MLDSA_CANDIDATES)
        if not ed_path or not mldsa_path:
            print("[build] ERROR: production signing keys not found. Provide --ed25519 "
                  "and --mldsa, or run with --throwaway for a CI dry run.", file=sys.stderr)
            print(f"        searched ed25519: {[str(c) for c in _ED25519_CANDIDATES]}", file=sys.stderr)
            print(f"        searched ml-dsa : {[str(c) for c in _MLDSA_CANDIDATES]}", file=sys.stderr)
            return 2
        print(f"[build] signing with REAL keys (reused): {ed_path.name}, {mldsa_path.name}")
        ed_priv = rel.load_ed25519_private(ed_path)
        mldsa_sk = rel.load_mldsa_sk(mldsa_path)

    try:
        result = rel.build_release(
            repo_root=REPO_ROOT,
            out_dir=out_dir,
            ed25519_priv=ed_priv,
            mldsa_sk=mldsa_sk,
            version=version,
            last_version=args.last_version,
        )
    except rel.BuildError as e:
        print(f"[build] FAILED (fail-closed leak gate): {e}", file=sys.stderr)
        return 1
    except rel.VersionError as e:
        print(f"[build] FAILED (version policy): {e}", file=sys.stderr)
        return 1

    print(f"[build] OK  artifact : {result.artifact}")
    print(f"[build]     sha256   : {result.sha256}")
    print(f"[build]     files    : {result.file_count}")
    print(f"[build]     manifest : {result.manifest}")
    print(f"[build]     sig      : {result.signature.name} (Ed25519)")
    print(f"[build]     pqc sig  : {result.pqc_signature.name} (ML-DSA-65)")
    return 0


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