"""Minimal LLM client for the Sentiment Agent (Anthropic or OpenAI).

Uses plain httpx — no SDK dependency. Activated only when an API key is set;
callers must handle `None` (fall back to mock scoring). Responses are forced
to strict JSON.
"""
import json
import logging
import os

import httpx

logger = logging.getLogger(__name__)

TIMEOUT = 20.0

_SYSTEM = (
    "You are a market sentiment analyst inside a paper-trading research tool. "
    "Given a stock symbol and recent price context, estimate current market "
    "sentiment. Respond with ONLY a JSON object: "
    '{"sentiment": <float -1.0..1.0>, "label": "positive|neutral|negative", '
    '"reason": "<one short sentence>"}'
)


def llm_available() -> bool:
    return bool(os.getenv("ANTHROPIC_API_KEY") or os.getenv("OPENAI_API_KEY"))


def score_sentiment(symbol: str, context: str) -> dict | None:
    """Returns {"sentiment": float, "label": str, "reason": str} or None."""
    prompt = f"Symbol: {symbol}\nRecent price context: {context}"
    try:
        if os.getenv("ANTHROPIC_API_KEY"):
            return _anthropic(prompt)
        if os.getenv("OPENAI_API_KEY"):
            return _openai(prompt)
    except Exception as exc:  # noqa: BLE001 — any failure → mock fallback
        logger.warning("LLM sentiment failed for %s: %s", symbol, exc)
    return None


def _parse(text: str) -> dict | None:
    try:
        start, end = text.index("{"), text.rindex("}") + 1
        data = json.loads(text[start:end])
        s = max(-1.0, min(1.0, float(data["sentiment"])))
        return {
            "sentiment": round(s, 3),
            "label": str(data.get("label", "neutral")),
            "reason": str(data.get("reason", ""))[:300],
        }
    except (ValueError, KeyError, TypeError):
        return None


def _anthropic(prompt: str) -> dict | None:
    r = httpx.post(
        "https://api.anthropic.com/v1/messages",
        headers={
            "x-api-key": os.environ["ANTHROPIC_API_KEY"],
            "anthropic-version": "2023-06-01",
            "content-type": "application/json",
        },
        json={
            "model": os.getenv("ANTHROPIC_MODEL", "claude-haiku-4-5-20251001"),
            "max_tokens": 200,
            "system": _SYSTEM,
            "messages": [{"role": "user", "content": prompt}],
        },
        timeout=TIMEOUT,
    )
    r.raise_for_status()
    return _parse(r.json()["content"][0]["text"])


def _openai(prompt: str) -> dict | None:
    r = httpx.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
        json={
            "model": os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
            "max_tokens": 200,
            "messages": [
                {"role": "system", "content": _SYSTEM},
                {"role": "user", "content": prompt},
            ],
        },
        timeout=TIMEOUT,
    )
    r.raise_for_status()
    return _parse(r.json()["choices"][0]["message"]["content"])
