#!/usr/bin/env python3
"""Offline-first DCC custody CLI. Dedicated age mode is recommended."""
import argparse, os, pathlib, subprocess, sys, tempfile
import stellar_recover

def read_one(path):
    return pathlib.Path(path).read_text(encoding="ascii").strip()

def age_decrypt(identity_path, source, output):
    subprocess.run(["age", "-d", "-i", str(identity_path), "-o", str(output), str(source)], check=True)

def stellar_identity_file(secret_path):
    # This conversion is reachable only from explicit --mode stellar.
    secret = read_one(secret_path)
    identity = stellar_recover.bech32_encode("age-secret-key-", stellar_recover.ed_seed_to_x(stellar_recover.stellar_seed(secret))).upper()
    tmp = tempfile.NamedTemporaryFile("w", encoding="ascii", prefix="dcc-stellar-age-", delete=False)
    try:
        os.chmod(tmp.name, 0o600); tmp.write(identity+"\n"); tmp.close()
        yield pathlib.Path(tmp.name)
    finally:
        try: os.unlink(tmp.name)
        except FileNotFoundError: pass

def identity_for(args):
    if args.mode == "age":
        value=read_one(args.age_identity_file)
        if not value.startswith("AGE-SECRET-KEY-1"):
            raise SystemExit("age mode requires an AGE-SECRET-KEY-1 identity; it is never parsed as a Stellar secret")
        return pathlib.Path(args.age_identity_file), None
    gen=stellar_identity_file(args.stellar_secret_file); return next(gen), gen

def finish(gen):
    if gen is not None:
        try: next(gen)
        except StopIteration: pass

def main():
    p=argparse.ArgumentParser(description="DCC custody recovery; run on an offline trusted host")
    sub=p.add_subparsers(dest="command",required=True)
    r=sub.add_parser("recipient"); r.add_argument("--mode",choices=("age","stellar"),required=True); r.add_argument("--age-recipient"); r.add_argument("--stellar-public")
    d=sub.add_parser("decrypt-share"); d.add_argument("--mode",choices=("age","stellar"),required=True); d.add_argument("--age-identity-file"); d.add_argument("--stellar-secret-file"); d.add_argument("envelope"); d.add_argument("output")
    c=sub.add_parser("combine-legacy"); c.add_argument("shares",nargs=3); c.add_argument("--output",required=True)
    a=sub.add_parser("decrypt-archive"); a.add_argument("--identity-file",required=True); a.add_argument("archive"); a.add_argument("output")
    args=p.parse_args()
    if args.command=="recipient":
        if args.mode=="age":
            if not args.age_recipient or args.stellar_public: p.error("age mode requires only --age-recipient")
            v=args.age_recipient.strip().lower()
            if not v.startswith("age1"): p.error("invalid age recipient")
            print(v)
        else:
            if not args.stellar_public or args.age_recipient: p.error("stellar mode requires only --stellar-public")
            print(stellar_recover.bech32_encode("age",stellar_recover.ed_pub_to_x(stellar_recover.stellar_pub(args.stellar_public))))
    elif args.command=="decrypt-share":
        if args.mode=="age" and (not args.age_identity_file or args.stellar_secret_file): p.error("age mode requires only --age-identity-file")
        if args.mode=="stellar" and (not args.stellar_secret_file or args.age_identity_file): p.error("stellar mode requires only --stellar-secret-file")
        identity,gen=identity_for(args)
        try: age_decrypt(identity,args.envelope,args.output)
        finally: finish(gen)
    elif args.command=="combine-legacy":
        shares=[]
        for name in args.shares:
            fields=pathlib.Path(name).read_text(encoding="ascii").split()
            shares.append((int(fields[0]),bytes.fromhex(fields[1])))
        pathlib.Path(args.output).write_text(stellar_recover.bech32_encode("age-secret-key-",stellar_recover.combine(shares)).upper()+"\n",encoding="ascii")
        os.chmod(args.output,0o600)
    else: age_decrypt(args.identity_file,args.archive,args.output)

if __name__=="__main__": main()
