#!/usr/bin/env python3
"""FlaskPaste command-line client."""

import argparse
import base64
import hashlib
import json
import os
import ssl
import sys
import urllib.error
import urllib.request
from datetime import UTC, datetime, timedelta
from pathlib import Path

# Optional cryptography support (for encryption and cert generation)
try:
    from cryptography import x509
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import ec, rsa
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    from cryptography.x509.oid import NameOID

    HAS_CRYPTO = True
except ImportError:
    HAS_CRYPTO = False


def get_config():
    """Load configuration from environment or config file."""
    config = {
        "server": os.environ.get("FLASKPASTE_SERVER", "http://localhost:5000"),
        "cert_sha1": os.environ.get("FLASKPASTE_CERT_SHA1", ""),
        "client_cert": os.environ.get("FLASKPASTE_CLIENT_CERT", ""),
        "client_key": os.environ.get("FLASKPASTE_CLIENT_KEY", ""),
        "ca_cert": os.environ.get("FLASKPASTE_CA_CERT", ""),
    }

    # Try config file
    config_file = Path.home() / ".config" / "fpaste" / "config"
    if config_file.exists():
        for line in config_file.read_text().splitlines():
            line = line.strip()
            if line and not line.startswith("#") and "=" in line:
                key, value = line.split("=", 1)
                key = key.strip().lower()
                value = value.strip().strip('"').strip("'")
                if key == "server":
                    config["server"] = value
                elif key == "cert_sha1":
                    config["cert_sha1"] = value
                elif key == "client_cert":
                    config["client_cert"] = value
                elif key == "client_key":
                    config["client_key"] = value
                elif key == "ca_cert":
                    config["ca_cert"] = value

    return config


def create_ssl_context(config):
    """Create SSL context for mTLS if certificates are configured."""
    client_cert = config.get("client_cert", "")
    client_key = config.get("client_key", "")
    ca_cert = config.get("ca_cert", "")

    if not client_cert:
        return None

    ctx = ssl.create_default_context()

    # Load CA certificate if specified
    if ca_cert:
        ctx.load_verify_locations(ca_cert)

    # Load client certificate and key
    try:
        ctx.load_cert_chain(certfile=client_cert, keyfile=client_key or None)
    except ssl.SSLError as e:
        die(f"failed to load client certificate: {e}")
    except FileNotFoundError as e:
        die(f"certificate file not found: {e}")

    return ctx


def request(url, method="GET", data=None, headers=None, ssl_context=None):
    """Make HTTP request and return response."""
    headers = headers or {}
    # User-configured server URL, audit is expected
    req = urllib.request.Request(url, data=data, headers=headers, method=method)  # noqa: S310

    try:
        # User-configured server URL, audit is expected
        with urllib.request.urlopen(req, timeout=30, context=ssl_context) as resp:  # noqa: S310
            return resp.status, resp.read(), dict(resp.headers)
    except urllib.error.HTTPError as e:
        return e.code, e.read(), dict(e.headers)
    except urllib.error.URLError as e:
        die(f"Connection failed: {e.reason}")


def die(msg, code=1):
    """Print error and exit."""
    print(f"error: {msg}", file=sys.stderr)
    sys.exit(code)


def encrypt_content(plaintext):
    """Encrypt content with AES-256-GCM. Returns (ciphertext, key)."""
    if not HAS_CRYPTO:
        die("encryption requires 'cryptography' package: pip install cryptography")
    key = os.urandom(32)
    nonce = os.urandom(12)  # 96-bit nonce for GCM
    aesgcm = AESGCM(key)
    ciphertext = aesgcm.encrypt(nonce, plaintext, None)
    return nonce + ciphertext, key


def decrypt_content(blob, key):
    """Decrypt AES-256-GCM encrypted content."""
    if not HAS_CRYPTO:
        die("decryption requires 'cryptography' package: pip install cryptography")
    if len(blob) < 12:
        die("encrypted content too short")
    nonce, ciphertext = blob[:12], blob[12:]
    aesgcm = AESGCM(key)
    try:
        return aesgcm.decrypt(nonce, ciphertext, None)
    except Exception:
        die("decryption failed (wrong key or corrupted data)")


