"""Ears adapter — emotion read over the tide-table CLAP engines.

Seam: sample_semantics.embed_audios([path]) → a normalized CLAP audio embed,
then emotion_ontology.score(embed) → {valence, arousal, top, dist, confidence}.
Both live in armada/tide-table/ and import each other flat, so we put that dir on
sys.path lazily. CLAP/torch only load inside embed_audios (first call ≈ cold).
"""
import sys
from functools import lru_cache
from pathlib import Path

import config


def _ensure_path():
    p = str(config.TIDE_TABLE)
    if p not in sys.path:
        sys.path.insert(0, p)


def available() -> tuple[bool, str]:
    """(ok, hint) — cheap check that the engines are importable. Does NOT load CLAP."""
    _ensure_path()
    try:
        import emotion_ontology  # noqa: F401  (imports numpy + sample_semantics, light)
        return True, "emotion engine importable (CLAP loads on first call)"
    except Exception as e:  # pragma: no cover - environment-dependent
        return False, f"emotion engine unavailable: {e!r}"


@lru_cache(maxsize=1)
def _engines():
    """Import + memoize the engine modules (CLAP still lazy within them)."""
    _ensure_path()
    import emotion_ontology as EMO
    import sample_semantics as SEM
    return EMO, SEM


def emotion_of(audio_path: str | Path) -> dict:
    """One audio file → emotion read. Heavy: triggers CLAP on first call.

    Returns the emotion_ontology.score() dict (valence/arousal in [-1,1], top
    emotions, confidence) — exactly the numbers the #90 hexa bundle uses, so the
    API and the batch bundle agree by construction.
    """
    EMO, SEM = _engines()
    ae, _ = SEM.embed_audios([Path(audio_path)])
    if not len(ae):
        raise ValueError("could not decode audio for embedding")
    return EMO.score(ae[0])
