"""API-Key-Lookup als Single Source für Bundle-Skills.

Zentralisiert die Drei-Stufen-Cascade (Env-Var → Projekt-.env → Skill-assets/.env)
und den robusten .env-Parser. Schließt die Drei-Brillen-Konvergenz aus v0.16.3
(E-1-01 + MH-H4-05 + Pre-Tag-M-K-04): UTF-8-BOM via encoding='utf-8-sig',
Präfix-Match mit '=' Suffix gegen VAR_NAME_SUFFIX=-Verwechslung, symmetrischer
Quote-Strip statt sequenzieller `.strip('"').strip("'")`.

Direkte Konsumenten seit v0.17.6 (via `from _lib.api_keys import ...`):
- nano-banana-image/scripts/find_api_key.py (Wrapper mit skill.config.yaml-Override)
- openai-image-gen/scripts/generate.py (Wrapper mit sk-...-Placeholder-Filter,
  CLI-Pfad UND Library-Pfad seit v0.17.5)

Indirekte Konsumenten (über Wrapper-Skript, kein direkter `_lib.api_keys`-Import):
- nano-banana-image/scripts/generate_image.py importiert `find_api_key.py` als
  Modul (`from find_api_key import ...`) und konsumiert `_lib.api_keys`
  transitiv über den Wrapper, der den skill.config.yaml-Override liefert.

Import-Pattern: Beide `_lib/`-Wurzeln sind seit v0.17.6 Namespace Packages
(PEP 420). Konsumenten müssen `.skills/` in sys.path haben (typisch via
`sys.path.insert(0, Path(__file__).resolve().parents[2])` im Skript-Header).
Bis v0.17.5 lief der Import via `importlib.util.spec_from_file_location` als
robuster Bootstrap (~17 LOC je Konsument) — diese Boilerplate ist eliminiert.

Transitive Konsumenten (kein direkter Import, aber Bundle-Sub-Skill-Routing):
- authentic-portrait konsumiert nano-banana-image und openai-image-gen via
  Claude-Code-Skill-Routing (kein Python-Import, kein Subprocess) → erbt
  Wave-I-Härtungen automatisch.

Wave-J-Konsumenten (seit v0.18.0):
- elevenlabs-api (direkt via get_elevenlabs_key) — Co-Migration mit video-creator
- video-creator (transitiv via authentic-portrait/nano-banana-image für
  Anker-Frames + transitiv via elevenlabs-api für Scribe/TTS)

Wave-II-Backlog:
- getmyinvoices/scripts/gmi_client.py:55 nutzt direkten os.environ.get
  (1-stufig, kein .env-Fallback). Bewusste Ausnahme, dokumentiert als
  Foundation-Layer-Inkonsistenz für künftige Migrations-Entscheidung.

Abgrenzung zu _lib/env_loader.py:
- env_loader = generic-purpose Env-Var-Pflicht-Lader mit Hard-Fail-Exception
  (require_env_var) und Pfad-Sicherheit (require_path_env_var).
- api_keys = API-Key-spezifische Soft-Cascade mit None-Tuple-Return,
  Drei-Stufen-Fallback (.env-Parser) und Bug-Fixes aus v0.16.3-Konvergenz.
Disjunkte Verantwortung, leichte Code-Überlappung in Stufe 1 (os.environ.get)
ist gewollt — wer Hard-Fail will, nutzt env_loader.

Sicherheits-Regel: Diese Modul-Funktionen geben NIEMALS den Key-Wert in Logs,
stderr oder stdout aus. Nur die Quelle (source-String) verlässt das Modul.
Seit v0.17.5: Source-String enthält keinen absoluten Pfad mehr (User-Home-Leak-
Vermeidung in CI/Shared-Terminal — siehe Brille R HIGH).
"""

from __future__ import annotations

import os
from pathlib import Path

__all__ = [
    "read_key_from_dotenv",
    "get_api_key",
    "get_gemini_key",
    "get_openai_key",
    "get_elevenlabs_key",
]