def encode_key(key):
    """Encode key as URL-safe base64."""
    return base64.urlsafe_b64encode(key).decode().rstrip("=")


def decode_key(encoded):
    """Decode URL-safe base64 key."""
    # Add padding if needed
    padding = 4 - (len(encoded) % 4)
    if padding != 4:
        encoded += "=" * padding
    try:
        return base64.urlsafe_b64decode(encoded)
    except Exception:
        die("invalid encryption key in URL")


def solve_pow(nonce, difficulty):
    """Solve proof-of-work challenge.

    Find a number N such that SHA256(nonce:N) has `difficulty` leading zero bits.
    """
    n = 0
    target_bytes = (difficulty + 7) // 8  # Bytes to check

    while True:
        work = f"{nonce}:{n}".encode()
        hash_bytes = hashlib.sha256(work).digest()

        # Count leading zero bits
        zero_bits = 0
        for byte in hash_bytes[: target_bytes + 1]:
            if byte == 0:
                zero_bits += 8
            else:
                zero_bits += 8 - byte.bit_length()
                break

        if zero_bits >= difficulty:
            return n

        n += 1
        # Progress indicator for high difficulty
        if n % 100000 == 0:
            print(f"\rsolving pow: {n} attempts...", end="", file=sys.stderr)

    return n


def get_challenge(config):
    """Fetch PoW challenge from server."""
    url = config["server"].rstrip("/") + "/challenge"
    status, body, _ = request(url, ssl_context=config.get("ssl_context"))

    if status != 200:
        return None

    data = json.loads(body)
    if not data.get("enabled"):
        return None

    return data


def cmd_create(args, config):
    """Create a new paste."""
    # Read content from file or stdin
    if args.file:
        if args.file == "-":
            content = sys.stdin.buffer.read()
        else:
            path = Path(args.file)
            if not path.exists():
                die(f"file not found: {args.file}")
            content = path.read_bytes()
    else:
        # No file specified, read from stdin
        if sys.stdin.isatty():
            die("no input provided (pipe data or specify file)")
        content = sys.stdin.buffer.read()

    if not content:
        die("empty content")

    # Encrypt by default (unless --no-encrypt)
    encryption_key = None
    if not getattr(args, "no_encrypt", False):
        if not HAS_CRYPTO:
            die("encryption requires 'cryptography' package (use -E to disable)")
        if not args.quiet:
            print("encrypting...", end="", file=sys.stderr)
        content, encryption_key = encrypt_content(content)
        if not args.quiet:
            print(" done", file=sys.stderr)

    headers = {}
    if config["cert_sha1"]:
        headers["X-SSL-Client-SHA1"] = config["cert_sha1"]

    # Add burn-after-read header
    if args.burn:
        headers["X-Burn-After-Read"] = "true"

    # Add custom expiry header
    if args.expiry:
        headers["X-Expiry"] = str(args.expiry)

    # Add password header
    if args.password:
        headers["X-Paste-Password"] = args.password

    # Get and solve PoW challenge if required
    challenge = get_challenge(config)
    if challenge:
        if not args.quiet:
            print(f"solving pow (difficulty={challenge['difficulty']})...", end="", file=sys.stderr)
        solution = solve_pow(challenge["nonce"], challenge["difficulty"])
        if not args.quiet:
            print(" done", file=sys.stderr)
        headers["X-PoW-Token"] = challenge["token"]
        headers["X-PoW-Solution"] = str(solution)

    url = config["server"].rstrip("/") + "/"
    status, body, _ = request(
        url, method="POST", data=content, headers=headers, ssl_context=config.get("ssl_context")
    )

    if status == 201:
        data = json.loads(body)
        # Append encryption key to URL fragment if encrypted
        key_fragment = ""
        if encryption_key:
            key_fragment = "#" + encode_key(encryption_key)

        if args.raw:
            print(config["server"].rstrip("/") + data["raw"] + key_fragment)
        elif args.quiet:
            print(data["id"] + key_fragment)
        else:
            print(config["server"].rstrip("/") + data["url"] + key_fragment)
    else:
        try:
            err = json.loads(body).get("error", body.decode())
        except (json.JSONDecodeError, UnicodeDecodeError):
            err = body.decode(errors="replace")
        die(f"create failed ({status}): {err}")


