#!/usr/bin/env python3
"""BSN Secrets Manager — GPG-verschlüsselte Secrets für alle BSN-Agenten.

Usage:
  bsn-secrets set <name>        # Wert von stdin einlesen und verschlüsselt speichern
  bsn-secrets get <name>        # Entschlüsselten Wert auf stdout ausgeben
  bsn-secrets list              # Alle gespeicherten Secrets auflisten
  bsn-secrets delete <name>     # Secret löschen

Konfiguration:
  Passphrase aus BSN_SECRETS_PASSPHRASE (aus Hermes .env)
  Store-Verzeichnis: ~/.password-store/bsn/

Beispiele:
  echo "abc123token" | bsn-secrets set gitea-token
  bsn-secrets get gitea-token
  TOKEN=$(bsn-secrets get gitea-token)
"""

import os
import sys
import subprocess
import json
from pathlib import Path

STORE = Path.home() / ".password-store" / "bsn"


def _get_passphrase() -> str:
    """Passphrase aus Umgebungsvariable oder .env-Dateien lesen."""
    pp = os.getenv("BSN_SECRETS_PASSPHRASE", "")
    if pp:
        return pp

    # Fallback: Aus Hermes .env lesen
    for env_path in [
        Path.home() / ".hermes" / "profiles" / "cody" / ".env",
        Path.home() / ".hermes" / ".env",
    ]:
        if env_path.exists():
            for line in env_path.read_text().split("\n"):
                if line.startswith("BSN_SECRETS_PASSPHRASE="):
                    return line.split("=", 1)[1].strip().strip('"').strip("'")

    return ""


def _encrypt(plaintext: str, passphrase: str) -> bytes:
    """GPG-symmetrisch verschlüsseln."""
    result = subprocess.run(
        [
            "gpg", "--symmetric", "--batch", "--yes",
            "--passphrase", passphrase,
            "--quiet", "--no-tty",
        ],
        input=plaintext.encode("utf-8"),
        capture_output=True,
    )
    if result.returncode != 0:
        print(f"❌ GPG-Verschlüsselung fehlgeschlagen: {result.stderr.decode()}", file=sys.stderr)
        sys.exit(1)
    return result.stdout


def _decrypt(filepath: Path, passphrase: str) -> str:
    """GPG-symmetrisch entschlüsseln."""
    result = subprocess.run(
        [
            "gpg", "--decrypt", "--batch", "--yes",
            "--passphrase", passphrase,
            "--quiet", "--no-tty",
            str(filepath),
        ],
        capture_output=True,
    )
    if result.returncode != 0:
        print(f"❌ GPG-Entschlüsselung fehlgeschlagen: {result.stderr.decode()}", file=sys.stderr)
        sys.exit(1)
    return result.stdout.decode("utf-8").strip()


def _resolve_path(name: str) -> Path:
    """Namen in Dateipfad auflösen. 'foo/bar' → STORE/foo/bar.gpg, 'bar' → STORE/bar.gpg."""
    parts = name.split("/")
    parent = STORE.joinpath(*parts[:-1]) if len(parts) > 1 else STORE
    parent.mkdir(parents=True, exist_ok=True)
    return parent / f"{parts[-1]}.gpg"


def cmd_set(name: str) -> None:
    """Secret von stdin lesen und verschlüsselt speichern."""
    passphrase = _get_passphrase()
    if not passphrase:
        print("❌ BSN_SECRETS_PASSPHRASE nicht gesetzt!", file=sys.stderr)
        sys.exit(1)

    plaintext = sys.stdin.read().strip()
    if not plaintext:
        print("❌ Kein Input auf stdin!", file=sys.stderr)
        sys.exit(1)

    encrypted = _encrypt(plaintext, passphrase)
    filepath = _resolve_path(name)
    filepath.write_bytes(encrypted)
    filepath.chmod(0o600)
    print(f"✅ Secret '{name}' gespeichert ({len(plaintext)} Zeichen)")


def cmd_get(name: str) -> None:
    """Secret entschlüsseln und auf stdout ausgeben."""
    passphrase = _get_passphrase()
    if not passphrase:
        print("❌ BSN_SECRETS_PASSPHRASE nicht gesetzt!", file=sys.stderr)
        sys.exit(1)

    filepath = _resolve_path(name)
    if not filepath.exists():
        print(f"❌ Secret '{name}' nicht gefunden!", file=sys.stderr)
        sys.exit(1)

    plaintext = _decrypt(filepath, passphrase)
    sys.stdout.write(plaintext)


def cmd_list() -> None:
    """Alle gespeicherten Secrets auflisten."""
    if not STORE.exists():
        print("(keine Secrets gespeichert)")
        return

    secrets = sorted(STORE.rglob("*.gpg"))
    if not secrets:
        print("(keine Secrets gespeichert)")
        return

    for f in secrets:
        rel = f.relative_to(STORE)
        name = str(rel.with_suffix(""))  # Ohne .gpg
        size = f.stat().st_size
        print(f"  🔒 {name:30s} ({size} bytes)")


def cmd_delete(name: str) -> None:
    """Secret löschen."""
    filepath = _resolve_path(name)
    if not filepath.exists():
        print(f"❌ Secret '{name}' nicht gefunden!", file=sys.stderr)
        sys.exit(1)

    filepath.unlink()
    print(f"🗑️  Secret '{name}' gelöscht")


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        sys.exit(0)

    cmd = sys.argv[1]

    if cmd == "set":
        if len(sys.argv) < 3:
            print("Usage: bsn-secrets set <name>", file=sys.stderr)
            sys.exit(1)
        cmd_set(sys.argv[2])

    elif cmd == "get":
        if len(sys.argv) < 3:
            print("Usage: bsn-secrets get <name>", file=sys.stderr)
            sys.exit(1)
        cmd_get(sys.argv[2])

    elif cmd == "list":
        cmd_list()

    elif cmd == "delete":
        if len(sys.argv) < 3:
            print("Usage: bsn-secrets delete <name>", file=sys.stderr)
            sys.exit(1)
        cmd_delete(sys.argv[2])

    else:
        print(f"❌ Unbekannter Befehl: {cmd}", file=sys.stderr)
        print(__doc__)
        sys.exit(1)


if __name__ == "__main__":
    main()
