#!/usr/bin/env python3
"""Example Hermes command-source helper for HashiCorp Vault.

Customize PROFILE_CONFIG, VAULT_ADDR, and the Vault binary path. The helper
prints dotenv-shaped records only on stdout; diagnostics never contain values.
"""
from __future__ import annotations

import json
import os
import re
import subprocess
import sys
from pathlib import Path
from typing import NoReturn

VAULT_ADDR = os.environ.get("VAULT_ADDR", "https://vault.example.com:8200")
VAULT_BIN = "/usr/bin/vault"
PROFILE_CONFIG = {
    "general": {
        "credential_dir": Path.home() / ".config/hermes-vault/general",
        "vault_path": "secret/data/hermes/profiles/general",
    },
    "k3s": {
        "credential_dir": Path.home() / ".config/hermes-vault/k3s",
        "vault_path": "secret/data/hermes/profiles/k3s",
    },
}
ENV_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")


def fail(message: str) -> NoReturn:
    print(f"hermes-vault-env: {message}", file=sys.stderr)
    raise SystemExit(1)


def vault_json(path: str, *, body: dict | None = None, token: str | None = None) -> dict:
    env = {
        "HOME": str(Path.home()),
        "PATH": "/usr/local/bin:/usr/bin:/bin",
        "VAULT_ADDR": VAULT_ADDR,
    }
    if token:
        env["VAULT_TOKEN"] = token
    if body is None:
        argv = [VAULT_BIN, "read", "-format=json", path]
        stdin = None
    else:
        argv = [VAULT_BIN, "write", "-format=json", path, "-"]
        stdin = json.dumps(body)
    try:
        result = subprocess.run(
            argv,
            input=stdin,
            text=True,
            capture_output=True,
            env=env,
            timeout=8,
            check=False,
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        fail(f"Vault CLI failed ({type(exc).__name__})")
    if result.returncode != 0:
        fail(f"Vault CLI returned exit code {result.returncode}")
    try:
        return json.loads(result.stdout)
    except json.JSONDecodeError:
        fail("Vault CLI returned invalid JSON")


def main() -> None:
    profile = os.environ.get("HERMES_VAULT_PROFILE", "").strip()
    cfg = PROFILE_CONFIG.get(profile)
    if not cfg:
        fail("profile is not allowlisted")

    try:
        role_id = (cfg["credential_dir"] / "role_id").read_text().strip()
        secret_id = (cfg["credential_dir"] / "secret_id").read_text().strip()
    except OSError as exc:
        fail(f"cannot read AppRole bootstrap files ({exc.errno})")
    if not role_id or not secret_id:
        fail("AppRole bootstrap credential is empty")

    login = vault_json(
        "auth/approle/login",
        body={"role_id": role_id, "secret_id": secret_id},
    )
    try:
        token = login["auth"]["client_token"]
    except (KeyError, TypeError):
        fail("Vault login returned no client token")

    response = vault_json(cfg["vault_path"], token=token)
    try:
        secrets = response["data"]["data"]
    except (KeyError, TypeError):
        fail("Vault KV response did not contain profile data")

    for key in sorted(secrets):
        value = secrets[key]
        if not isinstance(key, str) or not ENV_NAME.fullmatch(key):
            fail("Vault contains an invalid environment variable name")
        if not isinstance(value, str):
            fail(f"Vault value for {key} is not a string")
        if "\n" in value or "\r" in value:
            fail(f"Vault value for {key} contains a newline")
        print(f"{key}={value}")


if __name__ == "__main__":
    main()
