"""Sichere Env-Var-Loader für Bundle-Skills mit Sekret- und Pfad-Bedarf."""
from __future__ import annotations
import os
from pathlib import Path


class EnvVarMissingError(RuntimeError):
    """Erhoben, wenn eine Pflicht-Env-Var fehlt oder leer ist."""


class PathEnvVarUnsafeError(RuntimeError):
    """Erhoben, wenn ein Pfad aus einer Env-Var außerhalb erlaubter Wurzeln liegt."""


def require_env_var(name: str, skill_name: str, *, setup_hint: str | None = None) -> str:
    """Liefert den Wert der Env-Var oder erhebt EnvVarMissingError mit Setup-Anleitung.

    Pflicht-Eigenschaften:
    - Niemals Env-Var-Wert in Exceptions, Logs oder Returns.
    - Validierung nur per Länge, nicht per Wert.
    - Klare Fehlermeldung mit Setup-Anleitung, wenn die Var fehlt.
    - Skill-Name im Fehler, damit User weiß, welcher Skill die Var braucht.
    """
    value = os.environ.get(name, "")
    if not value:
        msg = f"Env-Var {name} fehlt. Skill {skill_name} braucht sie."
        if setup_hint:
            msg += f"\n\nSetup: {setup_hint}"
        raise EnvVarMissingError(msg)
    return value


def get_env_var(name: str, default: str | None = None) -> str | None:
    """Liefert die Env-Var oder Default. Leer-String zählt als nicht gesetzt."""
    value = os.environ.get(name, "")
    return value if value else default


def require_path_env_var(
    name: str,
    skill_name: str,
    *,
    allowed_roots: list[Path] | None = None,
    must_exist: bool = True,
) -> Path:
    """Liefert einen `Path` aus einer Env-Var, mit `resolve()` und Containment-Check.

    Wave-1-Lesson 10 (aus Post-Tag-Tiefen-Audit v0.10.0 Brillen F + H + I):
    `BUNDLE_ROOT` und vergleichbare Pfad-Env-Vars ohne `resolve()`+Containment
    sind Path-Traversal-Klasse — der Konsument lädt Module aus beliebigen
    Verzeichnissen, ein böswillig gesetzter Wert führt zu Arbitrary-Code-Load.

    Pflicht-Eigenschaften:
    - `Path.resolve()` normalisiert den Wert (entfernt `..`, Symlinks, `.`).
    - Wenn `allowed_roots` gesetzt: der resolvete Pfad muss unterhalb einer
      der erlaubten Wurzeln liegen, sonst `PathEnvVarUnsafeError`.
    - Wenn `must_exist`: Pfad muss existieren, sonst `FileNotFoundError`.
    - Niemals Env-Var-Wert in Exception leaken (siehe `require_env_var`).
    - Skill-Name im Fehler für klare Zuordnung.

    Args:
        name: Env-Var-Name, z.B. "BUNDLE_ROOT".
        skill_name: Skill-Name für die Fehlermeldung.
        allowed_roots: Optionale Liste erlaubter Wurzel-Pfade. Wenn None,
            kein Containment-Check (für Tests oder explizit unbeschränkte
            Use Cases). Wenn gesetzt: jeder Wurzel-Pfad wird selbst
            `resolve()`-normalisiert und der Env-Var-Pfad muss unter mindestens
            einem davon liegen.
        must_exist: Wenn True (Default), wird `FileNotFoundError` erhoben,
            wenn der Pfad nicht existiert.

    Returns:
        Den resolveten `Path`.

    Raises:
        EnvVarMissingError: wenn die Env-Var nicht gesetzt oder leer ist.
        PathEnvVarUnsafeError: wenn `allowed_roots` gesetzt ist und der
            Pfad außerhalb liegt. Message nennt den resolveten Pfad und die
            erlaubten Wurzeln, NICHT den Env-Var-Rohwert.
        FileNotFoundError: wenn `must_exist=True` und der Pfad nicht existiert.
    """
    raw = require_env_var(name, skill_name)
    resolved = Path(raw).resolve()
    if allowed_roots is not None:
        normalized_roots = [Path(r).resolve() for r in allowed_roots]
        if not any(
            resolved == root or root in resolved.parents
            for root in normalized_roots
        ):
            roots_str = ", ".join(str(r) for r in normalized_roots)
            raise PathEnvVarUnsafeError(
                f"Pfad aus Env-Var {name} ({resolved}) liegt außerhalb der "
                f"erlaubten Wurzeln [{roots_str}]. Skill {skill_name} blockt "
                "den Aufruf zum Schutz gegen Arbitrary-Code-Load."
            )
    if must_exist and not resolved.exists():
        raise FileNotFoundError(
            f"Pfad aus Env-Var {name} existiert nicht: {resolved}. "
            f"Skill {skill_name} braucht einen existierenden Pfad."
        )
    return resolved