def cmd_get(args, config):
    """Retrieve a paste."""
    # Parse URL for paste ID and optional encryption key fragment
    url_input = args.id
    encryption_key = None

    # Extract key from URL fragment (#...)
    if "#" in url_input:
        url_input, key_encoded = url_input.rsplit("#", 1)
        if key_encoded:
            encryption_key = decode_key(key_encoded)

    paste_id = url_input.split("/")[-1]  # Handle full URLs
    base = config["server"].rstrip("/")

    # Build headers for password-protected pastes
    headers = {}
    if args.password:
        headers["X-Paste-Password"] = args.password

    if args.meta:
        url = f"{base}/{paste_id}"
        status, body, _ = request(url, headers=headers, ssl_context=config.get("ssl_context"))
        if status == 200:
            data = json.loads(body)
            print(f"id:         {data['id']}")
            print(f"mime_type:  {data['mime_type']}")
            print(f"size:       {data['size']}")
            print(f"created_at: {data['created_at']}")
            if encryption_key:
                print("encrypted:  yes (key in URL)")
            if data.get("password_protected"):
                print("protected:  yes (password required)")
        elif status == 401:
            die("password required (-p)")
        elif status == 403:
            die("invalid password")
        else:
            die(f"not found: {paste_id}")
    else:
        url = f"{base}/{paste_id}/raw"
        ssl_ctx = config.get("ssl_context")
        status, body, _ = request(url, headers=headers, ssl_context=ssl_ctx)
        if status == 200:
            # Decrypt if encryption key was provided
            if encryption_key:
                body = decrypt_content(body, encryption_key)

            if args.output:
                Path(args.output).write_bytes(body)
                print(f"saved: {args.output}", file=sys.stderr)
            else:
                # Write binary to stdout
                sys.stdout.buffer.write(body)
                # Add newline if content doesn't end with one and stdout is tty
                if sys.stdout.isatty() and body and not body.endswith(b"\n"):
                    sys.stdout.buffer.write(b"\n")
        elif status == 401:
            die("password required (-p)")
        elif status == 403:
            die("invalid password")
        else:
            die(f"not found: {paste_id}")


def cmd_delete(args, config):
    """Delete a paste."""
    if not config["cert_sha1"]:
        die("authentication required (set FLASKPASTE_CERT_SHA1)")

    paste_id = args.id.split("/")[-1]
    base = config["server"].rstrip("/")
    url = f"{base}/{paste_id}"

    headers = {"X-SSL-Client-SHA1": config["cert_sha1"]}
    status, _, _ = request(
        url, method="DELETE", headers=headers, ssl_context=config.get("ssl_context")
    )

    if status == 200:
        print(f"deleted: {paste_id}")
    elif status == 404:
        die(f"not found: {paste_id}")
    elif status == 403:
        die("permission denied (not owner)")
    elif status == 401:
        die("authentication failed")
    else:
        die(f"delete failed ({status})")


def cmd_info(args, config):
    """Show server info."""
    url = config["server"].rstrip("/") + "/"
    status, body, _ = request(url, ssl_context=config.get("ssl_context"))

    if status == 200:
        data = json.loads(body)
        print(f"server:  {config['server']}")
        print(f"name:    {data.get('name', 'unknown')}")
        print(f"version: {data.get('version', 'unknown')}")
    else:
        die("failed to connect to server")


def cmd_pki_status(args, config):
    """Show PKI status and CA information."""
    url = config["server"].rstrip("/") + "/pki"
    status, body, _ = request(url, ssl_context=config.get("ssl_context"))

    if status == 404:
        die("PKI not enabled on this server")
    elif status != 200:
        die(f"failed to get PKI status ({status})")

    data = json.loads(body)

    print(f"pki enabled: {data.get('enabled', False)}")
    print(f"ca exists:   {data.get('ca_exists', False)}")

    if data.get("ca_exists"):
        print(f"common name: {data.get('common_name', 'unknown')}")
        print(f"fingerprint: {data.get('fingerprint_sha1', 'unknown')}")
        if data.get("created_at"):
            print(f"created:     {data.get('created_at')}")
        if data.get("expires_at"):
            print(f"expires:     {data.get('expires_at')}")
        print(f"download:    {config['server'].rstrip('/')}{data.get('download', '/pki/ca.crt')}")
    elif data.get("hint"):
        print(f"hint:        {data.get('hint')}")


