"""IIO Vault Client — reads secrets from HashiCorp Vault via AppRole.
Classification: own | Layer: 0 | Version: 1.0 | 2026-06-11
CS-Equivalent: secret store client with token caching + auto-renewal
"""
import json, os, urllib.request, urllib.error, yaml
from pathlib import Path
from typing import Optional

IIO_ROOT = Path(os.environ.get("IIO_ROOT", Path(__file__).parents[2]))
_CONFIG_PATH = IIO_ROOT / "specs/state/vault-client-config.yaml"
_CONFIG_PATH_ALT = IIO_ROOT / "vault-client-config.yaml"  # deployed path (same dir)
_TOKEN_CACHE: Optional[str] = None

def _load_config() -> dict:
    for p in [_CONFIG_PATH, _CONFIG_PATH_ALT]:
        if p.exists():
            return yaml.safe_load(p.read_text()).get("vault", {})
    return {}

def _login() -> str:
    cfg = _load_config()
    addr = cfg.get("addr", os.environ.get("VAULT_ADDR", "https://vault.iio.space"))
    role_id = cfg.get("role_id", os.environ.get("VAULT_ROLE_ID", ""))
    sid_path = cfg.get("secret_id_path", "/run/iio/vault-secret-id")
    secret_id = os.environ.get("VAULT_SECRET_ID", "")
    if not secret_id and Path(sid_path).exists():
        secret_id = Path(sid_path).read_text().strip()
    if not role_id or not secret_id:
        raise RuntimeError("Vault AppRole credentials not configured")
    body = json.dumps({"role_id": role_id, "secret_id": secret_id}).encode()
    req = urllib.request.Request(f"{addr}/v1/auth/approle/login", data=body,
        headers={"Content-Type": "application/json"})
    r = json.load(urllib.request.urlopen(req, timeout=10))
    return r["auth"]["client_token"]

def get_secret(path: str, field: Optional[str] = None) -> dict:
    """Read secret from Vault kv-v2. path = iio/<name>. Returns data dict."""
    global _TOKEN_CACHE
    cfg = _load_config()
    addr = cfg.get("addr", os.environ.get("VAULT_ADDR", "https://vault.iio.space"))
    mount = cfg.get("kv_mount", "iio")
    if not _TOKEN_CACHE:
        _TOKEN_CACHE = os.environ.get("VAULT_TOKEN") or _login()
    url = f"{addr}/v1/{mount}/data/{path}"
    req = urllib.request.Request(url, headers={"X-Vault-Token": _TOKEN_CACHE})
    try:
        d = json.load(urllib.request.urlopen(req, timeout=10))
        data = d["data"]["data"]
        return data.get(field) if field else data
    except urllib.error.HTTPError as e:
        if e.code == 403:
            _TOKEN_CACHE = None  # refresh token
            raise PermissionError(f"Vault: no access to {path}")
        raise

def env_from_vault(path: str) -> dict:
    """Load a secret as os.environ additions. Returns loaded keys count."""
    data = get_secret(path)
    for k, v in data.items():
        os.environ.setdefault(k, str(v))
    return data
