#!/usr/bin/env python3
"""Extract signing entitlements from an Apple provisioning profile."""

from __future__ import annotations

import argparse
import plistlib
import subprocess
import sys
from pathlib import Path


def decode_profile(path: Path) -> dict:
    commands = [
        ["security", "cms", "-D", "-i", str(path)],
        ["openssl", "smime", "-inform", "DER", "-verify", "-noverify", "-in", str(path)],
        ["openssl", "cms", "-inform", "DER", "-verify", "-noverify", "-in", str(path)],
    ]
    errors: list[str] = []
    for command in commands:
        try:
            proc = subprocess.run(
                command,
                check=False,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
        except FileNotFoundError:
            continue
        if proc.returncode == 0 and proc.stdout:
            return plistlib.loads(proc.stdout)
        errors.append(f"{command[0]} exited {proc.returncode}: {proc.stderr.decode(errors='replace').strip()}")
    raise RuntimeError(f"unable to decode provisioning profile {path}: {'; '.join(errors)}")


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("profile", type=Path)
    parser.add_argument("--output", "-o", type=Path, required=True)
    args = parser.parse_args(argv)

    profile = decode_profile(args.profile)
    entitlements = profile.get("Entitlements")
    if not isinstance(entitlements, dict):
        raise RuntimeError(f"profile has no Entitlements dictionary: {args.profile}")

    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_bytes(plistlib.dumps(entitlements, fmt=plistlib.FMT_XML))
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