def cmd_pki_issue(args, config):
    """Request a new client certificate from the server CA."""
    url = config["server"].rstrip("/") + "/pki/issue"

    headers = {"Content-Type": "application/json"}
    if config["cert_sha1"]:
        headers["X-SSL-Client-SHA1"] = config["cert_sha1"]

    payload = {"common_name": args.name}
    data = json.dumps(payload).encode()

    status, body, _ = request(
        url, method="POST", data=data, headers=headers, ssl_context=config.get("ssl_context")
    )

    if status == 404:
        # Could be PKI disabled or no CA
        try:
            err = json.loads(body).get("error", "PKI not available")
        except (json.JSONDecodeError, UnicodeDecodeError):
            err = "PKI not available"
        die(err)
    elif status == 400:
        try:
            err = json.loads(body).get("error", "bad request")
        except (json.JSONDecodeError, UnicodeDecodeError):
            err = "bad request"
        die(err)
    elif status != 201:
        die(f"certificate issuance failed ({status})")

    result = json.loads(body)

    # Determine output directory
    out_dir = Path(args.output) if args.output else Path.home() / ".config" / "fpaste"
    out_dir.mkdir(parents=True, exist_ok=True)

    # File paths
    key_file = out_dir / "client.key"
    cert_file = out_dir / "client.crt"

    # Check for existing files
    if not args.force:
        if key_file.exists():
            die(f"key file exists: {key_file} (use --force)")
        if cert_file.exists():
            die(f"cert file exists: {cert_file} (use --force)")

    # Write files
    key_file.write_text(result["private_key_pem"])
    key_file.chmod(0o600)
    cert_file.write_text(result["certificate_pem"])

    fingerprint = result.get("fingerprint_sha1", "unknown")

    print(f"key:         {key_file}", file=sys.stderr)
    print(f"certificate: {cert_file}", file=sys.stderr)
    print(f"fingerprint: {fingerprint}", file=sys.stderr)
    print(f"serial:      {result.get('serial', 'unknown')}", file=sys.stderr)
    print(f"common name: {result.get('common_name', args.name)}", file=sys.stderr)

    # Update config file if requested
    if args.configure:
        config_file = Path.home() / ".config" / "fpaste" / "config"
        config_file.parent.mkdir(parents=True, exist_ok=True)

        # Read existing config
        existing = {}
        if config_file.exists():
            for line in config_file.read_text().splitlines():
                line = line.strip()
                if line and not line.startswith("#") and "=" in line:
                    k, v = line.split("=", 1)
                    existing[k.strip().lower()] = v.strip()

        # Update values
        existing["client_cert"] = str(cert_file)
        existing["client_key"] = str(key_file)
        existing["cert_sha1"] = fingerprint

        # Write config
        lines = [f"{k} = {v}" for k, v in sorted(existing.items())]
        config_file.write_text("\n".join(lines) + "\n")
        print(f"config:      {config_file} (updated)", file=sys.stderr)

    # Output fingerprint to stdout for easy capture
    print(fingerprint)


def cmd_pki_download(args, config):
    """Download the CA certificate from the server."""
    url = config["server"].rstrip("/") + "/pki/ca.crt"
    status, body, _ = request(url, ssl_context=config.get("ssl_context"))

    if status == 404:
        die("CA certificate not available (PKI disabled or CA not generated)")
    elif status != 200:
        die(f"failed to download CA certificate ({status})")

    # Determine output
    if args.output:
        out_path = Path(args.output)
        out_path.write_bytes(body)
        print(f"saved: {out_path}", file=sys.stderr)

        # Calculate and show fingerprint if cryptography available
        if HAS_CRYPTO:
            cert = x509.load_pem_x509_certificate(body)
            # SHA1 is standard for X.509 fingerprints
            fp = hashlib.sha1(cert.public_bytes(serialization.Encoding.DER)).hexdigest()  # noqa: S324
            print(f"fingerprint: {fp}", file=sys.stderr)

        # Update config if requested
        if args.configure:
            config_file = Path.home() / ".config" / "fpaste" / "config"
            config_file.parent.mkdir(parents=True, exist_ok=True)

            existing = {}
            if config_file.exists():
                for line in config_file.read_text().splitlines():
                    line = line.strip()
                    if line and not line.startswith("#") and "=" in line:
                        k, v = line.split("=", 1)
                        existing[k.strip().lower()] = v.strip()

            existing["ca_cert"] = str(out_path)

            lines = [f"{k} = {v}" for k, v in sorted(existing.items())]
            config_file.write_text("\n".join(lines) + "\n")
            print(f"config:      {config_file} (updated)", file=sys.stderr)
    else:
        # Output to stdout
        sys.stdout.buffer.write(body)


