"""HalluFieldScorer — logit-based hallucination risk from token log-probabilities.

Requires access to token log-probs (logprobs=True in OpenAI API).
Experimental — logits access required.
Purpose:    Computes hallucination risk from mean token probability and fraction of low-confidence tokens. Requires logprobs=True access from the generating API.
Exports:    HalluFieldScorer, HalluFieldResult
Added:      v0.1.0
Status:     Experimental
"""
from __future__ import annotations
import math
from dataclasses import dataclass


@dataclass
class HalluFieldResult:
    risk: float
    mean_prob: float
    frac_low_conf: float
    n_tokens: int


class HalluFieldScorer:
    """Logit-based hallucination risk scorer.

    Args:
        low_conf_threshold: token prob below this is 'low confidence'.
        experimental: always True; logits access required.
    """

    def __init__(
        self,
        low_conf_threshold: float = 0.5,
        experimental: bool = True,
    ) -> None:
        self.low_conf_threshold = low_conf_threshold
        self.experimental = True  # always True

    def score_from_logprobs(self, token_logprobs: list[float]) -> HalluFieldResult:
        """Compute risk from token log-probabilities (<= 0).

        risk = 0.5 * (1 - mean_prob) + 0.5 * frac_low_conf
        """
        if not token_logprobs:
            return HalluFieldResult(risk=0.5, mean_prob=0.5,
                                    frac_low_conf=0.0, n_tokens=0)

        probs = [math.exp(lp) for lp in token_logprobs]
        mean_prob = sum(probs) / len(probs)
        n_low = sum(1 for p in probs if p < self.low_conf_threshold)
        frac_low = n_low / len(probs)
        risk = max(0.0, min(1.0, 0.5 * (1.0 - mean_prob) + 0.5 * frac_low))

        return HalluFieldResult(
            risk=risk,
            mean_prob=mean_prob,
            frac_low_conf=frac_low,
            n_tokens=len(probs),
        )

    def score_from_openai_response(self, response) -> HalluFieldResult:
        """Extract logprobs from an OpenAI ChatCompletion response (logprobs=True)."""
        try:
            content_logprobs = response.choices[0].logprobs.content
            token_logprobs = [tok.logprob for tok in content_logprobs]
            return self.score_from_logprobs(token_logprobs)
        except (AttributeError, IndexError, TypeError):
            return HalluFieldResult(risk=0.5, mean_prob=0.5,
                                    frac_low_conf=0.0, n_tokens=0)