def read_key_from_dotenv(path: Path, var_name: str) -> str | None:
    """Robuster .env-Parser für eine einzelne Variable.

    - UTF-8-BOM via encoding='utf-8-sig' (CHANGELOG [0.16.3] E-1-01 Bug-Fix #1)
    - Comment-Skip: Zeilen, die nach .strip() mit # beginnen
    - Präfix-Match mit '=' Suffix: startswith(var_name + '=')
      (CHANGELOG [0.16.3] MH-H4-05 Bug-Fix #2 — verhindert VAR_NAME_SUFFIX=-Match)
    - Symmetrischer Quote-Strip: nur wenn value[0] == value[-1] und in ('"', "'")
      (CHANGELOG [0.16.3] E-1-01 Bug-Fix #3 — KEY="value' bleibt unverändert)
    - Leere Werte und nur-Quoted-leere Werte werden übersprungen (fallthrough)

    Returns: Wert (str) oder None bei OSError / nicht gefunden / leerer Wert.
    """
    try:
        text = path.read_text(encoding="utf-8-sig")
    except OSError:
        return None
    prefix = var_name + "="
    for line in text.splitlines():
        stripped = line.strip()
        if stripped.startswith("#"):
            continue
        if stripped.startswith(prefix):
            value = stripped[len(prefix):]
            if len(value) >= 2 and value[0] in ('"', "'") and value[-1] == value[0]:
                value = value[1:-1]
            value = value.strip()
            if value:
                return value
    return None


def get_api_key(
    env_var_name: str,
    skill_dir: Path | None = None,
) -> tuple[str | None, str | None]:
    """Unified Drei-Stufen-Fallback für API-Keys.

    Reihenfolge:
      Stufe 1: os.environ.get(env_var_name)
               → ('<key>', f'Umgebungsvariable {env_var_name}')
      Stufe 2: Path.cwd() / '.env' parser
               → ('<key>', f'Projekt-.env ({absolut_pfad})')
      Stufe 3: skill_dir / 'assets' / '.env' parser (nur wenn skill_dir gesetzt)
               → ('<key>', 'assets/.env')

    Returns: (key, source) oder (None, None) wenn keine Stufe trifft.

    Args:
        env_var_name: Name der Umgebungsvariablen, z.B. 'GEMINI_API_KEY'.
                      Konsumenten können den Namen via Skill-Config überschreiben
                      und müssen den aufgelösten Namen hier durchreichen.
        skill_dir: Skill-Wurzelverzeichnis. Wenn None, entfällt Stufe 3.
                   Aufrufer ohne eigenen Skill-Kontext (z.B. ad-hoc-Helpers)
                   können None übergeben und bekommen nur Stufe 1+2.
    """
    key = os.environ.get(env_var_name)
    if key:
        return key, f"Umgebungsvariable {env_var_name}"

    project_env = Path.cwd() / ".env"
    if project_env.exists():
        key = read_key_from_dotenv(project_env, env_var_name)
        if key:
            # Source-String enthält bewusst keinen absoluten Pfad mehr
            # (v0.17.5 Brille R HIGH): User-Home-Pfad würde sonst über
            # stderr-Output (z.B. openai-image-gen/generate.py) in CI-Logs
            # oder Shared-Terminal-Sessions leaken.
            return key, "Projekt-.env (CWD)"

    if skill_dir is not None:
        skill_env = skill_dir / "assets" / ".env"
        if skill_env.exists():
            key = read_key_from_dotenv(skill_env, env_var_name)
            if key:
                return key, "assets/.env"

    return None, None


def get_gemini_key(skill_dir: Path | None = None) -> tuple[str | None, str | None]:
    """Liest GEMINI_API_KEY. Konsumiert von nano-banana-image (Default-Env-Var).

    Hinweis: nano-banana-image unterstützt skill.config.yaml-Override des
    Env-Var-Namens. Aufrufer mit Override müssen direkt get_api_key(env_var,
    skill_dir) aufrufen, nicht diesen Wrapper.
    """
    return get_api_key("GEMINI_API_KEY", skill_dir)


def get_openai_key(skill_dir: Path | None = None) -> tuple[str | None, str | None]:
    """Liest OPENAI_API_KEY. Konsumiert von openai-image-gen.

    Hinweis: openai-image-gen behält im Wrapper einen sk-...-Placeholder-Filter,
    der nicht hier gehört (OpenAI-spezifisch).
    """
    return get_api_key("OPENAI_API_KEY", skill_dir)


def get_elevenlabs_key(skill_dir: Path | None = None) -> tuple[str | None, str | None]:
    """Liest ELEVENLABS_API_KEY. Konsumiert von elevenlabs-api (seit v0.18.0).

    Direkter Konsument: elevenlabs-api/scripts/find_api_key.py (Wrapper).
    Transitive Konsumenten: elevenlabs-api/scripts/generate_tts.py +
    generate_scribe.py (via find_api_key-Wrapper). generate_music.py ist Stub
    (NotImplementedError), kein Konsument bis v0.19.0-Vollausbau.
    Pin-Test test_specialization_wrappers_use_correct_env_var_names schützt
    Env-Var-Name und Signatur.
    """
    return get_api_key("ELEVENLABS_API_KEY", skill_dir)