def cmd_cert(args, config):
    """Generate a self-signed client certificate for mTLS authentication."""
    if not HAS_CRYPTO:
        die("certificate generation requires 'cryptography' package: pip install cryptography")

    # Determine output directory
    out_dir = Path(args.output) if args.output else Path.home() / ".config" / "fpaste"
    out_dir.mkdir(parents=True, exist_ok=True)

    # File paths
    key_file = out_dir / "client.key"
    cert_file = out_dir / "client.crt"

    # Check for existing files
    if not args.force:
        if key_file.exists():
            die(f"key file exists: {key_file} (use --force)")
        if cert_file.exists():
            die(f"cert file exists: {cert_file} (use --force)")

    # Generate private key
    if args.algorithm == "rsa":
        key_size = args.bits or 4096
        print(f"generating {key_size}-bit RSA key...", file=sys.stderr)
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=key_size,
        )
    elif args.algorithm == "ec":
        curve_name = args.curve or "secp384r1"
        curves = {
            "secp256r1": ec.SECP256R1(),
            "secp384r1": ec.SECP384R1(),
            "secp521r1": ec.SECP521R1(),
        }
        if curve_name not in curves:
            die(f"unsupported curve: {curve_name} (use: secp256r1, secp384r1, secp521r1)")
        print(f"generating EC key ({curve_name})...", file=sys.stderr)
        private_key = ec.generate_private_key(curves[curve_name])
    else:
        die(f"unsupported algorithm: {args.algorithm}")

    # Certificate subject
    cn = args.name or os.environ.get("USER", "fpaste-client")
    subject = issuer = x509.Name(
        [
            x509.NameAttribute(NameOID.COMMON_NAME, cn),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "FlaskPaste Client"),
        ]
    )

    # Validity period
    days = args.days or 365
    now = datetime.now(UTC)

    # Build certificate
    cert_builder = (
        x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(issuer)
        .public_key(private_key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(now)
        .not_valid_after(now + timedelta(days=days))
        .add_extension(
            x509.BasicConstraints(ca=False, path_length=None),
            critical=True,
        )
        .add_extension(
            x509.KeyUsage(
                digital_signature=True,
                key_encipherment=True,
                content_commitment=False,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=False,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False,
            ),
            critical=True,
        )
        .add_extension(
            x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]),
            critical=False,
        )
    )

    # Sign certificate
    print("signing certificate...", file=sys.stderr)
    certificate = cert_builder.sign(private_key, hashes.SHA256())

    # Calculate SHA1 fingerprint (standard for X.509)
    cert_der = certificate.public_bytes(serialization.Encoding.DER)
    fingerprint = hashlib.sha1(cert_der).hexdigest()  # noqa: S324

    # Serialize private key
    if args.password_key:
        key_encryption = serialization.BestAvailableEncryption(args.password_key.encode("utf-8"))
    else:
        key_encryption = serialization.NoEncryption()

    key_pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=key_encryption,
    )

    # Serialize certificate
    cert_pem = certificate.public_bytes(serialization.Encoding.PEM)

    # Write files
    key_file.write_bytes(key_pem)
    key_file.chmod(0o600)  # Restrict permissions
    cert_file.write_bytes(cert_pem)

    print(f"key:         {key_file}", file=sys.stderr)
    print(f"certificate: {cert_file}", file=sys.stderr)
    print(f"fingerprint: {fingerprint}", file=sys.stderr)
    print(f"valid for:   {days} days", file=sys.stderr)
    print(f"common name: {cn}", file=sys.stderr)

    # Update config file if requested
    if args.configure:
        config_file = Path.home() / ".config" / "fpaste" / "config"
        config_file.parent.mkdir(parents=True, exist_ok=True)

        # Read existing config
        existing = {}
        if config_file.exists():
            for line in config_file.read_text().splitlines():
                line = line.strip()
                if line and not line.startswith("#") and "=" in line:
                    k, v = line.split("=", 1)
                    existing[k.strip().lower()] = v.strip()

        # Update values
        existing["client_cert"] = str(cert_file)
        existing["client_key"] = str(key_file)
        existing["cert_sha1"] = fingerprint

        # Write config
        lines = [f"{k} = {v}" for k, v in sorted(existing.items())]
        config_file.write_text("\n".join(lines) + "\n")
        print(f"config:      {config_file} (updated)", file=sys.stderr)

    # Output fingerprint to stdout for easy capture
    print(fingerprint)


