#!/usr/bin/env python3
"""
Post to PrivateBin from CLI — no pip packages needed.
Uses system python3-cryptography (no pip needed).

Usage:
  echo "hello world"   | ./noctalia-paste
  cat file.txt         | ./noctalia-paste
  echo "secret"        | ./noctalia-paste 1hour
  some_command 2>&1    | ./noctalia-paste

Expire options: 5min, 10min, 1hour, 1day, 1week, 1month, 1year, never
"""
import sys, os, json, base64, hashlib, shutil, subprocess
from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError

try:
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
except ImportError:
    print("Missing python3-cryptography. Install it via your system package manager.", file=sys.stderr)
    sys.exit(1)

PRIVATEBIN_URL = "https://paste.noctalia.dev"
B58_ALPHABET = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def b58encode(data: bytes) -> str:
    n = int.from_bytes(data, "big")
    res = b""
    while n > 0:
        n, r = divmod(n, 58)
        res = bytes([B58_ALPHABET[r]]) + res
    for byte in data:
        if byte == 0:
            res = b"1" + res
        else:
            break
    return res.decode()

def main():
    expire = sys.argv[1] if len(sys.argv) > 1 else "1week"

    if sys.stdin.isatty():
        print("Usage: echo 'text' | noctalia-paste [EXPIRE]", file=sys.stderr)
        print("Expire: 5min, 10min, 1hour, 1day, 1week, 1month, 1year, never", file=sys.stderr)
        sys.exit(1)

    paste = sys.stdin.read()
    if not paste:
        print("Error: empty input", file=sys.stderr)
        sys.exit(1)

    # Generate crypto parameters
    master_key = os.urandom(32)   # 256-bit key (goes in URL fragment, never sent to server)
    iv = os.urandom(16)           # 128-bit IV
    salt = os.urandom(8)          # 64-bit salt

    # Derive encryption key via PBKDF2
    derived_key = hashlib.pbkdf2_hmac("sha256", master_key, salt, 100000, dklen=32)

    # Build authenticated data array (also used as GCM AAD)
    adata = [
        [base64.b64encode(iv).decode(), base64.b64encode(salt).decode(),
         100000, 256, 128, "aes", "gcm", "none"],
        "plaintext", 0, 0
    ]
    adata_json = json.dumps(adata, separators=(",", ":"))

    # Encrypt paste content with AES-256-GCM
    plaintext = json.dumps({"paste": paste}, separators=(",", ":")).encode()
    ciphertext = AESGCM(derived_key).encrypt(iv, plaintext, adata_json.encode())

    # POST to PrivateBin
    body = json.dumps({
        "v": 2,
        "adata": adata,
        "ct": base64.b64encode(ciphertext).decode(),
        "meta": {"expire": expire}
    }).encode()

    req = Request(PRIVATEBIN_URL, data=body, headers={
        "Content-Type": "application/x-www-form-urlencoded",
        "X-Requested-With": "JSONHttpRequest",
    })

    try:
        with urlopen(req) as resp:
            result = json.loads(resp.read())
    except (URLError, HTTPError) as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

    if result.get("status") != 0:
        print(f"Error: {result.get('message', 'unknown error')}", file=sys.stderr)
        sys.exit(1)

    paste_url = f"{PRIVATEBIN_URL}/?{result['id']}#{b58encode(master_key)}"
    print(paste_url)

    # Copy to clipboard (try wayland first, then X11)
    if shutil.which("wl-copy"):
        subprocess.run(["wl-copy", paste_url], check=False)
    elif shutil.which("xclip"):
        subprocess.run(["xclip", "-selection", "clipboard"], input=paste_url.encode(), check=False)
    elif shutil.which("xsel"):
        subprocess.run(["xsel", "--clipboard", "--input"], input=paste_url.encode(), check=False)

if __name__ == "__main__":
    main()