def is_file_path(arg):
    """Check if argument looks like a file path."""
    if not arg or arg.startswith("-"):
        return False
    # Check if it's an existing file
    if Path(arg).exists():
        return True
    # Check if it looks like a path (contains / or \ or common extensions)
    if "/" in arg or "\\" in arg:
        return True
    # Check for common file extensions
    if "." in arg and not arg.startswith("."):
        ext = arg.rsplit(".", 1)[-1].lower()
        if ext in ("txt", "md", "py", "js", "json", "yaml", "yml", "xml", "html",
                   "css", "sh", "bash", "c", "cpp", "h", "go", "rs", "java",
                   "rb", "php", "sql", "log", "conf", "cfg", "ini", "png",
                   "jpg", "jpeg", "gif", "pdf", "zip", "tar", "gz"):
            return True
    return False


def main():
    # Pre-process arguments: if first positional looks like a file, insert "create"
    args_to_parse = sys.argv[1:]
    commands = {"create", "c", "new", "get", "g", "delete", "d", "rm", "info", "i", "cert", "pki"}

    # Find insertion point for "create" command
    insert_pos = 0
    has_command = False
    file_pos = -1

    i = 0
    while i < len(args_to_parse):
        arg = args_to_parse[i]
        if arg in ("-s", "--server"):
            insert_pos = i + 2  # After -s value
            i += 2
            continue
        if arg in ("-h", "--help"):
            i += 1
            insert_pos = i
            continue
        if arg.startswith("-"):
            # Unknown option - might be for create subcommand
            i += 1
            continue
        # Found positional argument
        if arg in commands:
            has_command = True
            break
        elif is_file_path(arg):
            file_pos = i
            break
        i += 1

    # Insert "create" if no command found and we have input (file path or piped stdin)
    if not has_command and (file_pos >= 0 or not sys.stdin.isatty()):
        args_to_parse.insert(insert_pos, "create")

    parser = argparse.ArgumentParser(
        prog="fpaste",
        description="FlaskPaste command-line client",
        epilog="Shortcut: fpaste <file> is equivalent to fpaste create <file>",
    )
    parser.add_argument(
        "-s",
        "--server",
        help="server URL (env: FLASKPASTE_SERVER)",
    )
    subparsers = parser.add_subparsers(dest="command", metavar="command")

    # create
    p_create = subparsers.add_parser("create", aliases=["c", "new"], help="create paste")
    p_create.add_argument("file", nargs="?", help="file to upload (- for stdin)")
    p_create.add_argument("-E", "--no-encrypt", action="store_true", help="disable encryption")
    p_create.add_argument("-b", "--burn", action="store_true", help="burn after read")
    p_create.add_argument("-x", "--expiry", type=int, metavar="SEC", help="expiry in seconds")
    p_create.add_argument("-p", "--password", metavar="PASS", help="password protect")
    p_create.add_argument("-r", "--raw", action="store_true", help="output raw URL")
    p_create.add_argument("-q", "--quiet", action="store_true", help="output ID only")

    # get
    p_get = subparsers.add_parser("get", aliases=["g"], help="retrieve paste")
    p_get.add_argument("id", help="paste ID or URL")
    p_get.add_argument("-o", "--output", help="save to file")
    p_get.add_argument("-p", "--password", metavar="PASS", help="password for protected paste")
    p_get.add_argument("-m", "--meta", action="store_true", help="show metadata only")

    # delete
    p_delete = subparsers.add_parser("delete", aliases=["d", "rm"], help="delete paste")
    p_delete.add_argument("id", help="paste ID or URL")

    # info
    subparsers.add_parser("info", aliases=["i"], help="show server info")

    # cert
    p_cert = subparsers.add_parser("cert", help="generate client certificate")
    p_cert.add_argument("-o", "--output", metavar="DIR", help="output directory")
    p_cert.add_argument(
        "-a", "--algorithm", choices=["rsa", "ec"], default="ec", help="key algorithm (default: ec)"
    )
    p_cert.add_argument("-b", "--bits", type=int, metavar="N", help="RSA key size (default: 4096)")
    p_cert.add_argument(
        "-c", "--curve", metavar="CURVE", help="EC curve: secp256r1, secp384r1, secp521r1"
    )
    p_cert.add_argument("-d", "--days", type=int, metavar="N", help="validity period in days")
    p_cert.add_argument("-n", "--name", metavar="CN", help="common name (default: $USER)")
    p_cert.add_argument("--password-key", metavar="PASS", help="encrypt private key with password")
    p_cert.add_argument(
        "--configure", action="store_true", help="update config file with generated cert paths"
    )
    p_cert.add_argument("-f", "--force", action="store_true", help="overwrite existing files")

    # pki (with subcommands)
    p_pki = subparsers.add_parser("pki", help="PKI operations (server-issued certificates)")
    pki_sub = p_pki.add_subparsers(dest="pki_command", metavar="subcommand")

    # pki status
    pki_sub.add_parser("status", help="show PKI status and CA info")

    # pki issue
    p_pki_issue = pki_sub.add_parser("issue", help="request certificate from server CA")
    p_pki_issue.add_argument(
        "-n", "--name", required=True, metavar="CN", help="common name for certificate (required)"
    )
    p_pki_issue.add_argument(
        "-o", "--output", metavar="DIR", help="output directory (default: ~/.config/fpaste)"
    )
    p_pki_issue.add_argument(
        "--configure", action="store_true", help="update config file with issued cert paths"
    )
    p_pki_issue.add_argument("-f", "--force", action="store_true", help="overwrite existing files")

    # pki download
    p_pki_download = pki_sub.add_parser("download", aliases=["dl"], help="download CA certificate")
    p_pki_download.add_argument(
        "-o", "--output", metavar="FILE", help="save to file (default: stdout)"
    )
    p_pki_download.add_argument(
        "--configure",
        action="store_true",
        help="update config file with CA cert path (requires -o)",
    )

    args = parser.parse_args(args_to_parse)
    config = get_config()

    if args.server:
        config["server"] = args.server

    # Create SSL context for mTLS if configured
    config["ssl_context"] = create_ssl_context(config)

    if not args.command:
        # Default: create from stdin if data is piped
        if not sys.stdin.isatty():
            args.command = "create"
            args.file = None
            args.no_encrypt = False  # Encrypt by default
            args.burn = False
            args.expiry = None
            args.password = None
            args.raw = False
            args.quiet = False
        else:
            parser.print_help()
            sys.exit(0)

    if args.command in ("create", "c", "new"):
        cmd_create(args, config)
    elif args.command in ("get", "g"):
        cmd_get(args, config)
    elif args.command in ("delete", "d", "rm"):
        cmd_delete(args, config)
    elif args.command in ("info", "i"):
        cmd_info(args, config)
    elif args.command == "cert":
        cmd_cert(args, config)
    elif args.command == "pki":
        if args.pki_command == "status":
            cmd_pki_status(args, config)
        elif args.pki_command == "issue":
            cmd_pki_issue(args, config)
        elif args.pki_command in ("download", "dl"):
            cmd_pki_download(args, config)
        else:
            # Show pki help if no subcommand
            parser.parse_args(["pki", "--help"])


if __name__ == "__main__":
    main()
